mirror of
https://github.com/godotengine/godot
synced 2024-09-04 21:28:28 +00:00
Merge pull request #62499 from fire/gltf-binary-img-compression
Handle gltf binary images
This commit is contained in:
commit
9d555f5c68
|
@ -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"
|
||||
|
@ -3090,9 +3091,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.
|
||||
|
@ -3128,6 +3132,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.
|
||||
|
@ -3153,6 +3158,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);
|
||||
|
||||
|
@ -3197,9 +3203,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()));
|
||||
|
@ -3269,12 +3336,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];
|
||||
}
|
||||
|
||||
|
@ -3685,7 +3764,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);
|
||||
|
@ -3713,7 +3792,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();
|
||||
}
|
||||
|
@ -3736,7 +3815,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));
|
||||
|
@ -3759,7 +3838,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);
|
||||
|
@ -3777,7 +3856,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")) {
|
||||
|
@ -3787,7 +3866,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);
|
||||
}
|
||||
|
@ -3805,7 +3884,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;
|
||||
|
@ -101,6 +104,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);
|
||||
|
||||
|
@ -116,6 +131,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