Files
OrcaSlicer/src/slic3r/GUI/Jobs/TextureDisplacementPrepareJob.cpp
T
SoftFever 0473da4ef8 Let texture bakes create a chosen number of mixed colours
The new Mixed colors setting caps how many mixed filaments a bake adds. They are picked from the
texture's colours, and only the ones the bake paints with are created. Previewing no longer creates
filament slots, both previews show a mix in its slot's colour, and the bake paints each mix with the
slot it actually got.
2026-10-09 22:23:07 +08:00

129 lines
5.7 KiB
C++

#include "TextureDisplacementPrepareJob.hpp"
#include <algorithm>
#include <functional>
#include <exception>
#include "libslic3r/TextureDisplacement.hpp"
#include <cstddef>
#include <memory>
#include <optional>
#include <utility>
#include <string>
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Jobs/Worker.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/CutUtils.hpp"
#include "libslic3r/TriangleMesh.hpp"
namespace Slic3r::GUI {
TextureDisplacementPrepareJob::TextureDisplacementPrepareJob(
TextureDisplacementPrepareInput &&input, std::function<void(TextureDisplacementPrepareOutcome)> on_finished)
: m_input(std::move(input)), m_on_finished(std::move(on_finished))
{
}
void TextureDisplacementPrepareJob::process(Ctl &ctl)
{
const std::string status = _u8L("Preparing mesh for texture displacement");
ctl.update_status(1, status);
// Only ever touches m_input (captured by value before this job was queued) and local state - never
// the live Model - so this is safe to run concurrently with the UI thread.
//
// The progress hook is not just cosmetic: the framework's notification only grows a close button
// once it reaches 100%, and below that it shows Cancel, which is wired through here. Reporting is
// throttled to whole percentage points because every call repaints the notification and wakes the
// idle loop.
int last_reported = 1;
m_result = GLGizmoTextureDisplacement::prepare_mesh(m_input.base_mesh, m_input.masks, m_input.layers,
m_input.params, m_input.color,
[&ctl, &status, &last_reported](int percent) {
if (ctl.was_canceled())
return false;
if (percent > last_reported) {
last_reported = percent;
ctl.update_status(percent, status);
}
return true;
});
ctl.update_status(100, status); // always finish at 100: this is what closes the notification
}
void TextureDisplacementPrepareJob::finalize(bool canceled, std::exception_ptr &eptr)
{
TextureDisplacementPrepareOutcome outcome = TextureDisplacementPrepareOutcome::Failed;
struct OnExit
{
std::function<void(TextureDisplacementPrepareOutcome)> fn;
const TextureDisplacementPrepareOutcome *outcome;
~OnExit() { if (fn) fn(*outcome); }
} on_exit{m_on_finished, &outcome};
if (canceled || eptr)
return;
if (m_result.paint_lost) {
outcome = TextureDisplacementPrepareOutcome::PaintLost;
return;
}
if (m_result.mesh.indices.empty()) {
outcome = TextureDisplacementPrepareOutcome::Unchanged;
return;
}
Plater *plater = wxGetApp().plater();
ModelVolume *volume = get_model_volume(m_input.volume_id, plater->model().objects);
// The lookup doubles as a staleness check: anything that replaces a volume's mesh also gives it a
// new id (set_new_unique_id()), so a prepare computed from a mesh that has since been replaced -
// by an undo, another bake, or a boolean - simply fails to find its volume and commits nothing.
if (volume == nullptr)
return;
ModelObject *object = volume->get_object();
if (object == nullptr)
return;
{
Plater::TakeSnapshot snapshot(plater, m_input.snapshot_name, UndoRedo::SnapshotType::GizmoAction);
// The four standard paint channels ride across on ModelVolume's own spatial remap. The eight
// texture-displacement masks do not go through it - prepare_mesh() has already carried them,
// exactly, from the source triangles the refinement records - so they are put back after
// restore_painting(), which resets every extra facet before remapping the channels it knows.
std::optional<TriangleSelector::SavedPainting> saved_painting = volume->save_painting();
volume->set_mesh(TriangleMesh(std::move(m_result.mesh)));
volume->set_new_unique_id();
volume->calculate_convex_hull();
volume->restore_painting(saved_painting);
for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
volume->texture_displacement_facet(i).set_data(std::move(m_result.masks[size_t(i)]));
if (ObjectList *obj_list = wxGetApp().obj_list()) {
const ModelObjectPtrs &objs = plater->model().objects;
auto it = std::find(objs.begin(), objs.end(), object);
if (it != objs.end())
obj_list->update_info_items(size_t(it - objs.begin()));
}
plater->changed_object(*object);
}
outcome = TextureDisplacementPrepareOutcome::Committed;
}
void queue_texture_displacement_prepare(TextureDisplacementPrepareInput &&input,
std::function<void(TextureDisplacementPrepareOutcome)> on_finished)
{
auto &worker = wxGetApp().plater()->get_ui_job_worker();
queue_job(worker, std::make_unique<TextureDisplacementPrepareJob>(std::move(input), std::move(on_finished)));
}
} // namespace Slic3r::GUI