Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/meshoptimizer/meshletutils.cpp
59209 views
1
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
2
#include "meshoptimizer.h"
3
4
#include <assert.h>
5
#include <float.h>
6
#include <math.h>
7
#include <string.h>
8
9
// This work is based on:
10
// Matthaeus Chajdas. GeometryFX 1.2 - Cluster Culling. 2016
11
// Jack Ritter. An Efficient Bounding Sphere. 1990
12
// Thomas Larsson. Fast and Tight Fitting Bounding Spheres. 2008
13
namespace meshopt
14
{
15
16
// This must be <= 256 since meshlet indices are stored as bytes
17
const size_t kMeshletMaxVertices = 256;
18
19
// A reasonable limit is around 2*max_vertices or less
20
const size_t kMeshletMaxTriangles = 512;
21
22
static void computeBoundingSphere(float result[4], const float* points, size_t count, size_t points_stride, const float* radii, size_t radii_stride, size_t axis_count, const unsigned int* indices = NULL)
23
{
24
static const float axes[7][3] = {
25
// X, Y, Z
26
{1, 0, 0},
27
{0, 1, 0},
28
{0, 0, 1},
29
30
// XYZ, -XYZ, X-YZ, XY-Z; normalized to unit length
31
{0.57735026f, 0.57735026f, 0.57735026f},
32
{-0.57735026f, 0.57735026f, 0.57735026f},
33
{0.57735026f, -0.57735026f, 0.57735026f},
34
{0.57735026f, 0.57735026f, -0.57735026f},
35
};
36
37
assert(count > 0);
38
assert(axis_count <= sizeof(axes) / sizeof(axes[0]));
39
40
size_t points_stride_float = points_stride / sizeof(float);
41
size_t radii_stride_float = radii_stride / sizeof(float);
42
43
// find extremum points along all axes; for each axis we get a pair of points with min/max coordinates
44
unsigned int pmin[7], pmax[7];
45
float tmin[7], tmax[7];
46
47
for (size_t axis = 0; axis < axis_count; ++axis)
48
{
49
pmin[axis] = pmax[axis] = 0;
50
tmin[axis] = FLT_MAX;
51
tmax[axis] = -FLT_MAX;
52
}
53
54
for (size_t i = 0; i < count; ++i)
55
{
56
unsigned int v = indices ? indices[i] : unsigned(i);
57
const float* p = points + v * points_stride_float;
58
float r = radii[v * radii_stride_float];
59
60
for (size_t axis = 0; axis < axis_count; ++axis)
61
{
62
const float* ax = axes[axis];
63
64
float tp = ax[0] * p[0] + ax[1] * p[1] + ax[2] * p[2];
65
float tpmin = tp - r, tpmax = tp + r;
66
67
pmin[axis] = (tpmin < tmin[axis]) ? v : pmin[axis];
68
pmax[axis] = (tpmax > tmax[axis]) ? v : pmax[axis];
69
tmin[axis] = (tpmin < tmin[axis]) ? tpmin : tmin[axis];
70
tmax[axis] = (tpmax > tmax[axis]) ? tpmax : tmax[axis];
71
}
72
}
73
74
// find the pair of points with largest distance
75
size_t paxis = 0;
76
float paxisdr = 0;
77
78
for (size_t axis = 0; axis < axis_count; ++axis)
79
{
80
const float* p1 = points + pmin[axis] * points_stride_float;
81
const float* p2 = points + pmax[axis] * points_stride_float;
82
float r1 = radii[pmin[axis] * radii_stride_float];
83
float r2 = radii[pmax[axis] * radii_stride_float];
84
85
float d2 = (p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1]) + (p2[2] - p1[2]) * (p2[2] - p1[2]);
86
float dr = sqrtf(d2) + r1 + r2;
87
88
if (dr > paxisdr)
89
{
90
paxisdr = dr;
91
paxis = axis;
92
}
93
}
94
95
// use the longest segment as the initial sphere diameter
96
const float* p1 = points + pmin[paxis] * points_stride_float;
97
const float* p2 = points + pmax[paxis] * points_stride_float;
98
float r1 = radii[pmin[paxis] * radii_stride_float];
99
float r2 = radii[pmax[paxis] * radii_stride_float];
100
101
float paxisd = sqrtf((p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1]) + (p2[2] - p1[2]) * (p2[2] - p1[2]));
102
float paxisk = paxisd > 0 ? (paxisd + r2 - r1) / (2 * paxisd) : 0.f;
103
104
float center[3] = {p1[0] + (p2[0] - p1[0]) * paxisk, p1[1] + (p2[1] - p1[1]) * paxisk, p1[2] + (p2[2] - p1[2]) * paxisk};
105
float radius = paxisdr / 2;
106
107
// iteratively adjust the sphere up until all points fit
108
for (size_t i = 0; i < count; ++i)
109
{
110
unsigned int v = indices ? indices[i] : unsigned(i);
111
const float* p = points + v * points_stride_float;
112
float r = radii[v * radii_stride_float];
113
114
float d2 = (p[0] - center[0]) * (p[0] - center[0]) + (p[1] - center[1]) * (p[1] - center[1]) + (p[2] - center[2]) * (p[2] - center[2]);
115
float d = sqrtf(d2);
116
117
if (d + r > radius)
118
{
119
float k = d > 0 ? (d + r - radius) / (2 * d) : 0.f;
120
121
center[0] += k * (p[0] - center[0]);
122
center[1] += k * (p[1] - center[1]);
123
center[2] += k * (p[2] - center[2]);
124
radius = (radius + d + r) / 2;
125
}
126
}
127
128
result[0] = center[0];
129
result[1] = center[1];
130
result[2] = center[2];
131
result[3] = radius;
132
}
133
134
static meshopt_Bounds computeClusterBounds(const unsigned int* indices, size_t index_count, const unsigned int* corners, size_t corner_count, const float* vertex_positions, size_t vertex_positions_stride)
135
{
136
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
137
138
// compute triangle normals (.w completes plane equation)
139
float normals[kMeshletMaxTriangles][4];
140
size_t triangles = 0;
141
142
for (size_t i = 0; i < index_count; i += 3)
143
{
144
unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];
145
146
const float* p0 = vertex_positions + vertex_stride_float * a;
147
const float* p1 = vertex_positions + vertex_stride_float * b;
148
const float* p2 = vertex_positions + vertex_stride_float * c;
149
150
float p10[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]};
151
float p20[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]};
152
153
float normalx = p10[1] * p20[2] - p10[2] * p20[1];
154
float normaly = p10[2] * p20[0] - p10[0] * p20[2];
155
float normalz = p10[0] * p20[1] - p10[1] * p20[0];
156
157
float area = sqrtf(normalx * normalx + normaly * normaly + normalz * normalz);
158
159
// no need to include degenerate triangles - they will be invisible anyway
160
if (area == 0.f)
161
continue;
162
163
normalx /= area;
164
normaly /= area;
165
normalz /= area;
166
167
// record triangle normals; normal and corner 0 define a plane equation
168
normals[triangles][0] = normalx;
169
normals[triangles][1] = normaly;
170
normals[triangles][2] = normalz;
171
normals[triangles][3] = -(normalx * p0[0] + normaly * p0[1] + normalz * p0[2]);
172
triangles++;
173
}
174
175
meshopt_Bounds bounds = {};
176
177
// degenerate cluster, no valid triangles => trivial reject (cone data is 0)
178
if (triangles == 0)
179
return bounds;
180
181
const float rzero = 0.f;
182
183
// compute cluster bounding sphere; we'll use the center to determine normal cone apex as well
184
float psphere[4] = {};
185
computeBoundingSphere(psphere, vertex_positions, corner_count, vertex_positions_stride, &rzero, 0, 7, corners);
186
187
float center[3] = {psphere[0], psphere[1], psphere[2]};
188
189
// treating triangle normals as points, find the bounding sphere - the sphere center determines the optimal cone axis
190
float nsphere[4] = {};
191
computeBoundingSphere(nsphere, normals[0], triangles, sizeof(float) * 4, &rzero, 0, 3);
192
193
float axis[3] = {nsphere[0], nsphere[1], nsphere[2]};
194
float axislength = sqrtf(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
195
float invaxislength = axislength == 0.f ? 0.f : 1.f / axislength;
196
197
axis[0] *= invaxislength;
198
axis[1] *= invaxislength;
199
axis[2] *= invaxislength;
200
201
// compute a tight cone around all normals, mindp = cos(angle/2)
202
float mindp = 1.f;
203
204
for (size_t i = 0; i < triangles; ++i)
205
{
206
float dp = normals[i][0] * axis[0] + normals[i][1] * axis[1] + normals[i][2] * axis[2];
207
208
mindp = (dp < mindp) ? dp : mindp;
209
}
210
211
// fill bounding sphere info; note that below we can return bounds without cone information for degenerate cones
212
bounds.center[0] = center[0];
213
bounds.center[1] = center[1];
214
bounds.center[2] = center[2];
215
bounds.radius = psphere[3];
216
217
// degenerate cluster, normal cone is larger than a hemisphere => trivial accept
218
// note that if mindp is positive but close to 0, the triangle intersection code below gets less stable
219
// we arbitrarily decide that if a normal cone is ~168 degrees wide or more, the cone isn't useful
220
if (mindp <= 0.1f)
221
{
222
bounds.cone_cutoff = 1;
223
bounds.cone_cutoff_s8 = 127;
224
return bounds;
225
}
226
227
float maxt = 0;
228
229
// we need to find the point on center-t*axis ray that lies in negative half-space of all triangles
230
for (size_t i = 0; i < triangles; ++i)
231
{
232
// dot(center-t*axis-corner, trinormal) = 0
233
// dot(center-corner, trinormal) - t * dot(axis, trinormal) = 0
234
float dc = center[0] * normals[i][0] + center[1] * normals[i][1] + center[2] * normals[i][2] + normals[i][3];
235
float dn = axis[0] * normals[i][0] + axis[1] * normals[i][1] + axis[2] * normals[i][2];
236
237
// dn should be larger than mindp cutoff above
238
assert(dn > 0.f);
239
float t = dc / dn;
240
241
maxt = (t > maxt) ? t : maxt;
242
}
243
244
// cone apex should be in the negative half-space of all cluster triangles by construction
245
bounds.cone_apex[0] = center[0] - axis[0] * maxt;
246
bounds.cone_apex[1] = center[1] - axis[1] * maxt;
247
bounds.cone_apex[2] = center[2] - axis[2] * maxt;
248
249
// note: this axis is the axis of the normal cone, but our test for perspective camera effectively negates the axis
250
bounds.cone_axis[0] = axis[0];
251
bounds.cone_axis[1] = axis[1];
252
bounds.cone_axis[2] = axis[2];
253
254
// cos(a) for normal cone is mindp; we need to add 90 degrees on both sides and invert the cone
255
// which gives us -cos(a+90) = -(-sin(a)) = sin(a) = sqrt(1 - cos^2(a))
256
bounds.cone_cutoff = sqrtf(1 - mindp * mindp);
257
258
// quantize axis & cutoff to 8-bit SNORM format
259
bounds.cone_axis_s8[0] = (signed char)(meshopt_quantizeSnorm(bounds.cone_axis[0], 8));
260
bounds.cone_axis_s8[1] = (signed char)(meshopt_quantizeSnorm(bounds.cone_axis[1], 8));
261
bounds.cone_axis_s8[2] = (signed char)(meshopt_quantizeSnorm(bounds.cone_axis[2], 8));
262
263
// for the 8-bit test to be conservative, we need to adjust the cutoff by measuring the max. error
264
float cone_axis_s8_e0 = fabsf(bounds.cone_axis_s8[0] / 127.f - bounds.cone_axis[0]);
265
float cone_axis_s8_e1 = fabsf(bounds.cone_axis_s8[1] / 127.f - bounds.cone_axis[1]);
266
float cone_axis_s8_e2 = fabsf(bounds.cone_axis_s8[2] / 127.f - bounds.cone_axis[2]);
267
268
// note that we need to round this up instead of rounding to nearest, hence +1
269
int cone_cutoff_s8 = int(127 * (bounds.cone_cutoff + cone_axis_s8_e0 + cone_axis_s8_e1 + cone_axis_s8_e2) + 1);
270
271
bounds.cone_cutoff_s8 = (cone_cutoff_s8 > 127) ? 127 : (signed char)(cone_cutoff_s8);
272
273
return bounds;
274
}
275
276
} // namespace meshopt
277
278
meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
279
{
280
using namespace meshopt;
281
282
assert(index_count % 3 == 0);
283
assert(index_count / 3 <= kMeshletMaxTriangles);
284
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
285
assert(vertex_positions_stride % sizeof(float) == 0);
286
287
(void)vertex_count;
288
289
unsigned int cache[512];
290
memset(cache, -1, sizeof(cache));
291
292
unsigned int corners[kMeshletMaxTriangles * 3 + 1]; // +1 for branchless slot
293
size_t corner_count = 0;
294
295
for (size_t i = 0; i < index_count; ++i)
296
{
297
unsigned int v = indices[i];
298
assert(v < vertex_count);
299
300
unsigned int& c = cache[v & (sizeof(cache) / sizeof(cache[0]) - 1)];
301
302
// branchless append if vertex isn't in cache
303
corners[corner_count] = v;
304
corner_count += (c != v);
305
c = v;
306
}
307
308
return computeClusterBounds(indices, index_count, corners, corner_count, vertex_positions, vertex_positions_stride);
309
}
310
311
meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
312
{
313
using namespace meshopt;
314
315
assert(triangle_count <= kMeshletMaxTriangles);
316
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
317
assert(vertex_positions_stride % sizeof(float) == 0);
318
319
(void)vertex_count;
320
321
unsigned int indices[kMeshletMaxTriangles * 3];
322
size_t corner_count = 0;
323
324
for (size_t i = 0; i < triangle_count * 3; ++i)
325
{
326
unsigned char t = meshlet_triangles[i];
327
unsigned int index = meshlet_vertices[t];
328
assert(index < vertex_count);
329
330
indices[i] = index;
331
332
// meshlet_vertices[] slice should only contain vertices used by triangle indices, which is the case for any well formed meshlet
333
corner_count = t >= corner_count ? t + 1 : corner_count;
334
}
335
336
return computeClusterBounds(indices, triangle_count * 3, meshlet_vertices, corner_count, vertex_positions, vertex_positions_stride);
337
}
338
339
meshopt_Bounds meshopt_computeSphereBounds(const float* positions, size_t count, size_t positions_stride, const float* radii, size_t radii_stride)
340
{
341
using namespace meshopt;
342
343
assert(positions_stride >= 12 && positions_stride <= 256);
344
assert(positions_stride % sizeof(float) == 0);
345
assert((radii_stride >= 4 && radii_stride <= 256) || radii == NULL);
346
assert(radii_stride % sizeof(float) == 0);
347
348
meshopt_Bounds bounds = {};
349
350
if (count == 0)
351
return bounds;
352
353
const float rzero = 0.f;
354
355
float psphere[4] = {};
356
computeBoundingSphere(psphere, positions, count, positions_stride, radii ? radii : &rzero, radii ? radii_stride : 0, 7);
357
358
bounds.center[0] = psphere[0];
359
bounds.center[1] = psphere[1];
360
bounds.center[2] = psphere[2];
361
bounds.radius = psphere[3];
362
363
return bounds;
364
}
365
366
void meshopt_optimizeMeshletLevel(unsigned int* meshlet_vertices, size_t vertex_count, unsigned char* meshlet_triangles, size_t triangle_count, int level)
367
{
368
using namespace meshopt;
369
370
assert(triangle_count <= kMeshletMaxTriangles);
371
assert(vertex_count <= kMeshletMaxVertices);
372
assert(level >= 0 && level <= 9);
373
374
unsigned char* indices = meshlet_triangles;
375
unsigned int* vertices = meshlet_vertices;
376
377
// cache tracks vertex timestamps (corresponding to triangle index! all 3 vertices are added at the same time and never removed)
378
unsigned char cache[kMeshletMaxVertices];
379
memset(cache, 0, vertex_count);
380
381
// note that we start from a value that means all vertices aren't in cache
382
unsigned char cache_last = 128;
383
const unsigned char cache_cutoff = 3; // 3 triangles = ~5..9 vertices depending on reuse
384
385
// vertex valence is used to prioritize triangles for level>0
386
// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic
387
unsigned char valence[kMeshletMaxVertices];
388
memset(valence, 0, vertex_count);
389
390
for (size_t i = 0; i < triangle_count; ++i)
391
{
392
unsigned char a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
393
assert(a < vertex_count && b < vertex_count && c < vertex_count);
394
395
valence[a]++;
396
valence[b]++;
397
valence[c]++;
398
}
399
400
for (size_t i = 0; i < triangle_count; ++i)
401
{
402
int next = -1;
403
int next_score = -1;
404
int edges = 0;
405
406
for (size_t j = i; j < triangle_count; ++j)
407
{
408
unsigned char a = indices[j * 3 + 0], b = indices[j * 3 + 1], c = indices[j * 3 + 2];
409
assert(a < vertex_count && b < vertex_count && c < vertex_count);
410
411
// compute cache distance using unsigned 8-bit subtraction, so cache timestamp overflow is handled gracefully
412
unsigned char ad = (unsigned char)(cache_last - cache[a]);
413
unsigned char bd = (unsigned char)(cache_last - cache[b]);
414
unsigned char cd = (unsigned char)(cache_last - cache[c]);
415
416
int match = (ad < cache_cutoff) + (bd < cache_cutoff) + (cd < cache_cutoff);
417
418
if (level)
419
{
420
// prefer low minimum valence
421
int vmin = valence[a] < valence[b] ? valence[a] : valence[b];
422
vmin = valence[c] < vmin ? valence[c] : vmin;
423
424
// prefer vertices with smaller cache distance and valence to improve traversal locality
425
int score = match * 1024 + (1023 - ad - bd - cd);
426
score = score * 256 + (255 - vmin);
427
428
next = (score > next_score) ? int(j) : next;
429
next_score = (score > next_score) ? score : next_score;
430
431
// terminate after finding enough edge matches
432
if (match >= 2 && ++edges >= level)
433
break;
434
}
435
else
436
{
437
int score = match;
438
439
next = (score > next_score) ? int(j) : next;
440
next_score = (score > next_score) ? score : next_score;
441
442
// settle for a first edge match, which makes the function ~linear in practice
443
if (match >= 2)
444
break;
445
}
446
}
447
448
assert(next >= 0);
449
450
unsigned char a = indices[next * 3 + 0], b = indices[next * 3 + 1], c = indices[next * 3 + 2];
451
452
// shift triangles before the next one forward so that we always keep an ordered partition
453
// note: this could have swapped triangles [i] and [next] but that distorts the order and may skew the output sequence
454
memmove(indices + (i + 1) * 3, indices + i * 3, (next - i) * 3 * sizeof(unsigned char));
455
456
indices[i * 3 + 0] = a;
457
indices[i * 3 + 1] = b;
458
indices[i * 3 + 2] = c;
459
460
// cache timestamp is the same between all vertices of each triangle to reduce overflow
461
cache_last++;
462
cache[a] = cache_last;
463
cache[b] = cache_last;
464
cache[c] = cache_last;
465
466
// update vertex valences for scoring heuristic
467
valence[a]--;
468
valence[b]--;
469
valence[c]--;
470
}
471
472
// rotate triangles to maximize compressibility; only done at level >= 1 for compatibility
473
if (level >= 1)
474
{
475
memset(cache, 0, vertex_count);
476
477
for (size_t i = 0; i < triangle_count; ++i)
478
{
479
unsigned char a = indices[i * 3 + 0], b = indices[i * 3 + 1], c = indices[i * 3 + 2];
480
481
// if only the middle vertex has been used, rotate triangle to ensure new vertices are always sequential
482
if (!cache[a] && cache[b] && !cache[c])
483
{
484
// abc -> bca
485
unsigned char t = a;
486
a = b, b = c, c = t;
487
}
488
else if (!cache[a] && !cache[b] && !cache[c])
489
{
490
// out of three edges, the edge ab can not be reused by subsequent triangles in some encodings
491
// if subsequent triangles don't share edges ca or bc, we can rotate the triangle to fix this
492
bool needab = false, needbc = false, needca = false;
493
494
for (size_t j = i + 1; j < triangle_count && j <= i + 3; ++j)
495
{
496
unsigned char oa = indices[j * 3 + 0], ob = indices[j * 3 + 1], oc = indices[j * 3 + 2];
497
498
// note: edge comparisons are reversed as reused edges are flipped
499
needab |= (oa == b && ob == a) || (ob == b && oc == a) || (oc == b && oa == a);
500
needbc |= (oa == c && ob == b) || (ob == c && oc == b) || (oc == c && oa == b);
501
needca |= (oa == a && ob == c) || (ob == a && oc == c) || (oc == a && oa == c);
502
}
503
504
if (needab && !needbc)
505
{
506
// abc -> bca
507
unsigned char t = a;
508
a = b, b = c, c = t;
509
}
510
else if (needab && !needca)
511
{
512
// abc -> cab
513
unsigned char t = c;
514
c = b, b = a, a = t;
515
}
516
}
517
518
indices[i * 3 + 0] = a, indices[i * 3 + 1] = b, indices[i * 3 + 2] = c;
519
520
cache[a] = cache[b] = cache[c] = 1;
521
}
522
}
523
524
// reorder meshlet vertices for access locality assuming index buffer is scanned sequentially
525
unsigned int order[kMeshletMaxVertices];
526
527
short remap[kMeshletMaxVertices];
528
memset(remap, -1, vertex_count * sizeof(short));
529
530
size_t vertex_offset = 0;
531
532
for (size_t i = 0; i < triangle_count * 3; ++i)
533
{
534
short& r = remap[indices[i]];
535
536
if (r < 0)
537
{
538
r = short(vertex_offset);
539
order[vertex_offset] = vertices[indices[i]];
540
vertex_offset++;
541
}
542
543
indices[i] = (unsigned char)r;
544
}
545
546
assert(vertex_offset <= vertex_count);
547
memcpy(vertices, order, vertex_offset * sizeof(unsigned int));
548
}
549
550
void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count)
551
{
552
meshopt_optimizeMeshletLevel(meshlet_vertices, vertex_count, meshlet_triangles, triangle_count, 0);
553
}
554
555
size_t meshopt_extractMeshletIndices(unsigned int* vertices, unsigned char* triangles, const unsigned int* indices, size_t index_count)
556
{
557
using namespace meshopt;
558
559
assert(index_count % 3 == 0);
560
assert(index_count / 3 <= kMeshletMaxTriangles);
561
562
size_t unique = 0;
563
564
// direct mapped cache for fast lookups based on low index bits; inspired by vk_lod_clusters from NVIDIA
565
short cache[1024];
566
memset(cache, -1, sizeof(cache));
567
568
for (size_t i = 0; i < index_count; ++i)
569
{
570
unsigned int v = indices[i];
571
unsigned int key = v & (sizeof(cache) / sizeof(cache[0]) - 1);
572
short c = cache[key];
573
574
// fast path: vertex has been seen before
575
if (c >= 0 && vertices[c] == v)
576
{
577
triangles[i] = (unsigned char)c;
578
continue;
579
}
580
581
// fast path: vertex has never been seen before
582
if (c < 0)
583
{
584
assert(unique < kMeshletMaxVertices);
585
cache[key] = short(unique);
586
triangles[i] = (unsigned char)unique;
587
vertices[unique++] = v;
588
continue;
589
}
590
591
// slow path: collision with a different vertex, so we need to look through all vertices
592
int pos = -1;
593
for (size_t j = 0; j < unique; ++j)
594
if (vertices[j] == v)
595
{
596
pos = int(j);
597
break;
598
}
599
600
if (pos < 0)
601
{
602
assert(unique < kMeshletMaxVertices);
603
pos = int(unique);
604
vertices[unique++] = v;
605
}
606
607
cache[key] = short(pos);
608
triangles[i] = (unsigned char)pos;
609
}
610
611
assert(unique <= kMeshletMaxVertices);
612
return unique;
613
}
614
615