Skip to content

Commit fc45e05

Browse files
authored
Merge pull request #846 from zeux/clflex
clusterizer: Add buildMeshletsFlex with flexible cluster sizing
2 parents 160f94b + f897fae commit fc45e05

File tree

5 files changed

+177
-41
lines changed

5 files changed

+177
-41
lines changed

demo/main.cpp

+42-12
Original file line numberDiff line numberDiff line change
@@ -990,30 +990,35 @@ static int follow(int* parents, int index)
990990
return index;
991991
}
992992

993-
void meshlets(const Mesh& mesh, bool scan = false, bool uniform = false)
993+
void meshlets(const Mesh& mesh, bool scan = false, bool uniform = false, bool flex = false, bool dump = false)
994994
{
995995
// NVidia-recommends 64/126; we round 126 down to a multiple of 4
996996
// alternatively we also test uniform configuration with 64/64 which is better for AMD
997997
const size_t max_vertices = 64;
998998
const size_t max_triangles = uniform ? 64 : 124;
999+
const size_t min_triangles = uniform ? 24 : 32; // only used in flex mode
9991000

10001001
// note: should be set to 0 unless cone culling is used at runtime!
1001-
const float cone_weight = 0.25f;
1002+
const float cone_weight = flex ? -1.0f : 0.25f;
1003+
const float split_factor = flex ? 2.0f : 0.0f;
10021004

10031005
// note: input mesh is assumed to be optimized for vertex cache and vertex fetch
10041006
double start = timestamp();
1005-
size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, max_triangles);
1007+
size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, min_triangles);
10061008
std::vector<meshopt_Meshlet> meshlets(max_meshlets);
10071009
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
10081010
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);
10091011

10101012
if (scan)
10111013
meshlets.resize(meshopt_buildMeshletsScan(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), mesh.vertices.size(), max_vertices, max_triangles));
1012-
else
1014+
else if (flex)
1015+
meshlets.resize(meshopt_buildMeshletsFlex(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), max_vertices, min_triangles, max_triangles, cone_weight, split_factor));
1016+
else // note: equivalent to the call of buildMeshletsFlex() with non-negative cone_weight and split_factor = 0
10131017
meshlets.resize(meshopt_buildMeshlets(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), max_vertices, max_triangles, cone_weight));
10141018

1015-
for (size_t i = 0; i < meshlets.size(); ++i)
1016-
meshopt_optimizeMeshlet(&meshlet_vertices[meshlets[i].vertex_offset], &meshlet_triangles[meshlets[i].triangle_offset], meshlets[i].triangle_count, meshlets[i].vertex_count);
1019+
if (!dump)
1020+
for (size_t i = 0; i < meshlets.size(); ++i)
1021+
meshopt_optimizeMeshlet(&meshlet_vertices[meshlets[i].vertex_offset], &meshlet_triangles[meshlets[i].triangle_offset], meshlets[i].triangle_count, meshlets[i].vertex_count);
10171022

10181023
if (meshlets.size())
10191024
{
@@ -1026,6 +1031,9 @@ void meshlets(const Mesh& mesh, bool scan = false, bool uniform = false)
10261031

10271032
double end = timestamp();
10281033

1034+
if (dump)
1035+
dumpObj(mesh.vertices, std::vector<unsigned>());
1036+
10291037
double avg_vertices = 0;
10301038
double avg_triangles = 0;
10311039
double avg_boundary = 0;
@@ -1042,10 +1050,23 @@ void meshlets(const Mesh& mesh, bool scan = false, bool uniform = false)
10421050
boundary[meshlet_vertices[m.vertex_offset + j]]++;
10431051
}
10441052

1053+
std::vector<unsigned int> cluster;
1054+
10451055
for (size_t i = 0; i < meshlets.size(); ++i)
10461056
{
10471057
const meshopt_Meshlet& m = meshlets[i];
10481058

1059+
if (dump)
1060+
{
1061+
cluster.clear();
1062+
for (unsigned int j = 0; j < m.triangle_count * 3; ++j)
1063+
cluster.push_back(meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + j]]);
1064+
1065+
char cname[32];
1066+
snprintf(cname, sizeof(cname), "ml_%d\n", int(i));
1067+
dumpObj(cname, cluster);
1068+
}
1069+
10491070
avg_vertices += m.vertex_count;
10501071
avg_triangles += m.triangle_count;
10511072
not_full += uniform ? m.triangle_count < max_triangles : m.vertex_count < max_vertices;
@@ -1084,7 +1105,7 @@ void meshlets(const Mesh& mesh, bool scan = false, bool uniform = false)
10841105
avg_connected /= double(meshlets.size());
10851106

10861107
printf("Meshlets%c: %d meshlets (avg vertices %.1f, avg triangles %.1f, avg boundary %.1f, avg connected %.2f, not full %d) in %.2f msec\n",
1087-
scan ? 'S' : (uniform ? 'U' : ' '),
1108+
scan ? 'S' : (flex ? 'F' : (uniform ? 'U' : ' ')),
10881109
int(meshlets.size()), avg_vertices, avg_triangles, avg_boundary, avg_connected, int(not_full), (end - start) * 1000);
10891110

10901111
float camera[3] = {100, 100, 100};
@@ -1412,11 +1433,13 @@ void process(const char* path)
14121433
meshopt_optimizeVertexFetch(&copystrip.vertices[0], &copystrip.indices[0], copystrip.indices.size(), &copystrip.vertices[0], copystrip.vertices.size(), sizeof(Vertex));
14131434

14141435
stripify(copy, false, ' ');
1415-
stripify(copy, true, 'R');
1416-
stripify(copystrip, true, 'S');
1436+
stripify(copy, /* use_restart= */ true, 'R');
1437+
stripify(copystrip, /* use_restart= */ true, 'S');
14171438

1418-
meshlets(copy, false);
1419-
meshlets(copy, true);
1439+
meshlets(copy, /* scan= */ true);
1440+
meshlets(copy, /* scan= */ false);
1441+
meshlets(copy, /* scan= */ false, /* uniform= */ true);
1442+
meshlets(copy, /* scan= */ false, /* uniform= */ false, /* flex= */ true);
14201443

14211444
shadow(copy);
14221445
tessellationAdjacency(copy);
@@ -1455,7 +1478,14 @@ void processDev(const char* path)
14551478
if (!loadMesh(mesh, path))
14561479
return;
14571480

1458-
simplifyClusters(mesh, 0.2f);
1481+
#ifdef _MSC_VER
1482+
#pragma warning(disable : 4996)
1483+
#endif
1484+
1485+
bool dump = getenv("DUMP") && atoi(getenv("DUMP"));
1486+
1487+
meshlets(mesh, /* scan= */ false, /* uniform= */ true, /* flex= */ false);
1488+
meshlets(mesh, /* scan= */ false, /* uniform= */ true, /* flex= */ true, dump);
14591489
}
14601490

14611491
void processNanite(const char* path)

demo/nanite.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <string.h>
1919

2020
#include <algorithm>
21-
#include <map>
21+
#include <map> // only for METIS
2222
#include <vector>
2323

2424
#ifndef _WIN32
@@ -145,14 +145,16 @@ static std::vector<Cluster> clusterize(const std::vector<Vertex>& vertices, cons
145145

146146
const size_t max_vertices = 192; // TODO: depends on kClusterSize, also may want to dial down for mesh shaders
147147
const size_t max_triangles = kClusterSize;
148+
const size_t min_triangles = (kClusterSize / 3) & ~3;
149+
const float split_factor = 2.0f;
148150

149-
size_t max_meshlets = meshopt_buildMeshletsBound(indices.size(), max_vertices, max_triangles);
151+
size_t max_meshlets = meshopt_buildMeshletsBound(indices.size(), max_vertices, min_triangles);
150152

151153
std::vector<meshopt_Meshlet> meshlets(max_meshlets);
152154
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
153155
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);
154156

155-
meshlets.resize(meshopt_buildMeshlets(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &indices[0], indices.size(), &vertices[0].px, vertices.size(), sizeof(Vertex), max_vertices, max_triangles, 0.f));
157+
meshlets.resize(meshopt_buildMeshletsFlex(&meshlets[0], &meshlet_vertices[0], &meshlet_triangles[0], &indices[0], indices.size(), &vertices[0].px, vertices.size(), sizeof(Vertex), max_vertices, min_triangles, max_triangles, 0.f, split_factor));
156158

157159
std::vector<Cluster> clusters(meshlets.size());
158160

demo/tests.cpp

+60
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,65 @@ static void meshletsSparse()
11141114
assert(memcmp(tri1, ibd + 3, 3 * sizeof(unsigned int)) == 0);
11151115
}
11161116

1117+
static void meshletsFlex()
1118+
{
1119+
// two tetrahedrons far apart
1120+
float vb[2 * 4 * 3] = {
1121+
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
1122+
10, 0, 0, 11, 0, 0, 10, 1, 0, 10, 0, 1, // clang-format :-/
1123+
};
1124+
1125+
unsigned int ib[2 * 4 * 3] = {
1126+
0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2,
1127+
4, 5, 6, 4, 6, 7, 4, 7, 5, 5, 7, 6, // clang-format :-/
1128+
};
1129+
1130+
// up to 2 meshlets with min_triangles=4
1131+
assert(meshopt_buildMeshletsBound(2 * 4 * 3, 16, 4) == 2);
1132+
1133+
meshopt_Meshlet ml[2];
1134+
unsigned int mv[2 * 16];
1135+
unsigned char mt[2 * 8 * 3]; // 2 meshlets with up to 8 triangles
1136+
1137+
// with regular function, we should get one meshlet (maxt=8) or two (maxt=4)
1138+
assert(meshopt_buildMeshlets(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 8, 0.f) == 1);
1139+
assert(ml[0].triangle_count == 8);
1140+
assert(ml[0].vertex_count == 8);
1141+
1142+
assert(meshopt_buildMeshlets(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 0.f) == 2);
1143+
assert(ml[0].triangle_count == 4);
1144+
assert(ml[0].vertex_count == 4);
1145+
assert(ml[1].triangle_count == 4);
1146+
assert(ml[1].vertex_count == 4);
1147+
1148+
// with flex function and mint=4 maxt=8 we should get one meshlet if split_factor is zero, or large enough to accomodate both
1149+
assert(meshopt_buildMeshletsFlex(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 8, 0.f, 0.f) == 1);
1150+
assert(ml[0].triangle_count == 8);
1151+
assert(ml[0].vertex_count == 8);
1152+
1153+
assert(meshopt_buildMeshletsFlex(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 8, 0.f, 10.f) == 1);
1154+
assert(ml[0].triangle_count == 8);
1155+
assert(ml[0].vertex_count == 8);
1156+
1157+
// however, with a smaller split factor we should get two meshlets
1158+
assert(meshopt_buildMeshletsFlex(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 8, 0.f, 1.f) == 2);
1159+
assert(ml[0].triangle_count == 4);
1160+
assert(ml[0].vertex_count == 4);
1161+
assert(ml[1].triangle_count == 4);
1162+
assert(ml[1].vertex_count == 4);
1163+
1164+
// this should hold when using axis-aligned metric as well (negative cone weight)
1165+
assert(meshopt_buildMeshletsFlex(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 8, -1.f, 10.f) == 1);
1166+
assert(ml[0].triangle_count == 8);
1167+
assert(ml[0].vertex_count == 8);
1168+
1169+
assert(meshopt_buildMeshletsFlex(ml, mv, mt, ib, sizeof(ib) / sizeof(ib[0]), vb, 8, sizeof(float) * 3, 16, 4, 8, -1.f, 1.f) == 2);
1170+
assert(ml[0].triangle_count == 4);
1171+
assert(ml[0].vertex_count == 4);
1172+
assert(ml[1].triangle_count == 4);
1173+
assert(ml[1].vertex_count == 4);
1174+
}
1175+
11171176
static void partitionBasic()
11181177
{
11191178
// 0 1 2
@@ -2201,6 +2260,7 @@ void runTests()
22012260
meshletsEmpty();
22022261
meshletsDense();
22032262
meshletsSparse();
2263+
meshletsFlex();
22042264

22052265
partitionBasic();
22062266

src/clusterizer.cpp

+44-26
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,25 @@ struct Cone
217217
float nx, ny, nz;
218218
};
219219

220-
static float getMeshletScore(float distance2, float spread, float cone_weight, float expected_radius)
220+
static float getDistance(float dx, float dy, float dz, bool aa)
221221
{
222+
if (!aa)
223+
return sqrtf(dx * dx + dy * dy + dz * dz);
224+
225+
float rx = fabsf(dx), ry = fabsf(dy), rz = fabsf(dz);
226+
float rxy = rx >= ry ? rx : ry;
227+
return rxy >= rz ? rxy : rz;
228+
}
229+
230+
static float getMeshletScore(float distance, float spread, float cone_weight, float expected_radius)
231+
{
232+
if (cone_weight < 0)
233+
return 1 + distance / expected_radius;
234+
222235
float cone = 1.f - spread * cone_weight;
223236
float cone_clamped = cone < 1e-3f ? 1e-3f : cone;
224237

225-
return (1 + sqrtf(distance2) / expected_radius * (1 - cone_weight)) * cone_clamped;
238+
return (1 + distance / expected_radius * (1 - cone_weight)) * cone_clamped;
226239
}
227240

228241
static Cone getMeshletCone(const Cone& acc, unsigned int triangle_count)
@@ -296,7 +309,7 @@ static void finishMeshlet(meshopt_Meshlet& meshlet, unsigned char* meshlet_trian
296309
meshlet_triangles[offset++] = 0;
297310
}
298311

299-
static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned char* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles)
312+
static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned char* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false)
300313
{
301314
unsigned char& av = used[a];
302315
unsigned char& bv = used[b];
@@ -306,7 +319,7 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
306319

307320
int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff);
308321

309-
if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles)
322+
if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles || split)
310323
{
311324
meshlets[meshlet_offset] = meshlet;
312325

@@ -396,14 +409,11 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
396409
{
397410
const Cone& tri_cone = triangles[triangle];
398411

399-
float distance2 =
400-
(tri_cone.px - meshlet_cone->px) * (tri_cone.px - meshlet_cone->px) +
401-
(tri_cone.py - meshlet_cone->py) * (tri_cone.py - meshlet_cone->py) +
402-
(tri_cone.pz - meshlet_cone->pz) * (tri_cone.pz - meshlet_cone->pz);
403-
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);
404414
float spread = tri_cone.nx * meshlet_cone->nx + tri_cone.ny * meshlet_cone->ny + tri_cone.nz * meshlet_cone->nz;
405415

406-
score = getMeshletScore(distance2, spread, cone_weight, meshlet_expected_radius);
416+
score = getMeshletScore(distance, spread, cone_weight, meshlet_expected_radius);
407417
}
408418
else
409419
{
@@ -533,7 +543,7 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
533543
return kdtreeBuild(next_offset, nodes, node_count, points, stride, indices + middle, count - middle, leaf_size);
534544
}
535545

536-
static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, unsigned int& result, float& limit)
546+
static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points, size_t stride, const unsigned char* emitted_flags, const float* position, bool aa, unsigned int& result, float& limit)
537547
{
538548
const KDNode& node = nodes[root];
539549

@@ -549,11 +559,8 @@ static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points,
549559

550560
const float* point = points + index * stride;
551561

552-
float distance2 =
553-
(point[0] - position[0]) * (point[0] - position[0]) +
554-
(point[1] - position[1]) * (point[1] - position[1]) +
555-
(point[2] - position[2]) * (point[2] - position[2]);
556-
float distance = sqrtf(distance2);
562+
float dx = point[0] - position[0], dy = point[1] - position[1], dz = point[2] - position[2];
563+
float distance = getDistance(dx, dy, dz, aa);
557564

558565
if (distance < limit)
559566
{
@@ -569,11 +576,11 @@ static void kdtreeNearest(KDNode* nodes, unsigned int root, const float* points,
569576
unsigned int first = (delta <= 0) ? 0 : node.children;
570577
unsigned int second = first ^ node.children;
571578

572-
kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, result, limit);
579+
kdtreeNearest(nodes, root + 1 + first, points, stride, emitted_flags, position, aa, result, limit);
573580

574581
// only process the other node if it can have a match based on closest distance so far
575582
if (fabsf(delta) <= limit)
576-
kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, result, limit);
583+
kdtreeNearest(nodes, root + 1 + second, points, stride, emitted_flags, position, aa, result, limit);
577584
}
578585
}
579586

@@ -601,7 +608,7 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_
601608
return meshlet_limit_vertices > meshlet_limit_triangles ? meshlet_limit_vertices : meshlet_limit_triangles;
602609
}
603610

604-
size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
611+
size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor)
605612
{
606613
using namespace meshopt;
607614

@@ -610,10 +617,11 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
610617
assert(vertex_positions_stride % sizeof(float) == 0);
611618

612619
assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
613-
assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
614-
assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
620+
assert(min_triangles >= 1 && min_triangles <= max_triangles && max_triangles <= kMeshletMaxTriangles);
621+
assert(min_triangles % 4 == 0 && max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
615622

616-
assert(cone_weight >= 0 && cone_weight <= 1);
623+
assert(cone_weight <= 1); // negative cone weight switches metric to optimize for axis-aligned meshlets
624+
assert(split_factor >= 0);
617625

618626
if (index_count == 0)
619627
return 0;
@@ -672,16 +680,19 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
672680
best_triangle = getNeighborTriangle(meshlet, NULL, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, 0.f);
673681
}
674682

683+
bool split = false;
684+
675685
// when we run out of neighboring triangles we need to switch to spatial search; we currently just pick the closest triangle irrespective of connectivity
676686
if (best_triangle == ~0u)
677687
{
678688
float position[3] = {meshlet_cone.px, meshlet_cone.py, meshlet_cone.pz};
679689
unsigned int index = ~0u;
680-
float limit = FLT_MAX;
690+
float distance = FLT_MAX;
681691

682-
kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, index, limit);
692+
kdtreeNearest(nodes, 0, &triangles[0].px, sizeof(Cone) / sizeof(float), emitted_flags, position, cone_weight < 0.f, index, distance);
683693

684694
best_triangle = index;
695+
split = meshlet.triangle_count >= min_triangles && split_factor > 0 && distance > meshlet_expected_radius * split_factor;
685696
}
686697

687698
if (best_triangle == ~0u)
@@ -691,7 +702,7 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
691702
assert(a < vertex_count && b < vertex_count && c < vertex_count);
692703

693704
// add meshlet to the output; when the current meshlet is full we reset the accumulated bounds
694-
if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles))
705+
if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles, split))
695706
{
696707
meshlet_offset++;
697708
memset(&meshlet_cone_acc, 0, sizeof(meshlet_cone_acc));
@@ -738,10 +749,17 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
738749
meshlets[meshlet_offset++] = meshlet;
739750
}
740751

741-
assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, max_triangles));
752+
assert(meshlet_offset <= meshopt_buildMeshletsBound(index_count, max_vertices, min_triangles));
742753
return meshlet_offset;
743754
}
744755

756+
size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
757+
{
758+
assert(cone_weight >= 0); // to use negative cone weight, use meshopt_buildMeshletsFlex
759+
760+
return meshopt_buildMeshletsFlex(meshlets, meshlet_vertices, meshlet_triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, max_triangles, cone_weight, 0.0f);
761+
}
762+
745763
size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)
746764
{
747765
using namespace meshopt;

0 commit comments

Comments
 (0)