Skip to content

Commit 0a9d917

Browse files
authored
Merge pull request #742 from zeux/simp-seam
simplify: Separate attribute quadrics on attribute discontinuities
2 parents c913d04 + d2367d8 commit 0a9d917

File tree

2 files changed

+132
-24
lines changed

2 files changed

+132
-24
lines changed

demo/tests.cpp

+73
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,78 @@ static void simplifyErrorAbsolute()
13381338
assert(fabsf(error - 0.85f) < 0.01f);
13391339
}
13401340

1341+
static void simplifySeam()
1342+
{
1343+
// xyz+attr
1344+
float vb[] = {
1345+
0, 0, 0, 0,
1346+
0, 1, 0, 0,
1347+
0, 1, 0, 1,
1348+
0, 2, 0, 1,
1349+
1, 0, 0, 0,
1350+
1, 1, 0.3f, 0,
1351+
1, 1, 0.3f, 1,
1352+
1, 2, 0, 1,
1353+
2, 0, 0, 0,
1354+
2, 1, 0.1f, 0,
1355+
2, 1, 0.1f, 1,
1356+
2, 2, 0, 1,
1357+
3, 0, 0, 0,
1358+
3, 1, 0, 0,
1359+
3, 1, 0, 1,
1360+
3, 2, 0, 1, // clang-format :-/
1361+
};
1362+
1363+
// 0 1-2 3
1364+
// 4 5-6 7
1365+
// 8 9-10 11
1366+
// 12 13-14 15
1367+
1368+
unsigned int ib[] = {
1369+
0, 1, 4,
1370+
4, 1, 5,
1371+
2, 3, 6,
1372+
6, 3, 7,
1373+
4, 5, 8,
1374+
8, 5, 9,
1375+
6, 7, 10,
1376+
10, 7, 11,
1377+
8, 9, 12,
1378+
12, 9, 13,
1379+
10, 11, 14,
1380+
14, 11, 15, // clang-format :-/
1381+
};
1382+
1383+
// note: vertices 1-2 and 13-14 are classified as locked, because they are on a seam & a border
1384+
// since seam->locked collapses are restriced, we only get to 3 triangles on each side as the seam is simplified to 3 vertices
1385+
1386+
// so we get this structure initially, and then one of the internal seam vertices is collapsed to the other one:
1387+
// 0 1-2 3
1388+
// 5-6
1389+
// 9-10
1390+
// 12 13-14 15
1391+
unsigned int expected[] = {
1392+
0, 1, 5,
1393+
2, 3, 6,
1394+
0, 5, 12,
1395+
12, 5, 13,
1396+
6, 3, 14,
1397+
14, 3, 15, // clang-format :-/
1398+
};
1399+
1400+
unsigned int res[36];
1401+
float error = 0.f;
1402+
1403+
assert(meshopt_simplify(res, ib, 36, vb, 16, 16, 18, 1.f, 0, &error) == 18);
1404+
assert(memcmp(res, expected, sizeof(expected)) == 0);
1405+
assert(fabsf(error - 0.04f) < 0.01f); // note: the error is not zero because there is a small difference in height between the seam vertices
1406+
1407+
float aw = 1;
1408+
assert(meshopt_simplifyWithAttributes(res, ib, 36, vb, 16, 16, vb + 3, 16, &aw, 1, NULL, 18, 2.f, 0, &error) == 18);
1409+
assert(memcmp(res, expected, sizeof(expected)) == 0);
1410+
assert(fabsf(error - 0.04f) < 0.01f); // note: this is the same error as above because the attribute is constant on either side of the seam
1411+
}
1412+
13411413
static void adjacency()
13421414
{
13431415
// 0 1/4
@@ -1556,6 +1628,7 @@ void runTests()
15561628
simplifyLockFlags();
15571629
simplifySparse();
15581630
simplifyErrorAbsolute();
1631+
simplifySeam();
15591632

15601633
adjacency();
15611634
tessellation();

src/simplifier.cpp

+59-24
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
850850
}
851851
}
852852

853-
static void fillAttributeQuadrics(Quadric* attribute_quadrics, QuadricGrad* attribute_gradients, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const float* vertex_attributes, size_t attribute_count, const unsigned int* remap)
853+
static void fillAttributeQuadrics(Quadric* attribute_quadrics, QuadricGrad* attribute_gradients, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const float* vertex_attributes, size_t attribute_count)
854854
{
855855
for (size_t i = 0; i < index_count; i += 3)
856856
{
@@ -862,14 +862,13 @@ static void fillAttributeQuadrics(Quadric* attribute_quadrics, QuadricGrad* attr
862862
QuadricGrad G[kMaxAttributes];
863863
quadricFromAttributes(QA, G, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], &vertex_attributes[i0 * attribute_count], &vertex_attributes[i1 * attribute_count], &vertex_attributes[i2 * attribute_count], attribute_count);
864864

865-
// TODO: This blends together attribute weights across attribute discontinuities, which is probably not a great idea
866-
quadricAdd(attribute_quadrics[remap[i0]], QA);
867-
quadricAdd(attribute_quadrics[remap[i1]], QA);
868-
quadricAdd(attribute_quadrics[remap[i2]], QA);
865+
quadricAdd(attribute_quadrics[i0], QA);
866+
quadricAdd(attribute_quadrics[i1], QA);
867+
quadricAdd(attribute_quadrics[i2], QA);
869868

870-
quadricAdd(&attribute_gradients[remap[i0] * attribute_count], G, attribute_count);
871-
quadricAdd(&attribute_gradients[remap[i1] * attribute_count], G, attribute_count);
872-
quadricAdd(&attribute_gradients[remap[i2] * attribute_count], G, attribute_count);
869+
quadricAdd(&attribute_gradients[i0 * attribute_count], G, attribute_count);
870+
quadricAdd(&attribute_gradients[i1 * attribute_count], G, attribute_count);
871+
quadricAdd(&attribute_gradients[i2 * attribute_count], G, attribute_count);
873872
}
874873
}
875874

@@ -914,7 +913,13 @@ static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vert
914913

915914
// early-out when at least one triangle flips due to a collapse
916915
if (hasTriangleFlip(vertex_positions[a], vertex_positions[b], v0, v1))
916+
{
917+
#if TRACE >= 2
918+
printf("edge block %d -> %d: flip welded %d %d %d\n", i0, i1, a, i0, b);
919+
#endif
920+
917921
return true;
922+
}
918923
}
919924

920925
return false;
@@ -1017,16 +1022,31 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
10171022
float ei = quadricError(vertex_quadrics[remap[i0]], vertex_positions[i1]);
10181023
float ej = quadricError(vertex_quadrics[remap[j0]], vertex_positions[j1]);
10191024

1025+
#if TRACE >= 2
1026+
float di = ei, dj = ej;
1027+
#endif
1028+
10201029
if (attribute_count)
10211030
{
1022-
ei += quadricError(attribute_quadrics[remap[i0]], &attribute_gradients[remap[i0] * attribute_count], attribute_count, vertex_positions[i1], &vertex_attributes[i1 * attribute_count]);
1023-
ej += quadricError(attribute_quadrics[remap[j0]], &attribute_gradients[remap[j0] * attribute_count], attribute_count, vertex_positions[j1], &vertex_attributes[j1 * attribute_count]);
1031+
// note: ideally we would evaluate max/avg of attribute errors for seam edges, but it's not clear if it's worth the extra cost
1032+
ei += quadricError(attribute_quadrics[i0], &attribute_gradients[i0 * attribute_count], attribute_count, vertex_positions[i1], &vertex_attributes[i1 * attribute_count]);
1033+
ej += quadricError(attribute_quadrics[j0], &attribute_gradients[j0 * attribute_count], attribute_count, vertex_positions[j1], &vertex_attributes[j1 * attribute_count]);
10241034
}
10251035

10261036
// pick edge direction with minimal error
10271037
c.v0 = ei <= ej ? i0 : j0;
10281038
c.v1 = ei <= ej ? i1 : j1;
10291039
c.error = ei <= ej ? ei : ej;
1040+
1041+
#if TRACE >= 2
1042+
if (i0 == j0) // c.bidi has been overwritten
1043+
printf("edge eval %d -> %d: error %f (pos %f, attr %f)\n", c.v0, c.v1,
1044+
sqrtf(c.error), sqrtf(ei <= ej ? di : dj), sqrtf(ei <= ej ? ei - di : ej - dj));
1045+
else
1046+
printf("edge eval %d -> %d: error %f (pos %f, attr %f); reverse %f (pos %f, attr %f)\n", c.v0, c.v1,
1047+
sqrtf(ei <= ej ? ei : ej), sqrtf(ei <= ej ? di : dj), sqrtf(ei <= ej ? ei - di : ej - dj),
1048+
sqrtf(ei <= ej ? ej : ei), sqrtf(ei <= ej ? dj : di), sqrtf(ei <= ej ? ej - dj : ei - di));
1049+
#endif
10301050
}
10311051
}
10321052

@@ -1108,6 +1128,8 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
11081128
unsigned int r0 = remap[i0];
11091129
unsigned int r1 = remap[i1];
11101130

1131+
unsigned char kind = vertex_kind[i0];
1132+
11111133
// we don't collapse vertices that had source or target vertex involved in a collapse
11121134
// it's important to not move the vertices twice since it complicates the tracking/remapping logic
11131135
// it's important to not move other vertices towards a moved vertex to preserve error since we don't re-rank collapses mid-pass
@@ -1126,33 +1148,46 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
11261148
continue;
11271149
}
11281150

1151+
#if TRACE >= 2
1152+
printf("edge commit %d -> %d: kind %d->%d, error %f\n", i0, i1, vertex_kind[i0], vertex_kind[i1], sqrtf(c.error));
1153+
#endif
1154+
11291155
assert(collapse_remap[r0] == r0);
11301156
assert(collapse_remap[r1] == r1);
11311157

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

11341160
if (attribute_count)
11351161
{
1136-
quadricAdd(attribute_quadrics[r1], attribute_quadrics[r0]);
1137-
quadricAdd(&attribute_gradients[r1 * attribute_count], &attribute_gradients[r0 * attribute_count], attribute_count);
1162+
quadricAdd(attribute_quadrics[i1], attribute_quadrics[i0]);
1163+
quadricAdd(&attribute_gradients[i1 * attribute_count], &attribute_gradients[i0 * attribute_count], attribute_count);
1164+
1165+
// 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
1166+
if (kind == Kind_Seam)
1167+
{
1168+
// seam collapses involve two edges so we need to update attribute quadrics for both target vertices; position quadrics are shared
1169+
unsigned int s0 = wedge[i0], s1 = wedge[i1];
1170+
1171+
quadricAdd(attribute_quadrics[s1], attribute_quadrics[s0]);
1172+
quadricAdd(&attribute_gradients[s1 * attribute_count], &attribute_gradients[s0 * attribute_count], attribute_count);
1173+
}
11381174
}
11391175

1140-
if (vertex_kind[i0] == Kind_Complex)
1176+
if (kind == Kind_Complex)
11411177
{
1178+
// remap all vertices in the complex to the target vertex
11421179
unsigned int v = i0;
11431180

11441181
do
11451182
{
1146-
collapse_remap[v] = r1;
1183+
collapse_remap[v] = i1;
11471184
v = wedge[v];
11481185
} while (v != i0);
11491186
}
1150-
else if (vertex_kind[i0] == Kind_Seam)
1187+
else if (kind == Kind_Seam)
11511188
{
11521189
// remap v0 to v1 and seam pair of v0 to seam pair of v1
1153-
unsigned int s0 = wedge[i0];
1154-
unsigned int s1 = wedge[i1];
1155-
1190+
unsigned int s0 = wedge[i0], s1 = wedge[i1];
11561191
assert(s0 != i0 && s1 != i1);
11571192
assert(wedge[s0] == i0 && wedge[s1] == i1);
11581193

@@ -1170,7 +1205,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
11701205
collapse_locked[r1] = 1;
11711206

11721207
// border edges collapse 1 triangle, other edges collapse 2 or more
1173-
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
1208+
triangle_collapses += (kind == Kind_Border) ? 1 : 2;
11741209
edge_collapses++;
11751210

11761211
result_error = result_error < c.error ? c.error : result_error;
@@ -1641,7 +1676,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
16411676
fillEdgeQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
16421677

16431678
if (attribute_count)
1644-
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
1679+
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count);
16451680

16461681
#if TRACE
16471682
size_t pass_count = 0;
@@ -1673,6 +1708,10 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
16731708
if (edge_collapse_count == 0)
16741709
break;
16751710

1711+
#if TRACE
1712+
printf("pass %d:%c", int(pass_count++), TRACE >= 2 ? '\n' : ' ');
1713+
#endif
1714+
16761715
rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_attributes, vertex_quadrics, attribute_quadrics, attribute_gradients, attribute_count, remap);
16771716

16781717
sortEdgeCollapses(collapse_order, edge_collapses, edge_collapse_count);
@@ -1684,10 +1723,6 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
16841723

16851724
memset(collapse_locked, 0, vertex_count);
16861725

1687-
#if TRACE
1688-
printf("pass %d: ", int(pass_count++));
1689-
#endif
1690-
16911726
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, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
16921727

16931728
// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit

0 commit comments

Comments
 (0)