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); +}