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: [PATCH] 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. --- core/string/ustring.cpp | 20 +++- core/string/ustring.h | 1 + core/variant/variant_call.cpp | 1 + doc/classes/String.xml | 6 ++ editor/import/resource_importer_texture.cpp | 16 ++- modules/basis_universal/register_types.cpp | 53 ++++----- modules/gltf/doc_classes/GLTFState.xml | 11 ++ .../editor/editor_scene_importer_blend.cpp | 6 +- .../editor/editor_scene_importer_gltf.cpp | 10 ++ .../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 +++++ scene/resources/texture.cpp | 32 +++++- 15 files changed, 241 insertions(+), 55 deletions(-) diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 9e468f7075f..b34d9f32711 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -4342,6 +4342,9 @@ bool String::is_valid_html_color() const { return Color::html_is_valid(*this); } +// Changes made to the set of invalid filename characters must also be reflected in the String documentation for is_valid_filename. +static const char *invalid_filename_characters = ": / \\ ? * \" | % < >"; + bool String::is_valid_filename() const { String stripped = strip_edges(); if (*this != stripped) { @@ -4352,7 +4355,22 @@ bool String::is_valid_filename() const { return false; } - return !(find(":") != -1 || find("/") != -1 || find("\\") != -1 || find("?") != -1 || find("*") != -1 || find("\"") != -1 || find("|") != -1 || find("%") != -1 || find("<") != -1 || find(">") != -1); + Vector chars = String(invalid_filename_characters).split(" "); + for (const String &ch : chars) { + if (contains(ch)) { + return false; + } + } + return true; +} + +String String::validate_filename() const { + Vector chars = String(invalid_filename_characters).split(" "); + String name = strip_edges(); + for (int i = 0; i < chars.size(); i++) { + name = name.replace(chars[i], "_"); + } + return name; } bool String::is_valid_ip_address() const { diff --git a/core/string/ustring.h b/core/string/ustring.h index 6338f1d3cd1..1582504c57d 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -433,6 +433,7 @@ public: static const String invalid_node_name_characters; String validate_node_name() const; String validate_identifier() const; + String validate_filename() const; bool is_valid_identifier() const; bool is_valid_int() const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 9e8c6fccb3a..9852ba91845 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1695,6 +1695,7 @@ static void _register_variant_builtin_methods() { bind_string_method(json_escape, sarray(), varray()); bind_string_method(validate_node_name, sarray(), varray()); + bind_string_method(validate_filename, sarray(), varray()); bind_string_method(is_valid_identifier, sarray(), varray()); bind_string_method(is_valid_int, sarray(), varray()); diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 97466e78603..15c44499ccc 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -1002,6 +1002,12 @@ [/codeblocks] + + + + Replace all characters that are not allowed in [method is_valid_filename] with underscores. + + diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 2f543ea02dd..b9b63914323 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -322,15 +322,11 @@ void ResourceImporterTexture::save_to_ctex_format(Ref f, const Refstore_16(p_image->get_height()); f->store_32(p_image->get_mipmap_count()); f->store_32(p_image->get_format()); - - for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { - Vector data = Image::basis_universal_packer(p_image->get_image_from_mipmap(i), p_channels); - int data_len = data.size(); - f->store_32(data_len); - - const uint8_t *r = data.ptr(); - f->store_buffer(r, data_len); - } + Vector data = Image::basis_universal_packer(p_image, p_channels); + int data_len = data.size(); + f->store_32(data_len); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } break; } } @@ -387,7 +383,7 @@ void ResourceImporterTexture::_save_ctex(const Ref &p_image, const String Ref image = p_image->duplicate(); - if (((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED && p_force_po2_for_compressed)) && p_mipmaps) { + if (p_force_po2_for_compressed && p_mipmaps && ((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED))) { image->resize_to_po2(); } diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 72515114603..ff7c01f9fc6 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -52,44 +52,50 @@ enum BasisDecompressFormat { #ifdef TOOLS_ENABLED static Vector basis_universal_packer(const Ref &p_image, Image::UsedChannels p_channels) { Vector budata; - { + basisu::basis_compressor_params params; Ref image = p_image->duplicate(); - - // unfortunately, basis universal does not support compressing supplied mipmaps, - // so for the time being, only compressing individual images will have to do. - - if (image->has_mipmaps()) { - image->clear_mipmaps(); - } if (image->get_format() != Image::FORMAT_RGBA8) { image->convert(Image::FORMAT_RGBA8); } - - basisu::image buimg(image->get_width(), image->get_height()); - + Ref image_single = image->duplicate(); { - Vector vec = image->get_data(); + if (image_single->has_mipmaps()) { + image_single->clear_mipmaps(); + } + basisu::image buimg(image_single->get_width(), image_single->get_height()); + Vector vec = image_single->get_data(); const uint8_t *r = vec.ptr(); - memcpy(buimg.get_ptr(), r, vec.size()); + params.m_source_images.push_back(buimg); + } + basisu::vector source_images; + for (int32_t mipmap_i = 1; mipmap_i < image->get_mipmap_count(); mipmap_i++) { + Ref mip = image->get_image_from_mipmap(mipmap_i); + basisu::image buimg(mip->get_width(), mip->get_height()); + Vector vec = mip->get_data(); + const uint8_t *r = vec.ptr(); + memcpy(buimg.get_ptr(), r, vec.size()); + source_images.push_back(buimg); } - basisu::basis_compressor_params params; params.m_uastc = true; - params.m_max_endpoint_clusters = 512; - params.m_max_selector_clusters = 512; + params.m_quality_level = basisu::BASISU_QUALITY_MIN; + + params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask; + + static const uint32_t s_level_flags[basisu::TOTAL_PACK_UASTC_LEVELS] = { basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster, basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower, basisu::cPackUASTCLevelVerySlow }; + params.m_pack_uastc_flags |= s_level_flags[0]; + params.m_rdo_uastc = 0.0f; + params.m_rdo_uastc_quality_scalar = 0.0f; + params.m_rdo_uastc_dict_size = 1024; + + params.m_mip_fast = true; params.m_multithreading = true; - //params.m_quality_level = 0; - //params.m_disable_hierarchical_endpoint_codebooks = true; - //params.m_no_selector_rdo = true; basisu::job_pool jpool(OS::get_singleton()->get_processor_count()); params.m_pJob_pool = &jpool; - params.m_mip_gen = false; //sorry, please some day support provided mipmaps. - params.m_source_images.push_back(buimg); - BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG; params.m_check_for_alpha = false; @@ -252,8 +258,7 @@ static Ref basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size }; }; - image.instantiate(); - image->set_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); + image = Image::create_from_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); return image; } diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 9a554a0d49a..7fbfa5a2b81 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 2b8c057ee5b..0075558dfc7 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 39956a6ff65..7c40afc8e7c 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 c2a4bf046d4..ed57ec8cdbc 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 b243ba933dd..72c715f0a6d 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; } - p_state->images.push_back(ImageTexture::create_from_image(img)); + 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(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 164c63c53cd..4653fb38efb 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 252453dfc2a..b67484fc8e7 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 488ad038aaf..47b98a91b6f 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); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 85e21d60560..7e3156d2fff 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -653,7 +653,7 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si uint32_t mipmaps = f->get_32(); Image::Format format = Image::Format(f->get_32()); - if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) { //look for a PNG or WebP file inside int sw = w; @@ -684,9 +684,7 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si } Ref img; - if (data_format == DATA_FORMAT_BASIS_UNIVERSAL && Image::basis_universal_unpacker) { - img = Image::basis_universal_unpacker(pv); - } else if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { + if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { img = Image::png_unpacker(pv); } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) { img = Image::webp_unpacker(pv); @@ -745,6 +743,32 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si return image; } + } else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + int sw = w; + int sh = h; + uint32_t size = f->get_32(); + if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { + //can't load this due to size limit + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + f->seek(f->get_position() + size); + return Ref(); + } + Vector pv; + pv.resize(size); + { + uint8_t *wr = pv.ptrw(); + f->get_buffer(wr, size); + } + Ref img; + img = Image::basis_universal_unpacker(pv); + if (img.is_null() || img->is_empty()) { + ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref()); + } + format = img->get_format(); + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + return img; } else if (data_format == DATA_FORMAT_IMAGE) { int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);