From 7c0a3ab916c9355166543166ec70fa396b641842 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Wed, 30 Sep 2026 19:31:48 +0800 Subject: [PATCH] Warn about the texture bake budget only when it cost detail Meeting the triangle budget by merging flat faces alone no longer raises the warning, and the warning now quotes the budget instead of the triangle count left after the flat faces were merged. --- .../TextureBake/TextureBakeDecimate.cpp | 2 ++ .../TextureBake/TextureBakeDecimate.hpp | 3 +++ .../TextureBake/TextureBakePipeline.cpp | 3 +-- .../TextureBake/TextureBakePipeline.hpp | 5 +++-- src/libslic3r/TextureDisplacement.cpp | 1 - src/libslic3r/TextureDisplacement.hpp | 7 +++---- .../GUI/Jobs/TextureDisplacementBakeJob.cpp | 2 +- tests/libslic3r/test_texture_displacement.cpp | 19 +++++++++++++++++++ 8 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index afb9976bb2..ccb6db811c 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -478,6 +478,8 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h const Vec3d target = collapse_target(v1, v2).cast().cast(); if (check_flipped(v1, v2, target) || check_flipped(v2, v1, target)) continue; + if (!reached_target && top.cost > harvest_ceil) + result.target_cost_detail = true; // v1 survives at the new position, v2 goes. pos[size_t(v1)] = target; diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp index 7a9e1873e2..5579778050 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.hpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.hpp @@ -48,6 +48,9 @@ struct DecimateResult // The locked faces alone met the target, so it was unreachable without touching preserved // geometry. bool locked_over_budget = false; + // Reaching the target took a collapse costing more than harvest_tol, so the target removed detail + // and not only flat faces a harvest would have taken anyway. + bool target_cost_detail = false; }; // `locked_faces`: one entry per input triangle; a vertex touching one may neither move nor be diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 45867c10bb..26f44671c7 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -341,6 +341,7 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, settings.harvest_tol, locked, [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; + result.budget_limited = result.simplified = dec.target_cost_detail; displaced = std::move(dec.geometry); lap("decimate", displaced, over_budget ? "over budget, simplified" : "flat faces harvested"); BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: " << before << " -> " << displaced.triangle_count() @@ -349,8 +350,6 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } result.triangles_refined = displaced_before_decimate; result.triangles_budget = target; - result.budget_limited = over_budget; - result.simplified = over_budget; if (!report("decimate", 1.0)) { result.canceled = true; return result; diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.hpp b/src/libslic3r/TextureBake/TextureBakePipeline.hpp index 54b40f46dd..482cd36311 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -110,8 +110,9 @@ struct PipelineResult size_t collapse_count = 0; bool canceled = false; // What the refinement produced, before decimation, and the count it had to fit into (the budget - // plus the preserved geometry). budget_limited says the refined mesh did not fit: the result - // carries less of the texture than the resolution asked for, which is what a caller warns about. + // plus the preserved geometry). budget_limited says the refined mesh did not fit without collapsing + // more than flat faces: the result carries less of the texture than the resolution asked for, + // which is what a caller warns about. size_t triangles_refined = 0; size_t triangles_budget = 0; bool budget_limited = false; diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index f3f902e821..ea52ac64ee 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2291,7 +2291,6 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set if (stats != nullptr) { stats->triangles_refined = result.triangles_refined; - stats->triangles_out = result.geometry.triangle_count(); stats->triangles_budget = result.triangles_budget; stats->budget_limited = result.budget_limited; } diff --git a/src/libslic3r/TextureDisplacement.hpp b/src/libslic3r/TextureDisplacement.hpp index bb39bc3266..e97178abca 100644 --- a/src/libslic3r/TextureDisplacement.hpp +++ b/src/libslic3r/TextureDisplacement.hpp @@ -824,14 +824,13 @@ void merge_small_color_regions(const indexed_triangle_set &mesh, std::vector #include "libslic3r/TextureDisplacement.hpp" +#include "libslic3r/TextureBake/TextureBakeDecimate.hpp" #include "libslic3r/TextureBake/TextureBakeFlip.hpp" #include "libslic3r/TextureBake/TextureBakeMesh.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -2013,3 +2014,21 @@ TEST_CASE("Each layer's texture is sampled only on its own painted area", "[Text CHECK_THAT(both(on_side, -Vec3f::UnitX()), WithinAbs(side_only(on_side, -Vec3f::UnitX()), 1e-5f)); CHECK_THAT(both(unpainted, Vec3f::UnitY()), WithinAbs(0.f, 1e-6f)); } + +TEST_CASE("A budget met by collapsing flat faces alone removes no detail", "[TextureDisplacement]") +{ + // 12 * 4^5 = 12288 triangles on six flat faces, which can go down to 12 without moving the surface. + const TextureBake::TriSoup cube = TextureBake::to_soup(subdivide_mesh_uniform(its_make_cube(20., 20., 20.), 0.f, 5)); + const TextureBake::DecimateResult res = TextureBake::decimate(cube, 1000, true, 0.005); + CHECK(res.geometry.triangle_count() <= 1000); + CHECK_FALSE(res.target_cost_detail); +} + +TEST_CASE("A budget met only by flattening curvature removes detail", "[TextureDisplacement]") +{ + // A 10 degree sphere of radius 10 bulges about 0.04 mm out of each facet, far past a 0.005 mm tolerance. + const TextureBake::TriSoup sphere = TextureBake::to_soup(its_make_sphere(10., PI / 18.)); + const TextureBake::DecimateResult res = TextureBake::decimate(sphere, 100, true, 0.005); + CHECK(res.geometry.triangle_count() <= 100); + CHECK(res.target_cost_detail); +}