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);