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();
]