Merge pull request #72440 from V-Sekai/gltf_embed_as_uncompressed

gltf: Add GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_UNCOMPRESSED
This commit is contained in:
Rémi Verschelde 2023-02-01 12:10:13 +01:00 committed by GitHub
commit c40020513a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 31 deletions

View File

@ -2289,7 +2289,14 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM
ERR_FAIL_COND_V(!importer.is_valid(), nullptr); ERR_FAIL_COND_V(!importer.is_valid(), nullptr);
Error err = OK; Error err = OK;
Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_options, nullptr, &err); HashMap<StringName, Variant> options_dupe = p_options;
// By default, the GLTF importer will extract embedded images into files on disk
// However, we do not want the advanced settings dialog to be able to write files on disk.
// To avoid this and also avoid compressing to basis every time, we are using the uncompressed option.
options_dupe["gltf/embedded_image_handling"] = 3; // Embed as Uncompressed defined in GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_UNCOMPRESSED
Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, options_dupe, nullptr, &err);
if (!scene || err != OK) { if (!scene || err != OK) {
return nullptr; return nullptr;
} }

View File

@ -264,10 +264,16 @@
</members> </members>
<constants> <constants>
<constant name="HANDLE_BINARY_DISCARD_TEXTURES" value="0"> <constant name="HANDLE_BINARY_DISCARD_TEXTURES" value="0">
Discards all embedded textures and uses untextured materials.
</constant> </constant>
<constant name="HANDLE_BINARY_EXTRACT_TEXTURES" value="1"> <constant name="HANDLE_BINARY_EXTRACT_TEXTURES" value="1">
Extracts embedded textures to be reimported and compressed. Editor only. Acts as uncompressed at runtime.
</constant> </constant>
<constant name="HANDLE_BINARY_EMBED_AS_BASISU" value="2"> <constant name="HANDLE_BINARY_EMBED_AS_BASISU" value="2">
Embeds textures VRAM compressed with Basis Universal into the generated scene.
</constant>
<constant name="HANDLE_BINARY_EMBED_AS_UNCOMPRESSED" value="3">
Embeds textures compressed losslessly into the generated scene, matching old behavior.
</constant> </constant>
</constants> </constants>
</class> </class>

View File

@ -51,8 +51,8 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
doc.instantiate(); doc.instantiate();
Ref<GLTFState> state; Ref<GLTFState> state;
state.instantiate(); state.instantiate();
if (p_options.has("meshes/handle_gltf_embedded_images")) { if (p_options.has("gltf/embedded_image_handling")) {
int32_t enum_option = p_options["meshes/handle_gltf_embedded_images"]; int32_t enum_option = p_options["gltf/embedded_image_handling"];
state->set_handle_binary_image(enum_option); state->set_handle_binary_image(enum_option);
} }
Error err = doc->append_from_file(p_path, state, p_flags); Error err = doc->append_from_file(p_path, state, p_flags);
@ -87,7 +87,7 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path, void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) { List<ResourceImporter::ImportOption> *r_options) {
r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "meshes/handle_gltf_embedded_images", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES)); r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES));
} }
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED

View File

@ -32,6 +32,7 @@
#include "extensions/gltf_spec_gloss.h" #include "extensions/gltf_spec_gloss.h"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h" #include "core/crypto/crypto_core.h"
#include "core/io/config_file.h" #include "core/io/config_file.h"
#include "core/io/dir_access.h" #include "core/io/dir_access.h"
@ -3220,8 +3221,8 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) {
p_state->images.push_back(Ref<Texture2D>()); p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>()); p_state->source_images.push_back(Ref<Image>());
continue; #ifdef TOOLS_ENABLED
} else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { } else if (Engine::get_singleton()->is_editor_hint() && GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) {
if (p_state->base_path.is_empty()) { if (p_state->base_path.is_empty()) {
p_state->images.push_back(Ref<Texture2D>()); p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>()); p_state->source_images.push_back(Ref<Image>());
@ -3230,26 +3231,56 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
p_state->images.push_back(Ref<Texture2D>()); p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>()); p_state->source_images.push_back(Ref<Image>());
} else { } else {
String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png";
Ref<ConfigFile> config;
config.instantiate();
if (FileAccess::exists(file_path + ".import")) {
config->load(file_path + ".import");
}
config->set_value("remap", "importer", "texture");
config->set_value("remap", "type", "Texture2D");
if (!config->has_section_key("params", "compress/mode")) {
config->set_value("remap", "compress/mode", 2); //user may want another compression, so leave it bes
}
if (!config->has_section_key("params", "mipmaps/generate")) {
config->set_value("params", "mipmaps/generate", true);
}
Error err = OK; Error err = OK;
err = config->save(file_path + ".import"); bool must_import = false;
ERR_FAIL_COND_V(err != OK, err); String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png";
img->save_png(file_path); if (!FileAccess::exists(file_path + ".import")) {
ERR_FAIL_COND_V(err != OK, err); Ref<ConfigFile> config;
ResourceLoader::import(file_path); config.instantiate();
config->set_value("remap", "importer", "texture");
config->set_value("remap", "type", "Texture2D");
// Currently, it will likely use project defaults of Detect 3D, so textures will be reimported again.
if (!config->has_section_key("params", "mipmaps/generate")) {
config->set_value("params", "mipmaps/generate", true);
}
if (ProjectSettings::get_singleton()->has_setting("importer_defaults/texture")) {
//use defaults if exist
Dictionary importer_defaults = GLOBAL_GET("importer_defaults/texture");
List<Variant> importer_def_keys;
importer_defaults.get_key_list(&importer_def_keys);
for (const Variant &key : importer_def_keys) {
if (!config->has_section_key("params", (String)key)) {
config->set_value("params", (String)key, importer_defaults[key]);
}
}
}
err = config->save(file_path + ".import");
ERR_FAIL_COND_V(err != OK, err);
must_import = true;
}
Vector<uint8_t> png_buffer = img->save_png_to_buffer();
if (ResourceLoader::exists(file_path)) {
Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::READ, &err);
if (err == OK && file.is_valid()) {
Vector<uint8_t> orig_png_buffer = file->get_buffer(file->get_length());
if (png_buffer != orig_png_buffer) {
must_import = true;
}
}
} else {
must_import = true;
}
if (must_import) {
Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(file.is_null(), FAILED);
file->store_buffer(png_buffer);
file->flush();
file.unref();
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
ResourceLoader::import(file_path);
}
Ref<Texture2D> saved_image = ResourceLoader::load(file_path, "Texture2D"); Ref<Texture2D> saved_image = ResourceLoader::load(file_path, "Texture2D");
if (saved_image.is_valid()) { if (saved_image.is_valid()) {
p_state->images.push_back(saved_image); p_state->images.push_back(saved_image);
@ -3261,7 +3292,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
p_state->source_images.push_back(Ref<Image>()); p_state->source_images.push_back(Ref<Image>());
} }
} }
continue; #endif
} else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) {
Ref<PortableCompressedTexture2D> tex; Ref<PortableCompressedTexture2D> tex;
tex.instantiate(); tex.instantiate();
@ -3271,11 +3302,15 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL); tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL);
p_state->images.push_back(tex); p_state->images.push_back(tex);
p_state->source_images.push_back(img); p_state->source_images.push_back(img);
continue; } else {
// This handles two cases: if editor hint and HANDLE_BINARY_EXTRACT_TEXTURES; or if HANDLE_BINARY_EMBED_AS_UNCOMPRESSED
Ref<ImageTexture> tex;
tex.instantiate();
tex->set_name(img->get_name());
tex->set_image(img);
p_state->images.push_back(tex);
p_state->source_images.push_back(img);
} }
p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>());
} }
print_verbose("glTF: Total images: " + itos(p_state->images.size())); print_verbose("glTF: Total images: " + itos(p_state->images.size()));

View File

@ -120,11 +120,12 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex, ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex,
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>> ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum
BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES); BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES);
BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES); BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES);
BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_BASISU); BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_BASISU);
BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_UNCOMPRESSED);
} }
void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) { void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) {

View File

@ -108,6 +108,7 @@ public:
HANDLE_BINARY_DISCARD_TEXTURES = 0, HANDLE_BINARY_DISCARD_TEXTURES = 0,
HANDLE_BINARY_EXTRACT_TEXTURES, HANDLE_BINARY_EXTRACT_TEXTURES,
HANDLE_BINARY_EMBED_AS_BASISU, HANDLE_BINARY_EMBED_AS_BASISU,
HANDLE_BINARY_EMBED_AS_UNCOMPRESSED, // if this value changes from 3, ResourceImporterScene::pre_import must be changed as well.
}; };
int32_t get_handle_binary_image() { int32_t get_handle_binary_image() {
return handle_binary_image; return handle_binary_image;