Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve support for cluster simplification #704

Merged
merged 13 commits into from
Jun 17, 2024
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
59 changes: 59 additions & 0 deletions demo/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,8 @@ void simplifyPoints(const Mesh& mesh, float threshold = 0.2f)
double start = timestamp();

size_t target_vertex_count = size_t(mesh.vertices.size() * threshold);
if (target_vertex_count == 0)
return;

std::vector<unsigned int> indices(target_vertex_count);
indices.resize(meshopt_simplifyPoints(&indices[0], &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), NULL, 0, 0, target_vertex_count));
Expand Down Expand Up @@ -641,6 +643,61 @@ void simplifyComplete(const Mesh& mesh)
}
}

void simplifyClusters(const Mesh& mesh, float threshold = 0.2f)
{
// 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
const size_t max_vertices = 255;
const size_t max_triangles = 512;

double start = timestamp();

size_t max_meshlets = meshopt_buildMeshletsBound(mesh.indices.size(), max_vertices, max_triangles);
std::vector<meshopt_Meshlet> meshlets(max_meshlets);
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);

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, 0.f));

double middle = timestamp();

float scale = meshopt_simplifyScale(&mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex));

std::vector<unsigned int> lod;
lod.reserve(mesh.indices.size());

float error = 0.f;

for (size_t i = 0; i < meshlets.size(); ++i)
{
const meshopt_Meshlet& m = meshlets[i];

size_t cluster_offset = lod.size();

for (size_t j = 0; j < m.triangle_count * 3; ++j)
lod.push_back(meshlet_vertices[m.vertex_offset + meshlet_triangles[m.triangle_offset + j]]);

unsigned int options = meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute;

float cluster_target_error = 1e-2f * scale;
size_t cluster_target = size_t(float(m.triangle_count) * threshold) * 3;
float cluster_error = 0.f;
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);

error = cluster_error > error ? cluster_error : error;

// simplified cluster is available in lod[cluster_offset..cluster_offset + cluster_size]
lod.resize(cluster_offset + cluster_size);
}

double end = timestamp();

printf("%-9s: %d triangles => %d triangles (%.2f%% deviation) in %.2f msec, clusterized in %.2f msec\n",
"SimplifyN", // N for Nanite
int(mesh.indices.size() / 3), int(lod.size() / 3),
error / scale * 100,
(end - middle) * 1000, (middle - start) * 1000);
}

void optimize(const Mesh& mesh, const char* name, void (*optf)(Mesh& mesh))
{
Mesh copy = mesh;
Expand Down Expand Up @@ -1231,6 +1288,7 @@ void process(const char* path)
simplifySloppy(mesh);
simplifyComplete(mesh);
simplifyPoints(mesh);
simplifyClusters(mesh);

spatialSort(mesh);
spatialSortTriangles(mesh);
Expand All @@ -1246,6 +1304,7 @@ void processDev(const char* path)
return;

simplify(mesh);
simplifyClusters(mesh);
}

int main(int argc, char** argv)
Expand Down
125 changes: 116 additions & 9 deletions demo/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1188,15 +1188,15 @@ static void simplifyAttr()
static void simplifyLockFlags()
{
float vb[] = {
0.000000f, 0.000000f, 0.000000f,
0.000000f, 1.000000f, 0.000000f,
0.000000f, 2.000000f, 0.000000f,
1.000000f, 0.000000f, 0.000000f,
1.000000f, 1.000000f, 0.000000f,
1.000000f, 2.000000f, 0.000000f,
2.000000f, 0.000000f, 0.000000f,
2.000000f, 1.000000f, 0.000000f,
2.000000f, 2.000000f, 0.000000f, // clang-format :-/
0, 0, 0,
0, 1, 0,
0, 2, 0,
1, 0, 0,
1, 1, 0,
1, 2, 0,
2, 0, 0,
2, 1, 0,
2, 2, 0, // clang-format :-/
};

unsigned char lock[9] = {
Expand Down Expand Up @@ -1233,6 +1233,111 @@ static void simplifyLockFlags()
assert(memcmp(ib, expected, sizeof(expected)) == 0);
}

static void simplifySparse()
{
float vb[] = {
0, 0, 100,
0, 1, 0,
0, 2, 100,
1, 0, 0.1f,
1, 1, 0.1f,
1, 2, 0.1f,
2, 0, 100,
2, 1, 0,
2, 2, 100, // clang-format :-/
};

float vba[] = {
100,
0.5f,
100,
0.5f,
0.5f,
0,
100,
0.5f,
100, // clang-format :-/
};

float aw[] = {
0.2f};

unsigned char lock[9] = {
8, 1, 8,
1, 0, 1,
8, 1, 8, // clang-format :-/
};

// 1
// 3 4 5
// 7

unsigned int ib[] = {
3, 1, 4,
1, 5, 4,
3, 4, 7,
4, 5, 7, // clang-format :-/
};

unsigned int res[12];

// vertices 3-4-5 are slightly elevated along Z which guides the collapses when only using geometry
unsigned int expected[] = {
1, 5, 3,
3, 5, 7, // clang-format :-/
};

assert(meshopt_simplify(res, ib, 12, vb, 9, 12, 6, 1e-3f, meshopt_SimplifySparse) == 6);
assert(memcmp(res, expected, sizeof(expected)) == 0);

// vertices 1-4-7 have a crease in the attribute value which guides the collapses the opposite way when weighing attributes sufficiently
unsigned int expecteda[] = {
3, 1, 7,
1, 5, 7, // clang-format :-/
};

assert(meshopt_simplifyWithAttributes(res, ib, 12, vb, 9, 12, vba, sizeof(float), aw, 1, lock, 6, 1e-1f, meshopt_SimplifySparse) == 6);
assert(memcmp(res, expecteda, sizeof(expecteda)) == 0);

// a final test validates that destination can alias when using sparsity
assert(meshopt_simplify(ib, ib, 12, vb, 9, 12, 6, 1e-3f, meshopt_SimplifySparse) == 6);
assert(memcmp(ib, expected, sizeof(expected)) == 0);
}

static void simplifyErrorAbsolute()
{
float vb[] = {
0, 0, 0,
0, 1, 0,
0, 2, 0,
1, 0, 0,
1, 1, 1,
1, 2, 0,
2, 0, 0,
2, 1, 0,
2, 2, 0, // clang-format :-/
};

// 0 1 2
// 3 4 5
// 6 7 8

unsigned int ib[] = {
0, 1, 3,
3, 1, 4,
1, 2, 4,
4, 2, 5,
3, 4, 6,
6, 4, 7,
4, 5, 7,
7, 5, 8, // clang-format :-/
};

float error = 0.f;
assert(meshopt_simplify(ib, ib, 24, vb, 9, 12, 18, 2.f, meshopt_SimplifyLockBorder | meshopt_SimplifyErrorAbsolute, &error) == 18);
assert(fabsf(error - 0.85f) < 0.01f);
}

static void adjacency()
{
// 0 1/4
Expand Down Expand Up @@ -1448,6 +1553,8 @@ void runTests()
simplifyLockBorder();
simplifyAttr();
simplifyLockFlags();
simplifySparse();
simplifyErrorAbsolute();

adjacency();
tessellation();
Expand Down
2 changes: 2 additions & 0 deletions js/meshopt_simplifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ var MeshoptSimplifier = (function() {

var simplifyOptions = {
LockBorder: 1,
Sparse: 2,
ErrorAbsolute: 4,
};

return {
Expand Down
2 changes: 1 addition & 1 deletion js/meshopt_simplifier.module.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of meshoptimizer library and is distributed under the terms of MIT License.
// Copyright (C) 2016-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
export type Flags = "LockBorder";
export type Flags = "LockBorder" | "Sparse" | "ErrorAbsolute";

export const MeshoptSimplifier: {
supported: boolean;
Expand Down
2 changes: 2 additions & 0 deletions js/meshopt_simplifier.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ var MeshoptSimplifier = (function() {

var simplifyOptions = {
LockBorder: 1,
Sparse: 2,
ErrorAbsolute: 4,
};

return {
Expand Down
4 changes: 4 additions & 0 deletions src/meshoptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ enum
{
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
meshopt_SimplifyLockBorder = 1 << 0,
/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */
meshopt_SimplifySparse = 1 << 1,
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
meshopt_SimplifyErrorAbsolute = 1 << 2,
};

/**
Expand Down
Loading