Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ Currently, the following APIs are experimental:
- `meshopt_SimplifyPermissive` mode for `meshopt_simplify*` functions (and associated `meshopt_SimplifyVertex_*` flags)
- `meshopt_encodeMeshlet` and `meshopt_encodeMeshletBound` functions
- `meshopt_decodeMeshlet` and `meshopt_decodeMeshletRaw` functions
- `meshopt_extractMeshletIndices` function

## License

Expand Down
50 changes: 1 addition & 49 deletions demo/clusterlod.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,55 +669,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut

size_t clodLocalIndices(unsigned int* vertices, unsigned char* triangles, const unsigned int* indices, size_t index_count)
{
size_t unique = 0;

// direct mapped cache for fast lookups based on low index bits; inspired by vk_lod_clusters from NVIDIA
short cache[1024];
memset(cache, -1, sizeof(cache));

for (size_t i = 0; i < index_count; ++i)
{
unsigned int v = indices[i];
unsigned int key = v & (sizeof(cache) / sizeof(cache[0]) - 1);
short c = cache[key];

// fast path: vertex has been seen before
if (c >= 0 && vertices[c] == v)
{
triangles[i] = (unsigned char)c;
continue;
}

// fast path: vertex has never been seen before
if (c < 0)
{
cache[key] = short(unique);
triangles[i] = (unsigned char)unique;
vertices[unique++] = v;
continue;
}

// slow path: hash collision with a different vertex, so we need to look through all vertices
int pos = -1;
for (size_t j = 0; j < unique; ++j)
if (vertices[j] == v)
{
pos = int(j);
break;
}

if (pos < 0)
{
pos = int(unique);
vertices[unique++] = v;
}

cache[key] = short(pos);
triangles[i] = (unsigned char)pos;
}

assert(unique <= 256);
return unique;
return meshopt_extractMeshletIndices(vertices, triangles, indices, index_count);
}
#endif

Expand Down
20 changes: 20 additions & 0 deletions demo/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,24 @@ static void meshletsMax()
}
}

static void extractMeshlet()
{
// 15 and 1039 collide in low 10 bits
const unsigned int indices[] = {0, 7, 15, 1039, 7, 15};

unsigned int vertices[4];
unsigned char triangles[6];
size_t unique = meshopt_extractMeshletIndices(vertices, triangles, indices, 6);
assert(unique == 4);

// verify the invariant: vertices[triangles[i]] == indices[i]
for (size_t i = 0; i < 6; ++i)
assert(vertices[triangles[i]] == indices[i]);

assert(vertices[0] == 0 && vertices[1] == 7 && vertices[2] == 15 && vertices[3] == 1039);
assert(triangles[0] == 0 && triangles[1] == 1 && triangles[2] == 2 && triangles[3] == 3 && triangles[4] == 1 && triangles[5] == 2);
}

static void meshletsSpatial()
{
// two tetrahedrons far apart
Expand Down Expand Up @@ -2987,6 +3005,8 @@ void runTests()
clusterBoundsDegenerate();
sphereBounds();

extractMeshlet();

meshletsEmpty();
meshletsDense();
meshletsSparse();
Expand Down
60 changes: 60 additions & 0 deletions src/meshletutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,63 @@ void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* mesh
assert(vertex_offset <= vertex_count);
memcpy(vertices, order, vertex_offset * sizeof(unsigned int));
}

size_t meshopt_extractMeshletIndices(unsigned int* vertices, unsigned char* triangles, const unsigned int* indices, size_t index_count)
{
using namespace meshopt;

assert(index_count % 3 == 0);
assert(index_count / 3 <= kMeshletMaxTriangles);

size_t unique = 0;

// direct mapped cache for fast lookups based on low index bits; inspired by vk_lod_clusters from NVIDIA
short cache[1024];
memset(cache, -1, sizeof(cache));

for (size_t i = 0; i < index_count; ++i)
{
unsigned int v = indices[i];
unsigned int key = v & (sizeof(cache) / sizeof(cache[0]) - 1);
short c = cache[key];

// fast path: vertex has been seen before
if (c >= 0 && vertices[c] == v)
{
triangles[i] = (unsigned char)c;
continue;
}

// fast path: vertex has never been seen before
if (c < 0)
{
assert(unique < kMeshletMaxVertices);
cache[key] = short(unique);
triangles[i] = (unsigned char)unique;
vertices[unique++] = v;
continue;
}

// slow path: collision with a different vertex, so we need to look through all vertices
int pos = -1;
for (size_t j = 0; j < unique; ++j)
if (vertices[j] == v)
{
pos = int(j);
break;
}

if (pos < 0)
{
assert(unique < kMeshletMaxVertices);
pos = int(unique);
vertices[unique++] = v;
}

cache[key] = short(pos);
triangles[i] = (unsigned char)pos;
}

assert(unique <= kMeshletMaxVertices);
return unique;
}
9 changes: 9 additions & 0 deletions src/meshoptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,15 @@ MESHOPTIMIZER_API struct meshopt_Bounds meshopt_computeMeshletBounds(const unsig
*/
MESHOPTIMIZER_API struct meshopt_Bounds meshopt_computeSphereBounds(const float* positions, size_t count, size_t positions_stride, const float* radii, size_t radii_stride);

/**
* Experimental: Extract meshlet-local vertex and triangle indices from absolute cluster indices.
* Fills triangles[] and vertices[] such that vertices[triangles[i]] == indices[i], and returns the number of unique vertices.
*
* indices should have at most 256 unique vertex indices
* index_count/3 must not exceed implementation limits (<= 512)
*/
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_extractMeshletIndices(unsigned int* vertices, unsigned char* triangles, const unsigned int* indices, size_t index_count);

/**
* Cluster partitioner
* Partitions clusters into groups of similar size, prioritizing grouping clusters that share vertices or are close to each other.
Expand Down