diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index e2fc5f0a9120..f3cd2a003865 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -544,6 +544,15 @@ If [param first_priority] is [code]true[/code], the new import plugin is inserted first in the list and takes precedence over pre-existing plugins. + + + + + + Add a [EditorTexturePostImportPlugin]. These plugins allow customizing the import process of [CompressedTexture2D]. + If [param first_priority] is [code]true[/code], the new import plugin is inserted first in the list and takes precedence over pre-existing plugins. + + @@ -732,6 +741,13 @@ Remove the [EditorScenePostImportPlugin], added with [method add_scene_post_import_plugin]. + + + + + Remove the [EditorTexturePostImportPlugin], added with [method add_texture_post_import_plugin]. + + diff --git a/doc/classes/EditorTexturePostImportPlugin.xml b/doc/classes/EditorTexturePostImportPlugin.xml new file mode 100644 index 000000000000..2fa410149a24 --- /dev/null +++ b/doc/classes/EditorTexturePostImportPlugin.xml @@ -0,0 +1,94 @@ + + + + Plugin to control and modify the process of importing a [CompressedTexture2D]. + + + This plugin type exists to extend [ResourceImporterTexture] and modify the process of importing [CompressedTexture2D], allowing to load custom file format, add import options, and change the image when processing. + + + + + + + + + + Override to add import options. These will appear in the Texture2D import dock on the editor. Add options via [method add_import_option] and [method add_import_option_advanced]. + + + + + + + + Override to change the visibility of import option. Should return [code]true[/code] to show the given option, [code]false[/code] to hide the given option, or [code]null[/code] to ignore. + + + + + + Gets the file extensions that will be added to the recognized extensions list of texture importer. + + + + + + + Override to add import options. These will appear in the Texture2D import dock on the editor. Add options via [method add_import_option] and [method add_import_option_advanced]. + + + + + + + Post process the image. This function is called after the image has been processed, such as fix alpha border, apply size limit. See also [ResourceImporterTexture]. + + + + + + + Pre Process the image. This function is called right after the image loader loaded the image and no changes have been made. + + + + + + + + Add a specific import option (name and default value only). This function can only be called from [method _get_import_options]. + + + + + + + + + + + + Add a specific import option. This function can only be called from [method _get_import_options]. + + + + + + + Query the value of an option. This function can only be called from [method _get_option_visibility], [method _load_image], [method _pre_process] and [method _post_process]. + + + + + + The 2D/3D (Auto-Detect) preset of texture importer. Can be accessed in [method _get_import_options]. + + + The 2D preset of texture importer. Can be accessed in [method _get_import_options]. + + + The 3D preset of texture importer. Can be accessed in [method _get_import_options]. + + + diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4855d86106dd..b2ac465e2f32 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -8381,6 +8381,7 @@ EditorNode::~EditorNode() { EditorInspector::cleanup_plugins(); EditorTranslationParser::get_singleton()->clean_parsers(); ResourceImporterScene::clean_up_importer_plugins(); + ResourceImporterTexture::clean_up_importer_plugins(); EditorContextMenuPluginManager::cleanup(); remove_print_handler(&print_handler); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 2464d5aee9bf..f2ff41f4bff1 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -41,6 +41,90 @@ #include "editor/themes/editor_theme_manager.h" #include "scene/resources/compressed_texture.h" +Variant EditorTexturePostImportPlugin::get_option_value(const StringName &p_name) const { + ERR_FAIL_COND_V_MSG(current_options == nullptr, Variant(), "get_option_value() called from a function where option values are not available."); + ERR_FAIL_COND_V_MSG(!current_options->has(p_name), Variant(), "get_option_value() called with unexisting option argument: " + String(p_name)); + return (*current_options)[p_name]; +} + +void EditorTexturePostImportPlugin::add_import_option(const String &p_name, const Variant &p_default_value) { + ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options()."); + add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value); +} + +void EditorTexturePostImportPlugin::add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) { + ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options()."); + current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value)); +} + +void EditorTexturePostImportPlugin::get_recognized_extensions(List *p_extensions) const { + Vector extensions; + GDVIRTUAL_CALL(_get_recognized_extensions, extensions); + for (int i = 0; i < extensions.size(); i++) { + p_extensions->push_back(extensions[i]); + } +} + +void EditorTexturePostImportPlugin::get_import_options(const String &p_path, List *r_options, Preset p_preset) const { + current_option_list = r_options; + GDVIRTUAL_CALL(_get_import_options, p_path, p_preset); + current_option_list = nullptr; +} + +Variant EditorTexturePostImportPlugin::get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const { + current_options = &p_options; + Variant ret; + GDVIRTUAL_CALL(_get_option_visibility, p_path, p_option, ret); + current_options = nullptr; + return ret; +} + +Ref EditorTexturePostImportPlugin::load_image(const String &p_source_file, bool *r_use_custom_loader, const HashMap &p_options) const { + Ref image; + current_options = &p_options; + bool use_custom_loader = GDVIRTUAL_CALL(_load_image, p_source_file, image); + current_options = nullptr; + if (r_use_custom_loader) { + *r_use_custom_loader = use_custom_loader; + } + return image; +} + +Ref EditorTexturePostImportPlugin::pre_process(Ref p_image, const HashMap &p_options) const { + Ref image = p_image; + current_options = &p_options; + GDVIRTUAL_CALL(_pre_process, p_image, image); + current_options = nullptr; + return image; +} + +Ref EditorTexturePostImportPlugin::post_process(Ref p_image, const HashMap &p_options) const { + Ref image = p_image; + current_options = &p_options; + GDVIRTUAL_CALL(_post_process, p_image, image); + current_options = nullptr; + return image; +} + +void EditorTexturePostImportPlugin::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_option_value", "name"), &EditorTexturePostImportPlugin::get_option_value); + ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorTexturePostImportPlugin::add_import_option); + ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorTexturePostImportPlugin::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT)); + + BIND_ENUM_CONSTANT(PRESET_DETECT); + BIND_ENUM_CONSTANT(PRESET_2D); + BIND_ENUM_CONSTANT(PRESET_3D); + + GDVIRTUAL_BIND(_get_recognized_extensions); + GDVIRTUAL_BIND(_get_import_options, "path", "preset_index"); + GDVIRTUAL_BIND(_get_option_visibility, "path", "option"); + GDVIRTUAL_BIND(_load_image, "source_file"); + GDVIRTUAL_BIND(_pre_process, "image"); + GDVIRTUAL_BIND(_post_process, "image"); +} + +/////////////////////////////////////////////////////// + void ResourceImporterTexture::_texture_reimport_roughness(const Ref &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) { ERR_FAIL_COND(p_tex.is_null()); @@ -170,6 +254,9 @@ String ResourceImporterTexture::get_visible_name() const { void ResourceImporterTexture::get_recognized_extensions(List *p_extensions) const { ImageLoader::get_recognized_extensions(p_extensions); + for (const Ref &plugin : texture_import_plugins) { + plugin->get_recognized_extensions(p_extensions); + } } String ResourceImporterTexture::get_save_extension() const { @@ -209,6 +296,13 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_path, const return p_options["mipmaps/generate"]; } + for (const Ref &plugin : texture_import_plugins) { + Variant ret = plugin->get_option_visibility(p_path, p_option, p_options); + if (ret.get_type() == Variant::BOOL && !ret) { + return false; + } + } + return true; } @@ -256,6 +350,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, Listpush_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/scale_with_editor_scale"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/convert_colors_with_editor_theme"), false)); } + + for (const Ref &plugin : texture_import_plugins) { + plugin->get_import_options(p_path, r_options); + } } void ResourceImporterTexture::save_to_ctex_format(Ref f, const Ref &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) { @@ -338,6 +436,23 @@ void ResourceImporterTexture::save_to_ctex_format(Ref f, const Ref p_plugin, bool p_first_priority) { + ERR_FAIL_COND(p_plugin.is_null()); + if (p_first_priority) { + texture_import_plugins.insert(0, p_plugin); + } else { + texture_import_plugins.push_back(p_plugin); + } +} + +void ResourceImporterTexture::remove_post_import_plugin(Ref p_importer) { + texture_import_plugins.erase(p_importer); +} + +void ResourceImporterTexture::clean_up_importer_plugins() { + texture_import_plugins.clear(); +} + void ResourceImporterTexture::_save_ctex(const Ref &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref &p_normal, Image::RoughnessChannel p_roughness_channel) { Ref f = FileAccess::open(p_to_path, FileAccess::WRITE); ERR_FAIL_COND(f.is_null()); @@ -559,10 +674,24 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String // Load the main image. Ref image; image.instantiate(); - Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale); - if (err != OK) { - return err; + Error err = OK; + bool use_custom_loader = false; + + for (const Ref &plugin : texture_import_plugins) { + image = plugin->load_image(p_source_file, &use_custom_loader, p_options); + } + ERR_FAIL_COND_V_MSG(image.is_null(), ERR_INVALID_DATA, "The returned image of _load_image() is null."); + + if (!use_custom_loader) { + err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale); + ERR_FAIL_COND_V(err != OK, err); } + + for (const Ref &plugin : texture_import_plugins) { + image = plugin->pre_process(image, p_options); + } + ERR_FAIL_COND_V_MSG(image.is_null(), ERR_INVALID_DATA, "The returned image of _pre_process() is null."); + images_imported.push_back(image); // Load the editor-only image. @@ -636,6 +765,11 @@ Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String } } + for (const Ref &plugin : texture_import_plugins) { + image = plugin->post_process(image, p_options); + } + ERR_FAIL_COND_V_MSG(image.is_null(), ERR_INVALID_DATA, "The returned image of _post_process() is null."); + bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0; bool detect_roughness = roughness == 0; bool detect_normal = normal == 0; @@ -837,6 +971,7 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path, co } ResourceImporterTexture *ResourceImporterTexture::singleton = nullptr; +Vector> ResourceImporterTexture::texture_import_plugins; ResourceImporterTexture::ResourceImporterTexture(bool p_singleton) { // This should only be set through the EditorNode. diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index d1f3ef23ee28..45b3001cee24 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -37,9 +37,50 @@ class CompressedTexture2D; +class EditorTexturePostImportPlugin : public RefCounted { + GDCLASS(EditorTexturePostImportPlugin, RefCounted); + +public: + enum Preset { + PRESET_DETECT, + PRESET_2D, + PRESET_3D, + }; + +private: + mutable const HashMap *current_options = nullptr; + mutable List *current_option_list = nullptr; + +protected: + GDVIRTUAL0RC(Vector, _get_recognized_extensions) + GDVIRTUAL2C(_get_import_options, String, int) + GDVIRTUAL2RC(Variant, _get_option_visibility, String, String) + GDVIRTUAL1RC(Ref, _load_image, String) + GDVIRTUAL1RC(Ref, _pre_process, Ref) + GDVIRTUAL1RC(Ref, _post_process, Ref) + + static void _bind_methods(); + +public: + Variant get_option_value(const StringName &p_name) const; + void add_import_option(const String &p_name, const Variant &p_default_value); + void add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT); + + virtual void get_recognized_extensions(List *p_extensions) const; + virtual void get_import_options(const String &p_path, List *r_options, Preset p_preset = PRESET_DETECT) const; + virtual Variant get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const; + + virtual Ref load_image(const String &p_source_file, bool *r_use_custom_loader, const HashMap &p_options) const; + virtual Ref pre_process(Ref p_image, const HashMap &p_options) const; + virtual Ref post_process(Ref p_image, const HashMap &p_options) const; +}; +VARIANT_ENUM_CAST(EditorTexturePostImportPlugin::Preset); + class ResourceImporterTexture : public ResourceImporter { GDCLASS(ResourceImporterTexture, ResourceImporter); + static Vector> texture_import_plugins; + public: enum CompressMode { COMPRESS_LOSSLESS, @@ -83,6 +124,11 @@ class ResourceImporterTexture : public ResourceImporter { static void save_to_ctex_format(Ref f, const Ref &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality); static ResourceImporterTexture *get_singleton() { return singleton; } + + static void add_post_import_plugin(Ref p_plugin, bool p_first_priority = false); + static void remove_post_import_plugin(Ref p_plugin); + static void clean_up_importer_plugins(); + virtual String get_importer_name() const override; virtual String get_visible_name() const override; virtual void get_recognized_extensions(List *p_extensions) const override; diff --git a/editor/plugins/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp index 677196368cd6..f48f44e98199 100644 --- a/editor/plugins/editor_plugin.cpp +++ b/editor/plugins/editor_plugin.cpp @@ -429,6 +429,14 @@ void EditorPlugin::remove_import_plugin(const Ref &p_importe } } +void EditorPlugin::add_texture_post_import_plugin(const Ref &p_importer, bool p_first_priority) { + ResourceImporterTexture::get_singleton()->add_post_import_plugin(p_importer, p_first_priority); +} + +void EditorPlugin::remove_texture_post_import_plugin(const Ref &p_importer) { + ResourceImporterTexture::get_singleton()->remove_post_import_plugin(p_importer); +} + void EditorPlugin::add_export_plugin(const Ref &p_exporter) { ERR_FAIL_COND(p_exporter.is_null()); EditorExport::get_singleton()->add_export_plugin(p_exporter); @@ -618,6 +626,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin); ClassDB::bind_method(D_METHOD("add_import_plugin", "importer", "first_priority"), &EditorPlugin::add_import_plugin, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin); + ClassDB::bind_method(D_METHOD("add_texture_post_import_plugin", "importer", "first_priority"), &EditorPlugin::add_texture_post_import_plugin, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("remove_texture_post_import_plugin", "importer"), &EditorPlugin::remove_texture_post_import_plugin); ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer", "first_priority"), &EditorPlugin::add_scene_format_importer_plugin, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::remove_scene_format_importer_plugin); ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin", "first_priority"), &EditorPlugin::add_scene_post_import_plugin, DEFVAL(false)); diff --git a/editor/plugins/editor_plugin.h b/editor/plugins/editor_plugin.h index b2cf92c52501..fc767a055fb9 100644 --- a/editor/plugins/editor_plugin.h +++ b/editor/plugins/editor_plugin.h @@ -31,6 +31,7 @@ #pragma once #include "core/io/config_file.h" +#include "editor/import/resource_importer_texture.h" #include "editor/plugins/editor_context_menu_plugin.h" #include "scene/3d/camera_3d.h" #include "scene/gui/control.h" @@ -222,6 +223,9 @@ class EditorPlugin : public Node { void add_import_plugin(const Ref &p_importer, bool p_first_priority = false); void remove_import_plugin(const Ref &p_importer); + void add_texture_post_import_plugin(const Ref &p_importer, bool p_first_priority = false); + void remove_texture_post_import_plugin(const Ref &p_importer); + void add_export_plugin(const Ref &p_exporter); void remove_export_plugin(const Ref &p_exporter); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 80551de80677..699ed5221a2c 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -205,6 +205,8 @@ void register_editor_types() { GDREGISTER_CLASS(ResourceImporterTextureAtlas); GDREGISTER_CLASS(ResourceImporterWAV); + GDREGISTER_CLASS(EditorTexturePostImportPlugin); + // This list is alphabetized, and plugins that depend on Node2D are in their own section below. EditorPlugins::add_by_type(); EditorPlugins::add_by_type();