From 797dd1197e2be640b00d74b46387f151d09ab9d5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Apr 2023 08:54:04 +0200 Subject: [PATCH] Allow the user to switch between visualizing original or processed volumes in 3D scene after slicing using SLA printers --- src/slic3r/GUI/3DScene.cpp | 26 ++++- src/slic3r/GUI/GLCanvas3D.cpp | 115 +++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 44 ++++++- src/slic3r/GUI/GUI_ObjectList.cpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/Selection.cpp | 37 ++++++ src/slic3r/GUI/Selection.hpp | 3 + 9 files changed, 231 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 71361e68f..cfd457151 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -613,10 +613,28 @@ void GLVolumeCollection::load_object_auxiliary( if (convex_hull.has_value()) v.set_convex_hull(*convex_hull); v.is_modifier = false; - v.shader_outside_printer_detection_enabled = (step == slaposSupportTree); + v.shader_outside_printer_detection_enabled = (step == slaposSupportTree || step == slaposDrillHoles); v.set_instance_transformation(model_instance.get_transformation()); }; + if (milestone == SLAPrintObjectStep::slaposDrillHoles) { + if (print_object->get_parts_to_slice().size() > 1) { + // Get the mesh. + TriangleMesh backend_mesh; + std::shared_ptr preview_mesh_ptr = print_object->get_mesh_to_print(); + if (preview_mesh_ptr != nullptr) + backend_mesh = TriangleMesh(*preview_mesh_ptr); + if (!backend_mesh.empty()) { + backend_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = backend_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposDrillHoles, backend_mesh, GLVolume::MODEL_COLOR[0], convex_hull); + } + } + } + } + // Get the support mesh. if (milestone == SLAPrintObjectStep::slaposSupportTree) { TriangleMesh supports_mesh = print_object->support_mesh(); @@ -624,8 +642,8 @@ void GLVolumeCollection::load_object_auxiliary( supports_mesh.transform(mesh_trafo_inv); TriangleMesh convex_hull = supports_mesh.convex_hull_3d(); for (const std::pair& instance_idx : instances) { - const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); } } } @@ -924,7 +942,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } for (GLVolume* volume : volumes) { - if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0) + if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->is_sla_pad() || volume->is_sla_support()) continue; int extruder_id = volume->extruder_id - 1; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 958346849..69bcd6ab5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1072,6 +1072,107 @@ void GLCanvas3D::load_arrange_settings() m_arrange_settings_fff_seq_print.alignment = arr_alignment ; } +static int processed_object_idx(const Model& model, const SLAPrint& sla_print, const GLVolumePtrs& volumes) +{ + for (const GLVolume* v : volumes) { + if (v->volume_idx() == -(int)slaposDrillHoles) { + const int mo_idx = v->object_idx(); + const ModelObject* model_object = (mo_idx < (int)model.objects.size()) ? model.objects[mo_idx] : nullptr; + if (model_object != nullptr && model_object->instances[v->instance_idx()]->is_printable()) { + const SLAPrintObject* print_object = sla_print.get_print_object_by_model_object_id(model_object->id()); + if (print_object != nullptr && print_object->get_parts_to_slice().size() > 1) + return mo_idx; + } + } + } + + return -1; +}; + +GLCanvas3D::ESLAViewType GLCanvas3D::SLAView::detect_type(const GLVolumePtrs& volumes) +{ + m_type = ESLAViewType::Original; + if (m_allow_type_detection) { + for (const GLVolume* v : volumes) { + if (v->volume_idx() == -(int)slaposDrillHoles) { + m_type = ESLAViewType::Processed; + break; + } + } + } + + m_parent.set_sla_view_type(m_type); + m_allow_type_detection = false; + return m_type; +} + +bool GLCanvas3D::SLAView::set_type(ESLAViewType type) { + if (m_type != type) { + m_type = type; + return true; + } + return false; +} + +void GLCanvas3D::SLAView::update_volumes(GLVolumePtrs& volumes) +{ + const SLAPrint* sla_print = m_parent.sla_print(); + const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, volumes) : -1; + const bool show_processed = m_type == ESLAViewType::Processed && mo_idx != -1; + + auto show = [show_processed](const GLVolume& v) { + return show_processed ? v.volume_idx() < 0 : v.volume_idx() != -(int)slaposDrillHoles; + }; + + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + + for (GLVolume* v : volumes) { + v->is_active = v->object_idx() != mo_idx || show(*v); + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(v->is_active); + } +} + +void GLCanvas3D::SLAView::render_switch_button() +{ + const SLAPrint* sla_print = m_parent.sla_print(); + const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, m_parent.get_volumes().volumes) : -1; + if (mo_idx == -1) + return; + + const BoundingBoxf ss_box = m_parent.get_selection().get_screen_space_bounding_box(); + if (ss_box.defined) { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::SetNextWindowPos(ImVec2((float)ss_box.max.x(), (float)ss_box.center().y()), ImGuiCond_Always, ImVec2(0.0, 0.5)); + imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + const wxString btn_text = (m_type == ESLAViewType::Original) ? _L("Processed") : _L("Original"); + if (imgui.button(btn_text)) { + switch (m_type) + { + case ESLAViewType::Original: { m_parent.set_sla_view_type(ESLAViewType::Processed); break; } + case ESLAViewType::Processed: { m_parent.set_sla_view_type(ESLAViewType::Original); break; } + default: { assert(false); break; } + } + } + imgui.end(); + ImGui::PopStyleColor(2); + } +} + +//void GLCanvas3D::SLAView::render_debug_window() +//{ +// ImGuiWrapper& imgui = *wxGetApp().imgui(); +// imgui.begin(std::string("SLAView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); +// imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Type:"); +// ImGui::SameLine(); +// const std::string text = (m_type == ESLAViewType::Original) ? "Original" : "Processed"; +// imgui.text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), text); +// imgui.end(); +//} + PrinterTechnology GLCanvas3D::current_printer_technology() const { return m_process->current_printer_technology(); @@ -1113,6 +1214,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_render_sla_auxiliaries(true) , m_labels(*this) , m_slope(m_volumes) + , m_sla_view(*this) { if (m_canvas != nullptr) { m_timer.SetOwner(m_canvas); @@ -1642,6 +1744,13 @@ void GLCanvas3D::render() GLModel::render_statistics(); #endif // ENABLE_GLMODEL_STATISTICS + if (wxGetApp().plater()->is_view3D_shown() && current_printer_technology() == ptSLA) { + const GLGizmosManager::EType type = m_gizmos.get_current_type(); + if (type == GLGizmosManager::EType::Undefined) + m_sla_view.render_switch_button(); +// m_sla_view.render_debug_window(); + } + std::string tooltip; // Negative coordinate means out of the window, likely because the window was deactivated. @@ -2114,6 +2223,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); } + + if (m_sla_view.detect_type(m_volumes.volumes) == ESLAViewType::Processed) + update_object_list = true; } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2169,6 +2281,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re else m_selection.volumes_changed(map_glvolume_old_to_new); + if (printer_technology == ptSLA) + m_sla_view.update_volumes(m_volumes.volumes); + m_gizmos.update_data(); m_gizmos.refresh_on_off_state(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a79bcff2e..cb76a79e9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -468,6 +468,12 @@ public: int alignment = 0; }; + enum class ESLAViewType + { + Original, + Processed + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -547,6 +553,26 @@ private: bool m_tooltip_enabled{ true }; Slope m_slope; + class SLAView + { + public: + explicit SLAView(GLCanvas3D& parent) : m_parent(parent) {} + void allow_type_detection(bool allow) { m_allow_type_detection = allow; } + ESLAViewType detect_type(const GLVolumePtrs& volumes); + ESLAViewType get_type() const { return m_type; } + bool set_type(ESLAViewType type); + void update_volumes(GLVolumePtrs& volumes); + void render_switch_button(); +// void render_debug_window(); + + private: + GLCanvas3D& m_parent; + ESLAViewType m_type{ ESLAViewType::Original }; + bool m_allow_type_detection{ false }; + }; + + SLAView m_sla_view; + ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, m_arrange_settings_fff_seq_print; @@ -656,7 +682,7 @@ private: GLModel m_background; public: - explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed); + GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed); ~GLCanvas3D(); bool is_initialized() const { return m_initialized; } @@ -962,6 +988,22 @@ public: std::pair> get_layers_height_data(int object_id); + void set_sla_view_type(ESLAViewType type) { + if (type == ESLAViewType::Processed) { + assert(!m_selection.is_empty()); + const GLVolume* v = m_selection.get_first_volume(); + m_selection.add_instance(v->object_idx(), v->instance_idx()); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + } + + m_dirty = type != m_sla_view.get_type(); + + if (m_sla_view.set_type(type)) + m_sla_view.update_volumes(m_volumes.volumes); + } + + void allow_sla_view_type_detection(bool allow) { m_sla_view.allow_type_detection(allow); } + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5f8b9a75b..c1536da0b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -747,6 +747,10 @@ void ObjectList::selection_changed() wxGetApp().obj_layers()->update_scene_from_editor_selection(); } } + else if (type & itVolume) { + if (printer_technology() == ptSLA) + wxGetApp().plater()->canvas3D()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + } } part_selection_changed(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fe7e1b4ea..5998d5b11 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -64,7 +64,6 @@ void GLGizmoSlaSupports::data_changed() // If we triggered autogeneration before, check backend and fetch results if they are there if (mo) { - m_c->instances_hider()->set_hide_full_scene(true); const SLAPrintObject* po = m_c->selection_info()->print_object(); const int required_step = get_min_sla_print_object_step(); auto last_comp_step = static_cast(po->last_completed_step()); @@ -83,6 +82,8 @@ void GLGizmoSlaSupports::data_changed() register_point_raycasters_for_picking(); else update_point_raycasters_for_picking_transform(); + + m_c->instances_hider()->set_hide_full_scene(true); } // m_parent.toggle_model_objects_visibility(false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index af93c07df..20444586d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -969,6 +969,8 @@ bool GLGizmosManager::activate_gizmo(EType type) return false; // gizmo refused to be turned on. } + m_parent.set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + new_gizmo.register_raycasters_for_picking(); // sucessful activation of gizmo diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 159a9693d..a2ed0bef4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3448,8 +3448,10 @@ unsigned int Plater::priv::update_restart_background_process(bool force_update_s { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(false); - if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) + if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) { + view3D->get_canvas3d()->allow_sla_view_type_detection(true); view3D->reload_scene(false); + } if (force_update_preview) this->preview->reload_print(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 28cf367fd..b10a45928 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -890,6 +890,43 @@ std::pair Selection::get_bounding_box_in_reference_s } #endif // ENABLE_WORLD_COORDINATE +BoundingBoxf Selection::get_screen_space_bounding_box() +{ + BoundingBoxf ss_box; + if (!is_empty()) { + const auto& [box, box_trafo] = get_bounding_box_in_current_reference_system(); + + // vertices + std::vector vertices = { + { box.min.x(), box.min.y(), box.min.z() }, + { box.max.x(), box.min.y(), box.min.z() }, + { box.max.x(), box.max.y(), box.min.z() }, + { box.min.x(), box.max.y(), box.min.z() }, + { box.min.x(), box.min.y(), box.max.z() }, + { box.max.x(), box.min.y(), box.max.z() }, + { box.max.x(), box.max.y(), box.max.z() }, + { box.min.x(), box.max.y(), box.max.z() } + }; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + + const double half_w = 0.5 * double(viewport[2]); + const double h = double(viewport[3]); + const double half_h = 0.5 * h; + for (const Vec3d& v : vertices) { + const Vec3d world = box_trafo * v; + const Vec4d clip = projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); + const Vec3d ndc = Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + const Vec2d ss = Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, h - (half_h * ndc.y() + double(viewport[1]) + half_h)); + ss_box.merge(ss); + } + } + + return ss_box; +} + void Selection::setup_cache() { if (!m_valid) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index d3f456702..0657a8d8a 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -400,6 +400,9 @@ public: std::pair get_bounding_box_in_reference_system(ECoordinatesType type) const; #endif // ENABLE_WORLD_COORDINATE + // Returns the screen space bounding box + BoundingBoxf get_screen_space_bounding_box(); + void setup_cache(); #if ENABLE_WORLD_COORDINATE