summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorK. S. Ernest (iFire) Lee <fire@users.noreply.github.com>2022-07-18 17:58:27 -0700
committerLyuma <xn.lyuma@gmail.com>2023-01-27 02:02:02 -0800
commit39922d7167178bab9587aa00a7a108b93db6240e (patch)
tree51e404a83385757d913207cee245a1af6aba8512
parentd1e5903c67956707948b1de370b807e3aad395b7 (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.
-rw-r--r--core/string/ustring.cpp20
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--doc/classes/String.xml6
-rw-r--r--editor/import/resource_importer_texture.cpp16
-rw-r--r--modules/basis_universal/register_types.cpp53
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml11
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp6
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp10
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.h2
-rw-r--r--modules/gltf/gltf_document.cpp101
-rw-r--r--modules/gltf/gltf_document.h6
-rw-r--r--modules/gltf/gltf_state.cpp7
-rw-r--r--modules/gltf/gltf_state.h24
-rw-r--r--scene/resources/texture.cpp32
15 files changed, 241 insertions, 55 deletions
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 9e468f7075..b34d9f3271 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<String> chars = String(invalid_filename_characters).split(" ");
+ for (const String &ch : chars) {
+ if (contains(ch)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+String String::validate_filename() const {
+ Vector<String> 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 6338f1d3cd..1582504c57 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 9e8c6fccb3..9852ba9184 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 97466e7860..15c44499cc 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -1002,6 +1002,12 @@
[/codeblocks]
</description>
</method>
+ <method name="validate_filename" qualifiers="const">
+ <return type="String" />
+ <description>
+ Replace all characters that are not allowed in [method is_valid_filename] with underscores.
+ </description>
+ </method>
<method name="validate_node_name" qualifiers="const">
<return type="String" />
<description>
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 2f543ea02d..b9b6391432 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<FileAccess> f, const Ref<I
f->store_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<uint8_t> 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<uint8_t> 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<Image> &p_image, const String
Ref<Image> 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 7251511460..ff7c01f9fc 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<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
Vector<uint8_t> budata;
-
{
+ basisu::basis_compressor_params params;
Ref<Image> 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> image_single = image->duplicate();
{
- Vector<uint8_t> 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<uint8_t> 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<basisu::image> source_images;
+ for (int32_t mipmap_i = 1; mipmap_i < image->get_mipmap_count(); mipmap_i++) {
+ Ref<Image> mip = image->get_image_from_mipmap(mipmap_i);
+ basisu::image buimg(mip->get_width(), mip->get_height());
+ Vector<uint8_t> 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<Image> 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 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);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 85e21d6056..7e3156d2ff 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -653,7 +653,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> 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<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si
}
Ref<Image> 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<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> 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<Image>();
+ }
+ Vector<uint8_t> pv;
+ pv.resize(size);
+ {
+ uint8_t *wr = pv.ptrw();
+ f->get_buffer(wr, size);
+ }
+ Ref<Image> 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<Image>());
+ }
+ 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);