From 93c0013dae3ba419adddf762bffcca9af9f4b52b Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 11 Jul 2024 11:53:17 -0700 Subject: [PATCH 1/3] simplify: Implement internal flag for topology debug information We have been relying on smuggling the kind/loop info computed during classification via meshopt_simplifyDebug global pointers. This information is valuable to visualize, and there's future improvements to classification that would need to be debugged, so it's not ready to remove but exposing this as globals results in issues as it expands the exported API surface, making it different between release & debug builds. Instead, this change adds a way to request this via an option bit, and return it via indices[] - instead of per-vertex information, we return this per-triangle-corner. It's not exactly identical, as there's some redundancy, but per-corner metadata is nice in general and this is generally sufficient to do the requisite visualization. The newly added option bit is *not* part of the official API surface - the format of the data may change at any point, and the entire feature may get dropped at any point once it stops being useful. --- src/simplifier.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/simplifier.cpp b/src/simplifier.cpp index e59b4afcd..6e93ea53e 100644 --- a/src/simplifier.cpp +++ b/src/simplifier.cpp @@ -1546,8 +1546,13 @@ static float interpolate(float y, float x0, float y0, float x1, float y1, float } // namespace meshopt +// Note: this is only exposed for debug visualization purposes; do *not* use +enum +{ + meshopt_SimplifyInternalDebug = 1 << 30 +}; + #ifndef NDEBUG -// Note: this is only exposed for debug visualization purposes; do *not* use these in debug builds MESHOPTIMIZER_API unsigned char* meshopt_simplifyDebugKind = NULL; MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = NULL; MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL; @@ -1561,7 +1566,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256); assert(vertex_positions_stride % sizeof(float) == 0); assert(target_index_count <= index_count); - assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute)) == 0); + assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute | meshopt_SimplifyInternalDebug)) == 0); assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256); assert(vertex_attributes_stride % sizeof(float) == 0); assert(attribute_count <= kMaxAttributes); @@ -1705,6 +1710,21 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic printf("result: %d triangles, error: %e; total %d passes\n", int(result_count / 3), sqrtf(result_error), int(pass_count)); #endif + // if debug visualization data is requested, fill it instead of index data; for simplicity, this doesn't work with sparsity + if ((options & meshopt_SimplifyInternalDebug) && !sparse_remap) + { + assert(Kind_Count <= 8 && vertex_count < (1 << 28)); // 3 bit kind, 1 bit loop + + for (size_t i = 0; i < result_count; i += 3) + { + unsigned int a = result[i + 0], b = result[i + 1], c = result[i + 2]; + + result[i + 0] |= (vertex_kind[a] << 28) | (unsigned(loop[a] == b || loopback[b] == a) << 31); + result[i + 1] |= (vertex_kind[b] << 28) | (unsigned(loop[b] == c || loopback[c] == b) << 31); + result[i + 2] |= (vertex_kind[c] << 28) | (unsigned(loop[c] == a || loopback[a] == c) << 31); + } + } + #ifndef NDEBUG if (meshopt_simplifyDebugKind) memcpy(meshopt_simplifyDebugKind, vertex_kind, vertex_count); From b42333e295dba59fd3d1b0eb30f0d0c4d6446945 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 11 Jul 2024 12:10:26 -0700 Subject: [PATCH 2/3] gltfpack: Switch to SimplifyInternalDebug for debugSimplify This code reworks the debug visualization for simplification to use new internal feature that communicates metadata via index data. For now we draw loops even for locked vertices and don't make an attempt to deduplicate points or lines, as visualization performance is not critical, and locked loops actually make it easier to debug in some cases. --- gltf/mesh.cpp | 78 +++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/gltf/mesh.cpp b/gltf/mesh.cpp index a956569ab..99950767b 100644 --- a/gltf/mesh.cpp +++ b/gltf/mesh.cpp @@ -527,8 +527,13 @@ static Stream* getStream(Mesh& mesh, cgltf_attribute_type type, int index = 0) return NULL; } -static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggressive, bool lock_borders) +static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggressive, bool lock_borders, bool debug = false) { + enum + { + meshopt_SimplifyInternalDebug = 1 << 30 + }; + assert(mesh.type == cgltf_primitive_type_triangles); if (mesh.indices.empty()) @@ -543,7 +548,11 @@ static void simplifyMesh(Mesh& mesh, float threshold, bool attributes, bool aggr size_t target_index_count = size_t(double(mesh.indices.size() / 3) * threshold) * 3; float target_error = 1e-2f; float target_error_aggressive = 1e-1f; - unsigned int options = lock_borders ? meshopt_SimplifyLockBorder : 0; + unsigned int options = 0; + if (lock_borders) + options |= meshopt_SimplifyLockBorder; + if (debug) + options |= meshopt_SimplifyInternalDebug; const Stream* attr = getStream(mesh, cgltf_attribute_type_color); attr = attr ? attr : getStream(mesh, cgltf_attribute_type_normal); @@ -784,10 +793,6 @@ void processMesh(Mesh& mesh, const Settings& settings) } #ifndef NDEBUG -extern MESHOPTIMIZER_API unsigned char* meshopt_simplifyDebugKind; -extern MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop; -extern MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack; - void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, bool attributes) { Mesh mesh = source; @@ -799,27 +804,9 @@ void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, bo reindexMesh(mesh); filterTriangles(mesh); - // before simplification we need to setup target kind/loop arrays size_t vertex_count = mesh.streams[0].data.size(); - std::vector kind(vertex_count); - std::vector loop(vertex_count); - std::vector loopback(vertex_count); - std::vector live(vertex_count); - - meshopt_simplifyDebugKind = &kind[0]; - meshopt_simplifyDebugLoop = &loop[0]; - meshopt_simplifyDebugLoopBack = &loopback[0]; - - simplifyMesh(mesh, ratio, attributes, /* aggressive= */ false, /* lock_borders= */ false); - - meshopt_simplifyDebugKind = NULL; - meshopt_simplifyDebugLoop = NULL; - meshopt_simplifyDebugLoopBack = NULL; - - // fill out live info - for (size_t i = 0; i < mesh.indices.size(); ++i) - live[mesh.indices[i]] = true; + simplifyMesh(mesh, ratio, attributes, /* aggressive= */ false, /* lock_borders= */ false, /* debug= */ true); // color palette for display static const Attr kPalette[] = { @@ -850,38 +837,37 @@ void debugSimplify(const Mesh& source, Mesh& kinds, Mesh& loops, float ratio, bo // transform kind/loop data into lines & points Stream colors = {cgltf_attribute_type_color}; - colors.data.resize(vertex_count); - - for (size_t i = 0; i < vertex_count; ++i) - colors.data[i] = kPalette[kind[i]]; + colors.data.resize(vertex_count, kPalette[0]); kinds.type = cgltf_primitive_type_points; - - kinds.streams.push_back(colors); - - for (size_t i = 0; i < vertex_count; ++i) - if (live[i] && kind[i] != 0) - kinds.indices.push_back(unsigned(i)); - loops.type = cgltf_primitive_type_lines; - loops.streams.push_back(colors); - - for (size_t i = 0; i < vertex_count; ++i) - if (live[i] && (kind[i] == 1 || kind[i] == 2)) + for (size_t i = 0; i < mesh.indices.size(); i += 3) + { + for (int k = 0; k < 3; ++k) { - if (loop[i] != ~0u && live[loop[i]]) + const unsigned int mask = (1 << 28) - 1; + unsigned int v = mesh.indices[i + k]; + unsigned int next = mesh.indices[i + (k + 1) % 3]; + unsigned int vk = (v >> 28) & 7; + unsigned int vl = v >> 31; + + if (vk) { - loops.indices.push_back(unsigned(i)); - loops.indices.push_back(loop[i]); + colors.data[v & mask] = kPalette[vk]; + kinds.indices.push_back(v & mask); } - if (loopback[i] != ~0u && live[loopback[i]]) + if (vl) { - loops.indices.push_back(loopback[i]); - loops.indices.push_back(unsigned(i)); + loops.indices.push_back(v & mask); + loops.indices.push_back(next & mask); } } + } + + kinds.streams.push_back(colors); + loops.streams.push_back(colors); } void debugMeshlets(const Mesh& source, Mesh& meshlets, int max_vertices, bool scan) From 3c6f9b0e25d56dc4b501ddb855f034c594998444 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Thu, 11 Jul 2024 12:13:07 -0700 Subject: [PATCH 3/3] simplify: Clean up old meshopt_simplifyDebug symbols We no longer rely on these in gltfpack. --- src/simplifier.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/simplifier.cpp b/src/simplifier.cpp index 6e93ea53e..d19e25fd5 100644 --- a/src/simplifier.cpp +++ b/src/simplifier.cpp @@ -1552,12 +1552,6 @@ enum meshopt_SimplifyInternalDebug = 1 << 30 }; -#ifndef NDEBUG -MESHOPTIMIZER_API unsigned char* meshopt_simplifyDebugKind = NULL; -MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = NULL; -MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL; -#endif - size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) { using namespace meshopt; @@ -1725,17 +1719,6 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic } } -#ifndef NDEBUG - if (meshopt_simplifyDebugKind) - memcpy(meshopt_simplifyDebugKind, vertex_kind, vertex_count); - - if (meshopt_simplifyDebugLoop) - memcpy(meshopt_simplifyDebugLoop, loop, vertex_count * sizeof(unsigned int)); - - if (meshopt_simplifyDebugLoopBack) - memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int)); -#endif - // convert resulting indices back into the dense space of the larger mesh if (sparse_remap) for (size_t i = 0; i < result_count; ++i)