Skip to content

Commit 5cb7f8f

Browse files
authored
Merge pull request #885 from zeux/gltf-shacc
gltfpack: Deindex meshes with abnormally large vertex accessors
2 parents 36e465e + e9b0029 commit 5cb7f8f

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

extern/cgltf.h

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,11 +2352,46 @@ const cgltf_accessor* cgltf_find_accessor(const cgltf_primitive* prim, cgltf_att
23522352
return NULL;
23532353
}
23542354

2355+
static const uint8_t* cgltf_find_sparse_index(const cgltf_accessor* accessor, cgltf_size needle)
2356+
{
2357+
const cgltf_accessor_sparse* sparse = &accessor->sparse;
2358+
const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view);
2359+
const uint8_t* value_data = cgltf_buffer_view_data(sparse->values_buffer_view);
2360+
2361+
if (index_data == NULL || value_data == NULL)
2362+
return NULL;
2363+
2364+
index_data += sparse->indices_byte_offset;
2365+
value_data += sparse->values_byte_offset;
2366+
2367+
cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
2368+
2369+
cgltf_size offset = 0;
2370+
cgltf_size length = sparse->count;
2371+
2372+
while (length)
2373+
{
2374+
cgltf_size rem = length % 2;
2375+
length /= 2;
2376+
2377+
cgltf_size index = cgltf_component_read_index(index_data + (offset + length) * index_stride, sparse->indices_component_type);
2378+
offset += index < needle ? length + rem : 0;
2379+
}
2380+
2381+
if (offset == sparse->count)
2382+
return NULL;
2383+
2384+
cgltf_size index = offset < sparse->count ? cgltf_component_read_index(index_data + offset * index_stride, sparse->indices_component_type) : (cgltf_size)-1;
2385+
return index == needle ? value_data + offset * accessor->stride : NULL;
2386+
}
2387+
23552388
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
23562389
{
23572390
if (accessor->is_sparse)
23582391
{
2359-
return 0;
2392+
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
2393+
if (element)
2394+
return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
23602395
}
23612396
if (accessor->buffer_view == NULL)
23622397
{
@@ -2500,11 +2535,13 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i
25002535
{
25012536
if (accessor->is_sparse)
25022537
{
2503-
return 0;
2538+
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
2539+
if (element)
2540+
return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size);
25042541
}
25052542
if (accessor->buffer_view == NULL)
25062543
{
2507-
memset(out, 0, element_size * sizeof( cgltf_uint ));
2544+
memset(out, 0, element_size * sizeof(cgltf_uint));
25082545
return 1;
25092546
}
25102547
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
25202557
{
25212558
if (accessor->is_sparse)
25222559
{
2523-
return 0; // This is an error case, but we can't communicate the error with existing interface.
2560+
const uint8_t* element = cgltf_find_sparse_index(accessor, index);
2561+
if (element)
2562+
return cgltf_component_read_index(element, accessor->component_type);
25242563
}
25252564
if (accessor->buffer_view == NULL)
25262565
{

gltf/parsegltf.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ static void readAccessor(std::vector<Attr>& data, const cgltf_accessor* accessor
6666
}
6767
}
6868

69+
static void readAccessor(std::vector<Attr>& data, const cgltf_accessor* accessor, const std::vector<unsigned int>& sparse)
70+
{
71+
data.resize(sparse.size());
72+
73+
for (size_t i = 0; i < sparse.size(); ++i)
74+
cgltf_accessor_read_float(accessor, sparse[i], &data[i].f[0], 4);
75+
}
76+
6977
static void fixupIndices(std::vector<unsigned int>& indices, cgltf_primitive_type& type)
7078
{
7179
if (type == cgltf_primitive_type_line_loop)
@@ -177,11 +185,8 @@ static void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes, std::ve
177185
Mesh& result = meshes.back();
178186

179187
result.scene = -1;
180-
181188
result.material = primitive.material;
182-
183189
result.extras = primitive.extras;
184-
185190
result.type = primitive.type;
186191

187192
result.streams.reserve(primitive.attributes_count);
@@ -199,14 +204,27 @@ static void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes, std::ve
199204
}
200205
else if (primitive.type != cgltf_primitive_type_points)
201206
{
202-
// note, while we could generate a good index buffer, reindexMesh will take care of this
207+
// note, while we could generate a good index buffer here, mesh will be reindexed during processing
203208
result.indices.resize(vertex_count);
204209
for (size_t i = 0; i < vertex_count; ++i)
205210
result.indices[i] = unsigned(i);
206211
}
207212

213+
// convert line loops and line/triangle strips to lists
208214
fixupIndices(result.indices, result.type);
209215

216+
std::vector<unsigned int> sparse;
217+
218+
// if the index data is very sparse, switch to deindexing on the fly to avoid the excessive cost of reading large accessors
219+
if (!result.indices.empty() && result.indices.size() < vertex_count / 2)
220+
{
221+
sparse = result.indices;
222+
223+
// mesh will be reindexed during processing
224+
for (size_t i = 0; i < result.indices.size(); ++i)
225+
result.indices[i] = unsigned(i);
226+
}
227+
210228
for (size_t ai = 0; ai < primitive.attributes_count; ++ai)
211229
{
212230
const cgltf_attribute& attr = primitive.attributes[ai];
@@ -232,7 +250,10 @@ static void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes, std::ve
232250
if (attr.type == cgltf_attribute_type_custom)
233251
s.custom_name = attr.name;
234252

235-
readAccessor(s.data, attr.data);
253+
if (sparse.empty())
254+
readAccessor(s.data, attr.data);
255+
else
256+
readAccessor(s.data, attr.data, sparse);
236257

237258
if (attr.type == cgltf_attribute_type_color && attr.data->type == cgltf_type_vec3)
238259
{
@@ -262,7 +283,10 @@ static void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes, std::ve
262283
s.index = attr.index;
263284
s.target = int(ti + 1);
264285

265-
readAccessor(s.data, attr.data);
286+
if (sparse.empty())
287+
readAccessor(s.data, attr.data);
288+
else
289+
readAccessor(s.data, attr.data, sparse);
266290
}
267291
}
268292

@@ -312,11 +336,11 @@ static void parseMeshInstancesGltf(std::vector<Transform>& instances, cgltf_node
312336
for (size_t i = 0; i < count; ++i)
313337
{
314338
if (translation)
315-
cgltf_accessor_read_float(translation, i, instance.translation, sizeof(float));
339+
cgltf_accessor_read_float(translation, i, instance.translation, 4);
316340
if (rotation)
317-
cgltf_accessor_read_float(rotation, i, instance.rotation, sizeof(float));
341+
cgltf_accessor_read_float(rotation, i, instance.rotation, 4);
318342
if (scale)
319-
cgltf_accessor_read_float(scale, i, instance.scale, sizeof(float));
343+
cgltf_accessor_read_float(scale, i, instance.scale, 4);
320344

321345
Transform xf;
322346
cgltf_node_transform_world(&instance, xf.data);

0 commit comments

Comments
 (0)