mirror of
https://github.com/openharmony/third_party_meshoptimizer.git
synced 2026-07-19 20:03:59 -04:00
Merge pull request #773 from zeux/simp-prune
simplify: Implement experimental support for component pruning
This commit is contained in:
@@ -322,6 +322,7 @@ For basic customization, a number of options can be passed via `options` bitmask
|
||||
- `meshopt_SimplifyLockBorder` restricts the simplifier from collapsing edges that are on the border of the mesh. This can be useful for simplifying mesh subsets independently, so that the LODs can be combined without introducing cracks.
|
||||
- `meshopt_SimplifyErrorAbsolute` changes the error metric from relative to absolute both for the input error limit as well as for the resulting error. This can be used instead of `meshopt_simplifyScale`.
|
||||
- `meshopt_SimplifySparse` improves simplification performance assuming input indices are a sparse subset of the mesh. This can be useful when simplifying small mesh subsets independently, and is intended to be used for meshlet simplification. For consistency, it is recommended to use absolute errors when sparse simplification is desired, as this flag changes the meaning of the relative errors.
|
||||
- `meshopt_SimplifyPrune` allows the simplifier to remove isolated components regardless of the topological restrictions inside the component. This is generally recommended for full-mesh simplification as it can improve quality and reduce triangle count; note that with this option, triangles connected to locked vertices may be removed as part of their component.
|
||||
|
||||
While `meshopt_simplify` is aware of attribute discontinuities by default (and infers them through the supplied index buffer) and tries to preserve them, it can be useful to provide information about attribute values. This allows the simplifier to take attribute error into account which can improve shading (by using vertex normals), texture deformation (by using texture coordinates), and may be necessary to preserve vertex colors when textures are not used in the first place. This can be done by using a variant of the simplification function that takes attribute values and weight factors, `meshopt_simplifyWithAttributes`:
|
||||
|
||||
|
||||
+6
-5
@@ -451,7 +451,7 @@ void packMesh(std::vector<PackedVertexOct>& pv, const std::vector<Vertex>& verti
|
||||
}
|
||||
}
|
||||
|
||||
void simplify(const Mesh& mesh, float threshold = 0.2f)
|
||||
void simplify(const Mesh& mesh, float threshold = 0.2f, unsigned int options = 0)
|
||||
{
|
||||
Mesh lod;
|
||||
|
||||
@@ -462,7 +462,7 @@ void simplify(const Mesh& mesh, float threshold = 0.2f)
|
||||
float result_error = 0;
|
||||
|
||||
lod.indices.resize(mesh.indices.size()); // note: simplify needs space for index_count elements in the destination array, not target_index_count
|
||||
lod.indices.resize(meshopt_simplify(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, 0, &result_error));
|
||||
lod.indices.resize(meshopt_simplify(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, options, &result_error));
|
||||
|
||||
lod.vertices.resize(lod.indices.size() < mesh.vertices.size() ? lod.indices.size() : mesh.vertices.size()); // note: this is just to reduce the cost of resize()
|
||||
lod.vertices.resize(meshopt_optimizeVertexFetch(&lod.vertices[0], &lod.indices[0], lod.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex)));
|
||||
@@ -476,7 +476,7 @@ void simplify(const Mesh& mesh, float threshold = 0.2f)
|
||||
(end - start) * 1000);
|
||||
}
|
||||
|
||||
void simplifyAttr(const Mesh& mesh, float threshold = 0.2f)
|
||||
void simplifyAttr(const Mesh& mesh, float threshold = 0.2f, unsigned int options = 0)
|
||||
{
|
||||
Mesh lod;
|
||||
|
||||
@@ -490,7 +490,7 @@ void simplifyAttr(const Mesh& mesh, float threshold = 0.2f)
|
||||
const float attr_weights[3] = {nrm_weight, nrm_weight, nrm_weight};
|
||||
|
||||
lod.indices.resize(mesh.indices.size()); // note: simplify needs space for index_count elements in the destination array, not target_index_count
|
||||
lod.indices.resize(meshopt_simplifyWithAttributes(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), &mesh.vertices[0].nx, sizeof(Vertex), attr_weights, 3, NULL, target_index_count, target_error, 0, &result_error));
|
||||
lod.indices.resize(meshopt_simplifyWithAttributes(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), &mesh.vertices[0].nx, sizeof(Vertex), attr_weights, 3, NULL, target_index_count, target_error, options, &result_error));
|
||||
|
||||
lod.vertices.resize(lod.indices.size() < mesh.vertices.size() ? lod.indices.size() : mesh.vertices.size()); // note: this is just to reduce the cost of resize()
|
||||
lod.vertices.resize(meshopt_optimizeVertexFetch(&lod.vertices[0], &lod.indices[0], lod.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex)));
|
||||
@@ -1372,6 +1372,7 @@ void process(const char* path)
|
||||
encodeVertex<PackedVertexOct>(copy, "O");
|
||||
|
||||
simplify(mesh);
|
||||
simplify(mesh, 0.1f, meshopt_SimplifyPrune);
|
||||
simplifyAttr(mesh);
|
||||
simplifySloppy(mesh);
|
||||
simplifyComplete(mesh);
|
||||
@@ -1391,7 +1392,7 @@ void processDev(const char* path)
|
||||
if (!loadMesh(mesh, path))
|
||||
return;
|
||||
|
||||
simplifyAttr(mesh);
|
||||
simplifyAttr(mesh, 0.1f, meshopt_SimplifyPrune);
|
||||
}
|
||||
|
||||
void processNanite(const char* path)
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
ratio: 1.0,
|
||||
debugOverlay: false,
|
||||
lockBorder: false,
|
||||
prune: false,
|
||||
weldVertices: false,
|
||||
useAttributes: false,
|
||||
errorThresholdLog10: 1,
|
||||
@@ -113,6 +114,7 @@
|
||||
var guiSimplify = gui.addFolder('Simplify');
|
||||
guiSimplify.add(settings, 'ratio', 0, 1, 0.01).onChange(simplify);
|
||||
guiSimplify.add(settings, 'lockBorder').onChange(simplify);
|
||||
guiSimplify.add(settings, 'prune').onChange(simplify);
|
||||
guiSimplify.add(settings, 'weldVertices').onChange(simplify);
|
||||
guiSimplify.add(settings, 'errorThresholdLog10', 0, 3, 0.1).onChange(simplify);
|
||||
guiSimplify.add(settings, 'useAttributes').onChange(simplify).onFinishChange(updateSettings);
|
||||
@@ -199,6 +201,9 @@
|
||||
if (settings.lockBorder) {
|
||||
flags.push('LockBorder');
|
||||
}
|
||||
if (settings.prune) {
|
||||
flags.push('Prune');
|
||||
}
|
||||
|
||||
var stride = geo.attributes.position instanceof THREE.InterleavedBufferAttribute ? geo.attributes.position.data.stride : 3;
|
||||
|
||||
|
||||
+124
-30
@@ -909,39 +909,19 @@ static void simplify()
|
||||
// 1 2
|
||||
// 3 4 5
|
||||
unsigned int ib[] = {
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
4,
|
||||
2,
|
||||
5,
|
||||
4,
|
||||
0, 2, 1,
|
||||
1, 2, 3,
|
||||
3, 2, 4,
|
||||
2, 5, 4, // clang-format :-/
|
||||
};
|
||||
|
||||
float vb[] = {
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
0,
|
||||
0, 4, 0,
|
||||
0, 1, 0,
|
||||
2, 2, 0,
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
4, 0, 0, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned int expected[] = {
|
||||
@@ -1431,6 +1411,117 @@ static void simplifySeamFake()
|
||||
assert(meshopt_simplify(ib, ib, 6, vb, 4, 16, 0, 1.f, 0, NULL) == 6);
|
||||
}
|
||||
|
||||
static void simplifyDebug()
|
||||
{
|
||||
// 0
|
||||
// 1 2
|
||||
// 3 4 5
|
||||
unsigned int ib[] = {
|
||||
0, 2, 1,
|
||||
1, 2, 3,
|
||||
3, 2, 4,
|
||||
2, 5, 4, // clang-format :-/
|
||||
};
|
||||
|
||||
float vb[] = {
|
||||
0, 4, 0,
|
||||
0, 1, 0,
|
||||
2, 2, 0,
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
4, 0, 0, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned int expected[] = {
|
||||
0 | (9u << 28),
|
||||
5 | (9u << 28),
|
||||
3 | (9u << 28),
|
||||
};
|
||||
|
||||
const unsigned int meshopt_SimplifyInternalDebug = 1 << 30;
|
||||
|
||||
float error;
|
||||
assert(meshopt_simplify(ib, ib, 12, vb, 6, 12, 3, 1e-2f, meshopt_SimplifyInternalDebug, &error) == 3);
|
||||
assert(error == 0.f);
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
}
|
||||
|
||||
static void simplifyPrune()
|
||||
{
|
||||
// 0
|
||||
// 1 2
|
||||
// 3 4 5
|
||||
// +
|
||||
// 6 7 8 (same position)
|
||||
unsigned int ib[] = {
|
||||
0, 2, 1,
|
||||
1, 2, 3,
|
||||
3, 2, 4,
|
||||
2, 5, 4,
|
||||
6, 7, 8, // clang-format :-/
|
||||
};
|
||||
|
||||
float vb[] = {
|
||||
0, 4, 0,
|
||||
0, 1, 0,
|
||||
2, 2, 0,
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
4, 0, 0,
|
||||
1, 1, 1,
|
||||
1, 1, 1,
|
||||
1, 1, 1, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned int expected[] = {
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
};
|
||||
|
||||
float error;
|
||||
assert(meshopt_simplify(ib, ib, 15, vb, 9, 12, 3, 1e-2f, meshopt_SimplifyPrune, &error) == 3);
|
||||
assert(error == 0.f);
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
|
||||
// re-run prune with and without sparsity on a small subset to make sure the component code correctly handles sparse subsets
|
||||
assert(meshopt_simplify(ib, ib, 3, vb, 9, 12, 3, 1e-2f, meshopt_SimplifyPrune, &error) == 3);
|
||||
assert(meshopt_simplify(ib, ib, 3, vb, 9, 12, 3, 1e-2f, meshopt_SimplifyPrune | meshopt_SimplifySparse, &error) == 3);
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
}
|
||||
|
||||
static void simplifyPruneCleanup()
|
||||
{
|
||||
unsigned int ib[] = {
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8, // clang-format :-/
|
||||
};
|
||||
|
||||
float vb[] = {
|
||||
0, 0, 0,
|
||||
0, 1, 0,
|
||||
1, 0, 0,
|
||||
0, 0, 1,
|
||||
0, 2, 1,
|
||||
2, 0, 1,
|
||||
0, 0, 2,
|
||||
0, 4, 2,
|
||||
4, 0, 2, // clang-format :-/
|
||||
};
|
||||
|
||||
unsigned int expected[] = {
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
};
|
||||
|
||||
float error;
|
||||
assert(meshopt_simplify(ib, ib, 9, vb, 9, 12, 3, 1.f, meshopt_SimplifyLockBorder | meshopt_SimplifyPrune, &error) == 3);
|
||||
assert(fabsf(error - 0.37f) < 0.01f);
|
||||
assert(memcmp(ib, expected, sizeof(expected)) == 0);
|
||||
}
|
||||
|
||||
static void adjacency()
|
||||
{
|
||||
// 0 1/4
|
||||
@@ -1687,6 +1778,9 @@ void runTests()
|
||||
simplifyErrorAbsolute();
|
||||
simplifySeam();
|
||||
simplifySeamFake();
|
||||
simplifyDebug();
|
||||
simplifyPrune();
|
||||
simplifyPruneCleanup();
|
||||
|
||||
adjacency();
|
||||
tessellation();
|
||||
|
||||
@@ -625,9 +625,13 @@ static void simplifyMesh(Mesh& mesh, float threshold, float error, bool attribut
|
||||
size_t target_index_count = size_t(double(mesh.indices.size() / 3) * threshold) * 3;
|
||||
float target_error = error;
|
||||
float target_error_aggressive = 1e-1f;
|
||||
|
||||
unsigned int options = 0;
|
||||
if (lock_borders)
|
||||
options |= meshopt_SimplifyLockBorder;
|
||||
else
|
||||
options |= meshopt_SimplifyPrune;
|
||||
|
||||
if (debug)
|
||||
options |= meshopt_SimplifyInternalDebug;
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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' | 'Sparse' | 'ErrorAbsolute';
|
||||
export type Flags = 'LockBorder' | 'Sparse' | 'ErrorAbsolute' | 'Prune';
|
||||
|
||||
export const MeshoptSimplifier: {
|
||||
supported: boolean;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -349,6 +349,8 @@ enum
|
||||
meshopt_SimplifySparse = 1 << 1,
|
||||
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
|
||||
meshopt_SimplifyErrorAbsolute = 1 << 2,
|
||||
/* Experimental: remove disconnected parts of the mesh during simplification incrementally, regardless of the topological restrictions inside components. */
|
||||
meshopt_SimplifyPrune = 1 << 3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+207
-1
@@ -1322,6 +1322,164 @@ static void remapEdgeLoops(unsigned int* loop, size_t vertex_count, const unsign
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int follow(unsigned int* parents, unsigned int index)
|
||||
{
|
||||
while (index != parents[index])
|
||||
{
|
||||
unsigned int parent = parents[index];
|
||||
parents[index] = parents[parent];
|
||||
index = parent;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static size_t buildComponents(unsigned int* components, size_t vertex_count, const unsigned int* indices, size_t index_count, const unsigned int* remap)
|
||||
{
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
components[i] = unsigned(i);
|
||||
|
||||
// compute a unique (but not sequential!) index for each component via union-find
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
static const int next[4] = {1, 2, 0, 1};
|
||||
|
||||
for (int e = 0; e < 3; ++e)
|
||||
{
|
||||
unsigned int i0 = indices[i + e];
|
||||
unsigned int i1 = indices[i + next[e]];
|
||||
|
||||
unsigned int r0 = remap[i0];
|
||||
unsigned int r1 = remap[i1];
|
||||
|
||||
r0 = follow(components, r0);
|
||||
r1 = follow(components, r1);
|
||||
|
||||
// merge components with larger indices into components with smaller indices
|
||||
// this guarantees that the root of the component is always the one with the smallest index
|
||||
if (r0 != r1)
|
||||
components[r0 < r1 ? r1 : r0] = r0 < r1 ? r0 : r1;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure each element points to the component root *before* we renumber the components
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
if (remap[i] == i)
|
||||
components[i] = follow(components, unsigned(i));
|
||||
|
||||
unsigned int next_component = 0;
|
||||
|
||||
// renumber components using sequential indices
|
||||
// a sequential pass is sufficient because component root always has the smallest index
|
||||
// note: it is unsafe to use follow() in this pass because we're replacing component links with sequential indices inplace
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
if (remap[i] == i)
|
||||
{
|
||||
unsigned int root = components[i];
|
||||
assert(root <= i); // make sure we already computed the component for non-roots
|
||||
components[i] = (root == i) ? next_component++ : components[root];
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(remap[i] < i); // make sure we already computed the component
|
||||
components[i] = components[remap[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return next_component;
|
||||
}
|
||||
|
||||
static void measureComponents(float* component_errors, size_t component_count, const unsigned int* components, const Vector3* vertex_positions, size_t vertex_count)
|
||||
{
|
||||
memset(component_errors, 0, component_count * 4 * sizeof(float));
|
||||
|
||||
// compute approximate sphere center for each component as an average
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
unsigned int c = components[i];
|
||||
assert(components[i] < component_count);
|
||||
|
||||
Vector3 v = vertex_positions[i]; // copy avoids aliasing issues
|
||||
|
||||
component_errors[c * 4 + 0] += v.x;
|
||||
component_errors[c * 4 + 1] += v.y;
|
||||
component_errors[c * 4 + 2] += v.z;
|
||||
component_errors[c * 4 + 3] += 1; // weight
|
||||
}
|
||||
|
||||
// complete the center computation, and reinitialize [3] as a radius
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
{
|
||||
float w = component_errors[i * 4 + 3];
|
||||
float iw = w == 0.f ? 0.f : 1.f / w;
|
||||
|
||||
component_errors[i * 4 + 0] *= iw;
|
||||
component_errors[i * 4 + 1] *= iw;
|
||||
component_errors[i * 4 + 2] *= iw;
|
||||
component_errors[i * 4 + 3] = 0; // radius
|
||||
}
|
||||
|
||||
// compute squared radius for each component
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
unsigned int c = components[i];
|
||||
|
||||
float dx = vertex_positions[i].x - component_errors[c * 4 + 0];
|
||||
float dy = vertex_positions[i].y - component_errors[c * 4 + 1];
|
||||
float dz = vertex_positions[i].z - component_errors[c * 4 + 2];
|
||||
float r = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
component_errors[c * 4 + 3] = component_errors[c * 4 + 3] < r ? r : component_errors[c * 4 + 3];
|
||||
}
|
||||
|
||||
// we've used the output buffer as scratch space, so we need to move the results to proper indices
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
{
|
||||
#if TRACE > 1
|
||||
printf("component %d: center %f %f %f, error %e\n", int(i),
|
||||
component_errors[i * 4 + 0], component_errors[i * 4 + 1], component_errors[i * 4 + 2], sqrtf(component_errors[i * 4 + 3]));
|
||||
#endif
|
||||
// note: we keep the squared error to make it match quadric error metric
|
||||
component_errors[i] = component_errors[i * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
static size_t pruneComponents(unsigned int* indices, size_t index_count, const unsigned int* components, const float* component_errors, size_t component_count, float error_cutoff, float& nexterror)
|
||||
{
|
||||
size_t write = 0;
|
||||
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
unsigned int c = components[indices[i]];
|
||||
assert(c == components[indices[i + 1]] && c == components[indices[i + 2]]);
|
||||
|
||||
if (component_errors[c] > error_cutoff)
|
||||
{
|
||||
indices[write + 0] = indices[i + 0];
|
||||
indices[write + 1] = indices[i + 1];
|
||||
indices[write + 2] = indices[i + 2];
|
||||
write += 3;
|
||||
}
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
size_t pruned_components = 0;
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
pruned_components += (component_errors[i] >= nexterror && component_errors[i] <= error_cutoff);
|
||||
|
||||
printf("pruned %d triangles in %d components (goal %e)\n", int((index_count - write) / 3), int(pruned_components), sqrtf(error_cutoff));
|
||||
#endif
|
||||
|
||||
// update next error with the smallest error of the remaining components for future pruning
|
||||
nexterror = FLT_MAX;
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
if (component_errors[i] > error_cutoff)
|
||||
nexterror = nexterror > component_errors[i] ? component_errors[i] : nexterror;
|
||||
|
||||
return write;
|
||||
}
|
||||
|
||||
struct CellHasher
|
||||
{
|
||||
const unsigned int* vertex_ids;
|
||||
@@ -1645,7 +1803,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
assert(vertex_positions_stride % sizeof(float) == 0);
|
||||
assert(target_index_count <= index_count);
|
||||
assert(target_error >= 0);
|
||||
assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute | meshopt_SimplifyInternalDebug)) == 0);
|
||||
assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute | meshopt_SimplifyPrune | 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);
|
||||
@@ -1736,6 +1894,28 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
if (attribute_count)
|
||||
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count);
|
||||
|
||||
unsigned int* components = NULL;
|
||||
float* component_errors = NULL;
|
||||
size_t component_count = 0;
|
||||
float component_nexterror = 0;
|
||||
|
||||
if (options & meshopt_SimplifyPrune)
|
||||
{
|
||||
components = allocator.allocate<unsigned int>(vertex_count);
|
||||
component_count = buildComponents(components, vertex_count, result, index_count, remap);
|
||||
|
||||
component_errors = allocator.allocate<float>(component_count * 4); // overallocate for temporary use inside measureComponents
|
||||
measureComponents(component_errors, component_count, components, vertex_positions, vertex_count);
|
||||
|
||||
component_nexterror = FLT_MAX;
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
component_nexterror = component_nexterror > component_errors[i] ? component_errors[i] : component_nexterror;
|
||||
|
||||
#if TRACE
|
||||
printf("components: %d (min error %e)\n", int(component_count), sqrtf(component_nexterror));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
size_t pass_count = 0;
|
||||
#endif
|
||||
@@ -1794,6 +1974,32 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
assert(new_count < result_count);
|
||||
|
||||
result_count = new_count;
|
||||
|
||||
if ((options & meshopt_SimplifyPrune) && result_count > target_index_count && component_nexterror <= result_error)
|
||||
result_count = pruneComponents(result, result_count, components, component_errors, component_count, result_error, component_nexterror);
|
||||
}
|
||||
|
||||
// we're done with the regular simplification but we're still short of the target; try pruning more aggressively towards error_limit
|
||||
while ((options & meshopt_SimplifyPrune) && result_count > target_index_count && component_nexterror <= error_limit)
|
||||
{
|
||||
#if TRACE
|
||||
printf("pass %d: cleanup; ", int(pass_count++));
|
||||
#endif
|
||||
|
||||
float component_cutoff = component_nexterror * 1.5f < error_limit ? component_nexterror * 1.5f : error_limit;
|
||||
|
||||
// track maximum error in eligible components as we are increasing resulting error
|
||||
float component_maxerror = 0;
|
||||
for (size_t i = 0; i < component_count; ++i)
|
||||
if (component_errors[i] > component_maxerror && component_errors[i] <= component_cutoff)
|
||||
component_maxerror = component_errors[i];
|
||||
|
||||
size_t new_count = pruneComponents(result, result_count, components, component_errors, component_count, component_cutoff, component_nexterror);
|
||||
if (new_count == result_count)
|
||||
break;
|
||||
|
||||
result_count = new_count;
|
||||
result_error = result_error < component_maxerror ? component_maxerror : result_error;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
|
||||
@@ -33,6 +33,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* buffer, size_t size)
|
||||
meshopt_simplify(res, ib, indices, vb[0], 100, sizeof(float) * 4, 0, FLT_MAX, 0, NULL);
|
||||
meshopt_simplify(res, ib, indices, vb[0], 100, sizeof(float) * 4, 0, FLT_MAX, meshopt_SimplifyLockBorder, NULL);
|
||||
meshopt_simplify(res, ib, indices, vb[0], 100, sizeof(float) * 4, 0, FLT_MAX, meshopt_SimplifySparse, NULL);
|
||||
meshopt_simplify(res, ib, indices, vb[0], 100, sizeof(float) * 4, 0, FLT_MAX, meshopt_SimplifyPrune, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user