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