Multiple beds (part 5):

- nicer active / virtual bed visualization
- select bed by left mouse click
- bed selector only shows in preview
This commit is contained in:
Lukas Matena 2024-10-03 08:41:19 +02:00
parent 9035caa337
commit c24e9fdfed
6 changed files with 83 additions and 35 deletions

View File

@ -39,6 +39,9 @@ public:
int get_thumbnail_bed_idx() const { return m_bed_for_thumbnails_generation; } int get_thumbnail_bed_idx() const { return m_bed_for_thumbnails_generation; }
bool is_glvolume_on_thumbnail_bed(const Model& model, int obj_idx, int instance_idx) const; bool is_glvolume_on_thumbnail_bed(const Model& model, int obj_idx, int instance_idx) const;
void set_last_hovered_bed(int i) { m_last_hovered_bed = i; }
int get_last_hovered_bed() const { return m_last_hovered_bed; }
private: private:
@ -52,7 +55,7 @@ private:
bool m_show_next_bed = false; bool m_show_next_bed = false;
std::map<ObjectID, int> m_inst_to_bed; std::map<ObjectID, int> m_inst_to_bed;
std::map<PrintBase*, size_t> m_printbase_to_texture; std::map<PrintBase*, size_t> m_printbase_to_texture;
int m_last_hovered_bed = -1;
}; };
extern MultipleBeds s_multiple_beds; extern MultipleBeds s_multiple_beds;

View File

@ -110,7 +110,7 @@ void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Tra
{ {
bool is_thumbnail = s_multiple_beds.get_thumbnail_bed_idx() != -1; bool is_thumbnail = s_multiple_beds.get_thumbnail_bed_idx() != -1;
bool is_preview = wxGetApp().plater()->is_preview_shown(); bool is_preview = wxGetApp().plater()->is_preview_shown();
int bed_to_highlight = -1; int bed_to_highlight = s_multiple_beds.get_active_bed();
static std::vector<int> beds_to_render; static std::vector<int> beds_to_render;
beds_to_render.clear(); beds_to_render.clear();
@ -121,14 +121,12 @@ void Bed3D::render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Tra
else { else {
beds_to_render.resize(s_multiple_beds.get_number_of_beds() + int(s_multiple_beds.should_show_next_bed())); beds_to_render.resize(s_multiple_beds.get_number_of_beds() + int(s_multiple_beds.should_show_next_bed()));
std::iota(beds_to_render.begin(), beds_to_render.end(), 0); std::iota(beds_to_render.begin(), beds_to_render.end(), 0);
if (s_multiple_beds.get_number_of_beds() != 1)
bed_to_highlight = s_multiple_beds.get_active_bed();
} }
for (int i : beds_to_render) { for (int i : beds_to_render) {
Transform3d mat = view_matrix; Transform3d mat = view_matrix;
mat.translate(s_multiple_beds.get_bed_translation(i)); mat.translate(s_multiple_beds.get_bed_translation(i));
render_internal(canvas, mat, projection_matrix, bottom, scale_factor, show_texture, false, i == bed_to_highlight); render_internal(canvas, mat, projection_matrix, bottom, scale_factor, show_texture, false, is_thumbnail || i == bed_to_highlight);
} }
} }
@ -150,9 +148,9 @@ void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix,
switch (m_type) switch (m_type)
{ {
case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom, show_texture); break; } case Type::System: { render_system(canvas, view_matrix, projection_matrix, bottom, show_texture, active); break; }
default: default:
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture, picking); break; } case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom, show_texture, picking, active); break; }
} }
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
@ -335,18 +333,18 @@ void Bed3D::render_axes()
m_axes.render(Transform3d::Identity(), 0.25f); m_axes.render(Transform3d::Identity(), 0.25f);
} }
void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture) void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool is_active)
{ {
if (!bottom) if (!bottom)
render_model(view_matrix, projection_matrix); render_model(view_matrix, projection_matrix);
if (show_texture) if (show_texture)
render_texture(bottom, canvas, view_matrix, projection_matrix); render_texture(bottom, canvas, view_matrix, projection_matrix, is_active);
else if (bottom) else if (bottom)
render_contour(view_matrix, projection_matrix); render_contour(view_matrix, projection_matrix);
} }
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix) void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool is_active)
{ {
if (m_texture_filename.empty()) { if (m_texture_filename.empty()) {
m_texture.reset(); m_texture.reset();
@ -414,7 +412,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& v
shader->start_using(); shader->start_using();
shader->set_uniform("view_model_matrix", view_matrix); shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", projection_matrix); shader->set_uniform("projection_matrix", projection_matrix);
shader->set_uniform("transparent_background", bottom); shader->set_uniform("transparent_background", bottom || ! is_active);
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
@ -489,7 +487,7 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj
} }
} }
void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking) void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking, bool is_active)
{ {
if (m_texture_filename.empty() && m_model_filename.empty()) { if (m_texture_filename.empty() && m_model_filename.empty()) {
render_default(bottom, picking, show_texture, view_matrix, projection_matrix); render_default(bottom, picking, show_texture, view_matrix, projection_matrix);
@ -500,7 +498,7 @@ void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, co
render_model(view_matrix, projection_matrix); render_model(view_matrix, projection_matrix);
if (show_texture) if (show_texture)
render_texture(bottom, canvas, view_matrix, projection_matrix); render_texture(bottom, canvas, view_matrix, projection_matrix, is_active);
else if (bottom) else if (bottom)
render_contour(view_matrix, projection_matrix); render_contour(view_matrix, projection_matrix);
} }

View File

@ -87,10 +87,10 @@ private:
static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape); static std::tuple<Type, std::string, std::string> detect_type(const Pointfs& shape);
void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, void render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor,
bool show_texture, bool picking, bool active); bool show_texture, bool picking, bool active);
void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture); void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool is_active);
void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool is_active);
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking); void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking, bool is_active);
void render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_default(bool bottom, bool picking, bool show_texture, const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_contour(const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_contour(const Transform3d& view_matrix, const Transform3d& projection_matrix);

View File

@ -102,6 +102,24 @@ static bool show_imgui_demo_window = false;
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
static void select_bed(int i)
{
int old_bed = s_multiple_beds.get_active_bed();
if (i == old_bed || i == -1)
return;
s_multiple_beds.set_active_bed(i);
if (wxGetApp().plater()->is_preview_shown()) {
s_reload_preview_after_switching_beds = true;
wxPostEvent(wxGetApp().plater(), SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW));
wxGetApp().plater()->get_camera().translate_world(s_multiple_beds.get_bed_translation(i) - s_multiple_beds.get_bed_translation(old_bed));
}
wxGetApp().plater()->sidebar().update_sliced_info_sizer();
}
#ifdef __WXGTK3__ #ifdef __WXGTK3__
// wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support. // wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support.
RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {} RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {}
@ -1460,7 +1478,6 @@ bool GLCanvas3D::check_volumes_outside_state(GLVolumeCollection& volumes, ModelI
bool contained_min_one = false; bool contained_min_one = false;
const Slic3r::BuildVolume& build_volume = m_bed.build_volume(); const Slic3r::BuildVolume& build_volume = m_bed.build_volume();
s_multiple_beds.request_next_bed(false);
const std::vector<unsigned int> volumes_idxs = volumes_to_process_idxs(); const std::vector<unsigned int> volumes_idxs = volumes_to_process_idxs();
for (unsigned int vol_idx : volumes_idxs) { for (unsigned int vol_idx : volumes_idxs) {
@ -1834,22 +1851,14 @@ void GLCanvas3D::render()
{ {
if (s_multiple_beds.get_number_of_beds() != 1) { if (s_multiple_beds.get_number_of_beds() != 1 && wxGetApp().plater()->is_preview_shown()) {
ImGui::Begin("Bed selector", 0, ImGuiWindowFlags_NoResize); ImGui::Begin("Bed selector", 0, ImGuiWindowFlags_NoResize);
for (int i = 0; i < s_multiple_beds.get_number_of_beds(); ++i) { for (int i = 0; i < s_multiple_beds.get_number_of_beds(); ++i) {
bool inactive = i != s_multiple_beds.get_active_bed(); bool inactive = i != s_multiple_beds.get_active_bed();
if (inactive) if (inactive)
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0., 0., 0., .5)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0., 0., 0., .5));
if (ImGui::ImageButton((void*)s_th_tex_id[i], ImVec2(100, 100), ImVec2(0, 1), ImVec2(1, 0))) { if (ImGui::ImageButton((void*)(int64_t)s_th_tex_id[i], ImVec2(100, 100), ImVec2(0, 1), ImVec2(1, 0)))
int old_bed = s_multiple_beds.get_active_bed(); select_bed(i);
s_multiple_beds.set_active_bed(i);
if (wxGetApp().plater()->is_preview_shown()) {
s_reload_preview_after_switching_beds = true;
wxPostEvent(wxGetApp().plater(), SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW));
camera.translate_world(s_multiple_beds.get_bed_translation(i) - s_multiple_beds.get_bed_translation(old_bed));
}
wxGetApp().plater()->sidebar().update_sliced_info_sizer();
}
if (inactive) if (inactive)
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
@ -3281,10 +3290,11 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
void GLCanvas3D::on_render_timer(wxTimerEvent& evt) void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
{ {
m_dirty = true;
// no need to wake up idle // no need to wake up idle
// right after this event, idle event is fired // right after this event, idle event is fired
// m_dirty = true; //wxWakeUpIdle();
// wxWakeUpIdle();
} }
@ -3376,6 +3386,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
Point pos(evt.GetX(), evt.GetY()); Point pos(evt.GetX(), evt.GetY());
static wxTimer s_virtual_bed_timer;
s_virtual_bed_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) { s_multiple_beds.request_next_bed(true); schedule_extra_frame(100); });
ImGuiWrapper* imgui = wxGetApp().imgui(); ImGuiWrapper* imgui = wxGetApp().imgui();
if (m_tooltip.is_in_imgui() && evt.LeftUp()) if (m_tooltip.is_in_imgui() && evt.LeftUp())
// ignore left up events coming from imgui windows and not processed by them // ignore left up events coming from imgui windows and not processed by them
@ -3613,6 +3626,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_dirty = true; m_dirty = true;
} }
} else if (evt.LeftDown()) {
select_bed(s_multiple_beds.get_last_hovered_bed());
} }
} }
@ -3640,6 +3655,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.Dragging() && evt.LeftIsDown() && !evt.CmdDown() && m_layers_editing.state == LayersEditing::Unknown && else if (evt.Dragging() && evt.LeftIsDown() && !evt.CmdDown() && m_layers_editing.state == LayersEditing::Unknown &&
m_mouse.drag.move_volume_idx != -1 && m_mouse.is_start_position_3D_defined()) { m_mouse.drag.move_volume_idx != -1 && m_mouse.is_start_position_3D_defined()) {
if (!m_mouse.drag.move_requires_threshold) { if (!m_mouse.drag.move_requires_threshold) {
static bool was_dragging = false;
was_dragging = m_mouse.dragging;
m_mouse.dragging = true; m_mouse.dragging = true;
Vec3d cur_pos = m_mouse.drag.start_position_3D; Vec3d cur_pos = m_mouse.drag.start_position_3D;
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
@ -3682,6 +3699,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
update_sequential_clearance(false); update_sequential_clearance(false);
wxGetApp().obj_manipul()->set_dirty(); wxGetApp().obj_manipul()->set_dirty();
m_dirty = true; m_dirty = true;
const Selection::IndicesList& list = m_selection.get_volume_idxs();
static bool was_outside = true;
bool is_outside = std::any_of(list.begin(), list.end(), [this](unsigned int i) { return m_volumes.volumes[i]->is_outside; });
if (is_outside && (! was_dragging || ! was_outside))
s_virtual_bed_timer.Start(1000, true);
was_outside = is_outside;
} }
} }
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) { else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) {
@ -3754,6 +3778,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_layers_editing.accept_changes(*this); m_layers_editing.accept_changes(*this);
} }
else if (m_mouse.drag.move_volume_idx != -1 && m_mouse.dragging) { else if (m_mouse.drag.move_volume_idx != -1 && m_mouse.dragging) {
s_multiple_beds.request_next_bed(false);
s_virtual_bed_timer.Stop();
do_move(L("Move Object")); do_move(L("Move Object"));
wxGetApp().obj_manipul()->set_dirty(); wxGetApp().obj_manipul()->set_dirty();
m_sequential_print_clearance.stop_dragging(); m_sequential_print_clearance.stop_dragging();

View File

@ -2052,8 +2052,8 @@ void Plater::priv::scale_selection_to_fit_print_volume()
void Plater::priv::schedule_background_process() void Plater::priv::schedule_background_process()
{ {
delayed_error_message.clear(); delayed_error_message.clear();
// Trigger the timer event after 0.5s // Trigger the timer event after 0.1s
this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); this->background_process_timer.Start(100, wxTIMER_ONE_SHOT);
// Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff. // Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff.
this->view3D->get_canvas3d()->set_config(this->config); this->view3D->get_canvas3d()->set_config(this->config);
} }
@ -3277,7 +3277,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
if (evt.data.second) { // right button was clicked on empty space if (evt.data.second) { // right button was clicked on empty space
if (!get_selection().is_empty()) // several objects are selected in 3DScene if (!get_selection().is_empty()) // several objects are selected in 3DScene
return; return;
menu = menus.default_menu(); if (s_multiple_beds.get_last_hovered_bed() != -1)
menu = menus.default_menu();
} }
else else
menu = menus.multi_selection_menu(); menu = menus.multi_selection_menu();

View File

@ -10,6 +10,8 @@
#include "Selection.hpp" #include "Selection.hpp"
#include "Plater.hpp" #include "Plater.hpp"
#include "libslic3r/MultipleBeds.hpp"
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
@ -164,9 +166,27 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
if (!item->is_active()) if (!item->is_active())
continue; continue;
bool sth_hit = false;
current_hit.raycaster_id = item->get_id(); current_hit.raycaster_id = item->get_id();
const Transform3d& trafo = item->get_transform(); const Transform3d& trafo_orig = item->get_transform();
if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) { Transform3d trafo = trafo_orig;
if (type == EType::Bed) {
int bed_idx_hit = -1;
for (int i = 0; i < s_multiple_beds.get_number_of_beds(); ++i) {
trafo = trafo_orig;
trafo.translate(s_multiple_beds.get_bed_translation(i));
sth_hit = item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane);
if (sth_hit) {
bed_idx_hit = i;
break;
}
}
s_multiple_beds.set_last_hovered_bed(bed_idx_hit);
} else
sth_hit = item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane);
if (sth_hit) {
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>(); current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>(); current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>();
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) { if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) {