Skip to content

Commit

Permalink
gltf: Add GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_UNCOMPRESSED
Browse files Browse the repository at this point in the history
This option allows for a safe fallback for embedded gltf textures in cases where VRAM compression is not needed.
Add an is_editor_hint guard around GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES, to use EMBED_AS_UNCOMPRESSED by default at runtime.
This provides an option for pixel art to be stored losslessly.
Additionally, respect project importer defaults for texture import settings.
Avoid writing and reimporting extracted textures identical to version on disk.
  • Loading branch information
lyuma committed Feb 1, 2023
1 parent 0810eca commit bc24d01
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 28 deletions.
9 changes: 8 additions & 1 deletion editor/import/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2289,7 +2289,14 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM
ERR_FAIL_COND_V(!importer.is_valid(), nullptr);

Error err = OK;
Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_options, nullptr, &err);
HashMap<StringName, Variant> options_dupe = p_options;

// By default, the GLTF importer will extract embedded images into files on disk
// However, we do not want the advanced settings dialog to be able to write files on disk.
// To avoid this and also avoid compressing to basis every time, we are using the uncompressed option.
options_dupe["gltf/embedded_image_handling"] = 3; // Embed as Uncompressed defined in GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_UNCOMPRESSED

Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, options_dupe, nullptr, &err);
if (!scene || err != OK) {
return nullptr;
}
Expand Down
6 changes: 6 additions & 0 deletions modules/gltf/doc_classes/GLTFState.xml
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,16 @@
</members>
<constants>
<constant name="HANDLE_BINARY_DISCARD_TEXTURES" value="0">
Discards all embedded textures and uses untextured materials.
</constant>
<constant name="HANDLE_BINARY_EXTRACT_TEXTURES" value="1">
Extracts embedded textures to be reimported and compressed. Editor only. Acts as uncompressed at runtime.
</constant>
<constant name="HANDLE_BINARY_EMBED_AS_BASISU" value="2">
Embeds textures VRAM compressed with Basis Universal into the generated scene.
</constant>
<constant name="HANDLE_BINARY_EMBED_AS_UNCOMPRESSED" value="3">
Embeds textures compressed losslessly into the generated scene, matching old behavior.
</constant>
</constants>
</class>
6 changes: 3 additions & 3 deletions modules/gltf/editor/editor_scene_importer_gltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ 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"];
if (p_options.has("gltf/embedded_image_handling")) {
int32_t enum_option = p_options["gltf/embedded_image_handling"];
state->set_handle_binary_image(enum_option);
}
Error err = doc->append_from_file(p_path, state, p_flags);
Expand Down Expand Up @@ -87,7 +87,7 @@ 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));
r_options->push_back(ResourceImporterScene::ImportOption(PropertyInfo(Variant::INT, "gltf/embedded_image_handling", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), GLTFState::HANDLE_BINARY_EXTRACT_TEXTURES));
}

#endif // TOOLS_ENABLED
81 changes: 58 additions & 23 deletions modules/gltf/gltf_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "extensions/gltf_spec_gloss.h"

#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
Expand Down Expand Up @@ -3220,8 +3221,8 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
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) {
#ifdef TOOLS_ENABLED
} else if (Engine::get_singleton()->is_editor_hint() && GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) {
if (p_state->base_path.is_empty()) {
p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>());
Expand All @@ -3230,26 +3231,56 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
p_state->images.push_back(Ref<Texture2D>());
p_state->source_images.push_back(Ref<Image>());
} else {
Error err = OK;
bool must_import = false;
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");
if (!FileAccess::exists(file_path + ".import")) {
Ref<ConfigFile> config;
config.instantiate();
config->set_value("remap", "importer", "texture");
config->set_value("remap", "type", "Texture2D");
// Currently, it will likely use project defaults of Detect 3D, so textures will be reimported again.
if (!config->has_section_key("params", "mipmaps/generate")) {
config->set_value("params", "mipmaps/generate", true);
}

if (ProjectSettings::get_singleton()->has_setting("importer_defaults/texture")) {
//use defaults if exist
Dictionary importer_defaults = GLOBAL_GET("importer_defaults/texture");
List<Variant> importer_def_keys;
importer_defaults.get_key_list(&importer_def_keys);
for (const Variant &key : importer_def_keys) {
if (!config->has_section_key("params", (String)key)) {
config->set_value("params", (String)key, importer_defaults[key]);
}
}
}
err = config->save(file_path + ".import");
ERR_FAIL_COND_V(err != OK, err);
must_import = true;
}
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
Vector<uint8_t> png_buffer = img->save_png_to_buffer();
if (ResourceLoader::exists(file_path)) {
Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::READ, &err);
if (err == OK && file.is_valid()) {
Vector<uint8_t> orig_png_buffer = file->get_buffer(file->get_length());
if (png_buffer != orig_png_buffer) {
must_import = true;
}
}
} else {
must_import = true;
}
if (!config->has_section_key("params", "mipmaps/generate")) {
config->set_value("params", "mipmaps/generate", true);
if (must_import) {
Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(file.is_null(), FAILED);
file->store_buffer(png_buffer);
file->flush();
file.unref();
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
ResourceLoader::import(file_path);
}
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);
Expand All @@ -3261,7 +3292,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
p_state->source_images.push_back(Ref<Image>());
}
}
continue;
#endif
} else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) {
Ref<PortableCompressedTexture2D> tex;
tex.instantiate();
Expand All @@ -3271,11 +3302,15 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
tex->create_from_image(img, PortableCompressedTexture2D::COMPRESSION_MODE_BASIS_UNIVERSAL);
p_state->images.push_back(tex);
p_state->source_images.push_back(img);
continue;
} else {
// This handles two cases: if editor hint and HANDLE_BINARY_EXTRACT_TEXTURES; or if HANDLE_BINARY_EMBED_AS_UNCOMPRESSED
Ref<ImageTexture> tex;
tex.instantiate();
tex->set_name(img->get_name());
tex->set_image(img);
p_state->images.push_back(tex);
p_state->source_images.push_back(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()));
Expand Down
3 changes: 2 additions & 1 deletion modules/gltf/gltf_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,12 @@ 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
ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed As Basis Universal,Embed as Uncompressed", 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);
BIND_CONSTANT(HANDLE_BINARY_EMBED_AS_UNCOMPRESSED);
}

void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) {
Expand Down
1 change: 1 addition & 0 deletions modules/gltf/gltf_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class GLTFState : public Resource {
HANDLE_BINARY_DISCARD_TEXTURES = 0,
HANDLE_BINARY_EXTRACT_TEXTURES,
HANDLE_BINARY_EMBED_AS_BASISU,
HANDLE_BINARY_EMBED_AS_UNCOMPRESSED, // if this value changes from 3, ResourceImporterScene::pre_import must be changed as well.
};
int32_t get_handle_binary_image() {
return handle_binary_image;
Expand Down

0 comments on commit bc24d01

Please sign in to comment.