diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index d2aac18fd..6d53eca4f 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -303,8 +303,10 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const ret.m_t = double(hit.t); ret.m_dir = dir; ret.m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { ret.m_normal = this->normal_by_face_id(hit.id); + ret.m_face_id = hit.id; + } return ret; } @@ -340,8 +342,10 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const outs.back().m_t = double(hit.t); outs.back().m_dir = dir; outs.back().m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { outs.back().m_normal = this->normal_by_face_id(hit.id); + outs.back().m_face_id = hit.id; + } } return outs; diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index bcf94ec16..014a57e82 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -55,6 +55,7 @@ public: class hit_result { // m_t holds a distance from m_source to the intersection. double m_t = infty(); + int m_face_id = -1; const EigenMesh3D *m_mesh = nullptr; Vec3d m_dir; Vec3d m_source; @@ -74,6 +75,7 @@ public: inline const Vec3d& direction() const { return m_dir; } inline const Vec3d& source() const { return m_source; } inline Vec3d position() const { return m_source + m_dir * m_t; } + inline int face() const { return m_face_id; } inline bool is_valid() const { return m_mesh != nullptr; } inline bool is_hit() const { return !std::isinf(m_t); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index dfe4bc9a5..6c42a8cd1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -105,13 +105,40 @@ void GLGizmoFdmSupports::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - + render_triangles(selection); render_clipping_plane(selection); glsafe(::glDisable(GL_BLEND)); } +void GLGizmoFdmSupports::render_triangles(const Selection& selection) const +{ + if (! m_mesh) + return; + // Get transformation of the instance + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + Transform3d trafo = vol->get_instance_transformation().get_matrix(); + + ::glColor3f(0.0f, 0.37f, 1.0f); + + for (size_t facet_idx : m_selected_facets) { + stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(m_mesh->its, facet_idx); + ::glPushMatrix(); + ::glTranslatef(normal(0), normal(1), normal(2)); + ::glMultMatrixd(trafo.data()); + + ::glBegin(GL_TRIANGLES); + ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](2)); + ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](2)); + ::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](2)); + ::glEnd(); + ::glPopMatrix(); + } + + + +} void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const { @@ -193,7 +220,7 @@ void GLGizmoFdmSupports::update_mesh() // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) +bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: if (! m_mesh_raycaster) @@ -208,11 +235,8 @@ bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); + if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get(), &facet_idx)) return true; - } else return false; } @@ -240,6 +264,22 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return true; } + if (action == SLAGizmoEventType::LeftDown || (action == SLAGizmoEventType::Dragging && m_wait_for_up_event)) { + size_t facet_idx = 0; + if (unproject_on_mesh(mouse_position, facet_idx)) { + m_selected_facets.push_back(facet_idx); + m_wait_for_up_event = true; + return true; + } + if (action == SLAGizmoEventType::Dragging && m_wait_for_up_event) + return true; + } + + if (action == SLAGizmoEventType::LeftUp && m_wait_for_up_event) { + m_wait_for_up_event = false; + return true; + } + return false; } @@ -332,11 +372,9 @@ std::string GLGizmoFdmSupports::on_get_name() const void GLGizmoFdmSupports::on_set_state() { - if (m_state == On) - std::cout << "zapinam se..." << std::endl; - else - std::cout << "vypinam se..." << std::endl; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! return; + // m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id m_model_object = nullptr; @@ -351,7 +389,7 @@ void GLGizmoFdmSupports::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("FDM gizmo turned on"))); if (is_mesh_update_necessary()) update_mesh(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 02a0877ee..1b1cfd134 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -3,9 +3,6 @@ #include "GLGizmoBase.hpp" -//#include "libslic3r/SLA/SLACommon.hpp" -//#include - #include @@ -24,7 +21,7 @@ private: ObjectID m_model_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb - bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + bool unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx); GLUquadricObj* m_quadric; @@ -34,6 +31,8 @@ private: const indexed_triangle_set* m_its; mutable std::vector m_triangles; + std::vector m_selected_facets; + public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -48,6 +47,7 @@ private: void on_render() const override; void on_render_for_picking() const override; + void render_triangles(const Selection& selection) const; void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 17cc6ff0c..d66304195 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -388,9 +388,12 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p if (m_current == SlaSupports) return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - if (m_current == Hollow) + else if (m_current == Hollow) return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - return false; + else if (m_current == FdmSupports) + return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else + return false; } ClippingPlane GLGizmosManager::get_clipping_plane() const @@ -444,7 +447,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow) { + if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -502,7 +505,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) + && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { @@ -530,7 +534,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ) + && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -603,9 +608,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow) && !m_parent.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging()) { - // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); processed = true; @@ -702,7 +707,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 37b6efd87..7e886a486 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -95,9 +95,16 @@ void MeshClipper::recalculate_triangles() } +Vec3f MeshRaycaster::get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx) +{ + Vec3f a(its.vertices[its.indices[facet_idx](1)] - its.vertices[its.indices[facet_idx](0)]); + Vec3f b(its.vertices[its.indices[facet_idx](2)] - its.vertices[its.indices[facet_idx](0)]); + return Vec3f(a.cross(b)).normalized(); +} bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, + size_t* facet_idx) const { const std::array& viewport = camera.get_viewport(); const Transform3d& model_mat = camera.get_view_matrix(); @@ -112,7 +119,21 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& pt1 = inv * pt1; pt2 = inv * pt2; - std::vector hits = m_emesh.query_ray_hits(pt1, pt2-pt1); + point = pt1; + direction = pt2-pt1; +} + + +bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, + size_t* facet_idx) const +{ + Vec3d point; + Vec3d direction; + line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); + + std::vector hits = m_emesh.query_ray_hits(point, direction); + if (hits.empty()) return false; // no intersection found @@ -134,6 +155,10 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& // Now stuff the points in the provided vector and calculate normals if asked about them: position = hits[i].position().cast(); normal = hits[i].normal().cast(); + + if (facet_idx) + *facet_idx = hits[i].face(); + return true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index b4ad03011..3c9580e59 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -4,6 +4,8 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/SLA/EigenMesh3D.hpp" +#include "admesh/stl.h" + #include @@ -118,7 +120,8 @@ public: const Camera& camera, // current camera position Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& normal, // normal of the triangle that was hit - const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) + const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) + size_t* facet_idx = nullptr // index of the facet hit ) const; // Given a vector of points in woorld coordinates, this returns vector @@ -134,8 +137,11 @@ public: // Given a point in world coords, the method returns closest point on the mesh. // The output is in mesh coords. // normal* can be used to also get normal of the respective triangle. + Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; + static Vec3f get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx); + private: sla::EigenMesh3D m_emesh; };