Multiple beds in SLA

This commit is contained in:
Lukas Matena 2024-11-18 23:28:18 +01:00
parent 324763a90e
commit 08eb318780
9 changed files with 110 additions and 16 deletions

View File

@ -22,6 +22,8 @@
#include <boost/filesystem/path.hpp>
#include <boost/log/trivial.hpp>
#include "libslic3r/MultipleBeds.hpp"
// #define SLAPRINT_DO_BENCHMARK
#ifdef SLAPRINT_DO_BENCHMARK
@ -298,6 +300,16 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
if (! material_diff.empty())
update_apply_status(this->invalidate_state_by_config_options(material_diff, invalidate_all_model_objects));
// Multiple beds hack: We currently use one SLAPrint for all beds. It must be invalidated
// when beds are switched. If not done explicitly, supports from previously sliced object
// might end up with wrong offset.
static int last_bed_idx = s_multiple_beds.get_active_bed();
int current_bed = s_multiple_beds.get_active_bed();
if (current_bed != last_bed_idx) {
invalidate_all_model_objects = true;
last_bed_idx = current_bed;
}
// Apply variables to placeholder parser. The placeholder parser is currently used
// only to generate the output file name.
if (! placeholder_parser_diff.empty()) {

View File

@ -34,6 +34,7 @@
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Tesselate.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/MultipleBeds.hpp"
#include <stdio.h>
#include <stdlib.h>
@ -656,6 +657,7 @@ void GLVolumeCollection::load_object_auxiliary(
std::shared_ptr<const indexed_triangle_set> preview_mesh_ptr = print_object->get_mesh_to_print();
if (preview_mesh_ptr != nullptr)
backend_mesh = TriangleMesh(*preview_mesh_ptr);
backend_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
if (!backend_mesh.empty()) {
backend_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = backend_mesh.convex_hull_3d();
@ -670,6 +672,7 @@ void GLVolumeCollection::load_object_auxiliary(
// Get the support mesh.
if (milestone == SLAPrintObjectStep::slaposSupportTree) {
TriangleMesh supports_mesh = print_object->support_mesh();
supports_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
if (!supports_mesh.empty()) {
supports_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = supports_mesh.convex_hull_3d();
@ -683,6 +686,7 @@ void GLVolumeCollection::load_object_auxiliary(
// Get the pad mesh.
if (milestone == SLAPrintObjectStep::slaposPad) {
TriangleMesh pad_mesh = print_object->pad_mesh();
pad_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
if (!pad_mesh.empty()) {
pad_mesh.transform(mesh_trafo_inv);
TriangleMesh convex_hull = pad_mesh.convex_hull_3d();

View File

@ -109,10 +109,22 @@ void GLCanvas3D::select_bed(int i, bool triggered_by_user)
int old_bed = s_multiple_beds.get_active_bed();
if ((i == old_bed && !s_multiple_beds.is_autoslicing()) || i == -1)
return;
if (current_printer_technology() == ptSLA) {
// Close SlaSupports or Hollow gizmos before switching beds. They rely on having access to SLAPrintObject to work.
if (GLGizmosManager::EType cur_giz = get_gizmos_manager().get_current_type();
cur_giz == GLGizmosManager::EType::SlaSupports || cur_giz == GLGizmosManager::EType::Hollow) {
if (! get_gizmos_manager().open_gizmo(get_gizmos_manager().get_current_type()))
return;
}
}
wxGetApp().plater()->canvas3D()->m_process->stop();
m_sequential_print_clearance.m_evaluating = true;
reset_sequential_print_clearance();
// The stop call above schedules some events that would be processed after the switch.
// Among else, on_process_completed would be called, which would stop slicing of
// the new bed. We need to stop the process, pump all the events out of the queue
@ -6085,6 +6097,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances());
m_volumes.set_show_non_manifold_edges(!m_gizmos.is_hiding_instances() && m_gizmos.get_current_type() != GLGizmosManager::Simplify);
const Camera& camera = wxGetApp().plater()->get_camera();
auto trafo = camera.get_view_matrix();
if (current_printer_technology() == ptSLA && wxGetApp().plater()->is_preview_shown()) {
trafo.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()));
}
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
if (shader != nullptr) {
shader->start_using();
@ -6096,8 +6114,8 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
{
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id;
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [object_id](const GLVolume& volume) {
m_volumes.render(type, false, trafo, camera.get_projection_matrix(), [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
});
@ -6107,7 +6125,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
else {
// do not cull backfaces to show broken geometry, if any
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), [this](const GLVolume& volume) {
m_volumes.render(type, m_picking_enabled, trafo, camera.get_projection_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
}
@ -6129,7 +6147,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
case GLVolumeCollection::ERenderType::Transparent:
{
const Camera& camera = wxGetApp().plater()->get_camera();
m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix());
m_volumes.render(type, false, trafo, camera.get_projection_matrix());
break;
}
}
@ -6382,6 +6400,7 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
height = win_size.y;
wxGetApp().imgui()->set_requires_extra_frame();
}
m_bed_selector_current_height = height;
float max_width = win_x_pos;
if (is_legend_shown())
@ -6739,7 +6758,7 @@ void GLCanvas3D::_render_camera_target_validation_box()
}
#endif // ENABLE_SHOW_CAMERA_TARGET
static void render_sla_layer_legend(const SLAPrint& print, int layer_idx, int cnv_width)
static void render_sla_layer_legend(const SLAPrint& print, int layer_idx, int cnv_width, float bed_sel_height)
{
const std::vector<double>& areas = print.print_statistics().layers_areas;
const std::vector<double>& times = print.print_statistics().layers_times_running_total;
@ -6750,7 +6769,7 @@ static void render_sla_layer_legend(const SLAPrint& print, int layer_idx, int cn
const double time_until_layer = times[layer_idx];
ImGuiWrapper& imgui = *wxGetApp().imgui();
ImGuiPureWrap::set_next_window_pos(float(cnv_width) - imgui.get_style_scaling() * 5.f, 5.f, ImGuiCond_Always, 1.0f, 0.0f);
ImGuiPureWrap::set_next_window_pos(float(cnv_width) - imgui.get_style_scaling() * 5.f, 5.f + bed_sel_height, ImGuiCond_Always, 1.0f, 0.0f);
ImGui::SetNextWindowBgAlpha(0.6f);
ImGuiPureWrap::begin(_u8L("Layer statistics"), ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoFocusOnAppearing);
@ -6786,7 +6805,7 @@ void GLCanvas3D::_render_sla_slices()
double slider_width = 0.;
if (const Preview* preview = dynamic_cast<Preview*>(m_canvas->GetParent()))
slider_width = preview->get_layers_slider_width();
render_sla_layer_legend(*print, m_layer_slider_index, get_canvas_size().get_width() - slider_width);
render_sla_layer_legend(*print, m_layer_slider_index, get_canvas_size().get_width() - slider_width, m_bed_selector_current_height);
}
double clip_min_z = -m_clipping_planes[0].get_data()[3];
@ -6887,6 +6906,7 @@ void GLCanvas3D::_render_sla_slices()
for (const SLAPrintObject::Instance& inst : obj->instances()) {
const Camera& camera = wxGetApp().plater()->get_camera();
Transform3d view_model_matrix = camera.get_view_matrix() *
Geometry::translation_transform(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed())) *
Geometry::translation_transform({ unscale<double>(inst.shift.x()), unscale<double>(inst.shift.y()), 0.0 }) *
Geometry::rotation_transform(inst.rotation * Vec3d::UnitZ());
if (obj->is_left_handed())

View File

@ -512,6 +512,7 @@ private:
// see request_extra_frame()
bool m_extra_frame_requested;
bool m_event_handlers_bound{ false };
float m_bed_selector_current_height = 0.f;
GLVolumeCollection m_volumes;
#if SLIC3R_OPENGL_ES

View File

@ -81,6 +81,12 @@ void GLGizmoHollow::data_changed(bool is_serializing)
void GLGizmoHollow::on_render()
{
if (! selected_print_object_exists(m_parent, wxEmptyString)) {
wxGetApp().CallAfter([this]() {
// Close current gizmo.
m_parent.get_gizmos_manager().open_gizmo(m_parent.get_gizmos_manager().get_current_type());
});
}
const Selection& selection = m_parent.get_selection();
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
@ -137,7 +143,7 @@ void GLGizmoHollow::render_points(const Selection& selection)
if (!inst)
return;
double shift_z = m_c->selection_info()->print_object()->get_current_elevation();
double shift_z = m_c->selection_info()->print_object() ? m_c->selection_info()->print_object()->get_current_elevation() : 0.;
Transform3d trafo(inst->get_transformation().get_matrix());
trafo.translation()(2) += shift_z;
const Geometry::Transformation transformation{trafo};
@ -849,6 +855,14 @@ void GLGizmoHollow::on_set_state()
if (m_state == m_old_state)
return;
if (m_state == On) {
// Make sure that current object is on current bed. Refuse to turn on otherwise.
if (! selected_print_object_exists(m_parent, _L("Selected object has to be on the active bed."))) {
m_state = Off;
return;
}
}
if (m_state == Off && m_old_state != Off) {
// the gizmo was just turned Off
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));

View File

@ -9,6 +9,10 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "libslic3r/MultipleBeds.hpp"
namespace Slic3r {
namespace GUI {
@ -21,6 +25,22 @@ GLGizmoSlaBase::GLGizmoSlaBase(GLCanvas3D& parent, const std::string& icon_filen
, m_min_sla_print_object_step((int)min_step)
{}
/*static*/ bool GLGizmoSlaBase::selected_print_object_exists(const GLCanvas3D& canvas, const wxString& text)
{
if (const Selection& sel = canvas.get_selection(); !sel.is_single_full_instance() || !sel.get_model()->objects[sel.get_object_idx()]
|| ! canvas.sla_print()->get_print_object_by_model_object_id(sel.get_model()->objects[sel.get_object_idx()]->id()))
{
if (! text.IsEmpty())
wxGetApp().CallAfter([text]() {
MessageDialog dlg(GUI::wxGetApp().mainframe, text,
_L("Bed selection mismatch"), wxICON_INFORMATION | wxOK);
dlg.ShowModal();
});
return false;
}
return true;
}
void GLGizmoSlaBase::reslice_until_step(SLAPrintObjectStep step, bool postpone_error_messages)
{
wxGetApp().CallAfter([this, step, postpone_error_messages]() {
@ -94,12 +114,14 @@ void GLGizmoSlaBase::update_volumes()
const Transform3d po_trafo_inverse = po->trafo().inverse();
// main mesh
backend_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
backend_mesh.transform(po_trafo_inverse);
add_volume(backend_mesh, 0, true);
// supports mesh
TriangleMesh supports_mesh = po->support_mesh();
if (!supports_mesh.empty()) {
supports_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
supports_mesh.transform(po_trafo_inverse);
add_volume(supports_mesh, -int(slaposSupportTree));
}
@ -107,6 +129,7 @@ void GLGizmoSlaBase::update_volumes()
// pad mesh
TriangleMesh pad_mesh = po->pad_mesh();
if (!pad_mesh.empty()) {
pad_mesh.translate(s_multiple_beds.get_bed_translation(s_multiple_beds.get_active_bed()).cast<float>());
pad_mesh.transform(po_trafo_inverse);
add_volume(pad_mesh, -int(slaposPad));
}

View File

@ -49,6 +49,8 @@ protected:
const GLVolumeCollection &volumes() const { return m_volumes; }
static bool selected_print_object_exists(const GLCanvas3D& canvas, const wxString& text);
private:
GLVolumeCollection m_volumes;
bool m_input_enabled{ false };

View File

@ -103,6 +103,13 @@ void GLGizmoSlaSupports::data_changed(bool is_serializing)
void GLGizmoSlaSupports::on_render()
{
if (! selected_print_object_exists(m_parent, wxEmptyString)) {
wxGetApp().CallAfter([this]() {
// Close current gizmo.
m_parent.get_gizmos_manager().open_gizmo(m_parent.get_gizmos_manager().get_current_type());
});
}
if (m_state == On) {
// This gizmo is showing the object elevated. Tell the common
// SelectionInfo object to lie about the actual shift.
@ -843,6 +850,13 @@ bool GLGizmoSlaSupports::ask_about_changes(std::function<void()> on_yes, std::fu
void GLGizmoSlaSupports::on_set_state()
{
if (m_state == On) { // the gizmo was just turned on
// Make sure that current object is on current bed. Refuse to turn on otherwise.
if (! selected_print_object_exists(m_parent, _L("Selected object has to be on the active bed."))) {
m_state = Off;
return;
}
// Set default head diameter from config.
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;

View File

@ -2129,12 +2129,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
int active_bed = s_multiple_beds.get_active_bed();
background_process.set_temp_output_path(active_bed);
background_process.set_fff_print(fff_prints[active_bed].get());
background_process.set_sla_print(sla_prints[active_bed].get());
background_process.set_fff_print(&q->active_fff_print());
background_process.set_sla_print(&q->active_sla_print());
background_process.set_gcode_result(&gcode_results[active_bed]);
background_process.select_technology(this->printer_technology);
if (s_beds_just_switched) {
if (s_beds_just_switched && printer_technology == ptFFF) {
PrintBase::SlicingStatus status(q->active_fff_print(), -1);
SlicingStatusEvent evt(EVT_SLICING_UPDATE, 0, status);
on_slicing_update(evt);
@ -3077,10 +3077,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
warning_steps.clear();
if (flags == PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) {
int i = 0;
while (i < int(psCount)) { warning_steps.push_back(i); ++i; }
while (i < int(printer_technology == ptFFF ? psCount : slapsCount)) { warning_steps.push_back(i); ++i; }
} else {
int i = 0;
while (i < int(posCount)) { warning_steps.push_back(i); ++i; }
while (i < int(printer_technology == ptFFF ? posCount : slaposCount)) { warning_steps.push_back(i); ++i; }
for (const PrintObject* po : wxGetApp().plater()->active_fff_print().objects())
object_ids.push_back(po->id());
}
@ -5652,7 +5652,7 @@ void Plater::export_gcode(bool prefer_removable)
start_dir,
from_path(default_output_file.filename()),
printer_technology() == ptFFF ? GUI::file_wildcards(FT_GCODE, ext) :
GUI::sla_wildcards(p->sla_prints.front()->printer_config().sla_archive_format.value.c_str(), ext),
GUI::sla_wildcards(active_sla_print().printer_config().sla_archive_format.value.c_str(), ext),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if (dlg.ShowModal() == wxID_OK) {
@ -5769,7 +5769,7 @@ void Plater::export_stl_obj(bool extended, bool selection_only)
auto mesh_to_export_sla = [&, this](const ModelObject& mo, int instance_id) {
TriangleMesh mesh;
const SLAPrintObject *object; // LUKAS = this->p->sla_print.get_print_object_by_model_object_id(mo.id());
const SLAPrintObject *object = this->active_sla_print().get_print_object_by_model_object_id(mo.id());
if (!object || !object->get_mesh_to_print() || object->get_mesh_to_print()->empty()) {
if (!extended)
@ -7330,7 +7330,11 @@ wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu()
Print& Plater::active_fff_print() { return *p->fff_prints[s_multiple_beds.get_active_bed()]; }
SLAPrint& Plater::active_sla_print() { return *p->sla_prints[s_multiple_beds.get_active_bed()]; }
//SLAPrint& Plater::active_sla_print() { return *p->sla_prints[s_multiple_beds.get_active_bed()]; }
// For now, only use the first SLAPrint for all the beds - it means reslicing
// everything when a bed is changed.
SLAPrint& Plater::active_sla_print() { return *p->sla_prints.front(); }
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :