mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-12 10:21:17 +00:00
Baking, painting colour, then baking again left colour debris on surfaces nothing had painted and destroyed what the earlier bake had laid down. TextureDisplacementBakeJob::finalize() deserialized the volume's pre-bake mmu_segmentation_facets into a TriangleSelector built on the post-bake mesh, on the assumption that the bake is topology-preserving. That holds for the classic path, which moves vertices and leaves the topology alone, but not for the one-run pipeline, which rebuilds the mesh. TriangleSelector::deserialize() only checks that the stored triangle ids are in range, and a bake almost always grows the triangle count, so every stale id passed that check and landed on an unrelated face. Measured on a 26 mm slab baked twice at 0.19 mm with a 750 k budget: 7580 triangles ended up carrying paint neither bake produced, as far as 31 mm from any colour that was actually laid down, while the first pass's colour was lost where the second bake left the surface alone. TriangleSelector::remap_painting() already transfers paint between meshes by position, and ModelVolume::restore_painting() uses it for the same reason, so the bake job now uses it too. It runs in process(), on the worker thread: it builds an AABB tree over both meshes and took around a second on the case above, which would have frozen the UI in finalize(). The bake's own colour is still applied last, so it wins where it painted and the rest is kept, which is what makes successive bakes accumulate rather than overwrite. If the volume is painted while the bake is running, a timestamp check falls back to the live mask so that stroke is not dropped. The out_triangle comment in TextureColorRequest claimed the bake is topology-preserving, which is what justified the original indexing; it now says which mesh the colours belong to. The regression test bakes twice, checks both results are closed, and checks the first bake's colour survives the second at the positions where it was laid down and nowhere else.
87 lines
4.3 KiB
C++
87 lines
4.3 KiB
C++
#ifndef slic3r_TextureDisplacementBakeJob_hpp_
|
|
#define slic3r_TextureDisplacementBakeJob_hpp_
|
|
|
|
#include <exception>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include "libslic3r/Point.hpp"
|
|
#include <vector>
|
|
|
|
#include "libslic3r/ObjectID.hpp"
|
|
#include "libslic3r/TextureDisplacement.hpp"
|
|
#include "libslic3r/TriangleMesh.hpp"
|
|
#include "libslic3r/TriangleSelector.hpp"
|
|
|
|
#include "Job.hpp"
|
|
|
|
namespace Slic3r { class ModelVolume; }
|
|
|
|
namespace Slic3r::GUI {
|
|
|
|
// Everything process() needs, captured by value on the main thread when the job is queued so the
|
|
// worker thread never touches the live Model concurrently with the UI (mirrors how EmbossJob's
|
|
// DataBase is captured before process() runs).
|
|
struct TextureDisplacementBakeInput
|
|
{
|
|
ObjectID volume_id;
|
|
indexed_triangle_set base_mesh;
|
|
std::vector<TextureDisplacementLayer> layers;
|
|
TextureDisplacementFacetsData facets_data;
|
|
TextureDisplacementOptions options;
|
|
// Mesh coordinates -> world millimetres. Captured here with everything else so the worker never
|
|
// reaches back into the live Model for it. See build_texture_displacement().
|
|
Transform3d volume_to_world = Transform3d::Identity();
|
|
// Captured on the main thread. Empty unless some layer is colouring, in which case the bake also
|
|
// writes the volume's mmu_segmentation_facets - the same per-triangle filament assignment the MMU
|
|
// paint gizmo writes - alongside the displaced geometry.
|
|
TextureColorSettings color;
|
|
// The colour already painted on the volume, indexed against `base_mesh`. Carried here rather than
|
|
// read at commit time because the one-run pipeline rebuilds the mesh, and bringing this across to
|
|
// the new topology is spatial work (see process()) that has no business on the UI thread.
|
|
TriangleSelector::TriangleSplittingData mmu_segmentation;
|
|
// What `mmu_segmentation` was taken from, so the commit can tell whether the volume was painted
|
|
// while the bake ran and fall back to the live data if it was.
|
|
ObjectBase::Timestamp mmu_segmentation_timestamp = 0;
|
|
// Whether this job pushes its own undo step when it commits. False when the caller has already
|
|
// taken one that is meant to cover the displacement as well - Standard mode's Bake, which remeshes
|
|
// and subdivides first and has to undo as a single action.
|
|
bool take_snapshot = true;
|
|
};
|
|
|
|
// Bakes a volume's painted texture-displacement layers into real mesh geometry in the background,
|
|
// then commits the result on the main thread - mirrors EmbossJob's UpdateJob/update_volume()
|
|
// bake-and-commit pattern (see EmbossJob.cpp).
|
|
class TextureDisplacementBakeJob : public Job
|
|
{
|
|
public:
|
|
TextureDisplacementBakeJob(TextureDisplacementBakeInput &&input, std::function<void()> on_finished);
|
|
|
|
void process(Ctl &ctl) override;
|
|
void finalize(bool canceled, std::exception_ptr &eptr) override;
|
|
|
|
private:
|
|
TextureDisplacementBakeInput m_input;
|
|
TriangleMesh m_result;
|
|
// What the bake spent, for the message it leaves behind when the budget capped the detail.
|
|
TextureBakeStats m_stats;
|
|
// Per triangle of m_result: the palette entry to print it in, as its index + 1 (0 = leave alone).
|
|
// Empty unless a layer asked for colour. See TextureColorRequest; finalize() turns these into
|
|
// filaments.
|
|
std::vector<uint8_t> m_triangle_color;
|
|
// m_input.mmu_segmentation brought onto m_result's topology, so finalize() can lay this bake's
|
|
// colour over the colour that was already there. Indexed against m_result, not base_mesh.
|
|
TriangleSelector::TriangleSplittingData m_carried_mmu;
|
|
std::function<void()> m_on_finished;
|
|
};
|
|
|
|
// Captures `volume`'s current mesh/layers/paint data and queues a TextureDisplacementBakeJob on
|
|
// the app's UI job worker. `on_finished` is always called once the job settles (success, failure,
|
|
// or cancellation), so the caller can clear its own "bake in progress" UI state. Must be called
|
|
// from the main thread.
|
|
void queue_texture_displacement_bake(const ModelVolume &volume, const TextureColorSettings &color,
|
|
std::function<void()> on_finished, bool take_snapshot = true);
|
|
|
|
} // namespace Slic3r::GUI
|
|
|
|
#endif // slic3r_TextureDisplacementBakeJob_hpp_
|