From 1e63a2a13232208a1932a320726b22244c6b630f Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Fri, 1 Mar 2024 13:07:07 -0800 Subject: [PATCH 1/2] Update buffer view target handling, encode sparse accessors as vec3. Co-authored-by: Lyuma --- modules/gltf/gltf_document.cpp | 224 +++++++++++++++++++++++++++------ modules/gltf/gltf_document.h | 9 +- 2 files changed, 187 insertions(+), 46 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index b53be7f8553..d50567bf1c4 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -884,42 +884,40 @@ Error GLTFDocument::_encode_accessors(Ref p_state) { d["componentType"] = accessor->component_type; d["count"] = accessor->count; d["type"] = _get_accessor_type_name(accessor->type); - d["byteOffset"] = accessor->byte_offset; d["normalized"] = accessor->normalized; d["max"] = accessor->max; d["min"] = accessor->min; - d["bufferView"] = accessor->buffer_view; //optional because it may be sparse... + if (accessor->buffer_view != -1) { + // bufferView may be omitted to zero-initialize the buffer. When this happens, byteOffset MUST also be omitted. + d["byteOffset"] = accessor->byte_offset; + d["bufferView"] = accessor->buffer_view; + } - // Dictionary s; - // s["count"] = accessor->sparse_count; - // ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR); + if (accessor->sparse_count > 0) { + Dictionary s; + s["count"] = accessor->sparse_count; - // s["indices"] = accessor->sparse_accessors; - // ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR); + Dictionary si; + si["bufferView"] = accessor->sparse_indices_buffer_view; + si["componentType"] = accessor->sparse_indices_component_type; + if (accessor->sparse_indices_byte_offset != -1) { + si["byteOffset"] = accessor->sparse_indices_byte_offset; + } + ERR_FAIL_COND_V(!si.has("bufferView") || !si.has("componentType"), ERR_PARSE_ERROR); + s["indices"] = si; - // Dictionary si; + Dictionary sv; + sv["bufferView"] = accessor->sparse_values_buffer_view; + if (accessor->sparse_values_byte_offset != -1) { + sv["byteOffset"] = accessor->sparse_values_byte_offset; + } + ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR); + s["values"] = sv; - // si["bufferView"] = accessor->sparse_indices_buffer_view; + ERR_FAIL_COND_V(!s.has("count") || !s.has("indices") || !s.has("values"), ERR_PARSE_ERROR); + d["sparse"] = s; + } - // ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR); - // si["componentType"] = accessor->sparse_indices_component_type; - - // if (si.has("byteOffset")) { - // si["byteOffset"] = accessor->sparse_indices_byte_offset; - // } - - // ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR); - // s["indices"] = si; - // Dictionary sv; - - // sv["bufferView"] = accessor->sparse_values_buffer_view; - // if (sv.has("byteOffset")) { - // sv["byteOffset"] = accessor->sparse_values_byte_offset; - // } - // ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR); - // s["values"] = sv; - // ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR); - // d["sparse"] = s; accessors.push_back(d); } @@ -1026,8 +1024,6 @@ Error GLTFDocument::_parse_accessors(Ref p_state) { } if (d.has("sparse")) { - //eeh.. - const Dictionary &s = d["sparse"]; ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR); @@ -1143,7 +1139,7 @@ Error GLTFDocument::_encode_buffer_view(Ref p_state, const double *p_ const uint32_t offset = bv->byte_offset = p_byte_offset; Vector &gltf_buffer = p_state->buffers.write[0]; - int stride = _get_component_type_size(p_component_type); + int stride = component_count * component_size; if (p_for_vertex && stride % 4) { stride += 4 - (stride % 4); //according to spec must be multiple of 4 } @@ -1152,13 +1148,14 @@ Error GLTFDocument::_encode_buffer_view(Ref p_state, const double *p_ print_verbose("glTF: encoding accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length)); - const int buffer_end = (stride * (p_count - 1)) + _get_component_type_size(p_component_type); + const int buffer_end = (stride * (p_count - 1)) + component_size; // TODO define bv->byte_stride bv->byte_offset = gltf_buffer.size(); if (p_for_vertex_indices) { bv->indices = true; } else if (p_for_vertex) { bv->vertex_attributes = true; + bv->byte_stride = stride; } switch (p_component_type) { @@ -1300,6 +1297,11 @@ Error GLTFDocument::_encode_buffer_view(Ref p_state, const double *p_ ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA); ERR_FAIL_COND_V((int)(offset + buffer_end) > gltf_buffer.size(), ERR_INVALID_DATA); + int pad_bytes = (4 - gltf_buffer.size()) & 3; + for (int i = 0; i < pad_bytes; i++) { + gltf_buffer.push_back(0); + } + r_accessor = bv->buffer = p_state->buffer_views.size(); p_state->buffer_views.push_back(bv); return OK; @@ -1519,8 +1521,12 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref p_state, type_max.resize(element_count); Vector type_min; type_min.resize(element_count); + int max_index = 0; for (int i = 0; i < p_attribs.size(); i++) { attribs.write[i] = p_attribs[i]; + if (p_attribs[i] > max_index) { + max_index = p_attribs[i]; + } if (i == 0) { for (int32_t type_i = 0; type_i < element_count; type_i++) { type_max.write[type_i] = attribs[(i * element_count) + type_i]; @@ -1539,7 +1545,12 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref p_state, GLTFBufferIndex buffer_view_i; int64_t size = p_state->buffers[0].size(); const GLTFType type = GLTFType::TYPE_SCALAR; - const int component_type = GLTFDocument::COMPONENT_TYPE_INT; + int component_type; + if (max_index > 65535 || p_for_vertex) { + component_type = GLTFDocument::COMPONENT_TYPE_INT; + } else { + component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; + } accessor->max = type_max; accessor->min = type_min; @@ -1976,6 +1987,106 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref p_state, return p_state->accessors.size() - 1; } +GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor) { + if (p_attribs.size() == 0) { + return -1; + } + + const int element_count = 3; + Vector attribs; + Vector type_max; + Vector type_min; + attribs.resize(p_attribs.size() * element_count); + type_max.resize(element_count); + type_min.resize(element_count); + + Vector changed_indices; + Vector changed_values; + int max_changed_index = 0; + + for (int i = 0; i < p_attribs.size(); i++) { + Vector3 attrib = p_attribs[i]; + attribs.write[(i * element_count) + 0] = _filter_number(attrib.x); + attribs.write[(i * element_count) + 1] = _filter_number(attrib.y); + attribs.write[(i * element_count) + 2] = _filter_number(attrib.z); + bool is_different = false; + if (i < p_reference_attribs.size()) { + is_different = !attrib.is_equal_approx(p_reference_attribs[i]); + } else { + is_different = !attrib.is_zero_approx(); + } + if (is_different) { + changed_indices.push_back(i); + if (i > max_changed_index) { + max_changed_index = i; + } + changed_values.push_back(_filter_number(attrib.x)); + changed_values.push_back(_filter_number(attrib.y)); + changed_values.push_back(_filter_number(attrib.z)); + } + _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); + } + _round_min_max_components(type_min, type_max); + + if (attribs.size() % element_count != 0) { + return -1; + } + + Ref sparse_accessor; + sparse_accessor.instantiate(); + int64_t size = p_state->buffers[0].size(); + const GLTFType type = GLTFType::TYPE_VEC3; + const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT; + + sparse_accessor->normalized = false; + sparse_accessor->count = p_attribs.size(); + sparse_accessor->type = type; + sparse_accessor->component_type = component_type; + if (p_reference_accessor < p_state->accessors.size() && p_reference_accessor >= 0 && p_state->accessors[p_reference_accessor].is_valid()) { + sparse_accessor->byte_offset = p_state->accessors[p_reference_accessor]->byte_offset; + sparse_accessor->buffer_view = p_state->accessors[p_reference_accessor]->buffer_view; + } + sparse_accessor->max = type_max; + sparse_accessor->min = type_min; + int sparse_accessor_index_stride = max_changed_index > 65535 ? 4 : 2; + + int sparse_accessor_storage_size = changed_indices.size() * (sparse_accessor_index_stride + element_count * sizeof(float)); + int conventional_storage_size = p_attribs.size() * element_count * sizeof(float); + + if (changed_indices.size() > 0 && sparse_accessor_storage_size < conventional_storage_size) { + // It must be worthwhile to use a sparse accessor. + + GLTFBufferIndex buffer_view_i_indices = -1; + GLTFBufferIndex buffer_view_i_values = -1; + if (sparse_accessor_index_stride == 4) { + sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_INT; + } else { + sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT; + } + if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFType::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) { + return -1; + } + // We use changed_indices.size() here, because we must pass the number of vec3 values rather than the number of components. + if (_encode_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->type, sparse_accessor->component_type, sparse_accessor->normalized, sparse_accessor->sparse_values_byte_offset, false, buffer_view_i_values) != OK) { + return -1; + } + sparse_accessor->sparse_indices_buffer_view = buffer_view_i_indices; + sparse_accessor->sparse_values_buffer_view = buffer_view_i_values; + sparse_accessor->sparse_count = changed_indices.size(); + } else if (changed_indices.size() > 0) { + GLTFBufferIndex buffer_view_i; + sparse_accessor->byte_offset = 0; + Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, sparse_accessor->normalized, size, p_for_vertex, buffer_view_i); + if (err != OK) { + return -1; + } + sparse_accessor->buffer_view = buffer_view_i; + } + p_state->accessors.push_back(sparse_accessor); + + return p_state->accessors.size() - 1; +} + GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { if (p_attribs.size() == 0) { return -1; @@ -2436,7 +2547,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]); } } - primitive["indices"] = _encode_accessor_as_ints(p_state, mesh_indices, true, true); + primitive["indices"] = _encode_accessor_as_ints(p_state, mesh_indices, false, true); } else { if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { //generate indices because they need to be swapped for CW/CCW @@ -2455,7 +2566,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { generated_indices.write[k + 2] = k + 1; } } - primitive["indices"] = _encode_accessor_as_ints(p_state, generated_indices, true, true); + primitive["indices"] = _encode_accessor_as_ints(p_state, generated_indices, false, true); } } } @@ -2466,6 +2577,23 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { print_verbose("glTF: Mesh has targets"); if (import_mesh->get_blend_shape_count()) { ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); + Vector reference_vertex_array = array[Mesh::ARRAY_VERTEX]; + Vector reference_normal_array = array[Mesh::ARRAY_NORMAL]; + Vector reference_tangent_array; + { + Vector tarr = array[Mesh::ARRAY_TANGENT]; + if (tarr.size()) { + const int ret_size = tarr.size() / 4; + reference_tangent_array.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Vector3 vec3; + vec3.x = tarr[(i * 4) + 0]; + vec3.y = tarr[(i * 4) + 1]; + vec3.z = tarr[(i * 4) + 2]; + reference_tangent_array.write[i] = vec3; + } + } + } for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i); Dictionary t; @@ -2479,13 +2607,24 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i]; } } - - t["POSITION"] = _encode_accessor_as_vec3(p_state, varr, true); + GLTFAccessorIndex position_accessor = attributes["POSITION"]; + if (position_accessor != -1) { + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, varr, reference_vertex_array, true, position_accessor); + if (new_accessor != -1) { + t["POSITION"] = new_accessor; + } + } } Vector narr = array_morph[Mesh::ARRAY_NORMAL]; if (narr.size()) { - t["NORMAL"] = _encode_accessor_as_vec3(p_state, narr, true); + GLTFAccessorIndex normal_accessor = attributes["NORMAL"]; + if (normal_accessor != -1) { + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, narr, reference_normal_array, true, normal_accessor); + if (new_accessor != -1) { + t["NORMAL"] = new_accessor; + } + } } Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; if (tarr.size()) { @@ -2497,8 +2636,15 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { vec3.x = tarr[(i * 4) + 0]; vec3.y = tarr[(i * 4) + 1]; vec3.z = tarr[(i * 4) + 2]; + attribs.write[i] = vec3; + } + GLTFAccessorIndex tangent_accessor = attributes["TANGENT"]; + if (tangent_accessor != -1) { + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, attribs, reference_tangent_array, true, tangent_accessor); + if (new_accessor != -1) { + t["TANGENT"] = new_accessor; + } } - t["TANGENT"] = _encode_accessor_as_vec3(p_state, attribs, true); } targets.push_back(t); } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 1682e9eeb7e..977b880ce41 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -57,13 +57,6 @@ public: ARRAY_BUFFER = 34962, ELEMENT_ARRAY_BUFFER = 34963, - TYPE_BYTE = 5120, - TYPE_UNSIGNED_BYTE = 5121, - TYPE_SHORT = 5122, - TYPE_UNSIGNED_SHORT = 5123, - TYPE_UNSIGNED_INT = 5125, - TYPE_FLOAT = 5126, - COMPONENT_TYPE_BYTE = 5120, COMPONENT_TYPE_UNSIGNED_BYTE = 5121, COMPONENT_TYPE_SHORT = 5122, @@ -255,6 +248,7 @@ private: GLTFAccessorIndex _encode_accessor_as_vec3(Ref p_state, const Vector p_attribs, const bool p_for_vertex); + GLTFAccessorIndex _encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor); GLTFAccessorIndex _encode_accessor_as_color(Ref p_state, const Vector p_attribs, const bool p_for_vertex); @@ -273,6 +267,7 @@ private: const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_indices = false); + Error _encode_accessors(Ref p_state); Error _encode_buffer_views(Ref p_state); Error _serialize_materials(Ref p_state); From db2c9571bbf3822684291c101fac6642342ccc2b Mon Sep 17 00:00:00 2001 From: Lyuma Date: Tue, 12 Mar 2024 02:39:15 -0700 Subject: [PATCH 2/2] glTF export: morph targets are relative, so use zero as reference --- modules/gltf/gltf_document.cpp | 68 +++++++++++++++++----------------- modules/gltf/gltf_document.h | 2 +- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index d50567bf1c4..1102073e81e 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -1987,7 +1987,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref p_state, return p_state->accessors.size() - 1; } -GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor) { +GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const float p_reference_multiplier, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor) { if (p_attribs.size() == 0) { return -1; } @@ -2006,15 +2006,21 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref p for (int i = 0; i < p_attribs.size(); i++) { Vector3 attrib = p_attribs[i]; + bool is_different = false; + if (i < p_reference_attribs.size()) { + is_different = !(attrib * p_reference_multiplier).is_equal_approx(p_reference_attribs[i]); + if (!is_different) { + attrib = p_reference_attribs[i]; + } + } else { + is_different = !(attrib * p_reference_multiplier).is_zero_approx(); + if (!is_different) { + attrib = Vector3(); + } + } attribs.write[(i * element_count) + 0] = _filter_number(attrib.x); attribs.write[(i * element_count) + 1] = _filter_number(attrib.y); attribs.write[(i * element_count) + 2] = _filter_number(attrib.z); - bool is_different = false; - if (i < p_reference_attribs.size()) { - is_different = !attrib.is_equal_approx(p_reference_attribs[i]); - } else { - is_different = !attrib.is_zero_approx(); - } if (is_different) { changed_indices.push_back(i); if (i > max_changed_index) { @@ -2577,39 +2583,23 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { print_verbose("glTF: Mesh has targets"); if (import_mesh->get_blend_shape_count()) { ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); - Vector reference_vertex_array = array[Mesh::ARRAY_VERTEX]; - Vector reference_normal_array = array[Mesh::ARRAY_NORMAL]; - Vector reference_tangent_array; - { - Vector tarr = array[Mesh::ARRAY_TANGENT]; - if (tarr.size()) { - const int ret_size = tarr.size() / 4; - reference_tangent_array.resize(ret_size); - for (int i = 0; i < ret_size; i++) { - Vector3 vec3; - vec3.x = tarr[(i * 4) + 0]; - vec3.y = tarr[(i * 4) + 1]; - vec3.z = tarr[(i * 4) + 2]; - reference_tangent_array.write[i] = vec3; - } - } - } + const float normal_tangent_sparse_rounding = 0.001; for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i); Dictionary t; Vector varr = array_morph[Mesh::ARRAY_VERTEX]; + Vector src_varr = array[Mesh::ARRAY_VERTEX]; Array mesh_arrays = import_mesh->get_surface_arrays(surface_i); - if (varr.size()) { - Vector src_varr = array[Mesh::ARRAY_VERTEX]; + if (varr.size() && varr.size() == src_varr.size()) { if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) { const int max_idx = src_varr.size(); for (int blend_i = 0; blend_i < max_idx; blend_i++) { - varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i]; + varr.write[blend_i] = varr[blend_i] - src_varr[blend_i]; } } GLTFAccessorIndex position_accessor = attributes["POSITION"]; if (position_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, varr, reference_vertex_array, true, position_accessor); + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, varr, Vector(), 1.0, true, -1); if (new_accessor != -1) { t["POSITION"] = new_accessor; } @@ -2617,30 +2607,38 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { } Vector narr = array_morph[Mesh::ARRAY_NORMAL]; - if (narr.size()) { + Vector src_narr = array[Mesh::ARRAY_NORMAL]; + if (narr.size() && narr.size() == src_narr.size()) { + if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) { + const int max_idx = src_narr.size(); + for (int blend_i = 0; blend_i < max_idx; blend_i++) { + narr.write[blend_i] = narr[blend_i] - src_narr[blend_i]; + } + } GLTFAccessorIndex normal_accessor = attributes["NORMAL"]; if (normal_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, narr, reference_normal_array, true, normal_accessor); + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, narr, Vector(), normal_tangent_sparse_rounding, true, -1); if (new_accessor != -1) { t["NORMAL"] = new_accessor; } } } Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; - if (tarr.size()) { + Vector src_tarr = array[Mesh::ARRAY_TANGENT]; + if (tarr.size() && tarr.size() == src_tarr.size()) { const int ret_size = tarr.size() / 4; Vector attribs; attribs.resize(ret_size); for (int i = 0; i < ret_size; i++) { Vector3 vec3; - vec3.x = tarr[(i * 4) + 0]; - vec3.y = tarr[(i * 4) + 1]; - vec3.z = tarr[(i * 4) + 2]; + vec3.x = tarr[(i * 4) + 0] - src_tarr[(i * 4) + 0]; + vec3.y = tarr[(i * 4) + 1] - src_tarr[(i * 4) + 1]; + vec3.z = tarr[(i * 4) + 2] - src_tarr[(i * 4) + 2]; attribs.write[i] = vec3; } GLTFAccessorIndex tangent_accessor = attributes["TANGENT"]; if (tangent_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, attribs, reference_tangent_array, true, tangent_accessor); + int new_accessor = _encode_sparse_accessor_as_vec3(p_state, attribs, Vector(), normal_tangent_sparse_rounding, true, -1); if (new_accessor != -1) { t["TANGENT"] = new_accessor; } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 977b880ce41..4f4e4175591 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -248,7 +248,7 @@ private: GLTFAccessorIndex _encode_accessor_as_vec3(Ref p_state, const Vector p_attribs, const bool p_for_vertex); - GLTFAccessorIndex _encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor); + GLTFAccessorIndex _encode_sparse_accessor_as_vec3(Ref p_state, const Vector p_attribs, const Vector p_reference_attribs, const float p_reference_multiplier, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor); GLTFAccessorIndex _encode_accessor_as_color(Ref p_state, const Vector p_attribs, const bool p_for_vertex);