diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 5b6620ef6..f0aaca9ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -5,19 +5,13 @@ #include -//#include -//#include -//#include - #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" -#include "libslic3r/OpenVDBUtils.hpp" namespace Slic3r { @@ -26,7 +20,6 @@ namespace GUI { GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_quadric(nullptr) - , m_its(nullptr) { m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_quadric = ::gluNewQuadric(); @@ -48,8 +41,8 @@ bool GLGizmoHollow::on_init() m_desc["head_diameter"] = _(L("Head diameter")) + ": "; m_desc["lock_supports"] = _(L("Lock supports under new islands")); - m_desc["remove_selected"] = _(L("Remove selected points")); - m_desc["remove_all"] = _(L("Remove all points")); + m_desc["remove_selected"] = _(L("Remove selected holes")); + m_desc["remove_all"] = _(L("Remove all holes")); m_desc["apply_changes"] = _(L("Apply changes")); m_desc["discard_changes"] = _(L("Discard changes")); m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; @@ -113,7 +106,7 @@ void GLGizmoHollow::on_render() const return; } - if (! m_its || ! m_mesh) + if (! m_mesh) const_cast(this)->update_mesh(); if (m_volume_with_cavity) { @@ -212,7 +205,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const ::glPopMatrix(); } - if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { + if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty()) { // The supports are hidden in the editing mode, so it makes no sense to render the cuts. ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); @@ -310,12 +303,14 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons //const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.); const double cone_rad_diff = m_new_cone_angle*cone_radius; glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, -cone_height/2.)); + glsafe(::glTranslated(0., 0., -cone_height/2.)); //::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1); ::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1); - glsafe(::glTranslatef(0.f, 0.f, cone_height)); - //::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); + glsafe(::glTranslated(0., 0., cone_height)); ::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1); + glsafe(::glTranslated(0., 0., -cone_height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, cone_radius+cone_rad_diff, 24, 1); glsafe(::glPopMatrix()); } //::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); @@ -354,7 +349,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const bool GLGizmoHollow::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_model_object_id) || m_its == nullptr); + && ((m_model_object->id() != m_model_object_id) || ! m_mesh); } @@ -368,7 +363,6 @@ void GLGizmoHollow::update_mesh() // this way we can use that mesh directly. // This mesh does not account for the possible Z up SLA offset. m_mesh = &m_model_object->volumes.front()->mesh(); - m_its = &m_mesh->its; // If this is different mesh than last time if (m_model_object_id != m_model_object->id()) { @@ -449,7 +443,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (m_selection_empty) { std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_parent.set_as_dirty(); m_wait_for_up_event = true; @@ -563,7 +557,7 @@ void GLGizmoHollow::delete_selected_points(bool force) std::abort(); } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); for (unsigned int idx=0; idxhollow(); +} + +void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr mesh) +{ + // Called from Plater when the UI job finishes + m_cavity_mesh = std::move(mesh); - imesh.merge(omesh); - m_cavity_mesh.reset(new TriangleMesh); - *m_cavity_mesh = sla::to_triangle_mesh(imesh); - m_cavity_mesh.get()->require_shared_vertices(); m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); m_object_clipper.reset(); + m_volume_with_cavity.reset(); - // create a new GLVolume that only has the cavity inside - m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); - m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); - m_volume_with_cavity->finalize_geometry(true); - m_volume_with_cavity->set_volume_transformation(m_model_object->volumes.front()->get_transformation()); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); - m_parent.toggle_model_objects_visibility(false, m_model_object, m_active_instance); + if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside + Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); + volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); + m_volume_with_cavity->finalize_geometry(true); + m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); + } + m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); } std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const @@ -695,9 +697,8 @@ RENDER_AGAIN: if (m_editing_mode) { - if (m_imgui->button(m_desc.at("hollow"))) { - hollow_mesh(m_offset, m_adaptibility); - } + if (m_imgui->button(m_desc.at("hollow"))) + hollow_mesh(); float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; if (m_new_point_head_diameter > diameter_upper_cap) @@ -728,7 +729,7 @@ RENDER_AGAIN: cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f; float backup = m_new_point_head_diameter; m_new_point_head_diameter = m_old_point_head_diameter; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change point head diameter"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); m_new_point_head_diameter = backup; for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) @@ -757,10 +758,10 @@ RENDER_AGAIN: m_imgui->text("Offset: "); ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); + ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); m_imgui->text("Adaptibility: "); ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_adaptibility, 0.f, 1.f, "%.1f"); + ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); @@ -912,7 +913,7 @@ void GLGizmoHollow::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); if (is_mesh_update_necessary()) update_mesh(); @@ -929,27 +930,16 @@ void GLGizmoHollow::on_set_state() m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_model_object && false; - if (will_ask) { - wxGetApp().CallAfter([this]() { - }); - // refuse to be turned off so the gizmo is active when the CallAfter is executed - m_state = m_old_state; - } - else { - // we are actually shutting down - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); - m_parent.toggle_model_objects_visibility(true); - m_normal_cache.clear(); - m_clipping_plane_distance = 0.f; - // Release clippers and the AABB raycaster. - m_its = nullptr; - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_mesh_raycaster.reset(); - m_cavity_mesh.reset(); - m_volume_with_cavity.reset(); - } + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); + m_parent.toggle_model_objects_visibility(true); + m_normal_cache.clear(); + m_clipping_plane_distance = 0.f; + // Release clippers and the AABB raycaster. + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_mesh_raycaster.reset(); + m_cavity_mesh.reset(); + m_volume_with_cavity.reset(); } m_old_state = m_state; } @@ -977,7 +967,7 @@ void GLGizmoHollow::on_stop_dragging() && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected { m_editing_cache[m_hover_id] = m_point_before_drag; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); m_editing_cache[m_hover_id] = backup; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 82455a9f4..afaaf659b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -36,7 +36,6 @@ private: std::unique_ptr m_cavity_mesh; std::unique_ptr m_volume_with_cavity; const TriangleMesh* m_mesh; - const indexed_triangle_set* m_its; mutable const TriangleMesh* m_supports_mesh; mutable std::vector m_triangles; mutable std::vector m_supports_triangles; @@ -78,6 +77,8 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(bool force = false); ClippingPlane get_sla_clipping_plane() const; + void update_hollowed_mesh(std::unique_ptr mesh); + void get_hollowing_parameters(TriangleMesh const** object_mesh, float& offset, float& adaptability) const; bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } @@ -92,7 +93,7 @@ private: void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); - void hollow_mesh(float offset = 2.f, float adaptability = 1.f); + void hollow_mesh(); bool unsaved_changes() const; const TriangleMesh* mesh() const; @@ -109,7 +110,7 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo float m_offset = 2.f; - float m_adaptibility = 1.f; + float m_adaptability = 1.f; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 0368e433e..2c4d71316 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -158,6 +158,7 @@ public: void update_data(); EType get_current_type() const { return m_current; } + GLGizmoBase* get_current() const; bool is_running() const; bool handle_shortcut(int key); @@ -206,8 +207,6 @@ private: float get_total_overlay_height() const; float get_total_overlay_width() const; - GLGizmoBase* get_current() const; - bool generate_icons_texture() const; void update_on_off_state(const Vec2d& mouse_pos); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c67fac733..eb7ec4977 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,6 +33,7 @@ #include "libslic3r/Format/3mf.hpp" #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/OpenVDBUtils.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" @@ -1559,7 +1560,8 @@ struct Plater::priv enum class Jobs : size_t { Arrange, - Rotoptimize + Rotoptimize, + Hollow }; class ArrangeJob : public Job @@ -1703,6 +1705,20 @@ struct Plater::priv void process() override; }; + class HollowJob : public Job + { + public: + using Job::Job; + void prepare() override; + void process() override; + void finalize() override; + private: + std::unique_ptr m_output; + const TriangleMesh* m_object_mesh = nullptr; + float m_offset = 0.f; + float m_adaptability = 0.f; + }; + // Jobs defined inside the group class will be managed so that only one can // run at a time. Also, the background process will be stopped if a job is // started. @@ -1714,6 +1730,7 @@ struct Plater::priv ArrangeJob arrange_job{m_plater}; RotoptimizeJob rotoptimize_job{m_plater}; + HollowJob hollow_job{m_plater}; // To create a new job, just define a new subclass of Job, implement // the process and the optional prepare() and finalize() methods @@ -1721,7 +1738,8 @@ struct Plater::priv // if it cannot run concurrently with other jobs in this group std::vector> m_jobs{arrange_job, - rotoptimize_job}; + rotoptimize_job, + hollow_job}; public: ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {} @@ -1816,6 +1834,7 @@ struct Plater::priv void reset(); void mirror(Axis axis); void arrange(); + void hollow(); void sla_optimize_rotation(); void split_object(); void split_volume(); @@ -2703,6 +2722,12 @@ void Plater::priv::arrange() m_ui_jobs.start(Jobs::Arrange); } +void Plater::priv::hollow() +{ + this->take_snapshot(_(L("Hollow"))); + m_ui_jobs.start(Jobs::Hollow); +} + // This method will find an optimal orientation for the currently selected item // Very similar in nature to the arrange method above... void Plater::priv::sla_optimize_rotation() { @@ -2834,6 +2859,38 @@ void Plater::priv::RotoptimizeJob::process() : _(L("Orientation found."))); } +void Plater::priv::HollowJob::prepare() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + const GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); + assert(gizmo_hollow); + gizmo_hollow->get_hollowing_parameters(&m_object_mesh, m_offset, m_adaptability); + m_output.reset(); +} + +void Plater::priv::HollowJob::process() +{ + Slic3r::sla::Contour3D imesh{*m_object_mesh}; + auto ptr = meshToVolume(imesh, {}); + sla::Contour3D omesh = volumeToMesh(*ptr, -m_offset, m_adaptability, true); + + if (omesh.empty()) + return; + + imesh.merge(omesh); + m_output.reset(new TriangleMesh()); + *m_output = sla::to_triangle_mesh(imesh); + m_output->require_shared_vertices(); +} + +void Plater::priv::HollowJob::finalize() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); + assert(gizmo_hollow); + gizmo_hollow->update_hollowed_mesh(std::move(m_output)); +} + void Plater::priv::split_object() { int obj_idx = get_selected_object_idx(); @@ -4661,6 +4718,11 @@ void Plater::export_toolpaths_to_obj() const p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } +void Plater::hollow() +{ + p->hollow(); +} + void Plater::reslice() { // Stop arrange and (or) optimize rotation tasks. diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 00ceb89bc..9ede19d71 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -190,6 +190,7 @@ public: void reload_from_disk(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj() const; + void hollow(); void reslice(); void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); void changed_object(int obj_idx);