diff --git a/src/libslic3r/TextureBake/TextureBakePipeline.cpp b/src/libslic3r/TextureBake/TextureBakePipeline.cpp index 37d0577b77..45867c10bb 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.cpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.cpp @@ -310,12 +310,13 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, // triangles to fit, so every bake after the first came out coarser than the one before. const size_t target = settings.max_triangles + preserved; const bool over_budget = displaced.triangle_count() > target; - // Only when the mesh is actually over budget: the decimation pass also welds the soup and is - // followed by the T-junction repair, and putting an under-budget bake through both changed the - // sliced result by a fifth even with the collapse tolerance at zero, i.e. with nothing - // collapsed. Harvesting flat faces on a mesh that already fits needs that path to leave the - // geometry alone first. - if (over_budget) { + // Flat faces are harvested whether or not the budget bites. Refinement is driven by the target + // edge length alone, so it leaves as fine a mesh over the flat parts of a texture as over its + // detail, and nothing else removes those: under its budget a bake kept every redundant triangle + // unless the budget was lowered until decimation had to run. Only collapses costing less than + // harvest_tol are taken, so this does not reach the relief. + const bool harvest_only = !over_budget && settings.harvest_flat && displaced.triangle_count() > 0; + if (over_budget || harvest_only) { // Colour per face on the fine mesh, so colour boundaries become creases the collapse // respects. Excluded (unpainted) faces take no colour. std::vector face_color; @@ -333,20 +334,23 @@ PipelineResult run_pipeline(const TriSoup &input, const HeightSampleFn &sample, } }); } + // Harvesting alone is asked for by handing it the count it already has: nothing is then + // over the target, so the loop only ever pops collapses under the tolerance. const size_t before = displaced.triangle_count(); - DecimateResult dec = decimate(displaced, target, settings.harvest_flat, + DecimateResult dec = decimate(displaced, over_budget ? target : before, settings.harvest_flat, settings.harvest_tol, locked, [&](double f) { return report("decimate", f); }, face_color); result.locked_over_budget = dec.locked_over_budget; displaced = std::move(dec.geometry); - lap("decimate", displaced, "over budget, simplified"); + lap("decimate", displaced, over_budget ? "over budget, simplified" : "flat faces harvested"); BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: " << before << " -> " << displaced.triangle_count() - << " (budget " << target << ")"; + << (over_budget ? " (budget " : " (flat harvest, budget ") << target << ")"; parent.clear(); // no longer meaningful } 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 22cc962141..54b40f46dd 100644 --- a/src/libslic3r/TextureBake/TextureBakePipeline.hpp +++ b/src/libslic3r/TextureBake/TextureBakePipeline.hpp @@ -115,6 +115,12 @@ struct PipelineResult size_t triangles_refined = 0; size_t triangles_budget = 0; bool budget_limited = false; + // The budget coarsened the mesh, so its triangles are no longer the fine, even ones the refinement + // laid down - they can be as large as the features on them, which a caller that post-processes per + // triangle (the colour despeckle) has to know. Harvesting flat faces does not count: what it + // removes carried nothing. Not the same question as "did a collapse run", which is why + // face_parent_id being empty is no longer used to answer it. + bool simplified = false; }; // `debug`, when given and enabled, receives the mesh after every stage that ran - which is the only diff --git a/src/libslic3r/TextureDisplacement.cpp b/src/libslic3r/TextureDisplacement.cpp index ad3bf3f267..00a66271cd 100644 --- a/src/libslic3r/TextureDisplacement.cpp +++ b/src/libslic3r/TextureDisplacement.cpp @@ -2178,6 +2178,12 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set BOOST_LOG_TRIVIAL(info) << "TextureBake resolution: " << settings.refine_length << " mm" << (auto_edge ? " (auto)" : "") << ", budget " << settings.max_triangles / 1000 << " k" << (auto_budget ? " (auto)" : ""); + // How far a flat-face harvest may move the surface, tied to the resolution: what a bake refined to + // 1.4 mm edges can merge flat is not what one refined to 0.1 mm may touch. A thirty-second of the + // edge is well under a layer either way - at the default resolution it sheds about 5% of the + // triangles on a relief that covers the whole surface, and far more of one with flat ground in it - + // and the floor keeps a very fine resolution from spending the pass for nothing. + settings.harvest_tol = std::clamp(settings.refine_length / 32.0, 0.005, 0.05); settings.preserve_untextured = true; // Always on: relief driven under the plate is unprintable whichever pipeline produced it, so this // is no longer a choice the user has to make. Only geometry that ends up below the model's own @@ -2335,7 +2341,9 @@ indexed_triangle_set build_texture_displacement_v2(const indexed_triangle_set // noise. A simplified mesh is neither: its triangles are as large as the colour regions // themselves and already end on the colour boundaries, so a majority vote among three // neighbours would repaint whole features. Bake mode (no simplification) keeps it. - const bool simplified = result.face_parent_id.empty(); + // Not "did a collapse run": a flat-face harvest leaves the mesh as fine as it was, and + // reading it as simplified switched the despeckle off for every bake that harvested. + const bool simplified = result.simplified; despeckle_triangle_colors(out, palette, simplified ? 0 : color->despeckle_passes); merge_small_color_regions(out, palette, color->min_color_region_mm2); for (size_t i = 0; i < out.indices.size(); ++i) {