@@ -657,12 +657,13 @@ void simplifyComplete(const Mesh& mesh)
657
657
658
658
void simplifyClusters (const Mesh& mesh, float threshold = 0 .2f )
659
659
{
660
- // note: we use clusters that are larger than normal to give simplifier room to work; in practice you'd use cluster groups merged from smaller clusters and build a cluster DAG
661
- const size_t max_vertices = 255 ;
662
- const size_t max_triangles = 512 ;
660
+ const size_t max_vertices = 64 ;
661
+ const size_t max_triangles = 64 ;
662
+ const size_t target_group_size = 8 ;
663
663
664
664
double start = timestamp ();
665
665
666
+ // build clusters (meshlets) out of the mesh
666
667
size_t max_meshlets = meshopt_buildMeshletsBound (mesh.indices .size (), max_vertices, max_triangles);
667
668
std::vector<meshopt_Meshlet> meshlets (max_meshlets);
668
669
std::vector<unsigned int > meshlet_vertices (max_meshlets * max_vertices);
@@ -672,6 +673,44 @@ void simplifyClusters(const Mesh& mesh, float threshold = 0.2f)
672
673
673
674
double middle = timestamp ();
674
675
676
+ // partition clusters in groups; each group will be simplified separately and the boundaries between groups will be preserved
677
+ std::vector<unsigned int > cluster_indices;
678
+ cluster_indices.reserve (mesh.indices .size ()); // slight underestimate, vector should realloc once
679
+ std::vector<unsigned int > cluster_sizes (meshlets.size ());
680
+
681
+ for (size_t i = 0 ; i < meshlets.size (); ++i)
682
+ {
683
+ const meshopt_Meshlet& m = meshlets[i];
684
+
685
+ for (size_t j = 0 ; j < m.triangle_count * 3 ; ++j)
686
+ cluster_indices.push_back (meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + j]]);
687
+
688
+ cluster_sizes[i] = m.triangle_count * 3 ;
689
+ }
690
+
691
+ // makes sure clusters are partitioned using position-only adjacency
692
+ meshopt_generateShadowIndexBuffer (&cluster_indices[0 ], &cluster_indices[0 ], cluster_indices.size (), &mesh.vertices [0 ].px , mesh.vertices .size (), sizeof (float ) * 3 , sizeof (Vertex));
693
+
694
+ std::vector<unsigned int > partition (meshlets.size ());
695
+ size_t partition_count = meshopt_partitionClusters (&partition[0 ], &cluster_indices[0 ], cluster_indices.size (), &cluster_sizes[0 ], cluster_sizes.size (), mesh.vertices .size (), target_group_size);
696
+
697
+ // convert partitions to linked lists to make it easier to iterate over (vectors of vectors would work too)
698
+ std::vector<int > partnext (meshlets.size (), -1 );
699
+ std::vector<int > partlast (partition_count, -1 );
700
+
701
+ for (size_t i = 0 ; i < meshlets.size (); ++i)
702
+ {
703
+ unsigned int part = partition[i];
704
+
705
+ if (partlast[part] >= 0 )
706
+ partnext[partlast[part]] = int (i);
707
+
708
+ partlast[part] = int (i);
709
+ partnext[i] = -1 ;
710
+ }
711
+
712
+ double parttime = timestamp ();
713
+
675
714
float scale = meshopt_simplifyScale (&mesh.vertices [0 ].px , mesh.vertices .size (), sizeof (Vertex));
676
715
677
716
std::vector<unsigned int > lod;
@@ -681,33 +720,47 @@ void simplifyClusters(const Mesh& mesh, float threshold = 0.2f)
681
720
682
721
for (size_t i = 0 ; i < meshlets.size (); ++i)
683
722
{
684
- const meshopt_Meshlet& m = meshlets[i];
723
+ if (partlast[partition[i]] < 0 )
724
+ continue ; // part of a group that was already processed
685
725
686
- size_t cluster_offset = lod.size ();
726
+ // mark group as processed
727
+ partlast[partition[i]] = -1 ;
687
728
688
- for (size_t j = 0 ; j < m.triangle_count * 3 ; ++j)
689
- lod.push_back (meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + j]]);
729
+ size_t group_offset = lod.size ();
730
+
731
+ for (int j = int (i); j >= 0 ; j = partnext[j])
732
+ {
733
+ const meshopt_Meshlet& m = meshlets[j];
734
+
735
+ for (size_t k = 0 ; k < m.triangle_count * 3 ; ++k)
736
+ lod.push_back (meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + k]]);
737
+ }
738
+
739
+ size_t group_triangles = (lod.size () - group_offset) / 3 ;
690
740
741
+ // simplify the group, preserving the border vertices
742
+ // note: this technically also locks the exterior border; a full mesh analysis (see nanite.cpp / lockBoundary) would work better for some meshes
691
743
unsigned int options = meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute;
692
744
693
- float cluster_target_error = 1e-2f * scale;
694
- size_t cluster_target = size_t (float (m. triangle_count ) * threshold) * 3 ;
695
- float cluster_error = 0 .f ;
696
- size_t cluster_size = meshopt_simplify (&lod[cluster_offset ], &lod[cluster_offset ], m. triangle_count * 3 , &mesh.vertices [0 ].px , mesh.vertices .size (), sizeof (Vertex), cluster_target, cluster_target_error , options, &cluster_error );
745
+ float group_target_error = 1e-2f * scale;
746
+ size_t group_target = size_t (float (group_triangles ) * threshold) * 3 ;
747
+ float group_error = 0 .f ;
748
+ size_t group_size = meshopt_simplify (&lod[group_offset ], &lod[group_offset ], group_triangles * 3 , &mesh.vertices [0 ].px , mesh.vertices .size (), sizeof (Vertex), group_target, group_target_error , options, &group_error );
697
749
698
- error = cluster_error > error ? cluster_error : error;
750
+ error = group_error > error ? group_error : error;
699
751
700
- // simplified cluster is available in lod[cluster_offset..cluster_offset + cluster_size ]
701
- lod.resize (cluster_offset + cluster_size );
752
+ // simplified group is available in lod[group_offset..group_offset + group_size ]
753
+ lod.resize (group_offset + group_size );
702
754
}
703
755
704
756
double end = timestamp ();
705
757
706
- printf (" %-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec, clusterized in %.2f msec\n " ,
758
+ printf (" %-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec, clusterized in %.2f msec, partitioned in %.2f msec (%d clusters in %d groups) \n " ,
707
759
" SimplifyN" , // N for Nanite
708
760
int (mesh.indices .size () / 3 ), int (lod.size () / 3 ),
709
761
error / scale * 100 ,
710
- (end - middle) * 1000 , (middle - start) * 1000 );
762
+ (end - parttime) * 1000 , (middle - start) * 1000 , (parttime - middle) * 1000 ,
763
+ int (meshlets.size ()), int (partition_count));
711
764
}
712
765
713
766
void optimize (const Mesh& mesh, const char * name, void (*optf)(Mesh& mesh))
@@ -1402,7 +1455,7 @@ void processDev(const char* path)
1402
1455
if (!loadMesh (mesh, path))
1403
1456
return ;
1404
1457
1405
- meshlets (mesh);
1458
+ simplifyClusters (mesh, 0 . 2f );
1406
1459
}
1407
1460
1408
1461
void processNanite (const char * path)
0 commit comments