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.
This commit is contained in:
SoftFever
2026-09-30 19:31:48 +08:00
parent 31e7dc0845
commit 7c0a3ab916
8 changed files with 32 additions and 10 deletions
@@ -478,6 +478,8 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h
const Vec3d target = collapse_target(v1, v2).cast<float>().cast<double>();
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;
@@ -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
@@ -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;
@@ -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;
-1
View File
@@ -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;
}
+3 -4
View File
@@ -824,14 +824,13 @@ void merge_small_color_regions(const indexed_triangle_set &mesh, std::vector<int
// classic path moves the vertices the mesh already has and has nothing to say here.
struct TextureBakeStats
{
// What the refinement produced, before simplification, and what the bake committed.
// What the refinement produced, before simplification.
size_t triangles_refined = 0;
size_t triangles_out = 0;
// What the result had to fit into: the budget for what this bake refines, plus the triangles it
// only preserves (an earlier bake's relief, which this one does not paint).
size_t triangles_budget = 0;
// The refined mesh did not fit, so the simplification had to take detail out of it to make it:
// the result carries less of the texture than the chosen resolution asked for.
// The refined mesh did not fit, and removing its flat faces was not enough, so the simplification
// had to take detail out of it: the result carries less of the texture than the resolution asked for.
bool budget_limited = false;
};
@@ -186,7 +186,7 @@ void TextureDisplacementBakeJob::finalize(bool canceled, std::exception_ptr &ept
NotificationType::CustomNotification, NotificationManager::NotificationLevel::WarningNotificationLevel,
Slic3r::format(_u8L("The triangle budget limited the detail: this resolution needs %1% triangles, "
"the budget kept %2%. Raise Budget or use a coarser Resolution for the full detail."),
count(m_stats.triangles_refined), count(m_stats.triangles_out)));
count(m_stats.triangles_refined), count(m_stats.triangles_budget)));
}
}
@@ -11,6 +11,7 @@
#include <boost/filesystem.hpp>
#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);
}