From 31e7dc0845e60e87b97911b50409c7ffac89a052 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:31:43 +0800 Subject: [PATCH] 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. --- .../TextureBake/TextureBakeDecimate.cpp | 17 ++++++++++++----- .../TextureBake/TextureBakeDecimate.hpp | 5 +++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index 13d18c5314..afb9976bb2 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -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; - std::vector vf_head(vert_count, -1), slot_face(S), slot_vert(S), slot_next(S, -1), - slot_prev(S, -1), face_slot(S, -1); + std::vector vf_head(vert_count, -1), vf_count(vert_count, 0), slot_face(S), slot_vert(S), + slot_next(S, -1), slot_prev(S, -1), face_slot(S, -1); for (size_t f = 0; f < face_count; ++f) for (int k = 0; k < 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; vf_head[size_t(v)] = s; face_slot[size_t(s)] = s; + ++vf_count[size_t(v)]; } 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; - 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; + --vf_count[size_t(v)]; }; const auto move_slot = [&](int s, int nv) { 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; vf_head[size_t(nv)] = s; slot_vert[size_t(s)] = nv; + ++vf_count[size_t(nv)]; }; std::vector active(vert_count, 1); @@ -463,6 +466,10 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h ++stale_pops; 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) continue; lk_epoch += 2; // +2 so ep and ep+1 cannot collide with the next call diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp index c40d9de266..7a9e1873e2 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -25,6 +25,11 @@ static constexpr double DECIMATE_FLIP_DOT = 0.2; static constexpr double DECIMATE_CREASE_COS = 0.5; // Quadric penalty weight for a crease plane. 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, // since the cost sums squared distances over all incident faces.