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.
This commit is contained in:
K. S. Ernest (iFire) Lee 2022-07-18 17:58:27 -07:00 committed by Lyuma
parent d1e5903c67
commit 39922d7167
15 changed files with 241 additions and 55 deletions

View file

@ -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;
}
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<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(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));
}