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:
parent
d1e5903c67
commit
39922d7167
15 changed files with 241 additions and 55 deletions
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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[]" />
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue