diff options
author | K. S. Ernest (iFire) Lee <fire@users.noreply.github.com> | 2022-07-18 17:58:27 -0700 |
---|---|---|
committer | Lyuma <xn.lyuma@gmail.com> | 2023-01-27 02:02:02 -0800 |
commit | 39922d7167178bab9587aa00a7a108b93db6240e (patch) | |
tree | 51e404a83385757d913207cee245a1af6aba8512 /modules/gltf | |
parent | d1e5903c67956707948b1de370b807e3aad395b7 (diff) |
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.
Diffstat (limited to 'modules/gltf')
-rw-r--r-- | modules/gltf/doc_classes/GLTFState.xml | 11 | ||||
-rw-r--r-- | modules/gltf/editor/editor_scene_importer_blend.cpp | 6 | ||||
-rw-r--r-- | modules/gltf/editor/editor_scene_importer_gltf.cpp | 10 | ||||
-rw-r--r-- | modules/gltf/editor/editor_scene_importer_gltf.h | 2 | ||||
-rw-r--r-- | modules/gltf/gltf_document.cpp | 101 | ||||
-rw-r--r-- | modules/gltf/gltf_document.h | 6 | ||||
-rw-r--r-- | modules/gltf/gltf_state.cpp | 7 | ||||
-rw-r--r-- | modules/gltf/gltf_state.h | 24 |
8 files changed, 151 insertions, 16 deletions
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 @@ <description> </description> </method> + <method name="get_handle_binary_image"> + <return type="int" /> + <description> + </description> + </method> <method name="get_images"> <return type="Texture2D[]" /> <description> @@ -155,6 +160,12 @@ <description> </description> </method> + <method name="set_handle_binary_image"> + <return type="void" /> + <param index="0" name="method" type="int" /> + <description> + </description> + </method> <method name="set_images"> <return type="void" /> <param index="0" name="images" type="Texture2D[]" /> 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 <shlwapi.h> #endif @@ -221,6 +221,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ gltf.instantiate(); Ref<GLTFState> 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<GLTFState> 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<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)); +} + #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<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override; + virtual void get_import_options(const String &p_path, + List<ResourceImporter::ImportOption> *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<GLTFState> 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<GLTFState> 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<GLTFState> 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<GLTFState> 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<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + continue; + } + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { + p_state->images.push_back(Ref<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + 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<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } 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<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } 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; + 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<Texture2D> 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<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); + } + } + continue; + } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref<PortableCompressedTexture2D> 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<Texture2D>()); + p_state->source_images.push_back(Ref<Image>()); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); @@ -3268,12 +3335,24 @@ GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> p_state, Ref<Texture2 return gltf_texture_i; } -Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture) { +Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture, int p_texture_types) { ERR_FAIL_INDEX_V(p_texture, p_state->textures.size(), Ref<Texture2D>()); const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image(); - ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref<Texture2D>()); - + if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) { + Ref<PortableCompressedTexture2D> portable_texture; + portable_texture.instantiate(); + portable_texture->set_keep_compressed_buffer(true); + Ref<Image> new_img = p_state->source_images[p_texture]->duplicate(); + ERR_FAIL_COND_V(new_img.is_null(), Ref<Texture2D>()); + 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<GLTFState> 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<Texture2D> diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"]); + Ref<Texture2D> 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<GLTFState> p_state) { if (sgm.has("specularGlossinessTexture")) { const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"]; if (spec_gloss_texture.has("index")) { - const Ref<Texture2D> orig_texture = _get_texture(p_state, spec_gloss_texture["index"]); + const Ref<Texture2D> 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<GLTFState> p_state) { Ref<GLTFTextureSampler> 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<GLTFState> p_state) { if (mr.has("metallicRoughnessTexture")) { const Dictionary &bct = mr["metallicRoughnessTexture"]; if (bct.has("index")) { - const Ref<Texture2D> t = _get_texture(p_state, bct["index"]); + const Ref<Texture2D> 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<GLTFState> 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<GLTFState> 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<GLTFState> 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<GLTFState> p_state, Ref<Texture2D> p_texture, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref<Texture2D> _get_texture(Ref<GLTFState> p_state, - const GLTFTextureIndex p_texture); + const GLTFTextureIndex p_texture, int p_texture_type); GLTFTextureSamplerIndex _set_sampler_for_mode(Ref<GLTFState> p_state, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats); Ref<GLTFTextureSampler> _get_sampler_for_texture(Ref<GLTFState> 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<GLTFSkeletonIndex, 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::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<Ref<GLTFNode>> nodes; Vector<Vector<uint8_t>> buffers; Vector<Ref<GLTFBufferView>> buffer_views; @@ -78,6 +80,7 @@ class GLTFState : public Resource { Vector<Ref<Texture2D>> images; Vector<String> extensions_used; Vector<String> extensions_required; + Vector<Ref<Image>> source_images; Vector<Ref<GLTFSkin>> skins; Vector<Ref<GLTFCamera>> 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); |