Skip to content

Commit 12358ad

Browse files
authored
Merge pull request #843 from zeux/partmore
Improve partitioning scoring and group size limits
2 parents 031ce80 + 810ef41 commit 12358ad

File tree

3 files changed

+30
-12
lines changed

3 files changed

+30
-12
lines changed

demo/nanite.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,8 @@ void nanite(const std::vector<Vertex>& vertices, const std::vector<unsigned int>
576576
while (pending.size() > 1)
577577
{
578578
std::vector<std::vector<int> > groups = partition(clusters, pending, remap);
579+
double avg_group = double(pending.size()) / double(groups.size());
580+
579581
pending.clear();
580582

581583
std::vector<int> retry;
@@ -686,9 +688,9 @@ void nanite(const std::vector<Vertex>& vertices, const std::vector<unsigned int>
686688
double inv_clusters = pending.empty() ? 0 : 1.0 / double(pending.size());
687689

688690
depth++;
689-
printf("lod %d: %d clusters (%.1f%% full, %.1f tri/cl, %.1f vtx/cl, %.2f connected, %.1f boundary), %d triangles",
691+
printf("lod %d: %d clusters (%.1f%% full, %.1f tri/cl, %.1f vtx/cl, %.2f connected, %.1f boundary, %.1f partition), %d triangles",
690692
depth, int(pending.size()),
691-
double(full_clusters) * inv_clusters * 100, double(triangles) * inv_clusters, double(xformed_lod) * inv_clusters, double(components_lod) * inv_clusters, double(boundary_lod) * inv_clusters,
693+
double(full_clusters) * inv_clusters * 100, double(triangles) * inv_clusters, double(xformed_lod) * inv_clusters, double(components_lod) * inv_clusters, double(boundary_lod) * inv_clusters, avg_group,
692694
int(triangles));
693695
if (stuck_clusters)
694696
printf("; stuck %d clusters (%d single, %d triangles)", stuck_clusters, single_clusters, int(stuck_triangles));

src/meshoptimizer.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,8 @@ inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int*
752752
template <typename T>
753753
inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
754754
template <typename T>
755+
inline size_t meshopt_partitionClusters(unsigned int* destination, const T* cluster_indices, size_t total_index_count, const unsigned int* cluster_index_counts, size_t cluster_count, size_t vertex_count, size_t target_partition_size);
756+
template <typename T>
755757
inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
756758
#endif
757759

@@ -1138,6 +1140,14 @@ inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t inde
11381140
return meshopt_computeClusterBounds(in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);
11391141
}
11401142

1143+
template <typename T>
1144+
inline size_t meshopt_partitionClusters(unsigned int* destination, const T* cluster_indices, size_t total_index_count, const unsigned int* cluster_index_counts, size_t cluster_count, size_t vertex_count, size_t target_partition_size)
1145+
{
1146+
meshopt_IndexAdapter<T> in(NULL, cluster_indices, total_index_count);
1147+
1148+
return meshopt_partitionClusters(destination, in.data, total_index_count, cluster_index_counts, cluster_count, vertex_count, target_partition_size);
1149+
}
1150+
11411151
template <typename T>
11421152
inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)
11431153
{

src/partition.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
#include "meshoptimizer.h"
33

44
#include <assert.h>
5+
#include <math.h>
56
#include <string.h>
67

78
namespace meshopt
89
{
910

10-
static const unsigned int kGroupSizeBias = 3;
11-
1211
struct ClusterAdjacency
1312
{
1413
unsigned int* offsets;
@@ -264,12 +263,14 @@ static unsigned int countShared(const ClusterGroup* groups, int group1, int grou
264263
return total;
265264
}
266265

267-
static int pickGroupToMerge(const ClusterGroup* groups, int id, const ClusterAdjacency& adjacency, size_t max_group_size)
266+
static int pickGroupToMerge(const ClusterGroup* groups, int id, const ClusterAdjacency& adjacency, size_t max_partition_size)
268267
{
269268
assert(groups[id].size > 0);
270269

270+
float group_rsqrt = 1.f / sqrtf(float(int(groups[id].vertices)));
271+
271272
int best_group = -1;
272-
unsigned int best_score = 0;
273+
float best_score = 0;
273274

274275
for (int ci = id; ci >= 0; ci = groups[ci].next)
275276
{
@@ -280,13 +281,14 @@ static int pickGroupToMerge(const ClusterGroup* groups, int id, const ClusterAdj
280281
continue;
281282

282283
assert(groups[other].size > 0);
283-
if (groups[id].size + groups[other].size > max_group_size)
284+
if (groups[id].size + groups[other].size > max_partition_size)
284285
continue;
285286

286-
unsigned int score = countShared(groups, id, other, adjacency);
287+
unsigned int shared = countShared(groups, id, other, adjacency);
288+
float other_rsqrt = 1.f / sqrtf(float(int(groups[other].vertices)));
287289

288-
// favor smaller target groups
289-
score += (unsigned(max_group_size) - groups[other].size) * kGroupSizeBias;
290+
// normalize shared count by the expected boundary of each group (+ keeps scoring symmetric)
291+
float score = float(int(shared)) * (group_rsqrt + other_rsqrt);
290292

291293
if (score > best_score)
292294
{
@@ -307,6 +309,8 @@ size_t meshopt_partitionClusters(unsigned int* destination, const unsigned int*
307309

308310
assert(target_partition_size > 0);
309311

312+
size_t max_partition_size = target_partition_size + target_partition_size * 3 / 8;
313+
310314
meshopt_Allocator allocator;
311315

312316
unsigned char* used = allocator.allocate<unsigned char>(vertex_count);
@@ -318,6 +322,8 @@ size_t meshopt_partitionClusters(unsigned int* destination, const unsigned int*
318322

319323
for (size_t i = 0; i < cluster_count; ++i)
320324
{
325+
assert(cluster_index_counts[i] > 0);
326+
321327
cluster_offsets[i] = cluster_nextoffset;
322328
cluster_nextoffset += cluster_index_counts[i];
323329
}
@@ -369,7 +375,7 @@ size_t meshopt_partitionClusters(unsigned int* destination, const unsigned int*
369375
if (groups[top.id].size >= target_partition_size)
370376
continue;
371377

372-
int best_group = pickGroupToMerge(groups, top.id, adjacency, target_partition_size + target_partition_size / 2);
378+
int best_group = pickGroupToMerge(groups, top.id, adjacency, max_partition_size);
373379

374380
// we can't grow the group any more, emit as is
375381
if (best_group == -1)
@@ -391,7 +397,7 @@ size_t meshopt_partitionClusters(unsigned int* destination, const unsigned int*
391397
// update group sizes; note, the vertex update is an approximation which avoids recomputing the true size via countTotal
392398
groups[top.id].size += groups[best_group].size;
393399
groups[top.id].vertices += groups[best_group].vertices;
394-
groups[top.id].vertices -= shared;
400+
groups[top.id].vertices = (groups[top.id].vertices > shared) ? groups[top.id].vertices - shared : 1;
395401

396402
groups[best_group].size = 0;
397403
groups[best_group].vertices = 0;

0 commit comments

Comments
 (0)