@@ -19,6 +19,10 @@ const size_t kMeshletMaxVertices = 255;
19
19
// A reasonable limit is around 2*max_vertices or less
20
20
const size_t kMeshletMaxTriangles = 512 ;
21
21
22
+ // We keep a limited number of seed triangles and add a few triangles per finished meshlet
23
+ const size_t kMeshletMaxSeeds = 256 ;
24
+ const size_t kMeshletAddSeeds = 4 ;
25
+
22
26
struct TriangleAdjacency2
23
27
{
24
28
unsigned int * counts;
@@ -362,7 +366,7 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
362
366
return result;
363
367
}
364
368
365
- static unsigned int getNeighborTriangle (const meshopt_Meshlet& meshlet, const Cone* meshlet_cone, unsigned int * meshlet_vertices, const unsigned int * indices, const TriangleAdjacency2& adjacency, const Cone* triangles, const unsigned int * live_triangles, const unsigned char * used, float meshlet_expected_radius, float cone_weight)
369
+ static unsigned int getNeighborTriangle (const meshopt_Meshlet& meshlet, const Cone& meshlet_cone, const unsigned int * meshlet_vertices, const unsigned int * indices, const TriangleAdjacency2& adjacency, const Cone* triangles, const unsigned int * live_triangles, const unsigned char * used, float meshlet_expected_radius, float cone_weight)
366
370
{
367
371
unsigned int best_triangle = ~0u ;
368
372
int best_priority = 5 ;
@@ -402,24 +406,13 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
402
406
if (priority > best_priority)
403
407
continue ;
404
408
405
- float score = 0 ;
409
+ const Cone& tri_cone = triangles[triangle] ;
406
410
407
- // caller selects one of two scoring functions: geometrical (based on meshlet cone) or topological (based on remaining triangles)
408
- if (meshlet_cone)
409
- {
410
- const Cone& tri_cone = triangles[triangle];
411
+ float dx = tri_cone.px - meshlet_cone.px , dy = tri_cone.py - meshlet_cone.py , dz = tri_cone.pz - meshlet_cone.pz ;
412
+ float distance = getDistance (dx, dy, dz, cone_weight < 0 );
413
+ float spread = tri_cone.nx * meshlet_cone.nx + tri_cone.ny * meshlet_cone.ny + tri_cone.nz * meshlet_cone.nz ;
411
414
412
- float dx = tri_cone.px - meshlet_cone->px , dy = tri_cone.py - meshlet_cone->py , dz = tri_cone.pz - meshlet_cone->pz ;
413
- float distance = getDistance (dx, dy, dz, cone_weight < 0 );
414
- float spread = tri_cone.nx * meshlet_cone->nx + tri_cone.ny * meshlet_cone->ny + tri_cone.nz * meshlet_cone->nz ;
415
-
416
- score = getMeshletScore (distance, spread, cone_weight, meshlet_expected_radius);
417
- }
418
- else
419
- {
420
- // each live_triangles entry is >= 1 since it includes the current triangle we're processing
421
- score = float (live_triangles[a] + live_triangles[b] + live_triangles[c] - 3 );
422
- }
415
+ float score = getMeshletScore (distance, spread, cone_weight, meshlet_expected_radius);
423
416
424
417
// note that topology-based priority is always more important than the score
425
418
// this helps maintain reasonable effectiveness of meshlet data and reduces scoring cost
@@ -435,6 +428,113 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
435
428
return best_triangle;
436
429
}
437
430
431
+ static size_t appendSeedTriangles (unsigned int * seeds, const meshopt_Meshlet& meshlet, const unsigned int * meshlet_vertices, const unsigned int * indices, const TriangleAdjacency2& adjacency, const Cone* triangles, const unsigned int * live_triangles, float cornerx, float cornery, float cornerz)
432
+ {
433
+ unsigned int best_seeds[kMeshletAddSeeds ];
434
+ unsigned int best_live[kMeshletAddSeeds ];
435
+ float best_score[kMeshletAddSeeds ];
436
+
437
+ for (size_t i = 0 ; i < kMeshletAddSeeds ; ++i)
438
+ {
439
+ best_seeds[i] = ~0u ;
440
+ best_live[i] = ~0u ;
441
+ best_score[i] = FLT_MAX;
442
+ }
443
+
444
+ for (size_t i = 0 ; i < meshlet.vertex_count ; ++i)
445
+ {
446
+ unsigned int index = meshlet_vertices[meshlet.vertex_offset + i];
447
+
448
+ unsigned int best_neighbor = ~0u ;
449
+ unsigned int best_neighbor_live = ~0u ;
450
+
451
+ // find the neighbor with the smallest live metric
452
+ unsigned int * neighbors = &adjacency.data [0 ] + adjacency.offsets [index];
453
+ size_t neighbors_size = adjacency.counts [index];
454
+
455
+ for (size_t j = 0 ; j < neighbors_size; ++j)
456
+ {
457
+ unsigned int triangle = neighbors[j];
458
+ unsigned int a = indices[triangle * 3 + 0 ], b = indices[triangle * 3 + 1 ], c = indices[triangle * 3 + 2 ];
459
+
460
+ unsigned int live = live_triangles[a] + live_triangles[b] + live_triangles[c];
461
+
462
+ if (live < best_neighbor_live)
463
+ {
464
+ best_neighbor = triangle;
465
+ best_neighbor_live = live;
466
+ }
467
+ }
468
+
469
+ // add the neighbor to the list of seeds; the list is unsorted and the replacement criteria is approximate
470
+ if (best_neighbor == ~0u )
471
+ continue ;
472
+
473
+ float best_neighbor_score = getDistance (triangles[best_neighbor].px - cornerx, triangles[best_neighbor].py - cornery, triangles[best_neighbor].pz - cornerz, false );
474
+
475
+ for (size_t j = 0 ; j < kMeshletAddSeeds ; ++j)
476
+ {
477
+ // non-strict comparison reduces the number of duplicate seeds (triangles adjacent to multiple vertices)
478
+ if (best_neighbor_live < best_live[j] || (best_neighbor_live == best_live[j] && best_neighbor_score <= best_score[j]))
479
+ {
480
+ best_seeds[j] = best_neighbor;
481
+ best_live[j] = best_neighbor_live;
482
+ best_score[j] = best_neighbor_score;
483
+ break ;
484
+ }
485
+ }
486
+ }
487
+
488
+ // add surviving seeds to the meshlet
489
+ size_t seed_count = 0 ;
490
+
491
+ for (size_t i = 0 ; i < kMeshletAddSeeds ; ++i)
492
+ if (best_seeds[i] != ~0u )
493
+ seeds[seed_count++] = best_seeds[i];
494
+
495
+ return seed_count;
496
+ }
497
+
498
+ static size_t pruneSeedTriangles (unsigned int * seeds, size_t seed_count, const unsigned char * emitted_flags)
499
+ {
500
+ size_t result = 0 ;
501
+
502
+ for (size_t i = 0 ; i < seed_count; ++i)
503
+ {
504
+ unsigned int index = seeds[i];
505
+
506
+ seeds[result] = index;
507
+ result += emitted_flags[index] == 0 ;
508
+ }
509
+
510
+ return result;
511
+ }
512
+
513
+ static unsigned int selectSeedTriangle (const unsigned int * seeds, size_t seed_count, const unsigned int * indices, const Cone* triangles, const unsigned int * live_triangles, float cornerx, float cornery, float cornerz)
514
+ {
515
+ unsigned int best_seed = ~0u ;
516
+ unsigned int best_live = ~0u ;
517
+ float best_score = FLT_MAX;
518
+
519
+ for (size_t i = 0 ; i < seed_count; ++i)
520
+ {
521
+ unsigned int index = seeds[i];
522
+ unsigned int a = indices[index * 3 + 0 ], b = indices[index * 3 + 1 ], c = indices[index * 3 + 2 ];
523
+
524
+ unsigned int live = live_triangles[a] + live_triangles[b] + live_triangles[c];
525
+ float score = getDistance (triangles[index].px - cornerx, triangles[index].py - cornery, triangles[index].pz - cornerz, false );
526
+
527
+ if (live < best_live || (live == best_live && score < best_score))
528
+ {
529
+ best_seed = index;
530
+ best_live = live;
531
+ best_score = score;
532
+ }
533
+ }
534
+
535
+ return best_seed;
536
+ }
537
+
438
538
struct KDNode
439
539
{
440
540
union
@@ -658,10 +758,43 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
658
758
KDNode* nodes = allocator.allocate <KDNode>(face_count * 2 );
659
759
kdtreeBuild (0 , nodes, face_count * 2 , &triangles[0 ].px , sizeof (Cone) / sizeof (float ), kdindices, face_count, /* leaf_size= */ 8 );
660
760
761
+ // find a specific corner of the mesh to use as a starting point for meshlet flow
762
+ float cornerx = FLT_MAX, cornery = FLT_MAX, cornerz = FLT_MAX;
763
+
764
+ for (size_t i = 0 ; i < face_count; ++i)
765
+ {
766
+ const Cone& tri = triangles[i];
767
+
768
+ cornerx = cornerx > tri.px ? tri.px : cornerx;
769
+ cornery = cornery > tri.py ? tri.py : cornery;
770
+ cornerz = cornerz > tri.pz ? tri.pz : cornerz;
771
+ }
772
+
661
773
// index of the vertex in the meshlet, 0xff if the vertex isn't used
662
774
unsigned char * used = allocator.allocate <unsigned char >(vertex_count);
663
775
memset (used, -1 , vertex_count);
664
776
777
+ // initial seed triangle is the one closest to the corner
778
+ unsigned int initial_seed = ~0u ;
779
+ float initial_score = FLT_MAX;
780
+
781
+ for (size_t i = 0 ; i < face_count; ++i)
782
+ {
783
+ const Cone& tri = triangles[i];
784
+
785
+ float score = getDistance (tri.px - cornerx, tri.py - cornery, tri.pz - cornerz, false );
786
+
787
+ if (initial_seed == ~0u || score < initial_score)
788
+ {
789
+ initial_seed = unsigned (i);
790
+ initial_score = score;
791
+ }
792
+ }
793
+
794
+ // seed triangles to continue meshlet flow
795
+ unsigned int seeds[kMeshletMaxSeeds ] = {};
796
+ size_t seed_count = 0 ;
797
+
665
798
meshopt_Meshlet meshlet = {};
666
799
size_t meshlet_offset = 0 ;
667
800
@@ -671,18 +804,18 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
671
804
{
672
805
Cone meshlet_cone = getMeshletCone (meshlet_cone_acc, meshlet.triangle_count );
673
806
674
- unsigned int best_triangle = getNeighborTriangle (meshlet, &meshlet_cone, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, cone_weight);
675
- int best_extra = best_triangle == ~0u ? -1 : (used[indices[best_triangle * 3 + 0 ]] == 0xff ) + (used[indices[best_triangle * 3 + 1 ]] == 0xff ) + (used[indices[best_triangle * 3 + 2 ]] == 0xff );
807
+ unsigned int best_triangle = ~0u ;
676
808
677
- // if the best triangle doesn't fit into current meshlet, the spatial scoring we've used is not very meaningful, so we re-select using topological scoring
678
- if (best_triangle != ~0u && (meshlet.vertex_count + best_extra > max_vertices || meshlet.triangle_count >= max_triangles))
679
- {
680
- best_triangle = getNeighborTriangle (meshlet, NULL , meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, 0 .f );
681
- }
809
+ // for the first triangle, we don't have a meshlet cone yet, so we use the initial seed
810
+ // to continue the meshlet, we select an adjacent triangle based on connectivity and spatial scoring
811
+ if (meshlet_offset == 0 && meshlet.triangle_count == 0 )
812
+ best_triangle = initial_seed;
813
+ else
814
+ best_triangle = getNeighborTriangle (meshlet, meshlet_cone, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, cone_weight);
682
815
683
816
bool split = false ;
684
817
685
- // when we run out of neighboring triangles we need to switch to spatial search; we currently just pick the closest triangle irrespective of connectivity
818
+ // when we run out of adjacent triangles we need to switch to spatial search; we currently just pick the closest triangle irrespective of connectivity
686
819
if (best_triangle == ~0u )
687
820
{
688
821
float position[3 ] = {meshlet_cone.px , meshlet_cone.py , meshlet_cone.pz };
@@ -698,6 +831,21 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
698
831
if (best_triangle == ~0u )
699
832
break ;
700
833
834
+ int best_extra = (used[indices[best_triangle * 3 + 0 ]] == 0xff ) + (used[indices[best_triangle * 3 + 1 ]] == 0xff ) + (used[indices[best_triangle * 3 + 2 ]] == 0xff );
835
+
836
+ // if the best triangle doesn't fit into current meshlet, we re-select using seeds to maintain global flow
837
+ if (split || (meshlet.vertex_count + best_extra > max_vertices || meshlet.triangle_count >= max_triangles))
838
+ {
839
+ seed_count = pruneSeedTriangles (seeds, seed_count, emitted_flags);
840
+ seed_count = (seed_count + kMeshletAddSeeds <= kMeshletMaxSeeds ) ? seed_count : kMeshletMaxSeeds - kMeshletAddSeeds ;
841
+ seed_count += appendSeedTriangles (seeds + seed_count, meshlet, meshlet_vertices, indices, adjacency, triangles, live_triangles, cornerx, cornery, cornerz);
842
+
843
+ unsigned int best_seed = selectSeedTriangle (seeds, seed_count, indices, triangles, live_triangles, cornerx, cornery, cornerz);
844
+
845
+ // we may not find a valid seed triangle if the mesh is disconnected as seeds are based on adjacency
846
+ best_triangle = best_seed != ~0u ? best_seed : best_triangle;
847
+ }
848
+
701
849
unsigned int a = indices[best_triangle * 3 + 0 ], b = indices[best_triangle * 3 + 1 ], c = indices[best_triangle * 3 + 2 ];
702
850
assert (a < vertex_count && b < vertex_count && c < vertex_count);
703
851
@@ -739,6 +887,7 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
739
887
meshlet_cone_acc.ny += triangles[best_triangle].ny ;
740
888
meshlet_cone_acc.nz += triangles[best_triangle].nz ;
741
889
890
+ assert (!emitted_flags[best_triangle]);
742
891
emitted_flags[best_triangle] = 1 ;
743
892
}
744
893
0 commit comments