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

simplify: Split quadrics update into a separate function #788

Merged
merged 3 commits into from
Oct 11, 2024
Merged
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
96 changes: 51 additions & 45 deletions src/simplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
}
}

static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* attribute_quadrics, QuadricGrad* attribute_gradients, size_t attribute_count, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error, float& vertex_error)
static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
{
size_t edge_collapses = 0;
size_t triangle_collapses = 0;
Expand Down Expand Up @@ -1193,43 +1193,6 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
assert(collapse_remap[r0] == r0);
assert(collapse_remap[r1] == r1);

unsigned int sx = i1;

// for seam collapses we need to move the seam pair together; this is a bit tricky to compute since we need to rely on edge loops as target vertex may be locked (and thus have more than two wedges)
if (kind == Kind_Seam)
{
unsigned int s0 = wedge[i0];
unsigned int s1 = loop[i0] == i1 ? loopback[s0] : loop[s0];
assert(s0 != i0 && wedge[s0] == i0);
assert(s1 != ~0u && remap[s1] == r1);

// additional asserts to verify that the seam pair is consistent
assert(kind != vertex_kind[i1] || s1 == wedge[i1]);
assert(loop[i0] == i1 || loopback[i0] == i1);
assert(loop[s0] == s1 || loopback[s0] == s1);

// note: this should never happen due to the assertion above, but when disabled if we ever hit this case we'll get a memory safety issue; for now play it safe
sx = (s1 != ~0u) ? s1 : wedge[i1];
}

quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);

if (attribute_count)
{
quadricAdd(attribute_quadrics[i1], attribute_quadrics[i0]);
quadricAdd(&attribute_gradients[i1 * attribute_count], &attribute_gradients[i0 * attribute_count], attribute_count);

// note: this is intentionally missing handling for Kind_Complex; we assume that complex vertices have similar attribute values so just using the primary vertex is fine
if (kind == Kind_Seam)
{
// seam collapses involve two edges so we need to update attribute quadrics for both target vertices; position quadrics are shared
unsigned int s0 = wedge[i0], s1 = sx;

quadricAdd(attribute_quadrics[s1], attribute_quadrics[s0]);
quadricAdd(&attribute_gradients[s1 * attribute_count], &attribute_gradients[s0 * attribute_count], attribute_count);
}
}

if (kind == Kind_Complex)
{
// remap all vertices in the complex to the target vertex
Expand All @@ -1243,10 +1206,19 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
}
else if (kind == Kind_Seam)
{
// remap v0 to v1 and seam pair of v0 to seam pair of v1
unsigned int s0 = wedge[i0], s1 = sx;
// for seam collapses we need to move the seam pair together; this is a bit tricky to compute since we need to rely on edge loops as target vertex may be locked (and thus have more than two wedges)
unsigned int s0 = wedge[i0];
unsigned int s1 = loop[i0] == i1 ? loopback[s0] : loop[s0];
assert(s0 != i0 && wedge[s0] == i0);
assert(remap[s1] == r1);
assert(s1 != ~0u && remap[s1] == r1);

// additional asserts to verify that the seam pair is consistent
assert(kind != vertex_kind[i1] || s1 == wedge[i1]);
assert(loop[i0] == i1 || loopback[i0] == i1);
assert(loop[s0] == s1 || loopback[s0] == s1);

// note: this should never happen due to the assertion above, but when disabled if we ever hit this case we'll get a memory safety issue; for now play it safe
s1 = (s1 != ~0u) ? s1 : wedge[i1];

collapse_remap[i0] = i1;
collapse_remap[s0] = s1;
Expand All @@ -1267,11 +1239,8 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
triangle_collapses += (kind == Kind_Border) ? 1 : 2;
edge_collapses++;

// when attributes are used, distance error needs to be recomputed as collapses don't track it; it is safe to do this after the quadric adjustment
float derr = attribute_count == 0 ? c.error : quadricError(vertex_quadrics[r0], vertex_positions[r1]);

result_error = result_error < c.error ? c.error : result_error;
vertex_error = vertex_error < derr ? derr : vertex_error;
}

#if TRACE
Expand All @@ -1287,6 +1256,38 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
return edge_collapses;
}

static void updateQuadrics(const unsigned int* collapse_remap, size_t vertex_count, Quadric* vertex_quadrics, Quadric* attribute_quadrics, QuadricGrad* attribute_gradients, size_t attribute_count, const Vector3* vertex_positions, const unsigned int* remap, float& vertex_error)
{
for (size_t i = 0; i < vertex_count; ++i)
{
if (collapse_remap[i] == i)
continue;

unsigned int i0 = unsigned(i);
unsigned int i1 = collapse_remap[i];

unsigned int r0 = remap[i0];
unsigned int r1 = remap[i1];

// ensure we only update vertex_quadrics once: primary vertex must be moved if any wedge is moved
if (i0 == r0)
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);

if (attribute_count)
{
quadricAdd(attribute_quadrics[i1], attribute_quadrics[i0]);
quadricAdd(&attribute_gradients[i1 * attribute_count], &attribute_gradients[i0 * attribute_count], attribute_count);

if (i0 == r0)
{
// when attributes are used, distance error needs to be recomputed as collapses don't track it; it is safe to do this after the quadric adjustment
float derr = quadricError(vertex_quadrics[r0], vertex_positions[r1]);
vertex_error = vertex_error < derr ? derr : vertex_error;
}
}
}
}

static size_t remapIndexBuffer(unsigned int* indices, size_t index_count, const unsigned int* collapse_remap)
{
size_t write = 0;
Expand Down Expand Up @@ -1975,12 +1976,17 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic

memset(collapse_locked, 0, vertex_count);

size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, attribute_quadrics, attribute_gradients, attribute_count, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, loop, loopback, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error, vertex_error);
size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, loop, loopback, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);

// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
if (collapses == 0)
break;

updateQuadrics(collapse_remap, vertex_count, vertex_quadrics, attribute_quadrics, attribute_gradients, attribute_count, vertex_positions, remap, vertex_error);

// updateQuadrics will update vertex error if we use attributes, but if we don't then result_error and vertex_error are equivalent
vertex_error = attribute_count == 0 ? result_error : vertex_error;

remapEdgeLoops(loop, vertex_count, collapse_remap);
remapEdgeLoops(loopback, vertex_count, collapse_remap);

Expand Down