Skip to content

Commit

Permalink
Merge pull request #788 from zeux/simp-qup
Browse files Browse the repository at this point in the history
simplify: Split quadrics update into a separate function
  • Loading branch information
zeux authored Oct 11, 2024
2 parents 1e48e96 + e4144c2 commit 5a9b07b
Showing 1 changed file with 51 additions and 45 deletions.
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

0 comments on commit 5a9b07b

Please sign in to comment.