Fix texture bake stalling at 99% while simplifying

A vertex pinned on flat ground could collect a fan of thousands of slivers, and validating each
collapse next to it walked the whole fan, turning a second of simplification into minutes. Collapses
that would leave more than 64 faces around a vertex are now skipped.
This commit is contained in:
SoftFever
2026-09-30 19:31:43 +08:00
parent 1e4489eb16
commit 31e7dc0845
2 changed files with 17 additions and 5 deletions
@@ -236,10 +236,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h
} }
} }
// Vertex-face incidence as intrusive linked lists of slots over flat arrays. // Vertex-face incidence as intrusive linked lists of slots over flat arrays, with each list's length.
const size_t S = face_count * 3; const size_t S = face_count * 3;
std::vector<int> vf_head(vert_count, -1), slot_face(S), slot_vert(S), slot_next(S, -1), std::vector<int> vf_head(vert_count, -1), vf_count(vert_count, 0), slot_face(S), slot_vert(S),
slot_prev(S, -1), face_slot(S, -1); slot_next(S, -1), slot_prev(S, -1), face_slot(S, -1);
for (size_t f = 0; f < face_count; ++f) for (size_t f = 0; f < face_count; ++f)
for (int k = 0; k < 3; ++k) { for (int k = 0; k < 3; ++k) {
const int s = int(f) * 3 + k; const int s = int(f) * 3 + k;
@@ -252,12 +252,14 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h
slot_prev[size_t(vf_head[size_t(v)])] = s; slot_prev[size_t(vf_head[size_t(v)])] = s;
vf_head[size_t(v)] = s; vf_head[size_t(v)] = s;
face_slot[size_t(s)] = s; face_slot[size_t(s)] = s;
++vf_count[size_t(v)];
} }
const auto unlink_slot = [&](int s) { const auto unlink_slot = [&](int s) {
const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)]; const int p = slot_prev[size_t(s)], nx = slot_next[size_t(s)], v = slot_vert[size_t(s)];
if (p >= 0) slot_next[size_t(p)] = nx; if (p >= 0) slot_next[size_t(p)] = nx;
else vf_head[size_t(slot_vert[size_t(s)])] = nx; else vf_head[size_t(v)] = nx;
if (nx >= 0) slot_prev[size_t(nx)] = p; if (nx >= 0) slot_prev[size_t(nx)] = p;
--vf_count[size_t(v)];
}; };
const auto move_slot = [&](int s, int nv) { const auto move_slot = [&](int s, int nv) {
unlink_slot(s); unlink_slot(s);
@@ -267,6 +269,7 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h
slot_prev[size_t(vf_head[size_t(nv)])] = s; slot_prev[size_t(vf_head[size_t(nv)])] = s;
vf_head[size_t(nv)] = s; vf_head[size_t(nv)] = s;
slot_vert[size_t(s)] = nv; slot_vert[size_t(s)] = nv;
++vf_count[size_t(nv)];
}; };
std::vector<uint8_t> active(vert_count, 1); std::vector<uint8_t> active(vert_count, 1);
@@ -463,6 +466,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h
++stale_pops; ++stale_pops;
continue; continue;
} }
// Ahead of the checks below, which walk the fans (see DECIMATE_MAX_VALENCE). The two shared
// faces, counted in both fans, go.
if (vf_count[size_t(v1)] + vf_count[size_t(v2)] - 4 > DECIMATE_MAX_VALENCE)
continue;
if (shared_face_count(v1, v2) < 2) if (shared_face_count(v1, v2) < 2)
continue; continue;
lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call
@@ -25,6 +25,11 @@ static constexpr double DECIMATE_FLIP_DOT = 0.2;
static constexpr double DECIMATE_CREASE_COS = 0.5; static constexpr double DECIMATE_CREASE_COS = 0.5;
// Quadric penalty weight for a crease plane. // Quadric penalty weight for a crease plane.
static constexpr double DECIMATE_CREASE_WEIGHT = 1e4; static constexpr double DECIMATE_CREASE_WEIGHT = 1e4;
// Most faces a collapse may leave around its surviving vertex. Uncapped, a pinned vertex on flat
// ground grows a fan of thousands of slivers, and validating each of its edges walks the whole fan,
// which turns a decimation of a second or two into minutes. At 64 the triangle count moves by about
// 1% at most, and by far less on large bakes.
static constexpr int DECIMATE_MAX_VALENCE = 64;
// Upper bound in mm on the deviation a harvested collapse may introduce; the real one is smaller, // Upper bound in mm on the deviation a harvested collapse may introduce; the real one is smaller,
// since the cost sums squared distances over all incident faces. // since the cost sums squared distances over all incident faces.