From 62acc21bf51c66e0ef7959a914a8c61131426468 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Sun, 20 Jul 2025 07:39:40 -0700 Subject: [PATCH] GLTF: Move accessor encoding functions to GLTFAccessor --- modules/gltf/gltf_document.cpp | 1259 ++---------------- modules/gltf/gltf_document.h | 61 - modules/gltf/structures/gltf_accessor.cpp | 828 ++++++++++++ modules/gltf/structures/gltf_accessor.h | 39 +- modules/gltf/structures/gltf_buffer_view.cpp | 61 +- modules/gltf/structures/gltf_buffer_view.h | 1 + 6 files changed, 1003 insertions(+), 1246 deletions(-) diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index adeebde5211..4987a70a07e 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -33,6 +33,7 @@ #include "extensions/gltf_document_extension_convert_importer_mesh.h" #include "extensions/gltf_spec_gloss.h" #include "gltf_state.h" +#include "gltf_template_convert.h" #include "skin_tool.h" #include "core/config/project_settings.h" @@ -933,34 +934,6 @@ String GLTFDocument::_get_accessor_type_name(const GLTFAccessor::GLTFAccessorTyp ERR_FAIL_V("SCALAR"); } -GLTFAccessor::GLTFAccessorType GLTFDocument::_get_accessor_type_from_str(const String &p_string) { - if (p_string == "SCALAR") { - return GLTFAccessor::TYPE_SCALAR; - } - - if (p_string == "VEC2") { - return GLTFAccessor::TYPE_VEC2; - } - if (p_string == "VEC3") { - return GLTFAccessor::TYPE_VEC3; - } - if (p_string == "VEC4") { - return GLTFAccessor::TYPE_VEC4; - } - - if (p_string == "MAT2") { - return GLTFAccessor::TYPE_MAT2; - } - if (p_string == "MAT3") { - return GLTFAccessor::TYPE_MAT3; - } - if (p_string == "MAT4") { - return GLTFAccessor::TYPE_MAT4; - } - - ERR_FAIL_V(GLTFAccessor::TYPE_SCALAR); -} - Error GLTFDocument::_parse_accessors(Ref p_state) { if (!p_state->json.has("accessors")) { return OK; @@ -981,14 +954,6 @@ Error GLTFDocument::_parse_accessors(Ref p_state) { return OK; } -double GLTFDocument::_filter_number(double p_float) { - if (!Math::is_finite(p_float)) { - // 3.6.2.2. "Values of NaN, +Infinity, and -Infinity MUST NOT be present." - return 0.0f; - } - return (double)(float)p_float; -} - String GLTFDocument::_get_component_type_name(const GLTFAccessor::GLTFComponentType p_component) { switch (p_component) { case GLTFAccessor::COMPONENT_TYPE_NONE: @@ -1020,317 +985,6 @@ String GLTFDocument::_get_component_type_name(const GLTFAccessor::GLTFComponentT return ""; } -Error GLTFDocument::_encode_accessor_into_buffer_view(Ref p_state, const double *p_src, const int64_t p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const GLTFAccessor::GLTFComponentType p_component_type, const bool p_normalized, const int64_t p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_buffer_view, const bool p_for_vertex_indices) { - const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[p_accessor_type]; - const int component_size = _get_component_type_size(p_component_type); - ERR_FAIL_COND_V(component_size == 0, FAILED); - // The byte offset of an accessor MUST be a multiple of the accessor's component size. - // See 3.6.2.4: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment - int64_t offset = p_byte_offset; - if (p_byte_offset % component_size != 0) { - offset += component_size - (p_byte_offset % component_size); - } - - int64_t skip_every = 0; - int64_t skip_bytes = 0; - // Accessors of matrix type have data stored in column-major order. The start of each column MUST be aligned to 4-byte boundaries. - // See 3.6.2.4: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment - switch (p_component_type) { - case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: { - if (p_accessor_type == GLTFAccessor::TYPE_MAT2) { - skip_every = 2; - skip_bytes = 2; - } - if (p_accessor_type == GLTFAccessor::TYPE_MAT3) { - skip_every = 3; - skip_bytes = 1; - } - } break; - case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: { - if (p_accessor_type == GLTFAccessor::TYPE_MAT3) { - skip_every = 6; - skip_bytes = 2; - } - } break; - default: { - } - } - - Ref bv; - bv.instantiate(); - const GLTFBufferIndex buffer0 = 0; - bv->buffer = buffer0; - bv->byte_offset = offset; - Vector &gltf_buffer = p_state->buffers.write[buffer0]; - - int64_t stride = component_count * component_size; - if (p_for_vertex && stride % 4) { - stride += 4 - (stride % 4); //according to spec must be multiple of 4 - } - //use to debug - print_verbose("glTF: encoding accessor type " + _get_accessor_type_name(p_accessor_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count)); - - print_verbose("glTF: encoding accessor offset " + itos(offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length)); - - const int64_t 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) { - case GLTFAccessor::COMPONENT_TYPE_NONE: { - ERR_FAIL_V_MSG(ERR_INVALID_DATA, "glTF: Failed to encode buffer view, component type not set."); - } - case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - if (p_normalized) { - encoded_data.write[dst_i] = d * 128.0; - } else { - encoded_data.write[dst_i] = d; - } - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(int8_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - if (p_normalized) { - encoded_data.write[dst_i] = d * 255.0; - } else { - encoded_data.write[dst_i] = d; - } - p_src++; - dst_i++; - } - } - gltf_buffer.append_array(encoded_data); - const size_t buffer_size = encoded_data.size() * sizeof(uint8_t); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - if (p_normalized) { - encoded_data.write[dst_i] = d * 32768.0; - } else { - encoded_data.write[dst_i] = d; - } - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(int16_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - if (p_normalized) { - encoded_data.write[dst_i] = d * 65535.0; - } else { - encoded_data.write[dst_i] = d; - } - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(uint16_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(int32_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(uint32_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(float); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(double); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT: { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Half float not supported yet."); - } break; - case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - // FIXME: This can result in precision loss because int64_t can store some values that double can't. - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(int64_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: { - Vector encoded_data; - encoded_data.resize(p_count * component_count); - int64_t dst_i = 0; - for (int64_t i = 0; i < p_count; i++) { - for (int64_t j = 0; j < component_count; j++) { - if (skip_every && j > 0 && (j % skip_every) == 0) { - dst_i += skip_bytes; - } - // FIXME: This can result in precision loss because int64_t can store some values that double can't. - double d = *p_src; - encoded_data.write[dst_i] = d; - p_src++; - dst_i++; - } - } - const int64_t old_size = gltf_buffer.size(); - const size_t buffer_size = encoded_data.size() * sizeof(uint64_t); - gltf_buffer.resize(old_size + buffer_size); - memcpy(gltf_buffer.ptrw() + old_size, encoded_data.ptrw(), buffer_size); - bv->byte_length = buffer_size; - } break; - } - 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); - int64_t pad_bytes = (4 - gltf_buffer.size()) & 3; - for (int64_t i = 0; i < pad_bytes; i++) { - gltf_buffer.push_back(0); - } - - r_buffer_view = p_state->buffer_views.size(); - p_state->buffer_views.push_back(bv); - return OK; -} - Error GLTFDocument::_decode_buffer_view(Ref p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int64_t p_skip_every, const int64_t p_skip_bytes, const int64_t p_element_size, const int64_t p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const int64_t p_component_count, const GLTFAccessor::GLTFComponentType p_component_type, const int64_t p_component_size, const bool p_normalized, const int64_t p_byte_offset, const bool p_for_vertex) { const Ref bv = p_state->buffer_views[p_buffer_view]; @@ -1556,68 +1210,6 @@ Vector GLTFDocument::_decode_accessor(Ref p_state, const GLTF return dst_buffer; } -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref p_state, const Vector p_attribs, const bool p_for_vertex, const bool p_for_vertex_indices) { - if (p_attribs.is_empty()) { - return -1; - } - const int element_count = 1; - const int ret_size = p_attribs.size(); - Vector attribs; - attribs.resize(ret_size); - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - int max_index = 0; - for (int64_t 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]; - type_min.write[type_i] = attribs[(i * element_count) + type_i]; - } - } - for (int32_t type_i = 0; type_i < element_count; type_i++) { - type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]); - type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]); - } - } - ERR_FAIL_COND_V(attribs.is_empty(), -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_SCALAR; - GLTFAccessor::GLTFComponentType component_type; - if (max_index > 65534 || p_for_vertex) { - component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT; - } else { - component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT; - } - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = ret_size; - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i, p_for_vertex_indices); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - Vector GLTFDocument::_decode_accessor_as_ints(Ref p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector &p_packed_vertex_ids) { const Vector attribs = _decode_accessor(p_state, p_accessor, p_for_vertex); Vector ret; @@ -1668,289 +1260,6 @@ Vector GLTFDocument::_decode_accessor_as_floats(Ref p_state, c return ret; } -void GLTFDocument::_round_min_max_components(Vector &r_type_min, Vector &r_type_max) { - // 3.6.2.5: For floating-point components, JSON-stored minimum and maximum values represent single precision - // floats and SHOULD be rounded to single precision before usage to avoid any potential boundary mismatches. - for (int32_t type_i = 0; type_i < r_type_min.size(); type_i++) { - r_type_min.write[type_i] = (double)(float)r_type_min[type_i]; - r_type_max.write[type_i] = (double)(float)r_type_max[type_i]; - } -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - const int element_count = 2; - - const int64_t ret_size = p_attribs.size() * element_count; - Vector attribs; - attribs.resize(ret_size); - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - - for (int64_t i = 0; i < p_attribs.size(); i++) { - Vector2 attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = _filter_number(attrib.x); - attribs.write[(i * element_count) + 1] = _filter_number(attrib.y); - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC2; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - - const int64_t ret_size = p_attribs.size() * 4; - Vector attribs; - attribs.resize(ret_size); - - const int element_count = 4; - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t i = 0; i < p_attribs.size(); i++) { - Color attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = _filter_number(attrib.r); - attribs.write[(i * element_count) + 1] = _filter_number(attrib.g); - attribs.write[(i * element_count) + 2] = _filter_number(attrib.b); - attribs.write[(i * element_count) + 3] = _filter_number(attrib.a); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - -void GLTFDocument::_calc_accessor_min_max(int p_i, const int64_t p_element_count, Vector &p_type_max, Vector p_attribs, Vector &p_type_min) { - if (p_i == 0) { - for (int32_t type_i = 0; type_i < p_element_count; type_i++) { - p_type_max.write[type_i] = p_attribs[(p_i * p_element_count) + type_i]; - p_type_min.write[type_i] = p_attribs[(p_i * p_element_count) + type_i]; - } - } - for (int32_t type_i = 0; type_i < p_element_count; type_i++) { - p_type_max.write[type_i] = MAX(p_attribs[(p_i * p_element_count) + type_i], p_type_max[type_i]); - p_type_min.write[type_i] = MIN(p_attribs[(p_i * p_element_count) + type_i], p_type_min[type_i]); - } -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - - const int64_t ret_size = p_attribs.size() * 4; - Vector attribs; - attribs.resize(ret_size); - - const int element_count = 4; - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t i = 0; i < p_attribs.size(); i++) { - Color attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = _filter_number(attrib.r); - attribs.write[(i * element_count) + 1] = _filter_number(attrib.g); - attribs.write[(i * element_count) + 2] = _filter_number(attrib.b); - attribs.write[(i * element_count) + 3] = _filter_number(attrib.a); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - - const int element_count = 4; - const int64_t ret_size = p_attribs.size() * element_count; - Vector attribs; - attribs.resize(ret_size); - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t i = 0; i < p_attribs.size(); i++) { - Color attrib = p_attribs[i]; - attribs.write[(i * element_count) + 0] = _filter_number(attrib.r); - attribs.write[(i * element_count) + 1] = _filter_number(attrib.g); - attribs.write[(i * element_count) + 2] = _filter_number(attrib.b); - attribs.write[(i * element_count) + 3] = _filter_number(attrib.a); - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - const int element_count = 4; - - const int64_t ret_size = p_attribs.size() * element_count; - Vector attribs; - attribs.resize(ret_size); - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t i = 0; i < p_attribs.size(); i++) { - Quaternion quaternion = p_attribs[i]; - attribs.write[(i * element_count) + 0] = _filter_number(quaternion.x); - attribs.write[(i * element_count) + 1] = _filter_number(quaternion.y); - attribs.write[(i * element_count) + 2] = _filter_number(quaternion.z); - attribs.write[(i * element_count) + 3] = _filter_number(quaternion.w); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - Vector GLTFDocument::_decode_accessor_as_vec2(Ref p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector &p_packed_vertex_ids) { const Vector attribs = _decode_accessor(p_state, p_accessor, p_for_vertex); Vector ret; @@ -1977,286 +1286,6 @@ Vector GLTFDocument::_decode_accessor_as_vec2(Ref p_state, c return ret; } -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - const int element_count = 1; - const int64_t ret_size = p_attribs.size(); - Vector attribs; - attribs.resize(ret_size); - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - - for (int64_t i = 0; i < p_attribs.size(); i++) { - attribs.write[i] = _filter_number(p_attribs[i]); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - - ERR_FAIL_COND_V(attribs.is_empty(), -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_SCALAR; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = ret_size; - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref p_state, const Vector p_attribs, const bool p_for_vertex) { - if (p_attribs.is_empty()) { - return -1; - } - const int element_count = 3; - const int64_t ret_size = p_attribs.size() * element_count; - Vector attribs; - attribs.resize(ret_size); - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t 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); - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC3; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - 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 float p_reference_multiplier, const bool p_for_vertex, const GLTFAccessorIndex p_reference_accessor) { - if (p_attribs.is_empty()) { - 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 (int64_t 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); - 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(); - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC3; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - sparse_accessor->normalized = false; - sparse_accessor->count = p_attribs.size(); - sparse_accessor->accessor_type = accessor_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; - int64_t sparse_accessor_index_stride = max_changed_index > 65534 ? 4 : 2; - - int64_t sparse_accessor_storage_size = changed_indices.size() * (sparse_accessor_index_stride + element_count * sizeof(float)); - int64_t 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 = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT; - } else { - sparse_accessor->sparse_indices_component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT; - } - if (_encode_accessor_into_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFAccessor::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_accessor_into_buffer_view(p_state, changed_values.ptr(), changed_indices.size(), sparse_accessor->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_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_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.is_empty()) { - return -1; - } - const int64_t element_count = 16; - const int64_t ret_size = p_attribs.size() * element_count; - Vector attribs; - attribs.resize(ret_size); - - Vector type_max; - type_max.resize(element_count); - Vector type_min; - type_min.resize(element_count); - for (int64_t i = 0; i < p_attribs.size(); i++) { - Transform3D attrib = p_attribs[i]; - Basis basis = attrib.get_basis(); - Vector3 axis_0 = basis.get_column(Vector3::AXIS_X); - - attribs.write[i * element_count + 0] = _filter_number(axis_0.x); - attribs.write[i * element_count + 1] = _filter_number(axis_0.y); - attribs.write[i * element_count + 2] = _filter_number(axis_0.z); - attribs.write[i * element_count + 3] = 0.0; - - Vector3 axis_1 = basis.get_column(Vector3::AXIS_Y); - attribs.write[i * element_count + 4] = _filter_number(axis_1.x); - attribs.write[i * element_count + 5] = _filter_number(axis_1.y); - attribs.write[i * element_count + 6] = _filter_number(axis_1.z); - attribs.write[i * element_count + 7] = 0.0; - - Vector3 axis_2 = basis.get_column(Vector3::AXIS_Z); - attribs.write[i * element_count + 8] = _filter_number(axis_2.x); - attribs.write[i * element_count + 9] = _filter_number(axis_2.y); - attribs.write[i * element_count + 10] = _filter_number(axis_2.z); - attribs.write[i * element_count + 11] = 0.0; - - Vector3 origin = attrib.get_origin(); - attribs.write[i * element_count + 12] = _filter_number(origin.x); - attribs.write[i * element_count + 13] = _filter_number(origin.y); - attribs.write[i * element_count + 14] = _filter_number(origin.z); - attribs.write[i * element_count + 15] = 1.0; - - _calc_accessor_min_max(i, element_count, type_max, attribs, type_min); - } - _round_min_max_components(type_min, type_max); - ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1); - - Ref accessor; - accessor.instantiate(); - GLTFBufferIndex buffer_view_i; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - int64_t size = p_state->buffers[0].size(); - const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_MAT4; - const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT; - - accessor->max = type_max; - accessor->min = type_min; - accessor->normalized = false; - accessor->count = p_attribs.size(); - accessor->accessor_type = accessor_type; - accessor->component_type = component_type; - accessor->byte_offset = 0; - Error err = _encode_accessor_into_buffer_view(p_state, attribs.ptr(), p_attribs.size(), accessor_type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i); - if (err != OK) { - return -1; - } - accessor->buffer_view = buffer_view_i; - p_state->accessors.push_back(accessor); - return p_state->accessors.size() - 1; -} - Vector GLTFDocument::_decode_accessor_as_vec3(Ref p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex, const Vector &p_packed_vertex_ids) { const Vector attribs = _decode_accessor(p_state, p_accessor, p_for_vertex); Vector ret; @@ -2568,145 +1597,6 @@ Vector GLTFDocument::_decode_accessor_as_variant(Ref p_state return ret; } -GLTFAccessorIndex GLTFDocument::_encode_accessor_as_variant(Ref p_state, Vector p_attribs, Variant::Type p_variant_type, GLTFAccessor::GLTFAccessorType p_accessor_type, GLTFAccessor::GLTFComponentType p_component_type) { - const int accessor_component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[p_accessor_type]; - Vector encoded_attribs; - for (const Variant &v : p_attribs) { - switch (p_variant_type) { - case Variant::NIL: - case Variant::BOOL: - case Variant::INT: - case Variant::FLOAT: { - // For scalar values, just append them. Variant can convert all of these to double. Some padding may also be needed. - encoded_attribs.append(v); - if (unlikely(accessor_component_count > 1)) { - for (int i = 1; i < accessor_component_count; i++) { - encoded_attribs.append(0.0); - } - } - } break; - case Variant::VECTOR2: - case Variant::VECTOR2I: - case Variant::VECTOR3: - case Variant::VECTOR3I: - case Variant::VECTOR4: - case Variant::VECTOR4I: { - // Variant can handle converting Vector2/2i/3/3i/4/4i to Vector4 for us. - Vector4 vec = v; - if (likely(accessor_component_count < 5)) { - for (int i = 0; i < accessor_component_count; i++) { - encoded_attribs.append(vec[i]); - } - } - } break; - case Variant::PLANE: { - Plane p = v; - if (likely(accessor_component_count == 4)) { - encoded_attribs.append(p.normal.x); - encoded_attribs.append(p.normal.y); - encoded_attribs.append(p.normal.z); - encoded_attribs.append(p.d); - } - } break; - case Variant::QUATERNION: { - Quaternion q = v; - if (likely(accessor_component_count < 5)) { - for (int i = 0; i < accessor_component_count; i++) { - encoded_attribs.append(q[i]); - } - } - } break; - case Variant::COLOR: { - Color c = v; - if (likely(accessor_component_count < 5)) { - for (int i = 0; i < accessor_component_count; i++) { - encoded_attribs.append(c[i]); - } - } - } break; - case Variant::RECT2: - case Variant::RECT2I: { - // Variant can handle converting Rect2i to Rect2 for us. - Rect2 r = v; - if (likely(accessor_component_count == 4)) { - encoded_attribs.append(r.position.x); - encoded_attribs.append(r.position.y); - encoded_attribs.append(r.size.x); - encoded_attribs.append(r.size.y); - } - } break; - case Variant::TRANSFORM2D: - case Variant::BASIS: - case Variant::TRANSFORM3D: - case Variant::PROJECTION: { - // Variant can handle converting Transform2D/Transform3D/Basis to Projection for us. - Projection p = v; - if (accessor_component_count == 16) { - for (int i = 0; i < 4; i++) { - encoded_attribs.append(p.columns[i][0]); - encoded_attribs.append(p.columns[i][1]); - encoded_attribs.append(p.columns[i][2]); - encoded_attribs.append(p.columns[i][3]); - } - } else if (accessor_component_count == 9) { - for (int i = 0; i < 3; i++) { - encoded_attribs.append(p.columns[i][0]); - encoded_attribs.append(p.columns[i][1]); - encoded_attribs.append(p.columns[i][2]); - } - } else if (accessor_component_count == 4) { - encoded_attribs.append(p.columns[0][0]); - encoded_attribs.append(p.columns[0][1]); - encoded_attribs.append(p.columns[1][0]); - encoded_attribs.append(p.columns[1][1]); - } - } break; - default: { - ERR_FAIL_V_MSG(-1, "glTF: Cannot encode accessor from Variant of type " + Variant::get_type_name(p_variant_type) + "."); - } - } - } - // Determine the min and max values for the accessor. - Vector type_max; - type_max.resize(accessor_component_count); - Vector type_min; - type_min.resize(accessor_component_count); - for (int64_t i = 0; i < encoded_attribs.size(); i++) { - if (Math::is_zero_approx(encoded_attribs[i])) { - encoded_attribs.write[i] = 0.0; - } else { - encoded_attribs.write[i] = _filter_number(encoded_attribs[i]); - } - } - for (int i = 0; i < p_attribs.size(); i++) { - _calc_accessor_min_max(i, accessor_component_count, type_max, encoded_attribs, type_min); - } - _round_min_max_components(type_min, type_max); - // Encode the data in a buffer view. - GLTFBufferIndex buffer_view_index = 0; - if (p_state->buffers.is_empty()) { - p_state->buffers.push_back(Vector()); - } - const int64_t buffer_size = p_state->buffers[buffer_view_index].size(); - Error err = _encode_accessor_into_buffer_view(p_state, encoded_attribs.ptr(), p_attribs.size(), p_accessor_type, p_component_type, false, buffer_size, false, buffer_view_index); - if (err != OK) { - return -1; - } - // Create the accessor and fill it with the data. - Ref accessor; - accessor.instantiate(); - accessor->max = type_max; - accessor->min = type_min; - accessor->count = p_attribs.size(); - accessor->accessor_type = p_accessor_type; - accessor->component_type = p_component_type; - accessor->byte_offset = 0; - accessor->buffer_view = buffer_view_index; - const GLTFAccessorIndex new_accessor_index = p_state->accessors.size(); - p_state->accessors.push_back(accessor); - return new_accessor_index; -} - Error GLTFDocument::_serialize_meshes(Ref p_state) { Array meshes; for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < p_state->meshes.size(); gltf_mesh_i++) { @@ -2769,7 +1659,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { { Vector a = array[Mesh::ARRAY_VERTEX]; ERR_FAIL_COND_V(a.is_empty(), ERR_INVALID_DATA); - attributes["POSITION"] = _encode_accessor_as_vec3(p_state, a, true); + attributes["POSITION"] = GLTFAccessor::encode_new_accessor_from_vector3s(p_state, a, GLTFBufferView::TARGET_ARRAY_BUFFER); vertex_num = a.size(); } { @@ -2786,7 +1676,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { out.a = a[(i * 4) + 3]; attribs.write[i] = out; } - attributes["TANGENT"] = _encode_accessor_as_color(p_state, attribs, true); + attributes["TANGENT"] = GLTFAccessor::encode_new_accessor_from_colors(p_state, attribs, GLTFBufferView::TARGET_ARRAY_BUFFER); } } { @@ -2798,19 +1688,19 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { for (int64_t i = 0; i < ret_size; i++) { attribs.write[i] = Vector3(a[i]).normalized(); } - attributes["NORMAL"] = _encode_accessor_as_vec3(p_state, attribs, true); + attributes["NORMAL"] = GLTFAccessor::encode_new_accessor_from_vector3s(p_state, attribs, GLTFBufferView::TARGET_ARRAY_BUFFER); } } { Vector a = array[Mesh::ARRAY_TEX_UV]; if (a.size()) { - attributes["TEXCOORD_0"] = _encode_accessor_as_vec2(p_state, a, true); + attributes["TEXCOORD_0"] = GLTFAccessor::encode_new_accessor_from_vector2s(p_state, a, GLTFBufferView::TARGET_ARRAY_BUFFER); } } { Vector a = array[Mesh::ARRAY_TEX_UV2]; if (a.size()) { - attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(p_state, a, true); + attributes["TEXCOORD_1"] = GLTFAccessor::encode_new_accessor_from_vector2s(p_state, a, GLTFBufferView::TARGET_ARRAY_BUFFER); } } for (int custom_i = 0; custom_i < 3; custom_i++) { @@ -2839,7 +1729,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { if (!attributes.has(gltf_texcoord_key)) { Vector empty; empty.resize(vertex_num); - attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, empty, true); + attributes[gltf_texcoord_key] = GLTFAccessor::encode_new_accessor_from_vector2s(p_state, empty, GLTFBufferView::TARGET_ARRAY_BUFFER); } } @@ -2860,15 +1750,15 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { } } gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i); - attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, Vector(first_channel), true); + attributes[gltf_texcoord_key] = GLTFAccessor::encode_new_accessor_from_vector2s(p_state, PackedVector2Array(first_channel), GLTFBufferView::TARGET_ARRAY_BUFFER); gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1); - attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, Vector(second_channel), true); + attributes[gltf_texcoord_key] = GLTFAccessor::encode_new_accessor_from_vector2s(p_state, PackedVector2Array(second_channel), GLTFBufferView::TARGET_ARRAY_BUFFER); } } { Vector a = array[Mesh::ARRAY_COLOR]; if (a.size()) { - attributes["COLOR_0"] = _encode_accessor_as_color(p_state, a, true); + attributes["COLOR_0"] = GLTFAccessor::encode_new_accessor_from_colors(p_state, a, GLTFBufferView::TARGET_ARRAY_BUFFER); } } HashMap joint_i_to_bone_i; @@ -2887,7 +1777,7 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { const Vector &vertex_array = array[Mesh::ARRAY_VERTEX]; if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { const int ret_size = a.size() / JOINT_GROUP_SIZE; - Vector attribs; + Vector attribs; attribs.resize(ret_size); { for (int array_i = 0; array_i < attribs.size(); array_i++) { @@ -2895,32 +1785,32 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { int32_t joint_1 = a[(array_i * JOINT_GROUP_SIZE) + 1]; int32_t joint_2 = a[(array_i * JOINT_GROUP_SIZE) + 2]; int32_t joint_3 = a[(array_i * JOINT_GROUP_SIZE) + 3]; - attribs.write[array_i] = Color(joint_0, joint_1, joint_2, joint_3); + attribs.write[array_i] = Vector4i(joint_0, joint_1, joint_2, joint_3); } } - attributes["JOINTS_0"] = _encode_accessor_as_joints(p_state, attribs, true); + attributes["JOINTS_0"] = GLTFAccessor::encode_new_accessor_from_vector4is(p_state, attribs, GLTFBufferView::TARGET_ARRAY_BUFFER); } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - Vector joints_0; + Vector joints_0; joints_0.resize(vertex_num); - Vector joints_1; + Vector joints_1; joints_1.resize(vertex_num); int32_t weights_8_count = JOINT_GROUP_SIZE * 2; for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { - Color joint_0; - joint_0.r = a[vertex_i * weights_8_count + 0]; - joint_0.g = a[vertex_i * weights_8_count + 1]; - joint_0.b = a[vertex_i * weights_8_count + 2]; - joint_0.a = a[vertex_i * weights_8_count + 3]; + Vector4i joint_0; + joint_0.x = a[vertex_i * weights_8_count + 0]; + joint_0.y = a[vertex_i * weights_8_count + 1]; + joint_0.z = a[vertex_i * weights_8_count + 2]; + joint_0.w = a[vertex_i * weights_8_count + 3]; joints_0.write[vertex_i] = joint_0; - Color joint_1; - joint_1.r = a[vertex_i * weights_8_count + 4]; - joint_1.g = a[vertex_i * weights_8_count + 5]; - joint_1.b = a[vertex_i * weights_8_count + 6]; - joint_1.a = a[vertex_i * weights_8_count + 7]; + Vector4i joint_1; + joint_1.x = a[vertex_i * weights_8_count + 4]; + joint_1.y = a[vertex_i * weights_8_count + 5]; + joint_1.z = a[vertex_i * weights_8_count + 6]; + joint_1.w = a[vertex_i * weights_8_count + 7]; joints_1.write[vertex_i] = joint_1; } - attributes["JOINTS_0"] = _encode_accessor_as_joints(p_state, joints_0, true); - attributes["JOINTS_1"] = _encode_accessor_as_joints(p_state, joints_1, true); + attributes["JOINTS_0"] = GLTFAccessor::encode_new_accessor_from_vector4is(p_state, joints_0, GLTFBufferView::TARGET_ARRAY_BUFFER); + attributes["JOINTS_1"] = GLTFAccessor::encode_new_accessor_from_vector4is(p_state, joints_1, GLTFBufferView::TARGET_ARRAY_BUFFER); } } { @@ -2928,46 +1818,46 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { const Vector &vertex_array = array[Mesh::ARRAY_VERTEX]; if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) { int32_t vertex_count = vertex_array.size(); - Vector attribs; + Vector attribs; attribs.resize(vertex_count); for (int i = 0; i < vertex_count; i++) { - Color weight_0(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]); - float divisor = weight_0.r + weight_0.g + weight_0.b + weight_0.a; + Vector4 weight_0(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]); + float divisor = weight_0.x + weight_0.y + weight_0.z + weight_0.w; if (Math::is_zero_approx(divisor) || !Math::is_finite(divisor)) { - divisor = 1.0; - weight_0 = Color(1, 0, 0, 0); + attribs.write[i] = Vector4(1, 0, 0, 0); + } else { + attribs.write[i] = weight_0 / divisor; } - attribs.write[i] = weight_0 / divisor; } - attributes["WEIGHTS_0"] = _encode_accessor_as_weights(p_state, attribs, true); + attributes["WEIGHTS_0"] = GLTFAccessor::encode_new_accessor_from_vector4s(p_state, attribs, GLTFBufferView::TARGET_ARRAY_BUFFER); } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) { - Vector weights_0; + Vector weights_0; weights_0.resize(vertex_num); - Vector weights_1; + Vector weights_1; weights_1.resize(vertex_num); int32_t weights_8_count = JOINT_GROUP_SIZE * 2; for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) { - Color weight_0; - weight_0.r = a[vertex_i * weights_8_count + 0]; - weight_0.g = a[vertex_i * weights_8_count + 1]; - weight_0.b = a[vertex_i * weights_8_count + 2]; - weight_0.a = a[vertex_i * weights_8_count + 3]; - Color weight_1; - weight_1.r = a[vertex_i * weights_8_count + 4]; - weight_1.g = a[vertex_i * weights_8_count + 5]; - weight_1.b = a[vertex_i * weights_8_count + 6]; - weight_1.a = a[vertex_i * weights_8_count + 7]; - float divisor = weight_0.r + weight_0.g + weight_0.b + weight_0.a + weight_1.r + weight_1.g + weight_1.b + weight_1.a; + Vector4 weight_0; + weight_0.x = a[vertex_i * weights_8_count + 0]; + weight_0.y = a[vertex_i * weights_8_count + 1]; + weight_0.z = a[vertex_i * weights_8_count + 2]; + weight_0.w = a[vertex_i * weights_8_count + 3]; + Vector4 weight_1; + weight_1.x = a[vertex_i * weights_8_count + 4]; + weight_1.y = a[vertex_i * weights_8_count + 5]; + weight_1.z = a[vertex_i * weights_8_count + 6]; + weight_1.w = a[vertex_i * weights_8_count + 7]; + float divisor = weight_0.x + weight_0.y + weight_0.z + weight_0.w + weight_1.x + weight_1.y + weight_1.z + weight_1.w; if (Math::is_zero_approx(divisor) || !Math::is_finite(divisor)) { - divisor = 1.0f; - weight_0 = Color(1, 0, 0, 0); - weight_1 = Color(0, 0, 0, 0); + weights_0.write[vertex_i] = Vector4(1, 0, 0, 0); + weights_1.write[vertex_i] = Vector4(0, 0, 0, 0); + } else { + weights_0.write[vertex_i] = weight_0 / divisor; + weights_1.write[vertex_i] = weight_1 / divisor; } - weights_0.write[vertex_i] = weight_0 / divisor; - weights_1.write[vertex_i] = weight_1 / divisor; } - attributes["WEIGHTS_0"] = _encode_accessor_as_weights(p_state, weights_0, true); - attributes["WEIGHTS_1"] = _encode_accessor_as_weights(p_state, weights_1, true); + attributes["WEIGHTS_0"] = GLTFAccessor::encode_new_accessor_from_vector4s(p_state, weights_0, GLTFBufferView::TARGET_ARRAY_BUFFER); + attributes["WEIGHTS_1"] = GLTFAccessor::encode_new_accessor_from_vector4s(p_state, weights_1, GLTFBufferView::TARGET_ARRAY_BUFFER); } } { @@ -2980,7 +1870,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, false, true); + primitive["indices"] = GLTFAccessor::encode_new_accessor_from_int32s(p_state, mesh_indices, GLTFBufferView::TARGET_ELEMENT_ARRAY_BUFFER); } else { if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) { // Generate indices because they need to be swapped for CW/CCW. @@ -2999,7 +1889,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, false, true); + primitive["indices"] = GLTFAccessor::encode_new_accessor_from_int32s(p_state, generated_indices, GLTFBufferView::TARGET_ELEMENT_ARRAY_BUFFER); } } } @@ -3024,9 +1914,9 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { varr.write[blend_i] = varr[blend_i] - src_varr[blend_i]; } } - GLTFAccessorIndex position_accessor = attributes["POSITION"]; + const GLTFAccessorIndex position_accessor = attributes["POSITION"]; if (position_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, varr, Vector(), 1.0, true, -1); + const GLTFAccessorIndex new_accessor = GLTFAccessor::encode_new_sparse_accessor_from_vec3s(p_state, varr, Vector(), 1.0, GLTFBufferView::TARGET_ARRAY_BUFFER); if (new_accessor != -1) { t["POSITION"] = new_accessor; } @@ -3042,9 +1932,9 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { narr.write[blend_i] = narr[blend_i] - src_narr[blend_i]; } } - GLTFAccessorIndex normal_accessor = attributes["NORMAL"]; + const GLTFAccessorIndex normal_accessor = attributes["NORMAL"]; if (normal_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, narr, Vector(), normal_tangent_sparse_rounding, true, -1); + const GLTFAccessorIndex new_accessor = GLTFAccessor::encode_new_sparse_accessor_from_vec3s(p_state, narr, Vector(), normal_tangent_sparse_rounding, GLTFBufferView::TARGET_ARRAY_BUFFER); if (new_accessor != -1) { t["NORMAL"] = new_accessor; } @@ -3063,9 +1953,9 @@ Error GLTFDocument::_serialize_meshes(Ref p_state) { vec3.z = tarr[(i * 4) + 2] - src_tarr[(i * 4) + 2]; attribs.write[i] = vec3; } - GLTFAccessorIndex tangent_accessor = attributes["TANGENT"]; + const GLTFAccessorIndex tangent_accessor = attributes["TANGENT"]; if (tangent_accessor != -1) { - int new_accessor = _encode_sparse_accessor_as_vec3(p_state, attribs, Vector(), normal_tangent_sparse_rounding, true, -1); + const GLTFAccessorIndex new_accessor = GLTFAccessor::encode_new_sparse_accessor_from_vec3s(p_state, attribs, Vector(), normal_tangent_sparse_rounding, GLTFBufferView::TARGET_ARRAY_BUFFER); if (new_accessor != -1) { t["TANGENT"] = new_accessor; } @@ -5051,7 +3941,8 @@ Error GLTFDocument::_serialize_skins(Ref p_state) { for (int skin_i = 0; skin_i < p_state->skins.size(); skin_i++) { Ref gltf_skin = p_state->skins[skin_i]; Dictionary json_skin; - json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(p_state, gltf_skin->inverse_binds, false); + Array inv_binds_arr = GLTFTemplateConvert::to_array(gltf_skin->inverse_binds); + json_skin["inverseBindMatrices"] = GLTFAccessor::encode_new_accessor_from_variants(p_state, inv_binds_arr, Variant::TRANSFORM3D, GLTFAccessor::TYPE_MAT4, GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT); json_skin["joints"] = gltf_skin->get_joints(); json_skin["name"] = gltf_skin->get_name(); json_skins.push_back(json_skin); @@ -5283,9 +4174,9 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { s["interpolation"] = interpolation_to_string(track.position_track.interpolation); Vector times = track.position_track.times; - s["input"] = _encode_accessor_as_floats(p_state, times, false); + s["input"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, times); Vector values = track.position_track.values; - s["output"] = _encode_accessor_as_vec3(p_state, values, false); + s["output"] = GLTFAccessor::encode_new_accessor_from_vector3s(p_state, values); samplers.push_back(s); @@ -5303,9 +4194,9 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation); Vector times = track.rotation_track.times; - s["input"] = _encode_accessor_as_floats(p_state, times, false); + s["input"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, times); Vector values = track.rotation_track.values; - s["output"] = _encode_accessor_as_quaternions(p_state, values, false); + s["output"] = GLTFAccessor::encode_new_accessor_from_quaternions(p_state, values); samplers.push_back(s); @@ -5323,9 +4214,9 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { s["interpolation"] = interpolation_to_string(track.scale_track.interpolation); Vector times = track.scale_track.times; - s["input"] = _encode_accessor_as_floats(p_state, times, false); + s["input"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, times); Vector values = track.scale_track.values; - s["output"] = _encode_accessor_as_vec3(p_state, values, false); + s["output"] = GLTFAccessor::encode_new_accessor_from_vector3s(p_state, values); samplers.push_back(s); @@ -5403,8 +4294,8 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { } s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation); - s["input"] = _encode_accessor_as_floats(p_state, all_track_times, false); - s["output"] = _encode_accessor_as_floats(p_state, all_track_values, false); + s["input"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, all_track_times); + s["output"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, all_track_values); samplers.push_back(s); @@ -5437,9 +4328,11 @@ Error GLTFDocument::_serialize_animations(Ref p_state) { channel["target"] = channel_target; channels.push_back(channel); Dictionary sampler; - sampler["input"] = _encode_accessor_as_floats(p_state, pointer_track.times, false); + sampler["input"] = GLTFAccessor::encode_new_accessor_from_float64s(p_state, pointer_track.times); sampler["interpolation"] = interpolation_to_string(pointer_track.interpolation); - sampler["output"] = _encode_accessor_as_variant(p_state, pointer_track.values, obj_model_prop->get_variant_type(), obj_model_prop->get_accessor_type()); + // TODO: This can be made faster after this pull request is merged: https://github.com/godotengine/godot/pull/109003 + Array values_arr = GLTFTemplateConvert::to_array(pointer_track.values); + sampler["output"] = GLTFAccessor::encode_new_accessor_from_variants(p_state, values_arr, obj_model_prop->get_variant_type(), obj_model_prop->get_accessor_type()); samplers.push_back(sampler); } } diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 80d5a7f0b60..720035ead40 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -108,8 +108,6 @@ public: private: void _build_parent_hierarchy(Ref p_state); - double _filter_number(double p_float); - void _round_min_max_components(Vector &r_type_min, Vector &r_type_max); String _get_component_type_name(const GLTFAccessor::GLTFComponentType p_component_type); int _get_component_type_size(const GLTFAccessor::GLTFComponentType p_component_type); Error _parse_scenes(Ref p_state); @@ -134,7 +132,6 @@ private: void _compute_node_heights(Ref p_state); Error _parse_buffers(Ref p_state, const String &p_base_path); Error _parse_buffer_views(Ref p_state); - GLTFAccessor::GLTFAccessorType _get_accessor_type_from_str(const String &p_string); Error _parse_accessors(Ref p_state); Error _decode_buffer_view(Ref p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, @@ -183,11 +180,6 @@ private: const GLTFAccessorIndex p_accessor, Variant::Type p_variant_type, GLTFAccessor::GLTFAccessorType p_accessor_type); - GLTFAccessorIndex _encode_accessor_as_variant(Ref p_state, - Vector p_attribs, - Variant::Type p_variant_type, - GLTFAccessor::GLTFAccessorType p_accessor_type, - GLTFAccessor::GLTFComponentType p_component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT); Error _parse_meshes(Ref p_state); Error _serialize_textures(Ref p_state); Error _serialize_texture_samplers(Ref p_state); @@ -230,59 +222,6 @@ private: T _interpolate_track(const Vector &p_times, const Vector &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp); - GLTFAccessorIndex _encode_accessor_as_quaternions(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - GLTFAccessorIndex _encode_accessor_as_weights(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - GLTFAccessorIndex _encode_accessor_as_joints(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - GLTFAccessorIndex _encode_accessor_as_floats(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - GLTFAccessorIndex _encode_accessor_as_vec2(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - - void _calc_accessor_vec2_min_max(int p_i, const int64_t p_element_count, Vector &p_type_max, Vector2 p_attribs, Vector &p_type_min) { - if (p_i == 0) { - for (int64_t type_i = 0; type_i < p_element_count; type_i++) { - p_type_max.write[type_i] = p_attribs[(p_i * p_element_count) + type_i]; - p_type_min.write[type_i] = p_attribs[(p_i * p_element_count) + type_i]; - } - } - for (int64_t type_i = 0; type_i < p_element_count; type_i++) { - p_type_max.write[type_i] = MAX(p_attribs[(p_i * p_element_count) + type_i], p_type_max[type_i]); - p_type_min.write[type_i] = MIN(p_attribs[(p_i * p_element_count) + type_i], p_type_min[type_i]); - p_type_max.write[type_i] = _filter_number(p_type_max.write[type_i]); - p_type_min.write[type_i] = _filter_number(p_type_min.write[type_i]); - } - } - - 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 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); - - void _calc_accessor_min_max(int p_i, const int64_t p_element_count, Vector &p_type_max, Vector p_attribs, Vector &p_type_min); - - GLTFAccessorIndex _encode_accessor_as_ints(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex, - const bool p_for_indices); - GLTFAccessorIndex _encode_accessor_as_xform(Ref p_state, - const Vector p_attribs, - const bool p_for_vertex); - Error _encode_accessor_into_buffer_view(Ref p_state, const double *p_src, - const int64_t p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, - const GLTFAccessor::GLTFComponentType p_component_type, const bool p_normalized, - const int64_t p_byte_offset, const bool p_for_vertex, - GLTFBufferViewIndex &r_buffer_view, const bool p_for_indices = false); Error _encode_accessors(Ref p_state); Error _encode_buffer_views(Ref p_state); diff --git a/modules/gltf/structures/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp index a508cea12e2..43cf45f2af0 100644 --- a/modules/gltf/structures/gltf_accessor.cpp +++ b/modules/gltf/structures/gltf_accessor.cpp @@ -230,6 +230,150 @@ void GLTFAccessor::set_sparse_values_byte_offset(int64_t p_sparse_values_byte_of // Trivial helper functions. +void GLTFAccessor::_calculate_min_and_max(const PackedFloat64Array &p_numbers) { + const int64_t vector_size = _get_vector_size(); + ERR_FAIL_COND(vector_size <= 0 || p_numbers.size() % vector_size != 0); + min.resize(vector_size); + max.resize(vector_size); + // Initialize min and max with the first vector element values. + for (int64_t in_vec = 0; in_vec < vector_size; in_vec++) { + min.write[in_vec] = p_numbers[in_vec]; + max.write[in_vec] = p_numbers[in_vec]; + } + // Iterate over the rest of the vectors. + for (int64_t which_vec = vector_size; which_vec < p_numbers.size(); which_vec += vector_size) { + for (int64_t in_vec = 0; in_vec < vector_size; in_vec++) { + min.write[in_vec] = MIN(p_numbers[which_vec + in_vec], min[in_vec]); + max.write[in_vec] = MAX(p_numbers[which_vec + in_vec], max[in_vec]); + } + } + // 3.6.2.5: For floating-point components, JSON-stored minimum and maximum values represent single precision + // floats and SHOULD be rounded to single precision before usage to avoid any potential boundary mismatches. + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#accessors-bounds + if (component_type == GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT) { + for (int64_t i = 0; i < min.size(); i++) { + min.write[i] = (double)(float)min[i]; + max.write[i] = (double)(float)max[i]; + } + } +} + +void GLTFAccessor::_determine_pad_skip(int64_t &r_skip_every, int64_t &r_skip_bytes) const { + // 3.6.2.4. Accessors of matrix type have data stored in column-major order. The start of each column MUST be aligned to 4-byte boundaries. + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment + switch (component_type) { + case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: { + if (accessor_type == GLTFAccessor::TYPE_MAT2) { + r_skip_every = 2; + r_skip_bytes = 2; + } + if (accessor_type == GLTFAccessor::TYPE_MAT3) { + r_skip_every = 3; + r_skip_bytes = 1; + } + } break; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: { + if (accessor_type == GLTFAccessor::TYPE_MAT3) { + r_skip_every = 6; + r_skip_bytes = 2; + } + } break; + default: { + } break; + } +} + +int64_t GLTFAccessor::_determine_padded_byte_count(int64_t p_raw_byte_size) const { + // 3.6.2.4. Accessors of matrix type have data stored in column-major order. The start of each column MUST be aligned to 4-byte boundaries. + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment + switch (component_type) { + case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: { + if (accessor_type == GLTFAccessor::TYPE_MAT2) { + return p_raw_byte_size * 2; + } + if (accessor_type == GLTFAccessor::TYPE_MAT3) { + return p_raw_byte_size * 4 / 3; + } + } break; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: { + if (accessor_type == GLTFAccessor::TYPE_MAT3) { + return p_raw_byte_size * 4 / 3; + } + } break; + default: { + } break; + } + return p_raw_byte_size; +} + +PackedFloat64Array GLTFAccessor::_filter_numbers(const PackedFloat64Array &p_numbers) const { + PackedFloat64Array filtered_numbers = p_numbers; + for (int64_t i = 0; i < p_numbers.size(); i++) { + const double num = p_numbers[i]; + if (!Math::is_finite(num)) { + // 3.6.2.2. "Values of NaN, +Infinity, and -Infinity MUST NOT be present." + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#accessor-data-types + filtered_numbers.set(i, 0.0); + } else if (component_type == GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT) { + filtered_numbers.set(i, (double)(float)num); + } + } + return filtered_numbers; +} + +String GLTFAccessor::_get_component_type_name(const GLTFComponentType p_component) { + // These names are only for debugging and printing error messages, glTF uses the numeric values. + switch (p_component) { + case GLTFAccessor::COMPONENT_TYPE_NONE: + return "None"; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: + return "Byte"; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: + return "UByte"; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: + return "Short"; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: + return "UShort"; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT: + return "Int"; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: + return "UInt"; + case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT: + return "Float"; + case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT: + return "Double"; + case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT: + return "Half"; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG: + return "Long"; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: + return "ULong"; + } + + return ""; +} + +GLTFAccessor::GLTFComponentType GLTFAccessor::_get_indices_component_type_for_size(const int64_t p_size) { + ERR_FAIL_COND_V(p_size < 0, GLTFAccessor::COMPONENT_TYPE_NONE); + // 3.7.2.1. indices accessor MUST NOT contain the maximum possible value for the component type used + // (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview + if (unlikely(p_size > 4294967294LL)) { + return GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG; + } + if (p_size > 65534LL) { + return GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT; + } + if (p_size > 254LL) { + return GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT; + } + return GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE; +} + GLTFAccessor::GLTFAccessorType GLTFAccessor::_get_accessor_type_from_str(const String &p_string) { if (p_string == "SCALAR") { return GLTFAccessor::TYPE_SCALAR; @@ -277,6 +421,690 @@ String GLTFAccessor::_get_accessor_type_name() const { ERR_FAIL_V("SCALAR"); } +int64_t GLTFAccessor::_get_vector_size() const { + switch (accessor_type) { + case GLTFAccessor::TYPE_SCALAR: + return 1; + case GLTFAccessor::TYPE_VEC2: + return 2; + case GLTFAccessor::TYPE_VEC3: + return 3; + case GLTFAccessor::TYPE_VEC4: + return 4; + case GLTFAccessor::TYPE_MAT2: + return 4; + case GLTFAccessor::TYPE_MAT3: + return 9; + case GLTFAccessor::TYPE_MAT4: + return 16; + default: + break; + } + ERR_FAIL_V(0); +} + +int64_t GLTFAccessor::_get_bytes_per_component(const GLTFComponentType p_component_type) { + switch (p_component_type) { + case GLTFAccessor::COMPONENT_TYPE_NONE: + ERR_FAIL_V(0); + case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: + return 1; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: + case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT: + return 2; + case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: + case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT: + return 4; + case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT: + case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG: + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: + return 8; + } + ERR_FAIL_V(0); +} + +int64_t GLTFAccessor::_get_bytes_per_vector() const { + const int64_t raw_byte_size = _get_bytes_per_component(component_type) * _get_vector_size(); + return _determine_padded_byte_count(raw_byte_size); +} + +bool GLTFAccessor::is_equal_exact(const Ref &p_other) const { + if (p_other.is_null()) { + return false; + } + return (buffer_view == p_other->buffer_view && + byte_offset == p_other->byte_offset && + component_type == p_other->component_type && + normalized == p_other->normalized && + count == p_other->count && + accessor_type == p_other->accessor_type && + min == p_other->min && + max == p_other->max && + sparse_count == p_other->sparse_count && + sparse_indices_buffer_view == p_other->sparse_indices_buffer_view && + sparse_indices_byte_offset == p_other->sparse_indices_byte_offset && + sparse_indices_component_type == p_other->sparse_indices_component_type && + sparse_values_buffer_view == p_other->sparse_values_buffer_view && + sparse_values_byte_offset == p_other->sparse_values_byte_offset); +} + +// Private encode functions. + +PackedFloat64Array GLTFAccessor::_encode_variants_as_floats(const Array &p_input_data, Variant::Type p_variant_type) const { + const int64_t vector_size = _get_vector_size(); + const int64_t input_size = p_input_data.size(); + PackedFloat64Array numbers; + numbers.resize(input_size * vector_size); + for (int64_t input_index = 0; input_index < input_size; input_index++) { + Variant variant = p_input_data[input_index]; + const int64_t vector_offset = input_index * vector_size; + switch (p_variant_type) { + case Variant::NIL: + case Variant::BOOL: + case Variant::INT: + case Variant::FLOAT: { + // For scalar values, just append them. Variant can convert all of these to double. Some padding may also be needed. + numbers.set(vector_offset, variant); + if (unlikely(vector_size > 1)) { + for (int64_t i = 1; i < vector_size; i++) { + numbers.set(vector_offset + i, 0.0); + } + } + } break; + case Variant::PLANE: + case Variant::QUATERNION: + case Variant::RECT2: { + // Evil hack that relies on the structure of Variant, but it's the + // only way to accomplish this without a ton of code duplication. + *(Variant::Type *)&variant = Variant::VECTOR4; + } + [[fallthrough]]; + case Variant::VECTOR2: + case Variant::VECTOR3: + case Variant::VECTOR4: { + // Variant can handle converting Vector2/3/4 to Vector4 for us. + Vector4 vec = variant; + for (int64_t i = 0; i < vector_size; i++) { + numbers.set(vector_offset + i, vec[i]); + } + if (unlikely(vector_size > 4)) { + for (int64_t i = 4; i < vector_size; i++) { + numbers.set(vector_offset + i, 0.0); + } + } + } break; + case Variant::RECT2I: { + *(Variant::Type *)&variant = Variant::VECTOR4I; + } + [[fallthrough]]; + case Variant::VECTOR2I: + case Variant::VECTOR3I: + case Variant::VECTOR4I: { + // Variant can handle converting Vector2i/3i/4i to Vector4i for us. + Vector4i vec = variant; + for (int64_t i = 0; i < vector_size; i++) { + numbers.set(vector_offset + i, vec[i]); + } + if (unlikely(vector_size > 4)) { + for (int64_t i = 4; i < vector_size; i++) { + numbers.set(vector_offset + i, 0.0); + } + } + } break; + case Variant::COLOR: { + Color c = variant; + for (int64_t i = 0; i < vector_size; i++) { + numbers.set(vector_offset + i, c[i]); + } + if (unlikely(vector_size > 4)) { + for (int64_t i = 4; i < vector_size; i++) { + numbers.set(vector_offset + i, 0.0); + } + } + } break; + case Variant::TRANSFORM2D: + case Variant::BASIS: + case Variant::TRANSFORM3D: + case Variant::PROJECTION: { + // Variant can handle converting Transform2D/Transform3D/Basis to Projection for us. + Projection p = variant; + if (vector_size == 16) { + for (int64_t i = 0; i < 4; i++) { + numbers.set(vector_offset + 4 * i, p.columns[i][0]); + numbers.set(vector_offset + 4 * i + 1, p.columns[i][1]); + numbers.set(vector_offset + 4 * i + 2, p.columns[i][2]); + numbers.set(vector_offset + 4 * i + 3, p.columns[i][3]); + } + } else if (vector_size == 9) { + for (int64_t i = 0; i < 3; i++) { + numbers.set(vector_offset + 3 * i, p.columns[i][0]); + numbers.set(vector_offset + 3 * i + 1, p.columns[i][1]); + numbers.set(vector_offset + 3 * i + 2, p.columns[i][2]); + } + } else if (vector_size == 4) { + numbers.set(vector_offset, p.columns[0][0]); + numbers.set(vector_offset + 1, p.columns[0][1]); + numbers.set(vector_offset + 2, p.columns[1][0]); + numbers.set(vector_offset + 3, p.columns[1][1]); + } + } break; + default: { + ERR_FAIL_V_MSG(PackedFloat64Array(), "glTF export: Cannot encode accessor from Variant of type " + Variant::get_type_name(p_variant_type) + "."); + } + } + } + return numbers; +} + +void GLTFAccessor::_store_sparse_indices_into_state(const Ref &p_gltf_state, const PackedInt64Array &p_sparse_indices, const bool p_deduplicate) { + // The byte offset of a sparse accessor's indices buffer view MUST be a multiple of the indices primitive componentType. + // https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/accessor.sparse.indices.schema.json + const int64_t bytes_per_index = _get_bytes_per_component(sparse_indices_component_type); + PackedByteArray indices_bytes; + indices_bytes.resize(bytes_per_index * p_sparse_indices.size()); + uint8_t *ret_write = indices_bytes.ptrw(); + int64_t ret_byte_offset = 0; + for (int64_t i = 0; i < p_sparse_indices.size(); i++) { + switch (sparse_indices_component_type) { + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: { + *(uint8_t *)&ret_write[ret_byte_offset] = p_sparse_indices[i]; + } break; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: { + *(uint16_t *)&ret_write[ret_byte_offset] = p_sparse_indices[i]; + } break; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: { + *(uint32_t *)&ret_write[ret_byte_offset] = p_sparse_indices[i]; + } break; + case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: { + *(uint64_t *)&ret_write[ret_byte_offset] = p_sparse_indices[i]; + } break; + default: { + ERR_FAIL_MSG("glTF export: Invalid sparse indices component type '" + _get_component_type_name(sparse_indices_component_type) + "' for sparse accessor indices."); + } break; + } + ret_byte_offset += bytes_per_index; + } + const GLTFBufferViewIndex buffer_view_index = GLTFBufferView::write_new_buffer_view_into_state(p_gltf_state, indices_bytes, bytes_per_index, GLTFBufferView::TARGET_NONE, -1, 0, p_deduplicate); + ERR_FAIL_COND_MSG(buffer_view_index == -1, "glTF export: Failed to write sparse indices into glTF state."); + set_sparse_indices_buffer_view(buffer_view_index); +} + +// Low-level encode functions. + +GLTFAccessor::GLTFComponentType GLTFAccessor::get_minimal_integer_component_type_from_ints(const PackedInt64Array &p_numbers) { + bool has_negative = false; + for (int64_t i = 0; i < p_numbers.size(); i++) { + if (p_numbers[i] < 0) { + has_negative = true; + break; + } + } + if (has_negative) { + GLTFComponentType ret = GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE; + for (int64_t i = 0; i < p_numbers.size(); i++) { + const int64_t num = p_numbers[i]; + if (ret == GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE && (num < -128LL || num > 127LL)) { + ret = GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT; + } + if (ret == GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT && (num < -32768LL || num > 32767LL)) { + ret = GLTFAccessor::COMPONENT_TYPE_SIGNED_INT; + } + if (ret == GLTFAccessor::COMPONENT_TYPE_SIGNED_INT && (num < -2147483648LL || num > 2147483647LL)) { + return GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG; + } + } + return ret; + } + GLTFComponentType ret = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE; + for (int64_t i = 0; i < p_numbers.size(); i++) { + const int64_t num = p_numbers[i]; + // 3.7.2.1. indices accessor MUST NOT contain the maximum possible value for the component type used + // (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview + if (ret == GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE && num > 254LL) { + ret = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT; + } + if (ret == GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT && num > 65534LL) { + ret = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT; + } + if (ret == GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT && num > 4294967294LL) { + return GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG; + } + } + return ret; +} + +PackedByteArray GLTFAccessor::encode_floats_as_bytes(const PackedFloat64Array &p_input_numbers) { + // Filter and update `count`, `min`, and `max` based on the given data. + PackedFloat64Array filtered_numbers = _filter_numbers(p_input_numbers); + count = filtered_numbers.size() / _get_vector_size(); + _calculate_min_and_max(filtered_numbers); + // Actually encode the data. + const int64_t input_size = filtered_numbers.size(); + const int64_t bytes_per_component = _get_bytes_per_component(component_type); + int64_t raw_byte_size = _determine_padded_byte_count(bytes_per_component * input_size); + int64_t skip_every = 0; + int64_t skip_bytes = 0; + _determine_pad_skip(skip_every, skip_bytes); + PackedByteArray ret; + ret.resize(raw_byte_size); + uint8_t *ret_write = ret.ptrw(); + int64_t ret_byte_offset = 0; + for (int64_t i = 0; i < input_size; i++) { + switch (component_type) { + case COMPONENT_TYPE_NONE: { + ERR_FAIL_V_MSG(ret, "glTF export: Invalid component type 'NONE' for glTF accessor."); + } break; + case COMPONENT_TYPE_SIGNED_BYTE: { + *(int8_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_BYTE: { + *(uint8_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_SIGNED_SHORT: { + *(int16_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_SHORT: { + *(uint16_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_SIGNED_INT: { + *(int32_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_INT: { + *(uint32_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_SINGLE_FLOAT: { + *(float *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_DOUBLE_FLOAT: { + *(double *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_HALF_FLOAT: { + *(uint16_t *)&ret_write[ret_byte_offset] = Math::make_half_float(filtered_numbers[i]); + } break; + case COMPONENT_TYPE_SIGNED_LONG: { + // Note: This can potentially result in precision loss because int64_t can store some values that double can't. + *(int64_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_LONG: { + // Note: This can potentially result in precision loss because uint64_t can store some values that double can't. + *(uint64_t *)&ret_write[ret_byte_offset] = filtered_numbers[i]; + } break; + default: { + ERR_FAIL_V_MSG(ret, "glTF export: Godot does not support writing glTF accessor components of type '" + itos(component_type) + "'."); + } break; + } + ret_byte_offset += bytes_per_component; + if (unlikely(skip_every > 0)) { + if ((i + 1) % skip_every == 0) { + ret_byte_offset += skip_bytes; + } + } + } + ERR_FAIL_COND_V_MSG(ret_byte_offset != raw_byte_size, ret, "glTF export: Accessor encoded data did not write exactly the expected number of bytes."); + return ret; +} + +PackedByteArray GLTFAccessor::encode_ints_as_bytes(const PackedInt64Array &p_input_numbers) { + // Filter and update `count`, `min`, and `max` based on the given data. + count = p_input_numbers.size() / _get_vector_size(); + _calculate_min_and_max(Variant(p_input_numbers)); + // Actually encode the data. + const int64_t input_size = p_input_numbers.size(); + const int64_t bytes_per_component = _get_bytes_per_component(component_type); + int64_t raw_byte_size = _determine_padded_byte_count(bytes_per_component * input_size); + int64_t skip_every = 0; + int64_t skip_bytes = 0; + _determine_pad_skip(skip_every, skip_bytes); + PackedByteArray ret; + ret.resize(raw_byte_size); + uint8_t *ret_write = ret.ptrw(); + int64_t ret_byte_offset = 0; + for (int64_t i = 0; i < input_size; i++) { + switch (component_type) { + case COMPONENT_TYPE_NONE: { + ERR_FAIL_V_MSG(ret, "glTF export: Invalid component type 'NONE' for glTF accessor."); + } break; + case COMPONENT_TYPE_SIGNED_BYTE: { + *(int8_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_BYTE: { + *(uint8_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_SIGNED_SHORT: { + *(int16_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_SHORT: { + *(uint16_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_SIGNED_INT: { + *(int32_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_INT: { + *(uint32_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_SINGLE_FLOAT: { + *(float *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_DOUBLE_FLOAT: { + *(double *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_HALF_FLOAT: { + *(uint16_t *)&ret_write[ret_byte_offset] = Math::make_half_float(p_input_numbers[i]); + } break; + case COMPONENT_TYPE_SIGNED_LONG: { + *(int64_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + case COMPONENT_TYPE_UNSIGNED_LONG: { + *(uint64_t *)&ret_write[ret_byte_offset] = p_input_numbers[i]; + } break; + default: { + ERR_FAIL_V_MSG(ret, "glTF export: Godot does not support writing glTF accessor components of type '" + itos(component_type) + "'."); + } break; + } + ret_byte_offset += bytes_per_component; + if (unlikely(skip_every > 0)) { + if ((i + 1) % skip_every == 0) { + ret_byte_offset += skip_bytes; + } + } + } + ERR_FAIL_COND_V_MSG(ret_byte_offset != raw_byte_size, ret, "glTF export: Accessor encoded data did not write exactly the expected number of bytes."); + return ret; +} + +PackedByteArray GLTFAccessor::encode_variants_as_bytes(const Array &p_input_data, Variant::Type p_variant_type) { + const int64_t bytes_per_vec = _get_bytes_per_vector(); + ERR_FAIL_COND_V_MSG(bytes_per_vec == 0, PackedByteArray(), "glTF export: Cannot encode an accessor of this type."); + PackedFloat64Array numbers = _encode_variants_as_floats(p_input_data, p_variant_type); + return encode_floats_as_bytes(numbers); +} + +GLTFAccessorIndex GLTFAccessor::store_accessor_data_into_state(const Ref &p_gltf_state, const PackedByteArray &p_data_bytes, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const GLTFBufferIndex p_buffer_index, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_data_bytes.is_empty(), -1, "glTF export: Cannot store nothing."); + // Update `count` based on the size of the data. It's possible that `count` may already be correct, but this function is public, so this prevents footguns. + const int64_t bytes_per_vec = _get_bytes_per_vector(); + ERR_FAIL_COND_V_MSG(bytes_per_vec == 0 || p_data_bytes.size() % bytes_per_vec != 0, -1, "glTF export: Tried to store an accessor with data that is not a multiple of the accessor's bytes per vector."); + count = p_data_bytes.size() / bytes_per_vec; + // 3.6.2.4. The byte offset of an accessor's buffer view MUST be a multiple of the accessor's primitive size. + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment + const int64_t alignment = _get_bytes_per_component(component_type); + // 3.6.2.4. Each element of a vertex attribute MUST be aligned to 4-byte boundaries inside a bufferView. + int64_t byte_stride = -1; + if (p_buffer_view_target == GLTFBufferView::TARGET_ARRAY_BUFFER) { + byte_stride = bytes_per_vec; + ERR_FAIL_COND_V_MSG(byte_stride < 4 || byte_stride % 4 != 0, -1, "glTF export: Vertex attributes using TARGET_ARRAY_BUFFER must have a byte stride that is a multiple of 4 as required by section 3.6.2.4 of the glTF specification."); + } + // Write the data into a new buffer view. + const GLTFBufferViewIndex buffer_view_index = GLTFBufferView::write_new_buffer_view_into_state(p_gltf_state, p_data_bytes, alignment, p_buffer_view_target, byte_stride, 0, p_deduplicate); + ERR_FAIL_COND_V_MSG(buffer_view_index == -1, -1, "glTF export: Accessor failed to write new buffer view into glTF state."); + set_buffer_view(buffer_view_index); + // Add the new accessor to the state, but check for duplicates first. + TypedArray state_accessors = p_gltf_state->get_accessors(); + const GLTFAccessorIndex accessor_count = state_accessors.size(); + for (GLTFAccessorIndex i = 0; i < accessor_count; i++) { + Ref existing_accessor = state_accessors[i]; + if (is_equal_exact(existing_accessor)) { + // An identical accessor already exists in the state, so just return the index. + return i; + } + } + Ref self = this; + state_accessors.append(self); + p_gltf_state->set_accessors(state_accessors); + return accessor_count; +} + +Ref GLTFAccessor::make_new_accessor_without_data(GLTFAccessorType p_accessor_type, GLTFComponentType p_component_type) { + Ref accessor; + accessor.instantiate(); + accessor->set_accessor_type(p_accessor_type); + accessor->set_component_type(p_component_type); + return accessor; +} + +// High-level encode functions. + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_colors(const Ref &p_gltf_state, const PackedColorArray &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedFloat64Array numbers; + numbers.resize(p_input_data.size() * 4); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Color &color = p_input_data[i]; + numbers.set(i * 4, color.r); + numbers.set(i * 4 + 1, color.g); + numbers.set(i * 4 + 2, color.b); + numbers.set(i * 4 + 3, color.a); + } + Ref accessor = make_new_accessor_without_data(TYPE_VEC4, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_float64s(const Ref &p_gltf_state, const PackedFloat64Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + Ref accessor = make_new_accessor_without_data(TYPE_SCALAR, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(p_input_data); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_int32s(const Ref &p_gltf_state, const PackedInt32Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedInt64Array numbers; + numbers.resize(p_input_data.size()); + for (int64_t i = 0; i < p_input_data.size(); i++) { + numbers.set(i, p_input_data[i]); + } + const GLTFComponentType component_type = get_minimal_integer_component_type_from_ints(numbers); + Ref accessor = make_new_accessor_without_data(TYPE_SCALAR, component_type); + PackedByteArray encoded_bytes = accessor->encode_ints_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_int64s(const Ref &p_gltf_state, const PackedInt64Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + const GLTFComponentType component_type = get_minimal_integer_component_type_from_ints(p_input_data); + Ref accessor = make_new_accessor_without_data(TYPE_SCALAR, component_type); + PackedByteArray encoded_bytes = accessor->encode_ints_as_bytes(p_input_data); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_quaternions(const Ref &p_gltf_state, const Vector &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedFloat64Array numbers; + numbers.resize(p_input_data.size() * 4); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Quaternion &quat = p_input_data[i]; + numbers.set(i * 4, quat.x); + numbers.set(i * 4 + 1, quat.y); + numbers.set(i * 4 + 2, quat.z); + numbers.set(i * 4 + 3, quat.w); + } + Ref accessor = make_new_accessor_without_data(TYPE_VEC4, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_variants(const Ref &p_gltf_state, const Array &p_input_data, Variant::Type p_variant_type, GLTFAccessorType p_accessor_type, GLTFComponentType p_component_type, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + Ref accessor = make_new_accessor_without_data(p_accessor_type, p_component_type); + // Write the data into a new buffer view. + PackedByteArray encoded_bytes = accessor->encode_variants_as_bytes(p_input_data, p_variant_type); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_vector2s(const Ref &p_gltf_state, const PackedVector2Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedFloat64Array numbers; + numbers.resize(p_input_data.size() * 2); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Vector2 &vec = p_input_data[i]; + numbers.set(i * 2, vec.x); + numbers.set(i * 2 + 1, vec.y); + } + Ref accessor = make_new_accessor_without_data(TYPE_VEC2, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_vector3s(const Ref &p_gltf_state, const PackedVector3Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedFloat64Array numbers; + numbers.resize(p_input_data.size() * 3); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Vector3 &vec = p_input_data[i]; + numbers.set(i * 3, vec.x); + numbers.set(i * 3 + 1, vec.y); + numbers.set(i * 3 + 2, vec.z); + } + Ref accessor = make_new_accessor_without_data(TYPE_VEC3, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_vector4s(const Ref &p_gltf_state, const PackedVector4Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedFloat64Array numbers; + numbers.resize(p_input_data.size() * 4); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Vector4 &vec = p_input_data[i]; + numbers.set(i * 4, vec.x); + numbers.set(i * 4 + 1, vec.y); + numbers.set(i * 4 + 2, vec.z); + numbers.set(i * 4 + 3, vec.w); + } + Ref accessor = make_new_accessor_without_data(TYPE_VEC4, COMPONENT_TYPE_SINGLE_FLOAT); + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_accessor_from_vector4is(const Ref &p_gltf_state, const Vector &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_input_data.is_empty(), -1, "glTF export: Cannot encode an accessor from an empty array."); + PackedInt64Array numbers; + numbers.resize(p_input_data.size() * 4); + for (int64_t i = 0; i < p_input_data.size(); i++) { + const Vector4i &vec = p_input_data[i]; + numbers.set(i * 4, vec.x); + numbers.set(i * 4 + 1, vec.y); + numbers.set(i * 4 + 2, vec.z); + numbers.set(i * 4 + 3, vec.w); + } + const GLTFComponentType component_type = get_minimal_integer_component_type_from_ints(numbers); + Ref accessor = make_new_accessor_without_data(TYPE_VEC4, component_type); + PackedByteArray encoded_bytes = accessor->encode_ints_as_bytes(numbers); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_buffer_view_target, 0, p_deduplicate); +} + +GLTFAccessorIndex GLTFAccessor::encode_new_sparse_accessor_from_vec3s(const Ref &p_gltf_state, const PackedVector3Array &p_input_data, const PackedVector3Array &p_base_reference_data, const double p_tolerance_multiplier, const GLTFBufferView::ArrayBufferTarget p_main_buffer_view_target, const bool p_deduplicate) { + const int64_t input_size = p_input_data.size(); + ERR_FAIL_COND_V_MSG(input_size == 0, -1, "glTF export: Cannot encode an accessor from an empty array."); + const bool is_base_empty = p_base_reference_data.is_empty(); + ERR_FAIL_COND_V_MSG(!is_base_empty && p_base_reference_data.size() != input_size, -1, "glTF export: Base reference data must either be empty, or have the same size as the main input data."); + PackedInt64Array sparse_indices; + PackedFloat64Array sparse_values; + PackedFloat64Array dense_values; + int64_t highest_index = 0; + dense_values.resize(input_size * 3); + for (int64_t i = 0; i < input_size; i++) { + Vector3 vec = p_input_data[i]; + Vector3 base_ref_vec; + Vector3 displacement; + if (is_base_empty) { + base_ref_vec = Vector3(); + displacement = vec; + } else { + base_ref_vec = p_base_reference_data[i]; + displacement = vec - base_ref_vec; + } + if ((displacement * p_tolerance_multiplier).is_zero_approx()) { + vec = base_ref_vec; + } else { + highest_index = i; + sparse_indices.append(i); + sparse_values.append(vec.x); + sparse_values.append(vec.y); + sparse_values.append(vec.z); + } + dense_values.set(i * 3, vec.x); + dense_values.set(i * 3 + 1, vec.y); + dense_values.set(i * 3 + 2, vec.z); + } + // Check if the sparse accessor actually saves space, or if it's better to just use a normal accessor. + const int64_t sparse_count = sparse_indices.size(); + const int64_t bytes_per_value_component = _get_bytes_per_component(COMPONENT_TYPE_SINGLE_FLOAT); + const GLTFComponentType indices_component_type = _get_indices_component_type_for_size(highest_index); + const int64_t sparse_data_bytes = _get_bytes_per_component(indices_component_type) * sparse_count + bytes_per_value_component * sparse_values.size(); + const int64_t dense_data_bytes = bytes_per_value_component * 3 * input_size; + // Sparse accessors require more JSON, a bit under 200 characters when minified, so factor that in. + constexpr int64_t sparse_json_fluff = 200; + Ref accessor = make_new_accessor_without_data(TYPE_VEC3, COMPONENT_TYPE_SINGLE_FLOAT); + if (sparse_data_bytes + sparse_json_fluff >= dense_data_bytes) { + // Sparse accessor is not worth it, just use a normal accessor instead. + // However, note that we use the calculated dense values instead of the original input data. + // This way, regardless of the underlying storage layout, the data is the same in both cases. + PackedByteArray encoded_bytes = accessor->encode_floats_as_bytes(dense_values); + ERR_FAIL_COND_V_MSG(encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, encoded_bytes, p_main_buffer_view_target, 0, p_deduplicate); + } + // Encode as a sparse accessor. + if (sparse_count > 0) { + accessor->set_sparse_count(sparse_count); + accessor->set_sparse_indices_component_type(indices_component_type); + accessor->_store_sparse_indices_into_state(p_gltf_state, sparse_indices, p_deduplicate); + const PackedByteArray sparse_values_encoded_bytes = accessor->encode_floats_as_bytes(sparse_values); + ERR_FAIL_COND_V_MSG(sparse_values_encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode sparse values as bytes."); + // Note: Sparse values always use TARGET_NONE, it does NOT match the target of the main buffer view. + const GLTFBufferViewIndex sparse_values_buffer_view_index = GLTFBufferView::write_new_buffer_view_into_state(p_gltf_state, sparse_values_encoded_bytes, bytes_per_value_component, GLTFBufferView::TARGET_NONE, -1, 0, p_deduplicate); + accessor->set_sparse_values_buffer_view(sparse_values_buffer_view_index); + } + // If the base reference data is empty, just directly add the accessor with only sparse data. + if (is_base_empty) { + // This is similar to `encode_floats_as_bytes` + `store_accessor_data_into_state` but we don't write a buffer view. + // Filter and update `count`, `min`, and `max` based on the given data. + accessor->set_count(input_size); + const PackedFloat64Array filtered_numbers = accessor->_filter_numbers(dense_values); + accessor->_calculate_min_and_max(filtered_numbers); + // Add the new accessor to the state, but check for duplicates first. + TypedArray state_accessors = p_gltf_state->get_accessors(); + const GLTFAccessorIndex accessor_count = state_accessors.size(); + for (GLTFAccessorIndex i = 0; i < accessor_count; i++) { + Ref existing_accessor = state_accessors[i]; + if (accessor->is_equal_exact(existing_accessor)) { + // An identical accessor already exists in the state, so just return the index. + return i; + } + } + state_accessors.append(accessor); + p_gltf_state->set_accessors(state_accessors); + return accessor_count; + } + // Encode the base reference alongside the sparse data. + PackedFloat64Array base_reference_values; + base_reference_values.resize(input_size * 3); + for (int64_t i = 0; i < input_size; i++) { + const Vector3 &base_ref_vec = p_base_reference_data[i]; + base_reference_values.set(i * 3, base_ref_vec.x); + base_reference_values.set(i * 3 + 1, base_ref_vec.y); + base_reference_values.set(i * 3 + 2, base_ref_vec.z); + } + const PackedByteArray base_reference_encoded_bytes = accessor->encode_floats_as_bytes(base_reference_values); + ERR_FAIL_COND_V_MSG(base_reference_encoded_bytes.is_empty(), -1, "glTF export: Accessor failed to encode data as bytes (was the input data empty?)."); + return accessor->store_accessor_data_into_state(p_gltf_state, base_reference_encoded_bytes, p_main_buffer_view_target, 0, p_deduplicate); +} + // Dictionary conversion. Ref GLTFAccessor::from_dictionary(const Dictionary &p_dict) { diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h index 2eb2aa3ebab..ffffd26eb9f 100644 --- a/modules/gltf/structures/gltf_accessor.h +++ b/modules/gltf/structures/gltf_accessor.h @@ -81,8 +81,21 @@ private: int64_t sparse_values_byte_offset = 0; // Trivial helper functions. - static GLTFAccessor::GLTFAccessorType _get_accessor_type_from_str(const String &p_string); + void _calculate_min_and_max(const PackedFloat64Array &p_numbers); + void _determine_pad_skip(int64_t &r_skip_every, int64_t &r_skip_bytes) const; + int64_t _determine_padded_byte_count(int64_t p_raw_byte_size) const; + PackedFloat64Array _filter_numbers(const PackedFloat64Array &p_numbers) const; + static String _get_component_type_name(const GLTFComponentType p_component); + static GLTFComponentType _get_indices_component_type_for_size(const int64_t p_size); + static GLTFAccessorType _get_accessor_type_from_str(const String &p_string); String _get_accessor_type_name() const; + int64_t _get_vector_size() const; + static int64_t _get_bytes_per_component(const GLTFComponentType p_component_type); + int64_t _get_bytes_per_vector() const; + + // Private encode functions. + PackedFloat64Array _encode_variants_as_floats(const Array &p_input_data, Variant::Type p_variant_type) const; + void _store_sparse_indices_into_state(const Ref &p_gltf_state, const PackedInt64Array &p_sparse_indices, const bool p_deduplicate = true); protected: static void _bind_methods(); @@ -156,6 +169,30 @@ public: int64_t get_sparse_values_byte_offset() const; void set_sparse_values_byte_offset(int64_t p_sparse_values_byte_offset); + bool is_equal_exact(const Ref &p_other) const; + + // Low-level encode functions. + static GLTFComponentType get_minimal_integer_component_type_from_ints(const PackedInt64Array &p_numbers); + PackedByteArray encode_floats_as_bytes(const PackedFloat64Array &p_input_numbers); + PackedByteArray encode_ints_as_bytes(const PackedInt64Array &p_input_numbers); + PackedByteArray encode_variants_as_bytes(const Array &p_input_data, Variant::Type p_variant_type); + + GLTFAccessorIndex store_accessor_data_into_state(const Ref &p_gltf_state, const PackedByteArray &p_data_bytes, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const GLTFBufferIndex p_buffer_index = 0, const bool p_deduplicate = true); + static Ref make_new_accessor_without_data(GLTFAccessorType p_accessor_type = TYPE_SCALAR, GLTFComponentType p_component_type = COMPONENT_TYPE_SINGLE_FLOAT); + + // High-level encode functions. + static GLTFAccessorIndex encode_new_accessor_from_colors(const Ref &p_gltf_state, const PackedColorArray &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_float64s(const Ref &p_gltf_state, const PackedFloat64Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_int32s(const Ref &p_gltf_state, const PackedInt32Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_int64s(const Ref &p_gltf_state, const PackedInt64Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_quaternions(const Ref &p_gltf_state, const Vector &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_variants(const Ref &p_gltf_state, const Array &p_input_data, Variant::Type p_variant_type, GLTFAccessorType p_accessor_type = TYPE_SCALAR, GLTFComponentType p_component_type = COMPONENT_TYPE_SINGLE_FLOAT, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_vector2s(const Ref &p_gltf_state, const PackedVector2Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_vector3s(const Ref &p_gltf_state, const PackedVector3Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_vector4s(const Ref &p_gltf_state, const PackedVector4Array &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_accessor_from_vector4is(const Ref &p_gltf_state, const Vector &p_input_data, const GLTFBufferView::ArrayBufferTarget p_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + static GLTFAccessorIndex encode_new_sparse_accessor_from_vec3s(const Ref &p_gltf_state, const PackedVector3Array &p_input_data, const PackedVector3Array &p_base_reference_data, const double p_tolerance_multiplier = 1.0, const GLTFBufferView::ArrayBufferTarget p_main_buffer_view_target = GLTFBufferView::TARGET_NONE, const bool p_deduplicate = true); + // Dictionary conversion. static Ref from_dictionary(const Dictionary &p_dict); Dictionary to_dictionary() const; diff --git a/modules/gltf/structures/gltf_buffer_view.cpp b/modules/gltf/structures/gltf_buffer_view.cpp index 782f77ac669..2bb7c8eedd5 100644 --- a/modules/gltf/structures/gltf_buffer_view.cpp +++ b/modules/gltf/structures/gltf_buffer_view.cpp @@ -110,14 +110,73 @@ void GLTFBufferView::set_vertex_attributes(bool p_attributes) { Vector GLTFBufferView::load_buffer_view_data(const Ref p_gltf_state) const { ERR_FAIL_COND_V(p_gltf_state.is_null(), Vector()); - ERR_FAIL_COND_V_MSG(byte_stride > 0, Vector(), "Buffer views with byte stride are not yet supported by this method."); const TypedArray> &buffers = p_gltf_state->get_buffers(); ERR_FAIL_INDEX_V(buffer, buffers.size(), Vector()); const PackedByteArray &buffer_data = buffers[buffer]; const int64_t byte_end = byte_offset + byte_length; + // Note that for buffer views with a byte stride, the parts of this data which get used may + // only be determined in combination with the accessors that reference this buffer view. return buffer_data.slice(byte_offset, byte_end); } +GLTFBufferViewIndex GLTFBufferView::write_new_buffer_view_into_state(const Ref &p_gltf_state, const PackedByteArray &p_input_data, const int64_t p_alignment, const ArrayBufferTarget p_target, const int64_t p_byte_stride, const GLTFBufferIndex p_buffer_index, const bool p_deduplicate) { + ERR_FAIL_COND_V_MSG(p_buffer_index < 0, -1, "Buffer index must be greater than or equal to zero."); + const bool target_is_indices = p_target == ArrayBufferTarget::TARGET_ELEMENT_ARRAY_BUFFER; + const bool target_is_vertex_attributes = p_target == ArrayBufferTarget::TARGET_ARRAY_BUFFER; + if (target_is_vertex_attributes) { + ERR_FAIL_COND_V_MSG(p_byte_stride < 4 || p_byte_stride % 4 != 0, -1, "glTF export: Vertex attributes using TARGET_ARRAY_BUFFER must have a byte stride that is a multiple of 4 as required by section 3.6.2.4 of the glTF specification."); + } + // Check for duplicate buffer views before adding a new one. + TypedArray state_buffer_views = p_gltf_state->get_buffer_views(); + const int buffer_view_index = state_buffer_views.size(); + if (p_deduplicate) { + for (int i = 0; i < buffer_view_index; i++) { + const Ref existing_buffer_view = state_buffer_views[i]; + if (existing_buffer_view->get_byte_offset() % p_alignment == 0 && + existing_buffer_view->get_byte_length() == p_input_data.size() && + existing_buffer_view->get_byte_stride() == p_byte_stride && + existing_buffer_view->get_indices() == target_is_indices && + existing_buffer_view->get_vertex_attributes() == target_is_vertex_attributes) { + if (existing_buffer_view->load_buffer_view_data(p_gltf_state) == p_input_data) { + // Duplicate found, return the index of the existing buffer view. + return i; + } + } + } + } + // Write the data into the buffer at the specified index. + TypedArray state_buffers = p_gltf_state->get_buffers(); + if (state_buffers.size() <= p_buffer_index) { + state_buffers.resize(p_buffer_index + 1); + } + PackedByteArray state_buffer = state_buffers[p_buffer_index]; + const int64_t input_data_size = p_input_data.size(); + // This is used by accessors. The byte offset of an accessor MUST be a multiple of the accessor's component size. + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#data-alignment + int64_t byte_offset = state_buffer.size(); + if (byte_offset % p_alignment != 0) { + byte_offset += p_alignment - (byte_offset % p_alignment); + } + state_buffer.resize(byte_offset + input_data_size); + uint8_t *buffer_ptr = state_buffer.ptrw(); + memcpy(buffer_ptr + byte_offset, p_input_data.ptr(), input_data_size); + state_buffers[p_buffer_index] = state_buffer; + p_gltf_state->set_buffers(state_buffers); + // Create a new GLTFBufferView that references the new buffer. + Ref buffer_view; + buffer_view.instantiate(); + buffer_view->set_buffer(p_buffer_index); + buffer_view->set_byte_offset(byte_offset); + buffer_view->set_byte_length(input_data_size); + buffer_view->set_byte_stride(p_byte_stride); + buffer_view->set_indices(target_is_indices); + buffer_view->set_vertex_attributes(target_is_vertex_attributes); + // Add the new buffer view to the state. + state_buffer_views.append(buffer_view); + p_gltf_state->set_buffer_views(state_buffer_views); + return buffer_view_index; +} + Ref GLTFBufferView::from_dictionary(const Dictionary &p_dict) { // See https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/bufferView.schema.json Ref buffer_view; diff --git a/modules/gltf/structures/gltf_buffer_view.h b/modules/gltf/structures/gltf_buffer_view.h index 3d0c1b7eed3..97a22906bdf 100644 --- a/modules/gltf/structures/gltf_buffer_view.h +++ b/modules/gltf/structures/gltf_buffer_view.h @@ -90,6 +90,7 @@ public: void set_vertex_attributes(bool p_attributes); Vector load_buffer_view_data(const Ref p_gltf_state) const; + static GLTFBufferViewIndex write_new_buffer_view_into_state(const Ref &p_gltf_state, const PackedByteArray &p_input_data, const int64_t p_alignment = 1, const ArrayBufferTarget p_target = TARGET_NONE, const int64_t p_byte_stride = -1, const GLTFBufferIndex p_buffer_index = 0, const bool p_deduplicate = true); static Ref from_dictionary(const Dictionary &p_dict); Dictionary to_dictionary() const;