From 2b47e72aa6bfd67bba181ccff0e516a7b812769b Mon Sep 17 00:00:00 2001 From: yesfish Date: Thu, 3 Oct 2024 07:25:00 +0100 Subject: [PATCH] Import custom attributes --- modules/gltf/config.py | 1 + modules/gltf/doc_classes/GLTFAttributeMap.xml | 51 ++++++ .../doc_classes/GLTFDocumentExtension.xml | 8 + .../editor/editor_scene_importer_blend.cpp | 6 + .../gltf/extensions/gltf_attribute_map.cpp | 156 ++++++++++++++++++ modules/gltf/extensions/gltf_attribute_map.h | 94 +++++++++++ .../extensions/gltf_document_extension.cpp | 7 + .../gltf/extensions/gltf_document_extension.h | 3 + modules/gltf/gltf_document.cpp | 140 +++++++++------- modules/gltf/register_types.cpp | 2 + 10 files changed, 412 insertions(+), 56 deletions(-) create mode 100644 modules/gltf/doc_classes/GLTFAttributeMap.xml create mode 100644 modules/gltf/extensions/gltf_attribute_map.cpp create mode 100644 modules/gltf/extensions/gltf_attribute_map.h diff --git a/modules/gltf/config.py b/modules/gltf/config.py index c2f909dacf4e..95840b5d9736 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -13,6 +13,7 @@ def get_doc_classes(): "EditorSceneFormatImporterGLTF", "GLTFAccessor", "GLTFAnimation", + "GLTFAttributeMap", "GLTFBufferView", "GLTFCamera", "GLTFDocument", diff --git a/modules/gltf/doc_classes/GLTFAttributeMap.xml b/modules/gltf/doc_classes/GLTFAttributeMap.xml new file mode 100644 index 000000000000..471c5fa6e12c --- /dev/null +++ b/modules/gltf/doc_classes/GLTFAttributeMap.xml @@ -0,0 +1,51 @@ + + + + Controls how glTF attributes are mapped on import. + + + Controls how glTF attributes are mapped to the resulting [Mesh]. Used by [method GLTFDocumentExtension._import_get_attribute_map]. + Multiplexing is supported with [code]CUSTOM*[/code] attributes. Setting [code]custom*[/code] and [code]custom*_mux[/code] will combine them into the one array. + A pair of [code]custom*[/code] and [code]custom*_mux[/code] attributes must total no larger than [code]vec4[/code]. For instance, you can set [member custom0] to a scalar and [member custom0_mux] to a [code]vec3[/code], but you cannot set [member custom0] to a [code]vec2[/code] and [member custom0_mux] to a [code]vec3[/code] (as this would require 5 values). + If the [code]custom*_mux[/code] attribute that is requested is missing, it will act like only the first was set. + [b]Note:[/b] It may be necessary to disable [code]generate_lods[/code] in the mesh import settings to preserve custom data. + + + + + + Targets [constant Mesh.ARRAY_COLOR]. Supported attributes: vec3, vec4. + + + Targets [constant Mesh.ARRAY_CUSTOM0]. Supported attributes: float, vec2, vec3, vec4. + + + Targets [constant Mesh.ARRAY_CUSTOM0]. Supported attributes: float, vec2, vec3. + + + Targets [constant Mesh.ARRAY_CUSTOM1]. Supported attributes: float, vec2, vec3, vec4. + + + Targets [constant Mesh.ARRAY_CUSTOM1]. Supported attributes: float, vec2, vec3. + + + Targets [constant Mesh.ARRAY_CUSTOM2]. Supported attributes: float, vec2, vec3, vec4. + + + Targets [constant Mesh.ARRAY_CUSTOM2]. Supported attributes: float, vec2, vec3. + + + Targets [constant Mesh.ARRAY_CUSTOM3]. Supported attributes: float, vec2, vec3, vec4. + + + Targets [constant Mesh.ARRAY_CUSTOM3]. Supported attributes: float, vec2, vec3. + + + Targets [constant Mesh.ARRAY_TEX_UV]. Supported attributes: vec2. + [b]Note:[/b] The importer may use ARRAY_TEX_UV to generate tangents. + + + Targets [constant Mesh.ARRAY_TEX_UV2]. Supported attributes: vec2. + + + diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index abdbce7eebdf..da3c4459e1ec 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -112,6 +112,14 @@ Returns an array of the glTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a glTF file with required extensions can be loaded. + + + + + + Part of the import process. Allows any mesh to be processed with a custom [GLTFAttributeMap]. Make this function return [code]null[/code] to ignore. + + diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 89a010361633..a9b8d8b4276c 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -288,6 +288,11 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ } else { parameters_map["export_def_bones"] = false; } + if (p_options.has(SNAME("blender/meshes/export_attributes")) && p_options[SNAME("blender/meshes/export_attributes")]) { + parameters_map["export_attributes"] = true; + } else { + parameters_map["export_attributes"] = false; + } if (p_options.has(SNAME("blender/nodes/modifiers")) && p_options[SNAME("blender/nodes/modifiers")]) { parameters_map["export_apply"] = true; } else { @@ -380,6 +385,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li ADD_OPTION_BOOL("blender/meshes/tangents", true); ADD_OPTION_ENUM("blender/meshes/skins", "None,4 Influences (Compatible),All Influences", BLEND_BONE_INFLUENCES_ALL); ADD_OPTION_BOOL("blender/meshes/export_bones_deforming_mesh_only", false); + ADD_OPTION_BOOL("blender/meshes/export_attributes", false); ADD_OPTION_BOOL("blender/materials/unpack_enabled", true); ADD_OPTION_ENUM("blender/materials/export_materials", "Placeholder,Export,Named Placeholder", BLEND_MATERIAL_EXPORT_EXPORT); ADD_OPTION_BOOL("blender/animation/limit_playback", true); diff --git a/modules/gltf/extensions/gltf_attribute_map.cpp b/modules/gltf/extensions/gltf_attribute_map.cpp new file mode 100644 index 000000000000..a824da011e0c --- /dev/null +++ b/modules/gltf/extensions/gltf_attribute_map.cpp @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* gltf_attribute_map.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "gltf_attribute_map.h" + +void GLTFAttributeMap::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_color"), &GLTFAttributeMap::get_color); + ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFAttributeMap::set_color); + ClassDB::bind_method(D_METHOD("get_uv"), &GLTFAttributeMap::get_uv); + ClassDB::bind_method(D_METHOD("set_uv", "uv"), &GLTFAttributeMap::set_uv); + ClassDB::bind_method(D_METHOD("get_uv2"), &GLTFAttributeMap::get_uv2); + ClassDB::bind_method(D_METHOD("set_uv2", "uv2"), &GLTFAttributeMap::set_uv2); + ClassDB::bind_method(D_METHOD("get_custom0"), &GLTFAttributeMap::get_custom0); + ClassDB::bind_method(D_METHOD("set_custom0", "custom0"), &GLTFAttributeMap::set_custom0); + ClassDB::bind_method(D_METHOD("get_custom0_mux"), &GLTFAttributeMap::get_custom0_mux); + ClassDB::bind_method(D_METHOD("set_custom0_mux", "custom0_mux"), &GLTFAttributeMap::set_custom0_mux); + ClassDB::bind_method(D_METHOD("get_custom1"), &GLTFAttributeMap::get_custom1); + ClassDB::bind_method(D_METHOD("set_custom1", "custom1"), &GLTFAttributeMap::set_custom1); + ClassDB::bind_method(D_METHOD("get_custom1_mux"), &GLTFAttributeMap::get_custom1_mux); + ClassDB::bind_method(D_METHOD("set_custom1_mux", "custom1_mux"), &GLTFAttributeMap::set_custom1_mux); + ClassDB::bind_method(D_METHOD("get_custom2"), &GLTFAttributeMap::get_custom2); + ClassDB::bind_method(D_METHOD("set_custom2", "custom2"), &GLTFAttributeMap::set_custom2); + ClassDB::bind_method(D_METHOD("get_custom2_mux"), &GLTFAttributeMap::get_custom2_mux); + ClassDB::bind_method(D_METHOD("set_custom2_mux", "custom2_mux"), &GLTFAttributeMap::set_custom2_mux); + ClassDB::bind_method(D_METHOD("get_custom3"), &GLTFAttributeMap::get_custom3); + ClassDB::bind_method(D_METHOD("set_custom3", "custom3"), &GLTFAttributeMap::set_custom3); + ClassDB::bind_method(D_METHOD("get_custom3_mux"), &GLTFAttributeMap::get_custom3_mux); + ClassDB::bind_method(D_METHOD("set_custom3_mux", "custom3_mux"), &GLTFAttributeMap::set_custom3_mux); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "uv"), "set_uv", "get_uv"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "uv2"), "set_uv2", "get_uv2"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom0"), "set_custom0", "get_custom0"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom0_mux"), "set_custom0_mux", "get_custom0_mux"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom1"), "set_custom1", "get_custom1"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom1_mux"), "set_custom1_mux", "get_custom1_mux"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom2"), "set_custom2", "get_custom2"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom2_mux"), "set_custom2_mux", "get_custom2_mux"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom3"), "set_custom3", "get_custom3"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom3_mux"), "set_custom3_mux", "get_custom3_mux"); +} + +String GLTFAttributeMap::get_color() const { + return _color; +} + +void GLTFAttributeMap::set_color(const String &p_color) { + _color = p_color; +} + +String GLTFAttributeMap::get_uv() const { + return _uv; +} + +void GLTFAttributeMap::set_uv(const String &p_uv) { + _uv = p_uv; +} + +String GLTFAttributeMap::get_uv2() const { + return _uv2; +} + +void GLTFAttributeMap::set_uv2(const String &p_uv2) { + _uv2 = p_uv2; +} + +String GLTFAttributeMap::get_custom0() const { + return _custom[0]; +} + +void GLTFAttributeMap::set_custom0(const String &p_custom0) { + _custom[0] = p_custom0; +} + +String GLTFAttributeMap::get_custom0_mux() const { + return _custom_mux[0]; +} + +void GLTFAttributeMap::set_custom0_mux(const String &p_custom0_mux) { + _custom_mux[0] = p_custom0_mux; +} + +String GLTFAttributeMap::get_custom1() const { + return _custom[1]; +} + +void GLTFAttributeMap::set_custom1(const String &p_custom1) { + _custom[1] = p_custom1; +} + +String GLTFAttributeMap::get_custom1_mux() const { + return _custom_mux[1]; +} + +void GLTFAttributeMap::set_custom1_mux(const String &p_custom1_mux) { + _custom_mux[1] = p_custom1_mux; +} + +String GLTFAttributeMap::get_custom2() const { + return _custom[2]; +} + +void GLTFAttributeMap::set_custom2(const String &p_custom2) { + _custom[2] = p_custom2; +} + +String GLTFAttributeMap::get_custom2_mux() const { + return _custom_mux[2]; +} + +void GLTFAttributeMap::set_custom2_mux(const String &p_custom2_mux) { + _custom_mux[2] = p_custom2_mux; +} + +String GLTFAttributeMap::get_custom3() const { + return _custom[3]; +} + +void GLTFAttributeMap::set_custom3(const String &p_custom3) { + _custom[3] = p_custom3; +} + +String GLTFAttributeMap::get_custom3_mux() const { + return _custom_mux[3]; +} + +void GLTFAttributeMap::set_custom3_mux(const String &p_custom3_mux) { + _custom_mux[3] = p_custom3_mux; +} diff --git a/modules/gltf/extensions/gltf_attribute_map.h b/modules/gltf/extensions/gltf_attribute_map.h new file mode 100644 index 000000000000..9352d9c462b2 --- /dev/null +++ b/modules/gltf/extensions/gltf_attribute_map.h @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* gltf_attribute_map.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "core/io/resource.h" + +class GLTFAttributeMap : public Resource { + GDCLASS(GLTFAttributeMap, Resource); + +protected: + static void _bind_methods(); + +private: + friend class GLTFDocument; + + String _color = "COLOR_0"; + String _uv = "TEXCOORD_0"; + String _uv2 = "TEXCOORD_1"; + + String _custom[4]{ + "TEXCOORD_2", + "TEXCOORD_4", + "TEXCOORD_6", + "" + }; + String _custom_mux[4]{ + "TEXCOORD_3", + "TEXCOORD_5", + "TEXCOORD_7", + "" + }; + +public: + String get_color() const; + void set_color(const String &p_color); + + String get_uv() const; + void set_uv(const String &p_uv); + + String get_uv2() const; + void set_uv2(const String &p_uv2); + + String get_custom0() const; + void set_custom0(const String &p_custom0); + + String get_custom0_mux() const; + void set_custom0_mux(const String &p_custom0_mux); + + String get_custom1() const; + void set_custom1(const String &p_custom1); + + String get_custom1_mux() const; + void set_custom1_mux(const String &p_custom1_mux); + + String get_custom2() const; + void set_custom2(const String &p_custom2); + + String get_custom2_mux() const; + void set_custom2_mux(const String &p_custom2_mux); + + String get_custom3() const; + void set_custom3(const String &p_custom3); + + String get_custom3_mux() const; + void set_custom3_mux(const String &p_custom3_mux); +}; diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 1e912047a37d..eef6bbddcfcf 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -33,6 +33,7 @@ void GLTFDocumentExtension::_bind_methods() { // Import process. GDVIRTUAL_BIND(_import_preflight, "state", "extensions"); + GDVIRTUAL_BIND(_import_get_attribute_map, "state", "mesh_index"); GDVIRTUAL_BIND(_get_supported_extensions); GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions"); GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image"); @@ -66,6 +67,12 @@ Error GLTFDocumentExtension::import_preflight(Ref p_state, const Vect return err; } +Ref GLTFDocumentExtension::import_get_attribute_map(Ref p_state, GLTFMeshIndex p_index) { + Ref ret; + GDVIRTUAL_CALL(_import_get_attribute_map, p_state, p_index, ret); + return ret; +} + Vector GLTFDocumentExtension::get_supported_extensions() { Vector ret; GDVIRTUAL_CALL(_get_supported_extensions, ret); diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index 5d6c5328c9e3..bcd78ced7db0 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -31,6 +31,7 @@ #pragma once #include "../gltf_state.h" +#include "gltf_attribute_map.h" #include "scene/3d/node_3d.h" @@ -43,6 +44,7 @@ class GLTFDocumentExtension : public Resource { public: // Import process. virtual Error import_preflight(Ref p_state, const Vector &p_extensions); + virtual Ref import_get_attribute_map(Ref p_state, GLTFMeshIndex p_index); virtual Vector get_supported_extensions(); virtual Error parse_node_extensions(Ref p_state, Ref p_gltf_node, const Dictionary &p_extensions); virtual Error parse_image_data(Ref p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref r_image); @@ -69,6 +71,7 @@ class GLTFDocumentExtension : public Resource { // Import process. GDVIRTUAL2R(Error, _import_preflight, Ref, Vector); + GDVIRTUAL2R(Ref, _import_get_attribute_map, Ref, GLTFMeshIndex); GDVIRTUAL0R(Vector, _get_supported_extensions); GDVIRTUAL3R(Error, _parse_node_extensions, Ref, Ref, Dictionary); GDVIRTUAL4R(Error, _parse_image_data, Ref, PackedByteArray, String, Ref); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 6a675fe279fe..e4e6b9313183 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -1410,6 +1410,9 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { return OK; } + Ref general_attrmap; + general_attrmap.instantiate(); + Array meshes = p_state->json["meshes"]; for (GLTFMeshIndex i = 0; i < meshes.size(); i++) { print_verbose("glTF: Parsing mesh: " + itos(i)); @@ -1466,6 +1469,14 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { primitive = primitives2[mode]; } + Ref attrmap = general_attrmap; + for (Ref ext : document_extensions) { + Ref am = ext->import_get_attribute_map(p_state, i); + if (am.is_valid()) { + attrmap = am; + } + } + int32_t orig_vertex_num = 0; ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR); if (a.has("POSITION")) { @@ -1528,69 +1539,86 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { if (a.has("TANGENT")) { array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_float32s(p_state, a["TANGENT"], indices_vec4_mapping); } - if (a.has("TEXCOORD_0")) { - array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_0"], indices_mapping); + if (a.has(attrmap->_color)) { + array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(p_state, a[attrmap->_color], indices_mapping); + has_vertex_color = true; } - if (a.has("TEXCOORD_1")) { - array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_1"], indices_mapping); + if (a.has(attrmap->_uv)) { + array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(p_state, a[attrmap->_uv], indices_mapping); } - for (int custom_i = 0; custom_i < 3; custom_i++) { - Vector cur_custom; - Vector texcoord_first; - Vector texcoord_second; - - int texcoord_i = 2 + 2 * custom_i; - String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); - int num_channels = 0; - if (a.has(gltf_texcoord_key)) { - texcoord_first = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], indices_mapping); - num_channels = 2; - } - gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); - if (a.has(gltf_texcoord_key)) { - texcoord_second = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], indices_mapping); - num_channels = 4; - } - if (!num_channels) { - break; + if (a.has(attrmap->_uv2)) { + array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(p_state, a[attrmap->_uv2], indices_mapping); + } + for (int custom_i = 0; custom_i < 4; custom_i++) { + const String &attr = attrmap->_custom[custom_i]; + const String &mux = attrmap->_custom_mux[custom_i]; + + if (!a.has(attr)) { + continue; } - if (num_channels == 2 || num_channels == 4) { - cur_custom.resize(vertex_num * num_channels); - for (int32_t uv_i = 0; uv_i < texcoord_first.size() && uv_i < vertex_num; uv_i++) { - cur_custom.write[uv_i * num_channels + 0] = texcoord_first[uv_i].x; - cur_custom.write[uv_i * num_channels + 1] = texcoord_first[uv_i].y; + + Mesh::ArrayCustomFormat format; + + if (a.has(mux)) { + const Ref &acc = p_state->accessors[a[attr]]; + const Ref &acc2 = p_state->accessors[a[mux]]; + if (acc->accessor_type > GLTFAccessor::TYPE_VEC4 || acc2->accessor_type > GLTFAccessor::TYPE_VEC4) { + Array arr; + arr.push_back(attr); + arr.push_back(mux); + ERR_PRINT(String("Unsupported format for multiplex {}<->{}, ignoring attributes.").format(arr)); + continue; } - // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: - for (int32_t uv_i = texcoord_first.size(); uv_i < vertex_num; uv_i++) { - cur_custom.write[uv_i * num_channels + 0] = 0; - cur_custom.write[uv_i * num_channels + 1] = 0; + + // assumes GLTFAccessor::SCALAR = 0 + const int stride_a = 1 + acc->accessor_type; + const int stride_b = (1 + acc2->accessor_type); + const int stride = stride_a + stride_b; + if (stride > 4) { + Array arr; + arr.push_back(attr); + arr.push_back(mux); + ERR_PRINT(String("Format for multiplex {}<->{} exceeds vec4, ignoring attributes.").format(arr)); + continue; } - } - if (num_channels == 4) { - for (int32_t uv_i = 0; uv_i < texcoord_second.size() && uv_i < vertex_num; uv_i++) { - // num_channels must be 4 - cur_custom.write[uv_i * num_channels + 2] = texcoord_second[uv_i].x; - cur_custom.write[uv_i * num_channels + 3] = texcoord_second[uv_i].y; + format = (Mesh::ArrayCustomFormat)((int)Mesh::ARRAY_CUSTOM_R_FLOAT + (stride - 1)); + + Vector src_a = _decode_accessor_as_float32s(p_state, a[attr], indices_mapping); + Vector src_b = _decode_accessor_as_float32s(p_state, a[mux], indices_mapping); + const int frames = src_a.size() / stride_a; + if (frames != src_b.size() / stride_b) { + Array arr; + arr.push_back(attr); + arr.push_back(mux); + ERR_PRINT(String("Element count for multiplex {}<->{} are not equal, ignoring attributes.").format(arr)); + continue; } - // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0: - for (int32_t uv_i = texcoord_second.size(); uv_i < vertex_num; uv_i++) { - cur_custom.write[uv_i * num_channels + 2] = 0; - cur_custom.write[uv_i * num_channels + 3] = 0; + + Vector data; + data.resize_initialized(frames * stride); + for (int f = 0; f < frames; f++) { + for (int s = 0; s < stride_a; s++) { + data.set(f * stride + s, src_a[f * stride_a + s]); + } + for (int s = 0; s < stride_b; s++) { + data.set(f * stride + stride_a + s, src_b[f * stride_b + s]); + } } - } - if (cur_custom.size() > 0) { - array[Mesh::ARRAY_CUSTOM0 + custom_i] = cur_custom; - int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS; - if (num_channels == 2) { - flags |= Mesh::ARRAY_CUSTOM_RG_FLOAT << custom_shift; - } else { - flags |= Mesh::ARRAY_CUSTOM_RGBA_FLOAT << custom_shift; + array[Mesh::ARRAY_CUSTOM0 + custom_i] = data; + + } else { + const Ref &accessor = p_state->accessors[a[attr]]; + if (accessor->accessor_type > GLTFAccessor::TYPE_VEC4) { + Array arr; + arr.push_back(attr); + ERR_PRINT(String("Unsupported format for {}, ignoring attribute.").format(arr)); + continue; } + format = (Mesh::ArrayCustomFormat)((int)Mesh::ARRAY_CUSTOM_R_FLOAT + (int)accessor->accessor_type); + array[Mesh::ARRAY_CUSTOM0 + custom_i] = _decode_accessor_as_float32s(p_state, a[attr], indices_mapping); } - } - if (a.has("COLOR_0")) { - array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(p_state, a["COLOR_0"], indices_mapping); - has_vertex_color = true; + + flags |= format << (Mesh::ARRAY_FORMAT_CUSTOM_BASE + Mesh::ARRAY_FORMAT_CUSTOM_BITS * custom_i); } if (a.has("JOINTS_0") && !a.has("JOINTS_1")) { PackedInt32Array joints_0 = _decode_accessor_as_int32s(p_state, a["JOINTS_0"], indices_vec4_mapping); @@ -1717,7 +1745,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { bool generate_tangents = p_state->force_generate_tangents && (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("NORMAL")); - if (generate_tangents && !a.has("TEXCOORD_0")) { + if (generate_tangents && !a.has(attrmap->_uv)) { // If we don't have UVs we provide a dummy tangent array. Vector tangents; tangents.resize(vertex_num * 4); @@ -1755,7 +1783,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); } mesh_surface_tool->index(); - if (generate_tangents && a.has("TEXCOORD_0")) { + if (generate_tangents && a.has(attrmap->_uv)) { //must generate mikktspace tangents.. ergh.. mesh_surface_tool->generate_tangents(); } diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index 8a211649e99e..497263317c90 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -30,6 +30,7 @@ #include "register_types.h" +#include "extensions/gltf_attribute_map.h" #include "extensions/gltf_document_extension_convert_importer_mesh.h" #include "extensions/gltf_document_extension_texture_ktx.h" #include "extensions/gltf_document_extension_texture_webp.h" @@ -108,6 +109,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) { // glTF API available at runtime. GDREGISTER_CLASS(GLTFAccessor); GDREGISTER_CLASS(GLTFAnimation); + GDREGISTER_CLASS(GLTFAttributeMap); GDREGISTER_CLASS(GLTFBufferView); GDREGISTER_CLASS(GLTFCamera); GDREGISTER_CLASS(GLTFDocument);