diff --git a/extern/cgltf.h b/extern/cgltf.h index 767e7b243..03d7da6b0 100644 --- a/extern/cgltf.h +++ b/extern/cgltf.h @@ -2352,11 +2352,46 @@ const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_att return NULL; } +static const uint8_t* cgltf_find_sparse_index(const cgltf_accessor* accessor, cgltf_size needle) +{ + const cgltf_accessor_sparse* sparse = &accessor->sparse; + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* value_data = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || value_data == NULL) + return NULL; + + index_data += sparse->indices_byte_offset; + value_data += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + + cgltf_size offset = 0; + cgltf_size length = sparse->count; + + while (length) + { + cgltf_size rem = length % 2; + length /= 2; + + cgltf_size index = cgltf_component_read_index(index_data + (offset + length) * index_stride, sparse->indices_component_type); + offset += index < needle ? length + rem : 0; + } + + if (offset == sparse->count) + return NULL; + + cgltf_size index = offset < sparse->count ? cgltf_component_read_index(index_data + offset * index_stride, sparse->indices_component_type) : (cgltf_size)-1; + return index == needle ? value_data + offset * accessor->stride : NULL; +} + cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) { if (accessor->is_sparse) { - return 0; + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); } if (accessor->buffer_view == NULL) { @@ -2500,11 +2535,13 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i { if (accessor->is_sparse) { - return 0; + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); } if (accessor->buffer_view == NULL) { - memset(out, 0, element_size * sizeof( cgltf_uint )); + memset(out, 0, element_size * sizeof(cgltf_uint)); return 1; } const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); @@ -2520,7 +2557,9 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size { if (accessor->is_sparse) { - return 0; // This is an error case, but we can't communicate the error with existing interface. + const uint8_t* element = cgltf_find_sparse_index(accessor, index); + if (element) + return cgltf_component_read_index(element, accessor->component_type); } if (accessor->buffer_view == NULL) { diff --git a/gltf/parsegltf.cpp b/gltf/parsegltf.cpp index aa7cfc7ec..268a85daa 100644 --- a/gltf/parsegltf.cpp +++ b/gltf/parsegltf.cpp @@ -66,6 +66,14 @@ static void readAccessor(std::vector& data, const cgltf_accessor* accessor } } +static void readAccessor(std::vector& data, const cgltf_accessor* accessor, const std::vector& sparse) +{ + data.resize(sparse.size()); + + for (size_t i = 0; i < sparse.size(); ++i) + cgltf_accessor_read_float(accessor, sparse[i], &data[i].f[0], 4); +} + static void fixupIndices(std::vector& indices, cgltf_primitive_type& type) { if (type == cgltf_primitive_type_line_loop) @@ -177,11 +185,8 @@ static void parseMeshesGltf(cgltf_data* data, std::vector& meshes, std::ve Mesh& result = meshes.back(); result.scene = -1; - result.material = primitive.material; - result.extras = primitive.extras; - result.type = primitive.type; result.streams.reserve(primitive.attributes_count); @@ -199,14 +204,27 @@ static void parseMeshesGltf(cgltf_data* data, std::vector& meshes, std::ve } else if (primitive.type != cgltf_primitive_type_points) { - // note, while we could generate a good index buffer, reindexMesh will take care of this + // note, while we could generate a good index buffer here, mesh will be reindexed during processing result.indices.resize(vertex_count); for (size_t i = 0; i < vertex_count; ++i) result.indices[i] = unsigned(i); } + // convert line loops and line/triangle strips to lists fixupIndices(result.indices, result.type); + std::vector sparse; + + // if the index data is very sparse, switch to deindexing on the fly to avoid the excessive cost of reading large accessors + if (!result.indices.empty() && result.indices.size() < vertex_count / 2) + { + sparse = result.indices; + + // mesh will be reindexed during processing + for (size_t i = 0; i < result.indices.size(); ++i) + result.indices[i] = unsigned(i); + } + for (size_t ai = 0; ai < primitive.attributes_count; ++ai) { const cgltf_attribute& attr = primitive.attributes[ai]; @@ -232,7 +250,10 @@ static void parseMeshesGltf(cgltf_data* data, std::vector& meshes, std::ve if (attr.type == cgltf_attribute_type_custom) s.custom_name = attr.name; - readAccessor(s.data, attr.data); + if (sparse.empty()) + readAccessor(s.data, attr.data); + else + readAccessor(s.data, attr.data, sparse); if (attr.type == cgltf_attribute_type_color && attr.data->type == cgltf_type_vec3) { @@ -262,7 +283,10 @@ static void parseMeshesGltf(cgltf_data* data, std::vector& meshes, std::ve s.index = attr.index; s.target = int(ti + 1); - readAccessor(s.data, attr.data); + if (sparse.empty()) + readAccessor(s.data, attr.data); + else + readAccessor(s.data, attr.data, sparse); } } @@ -312,11 +336,11 @@ static void parseMeshInstancesGltf(std::vector& instances, cgltf_node for (size_t i = 0; i < count; ++i) { if (translation) - cgltf_accessor_read_float(translation, i, instance.translation, sizeof(float)); + cgltf_accessor_read_float(translation, i, instance.translation, 4); if (rotation) - cgltf_accessor_read_float(rotation, i, instance.rotation, sizeof(float)); + cgltf_accessor_read_float(rotation, i, instance.rotation, 4); if (scale) - cgltf_accessor_read_float(scale, i, instance.scale, sizeof(float)); + cgltf_accessor_read_float(scale, i, instance.scale, 4); Transform xf; cgltf_node_transform_world(&instance, xf.data);