From 39922d7167178bab9587aa00a7a108b93db6240e Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Mon, 18 Jul 2022 17:58:27 -0700 Subject: Handle gltf binary [ Ignore and Warn | Extract Textures (default) | Optimize Loading Embedded as Basisu ] Enable compressed mip maps from Basis Universal for faster compressions. Increase the quality of Basis to avoid corruption. To keep compatibility use the first mip of the previous internal Godot format. Because texture names may have invalid filename characters, adds String::validate_filename to sanitize filenames for import pipeline use. --- modules/gltf/doc_classes/GLTFState.xml | 11 +++ .../gltf/editor/editor_scene_importer_blend.cpp | 6 +- modules/gltf/editor/editor_scene_importer_gltf.cpp | 10 ++ modules/gltf/editor/editor_scene_importer_gltf.h | 2 + modules/gltf/gltf_document.cpp | 101 ++++++++++++++++++--- modules/gltf/gltf_document.h | 6 +- modules/gltf/gltf_state.cpp | 7 ++ modules/gltf/gltf_state.h | 24 +++++ 8 files changed, 151 insertions(+), 16 deletions(-) (limited to 'modules/gltf') diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 9a554a0d49..7fbfa5a2b8 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -55,6 +55,11 @@ + + + + + @@ -155,6 +160,12 @@ + + + + + + diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 2b8c057ee5..0075558dfc 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -32,6 +32,7 @@ #ifdef TOOLS_ENABLED +#include "../gltf_defines.h" #include "../gltf_document.h" #include "core/config/project_settings.h" @@ -43,7 +44,6 @@ #include "scene/gui/line_edit.h" #ifdef WINDOWS_ENABLED -// Code by Pedro Estebanez (https://github.com/godotengine/godot/pull/59766) #include #endif @@ -221,6 +221,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ gltf.instantiate(); Ref state; state.instantiate(); + String base_dir; if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { base_dir = sink.get_base_dir(); @@ -274,9 +275,6 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li ADD_OPTION_BOOL("blender/animation/limit_playback", true); ADD_OPTION_BOOL("blender/animation/always_sample", true); ADD_OPTION_BOOL("blender/animation/group_tracks", true); - -#undef ADD_OPTION_BOOL -#undef ADD_OPTION_ENUM } /////////////////////////// diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index 39956a6ff6..7c40afc8e7 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -32,6 +32,7 @@ #include "editor_scene_importer_gltf.h" +#include "../gltf_defines.h" #include "../gltf_document.h" uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { @@ -50,6 +51,10 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t doc.instantiate(); Ref state; state.instantiate(); + if (p_options.has("meshes/handle_gltf_embedded_images")) { + int32_t enum_option = p_options["meshes/handle_gltf_embedded_images"]; + state->set_handle_binary_image(enum_option); + } Error err = doc->append_from_file(p_path, state, p_flags); if (err != OK) { if (r_err) { @@ -68,4 +73,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t } } +void EditorSceneFormatImporterGLTF::get_import_options(const String &p_path, + List *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)); +} + #endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h index c2a4bf046d..ed57ec8cdb 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -47,6 +47,8 @@ public: virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap &p_options, List *r_missing_deps, Error *r_err = nullptr) override; + virtual void get_import_options(const String &p_path, + List *r_options) override; }; #endif // TOOLS_ENABLED diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b243ba933d..72c715f0a6 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -33,6 +33,7 @@ #include "extensions/gltf_spec_gloss.h" #include "core/crypto/crypto_core.h" +#include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/file_access_memory.h" @@ -3089,9 +3090,12 @@ Error GLTFDocument::_parse_images(Ref p_state, const String &p_base_p const uint8_t *data_ptr = nullptr; int data_size = 0; + String image_name; + if (d.has("uri")) { // Handles the first two bullet points from the spec (embedded data, or external file). String uri = d["uri"]; + image_name = uri; if (uri.begins_with("data:")) { // Embedded data using base64. // Validate data MIME types and throw a warning if it's one we don't know/support. @@ -3127,6 +3131,7 @@ Error GLTFDocument::_parse_images(Ref p_state, const String &p_base_p String extension = uri.get_extension().to_lower(); if (texture.is_valid()) { p_state->images.push_back(texture); + p_state->source_images.push_back(texture->get_image()); continue; } else if (mimetype == "image/png" || mimetype == "image/jpeg" || extension == "png" || extension == "jpg" || extension == "jpeg") { // Fallback to loading as byte array. @@ -3152,6 +3157,7 @@ Error GLTFDocument::_parse_images(Ref p_state, const String &p_base_p vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i)); const GLTFBufferViewIndex bvi = d["bufferView"]; + image_name = itos(bvi); ERR_FAIL_INDEX_V(bvi, p_state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); @@ -3196,9 +3202,70 @@ Error GLTFDocument::_parse_images(Ref p_state, const String &p_base_p if (img.is_null()) { ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype)); p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); + continue; + } + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { + p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); + continue; + } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { + String extracted_image_name = image_name.get_file().get_basename().validate_filename(); + img->set_name(extracted_image_name); + if (p_state->base_path.is_empty()) { + p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); + } else if (img->get_name().is_empty()) { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be named. Skipping it.", i)); + p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); + } else { + String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + img->get_name() + ".png"; + Ref 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; + err = config->save(file_path + ".import"); + ERR_FAIL_COND_V(err != OK, err); + img->save_png(file_path); + ERR_FAIL_COND_V(err != OK, err); + ResourceLoader::import(file_path); + Ref saved_image = ResourceLoader::load(file_path, "Texture2D"); + if (saved_image.is_valid()) { + p_state->images.push_back(saved_image); + p_state->source_images.push_back(img); + } else { + WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded with the name: %s. Skipping it.", i, img->get_name())); + // Placeholder to keep count. + p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); + } + } + continue; + } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref tex; + tex.instantiate(); + tex->set_name(img->get_name()); + tex->set_keep_compressed_buffer(true); + p_state->source_images.push_back(img); + tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL); + p_state->images.push_back(tex); + p_state->source_images.push_back(img); continue; } - p_state->images.push_back(ImageTexture::create_from_image(img)); + + p_state->images.push_back(Ref()); + p_state->source_images.push_back(Ref()); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); @@ -3268,12 +3335,24 @@ GLTFTextureIndex GLTFDocument::_set_texture(Ref p_state, Ref GLTFDocument::_get_texture(Ref p_state, const GLTFTextureIndex p_texture) { +Ref GLTFDocument::_get_texture(Ref p_state, const GLTFTextureIndex p_texture, int p_texture_types) { ERR_FAIL_INDEX_V(p_texture, p_state->textures.size(), Ref()); const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image(); - ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref()); - + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref portable_texture; + portable_texture.instantiate(); + portable_texture->set_keep_compressed_buffer(true); + Ref new_img = p_state->source_images[p_texture]->duplicate(); + ERR_FAIL_COND_V(new_img.is_null(), Ref()); + new_img->generate_mipmaps(); + if (p_texture_types) { + portable_texture->create_from_image(new_img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL, true); + } else { + portable_texture->create_from_image(new_img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL, false); + } + p_state->images.write[image] = portable_texture; + } return p_state->images[image]; } @@ -3684,7 +3763,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { material->set_texture_filter(diffuse_sampler->get_filter_mode()); material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, diffuse_sampler->get_wrap_mode()); } - Ref diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"]); + Ref diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"], TEXTURE_TYPE_GENERIC); if (diffuse_texture.is_valid()) { spec_gloss->diffuse_img = diffuse_texture->get_image(); material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_texture); @@ -3712,7 +3791,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { if (sgm.has("specularGlossinessTexture")) { const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"]; if (spec_gloss_texture.has("index")) { - const Ref orig_texture = _get_texture(p_state, spec_gloss_texture["index"]); + const Ref orig_texture = _get_texture(p_state, spec_gloss_texture["index"], TEXTURE_TYPE_GENERIC); if (orig_texture.is_valid()) { spec_gloss->spec_gloss_img = orig_texture->get_image(); } @@ -3735,7 +3814,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { Ref bct_sampler = _get_sampler_for_texture(p_state, bct["index"]); material->set_texture_filter(bct_sampler->get_filter_mode()); material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, bct_sampler->get_wrap_mode()); - material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); } if (!mr.has("baseColorFactor")) { material->set_albedo(Color(1, 1, 1)); @@ -3758,7 +3837,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { if (mr.has("metallicRoughnessTexture")) { const Dictionary &bct = mr["metallicRoughnessTexture"]; if (bct.has("index")) { - const Ref t = _get_texture(p_state, bct["index"]); + const Ref t = _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC); material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, t); material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE); material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, t); @@ -3776,7 +3855,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { if (d.has("normalTexture")) { const Dictionary &bct = d["normalTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"], TEXTURE_TYPE_NORMAL)); material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true); } if (bct.has("scale")) { @@ -3786,7 +3865,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { if (d.has("occlusionTexture")) { const Dictionary &bct = d["occlusionTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED); material->set_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION, true); } @@ -3804,7 +3883,7 @@ Error GLTFDocument::_parse_materials(Ref p_state) { if (d.has("emissiveTexture")) { const Dictionary &bct = d["emissiveTexture"]; if (bct.has("index")) { - material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"])); + material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); material->set_emission(Color(0, 0, 0)); } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 164c63c53c..4653fb38ef 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -64,6 +64,10 @@ public: COMPONENT_TYPE_INT = 5125, COMPONENT_TYPE_FLOAT = 5126, }; + enum { + TEXTURE_TYPE_GENERIC = 0, + TEXTURE_TYPE_NORMAL = 1, + }; protected: static void _bind_methods(); @@ -92,7 +96,7 @@ private: GLTFTextureIndex _set_texture(Ref p_state, Ref p_texture, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref _get_texture(Ref p_state, - const GLTFTextureIndex p_texture); + const GLTFTextureIndex p_texture, int p_texture_type); GLTFTextureSamplerIndex _set_sampler_for_mode(Ref p_state, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref _get_sampler_for_texture(Ref p_state, diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 252453dfc2..b67484fc8e 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -91,6 +91,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node); ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data); ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); + ClassDB::bind_method(D_METHOD("get_handle_binary_image"), &GLTFState::get_handle_binary_image); + ClassDB::bind_method(D_METHOD("set_handle_binary_image", "method"), &GLTFState::set_handle_binary_image); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -118,6 +120,11 @@ 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> + 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 + + BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES); + BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES); + BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_BASISU); } void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) { diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 488ad038aa..47b98a91b6 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -59,6 +59,8 @@ class GLTFState : public Resource { bool discard_meshes_and_materials = false; bool create_animations = true; + int handle_binary_image = HANDLE_BINARY_EXTRACT_TEXTURES; + Vector> nodes; Vector> buffers; Vector> buffer_views; @@ -78,6 +80,7 @@ class GLTFState : public Resource { Vector> images; Vector extensions_used; Vector extensions_required; + Vector> source_images; Vector> skins; Vector> cameras; @@ -100,6 +103,18 @@ protected: public: void add_used_extension(const String &p_extension, bool p_required = false); + enum GLTFHandleBinary { + HANDLE_BINARY_DISCARD_TEXTURES = 0, + HANDLE_BINARY_EXTRACT_TEXTURES, + HANDLE_BINARY_EMBED_AS_BASISU, + }; + int32_t get_handle_binary_image() { + return handle_binary_image; + } + void set_handle_binary_image(int32_t p_handle_binary_image) { + handle_binary_image = p_handle_binary_image; + } + Dictionary get_json(); void set_json(Dictionary p_json); @@ -115,6 +130,15 @@ public: bool get_use_named_skin_binds(); void set_use_named_skin_binds(bool p_use_named_skin_binds); + bool get_discard_textures(); + void set_discard_textures(bool p_discard_textures); + + bool get_embed_as_basisu(); + void set_embed_as_basisu(bool p_embed_as_basisu); + + bool get_extract_textures(); + void set_extract_textures(bool p_extract_textures); + bool get_discard_meshes_and_materials(); void set_discard_meshes_and_materials(bool p_discard_meshes_and_materials); -- cgit v1.2.3