diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index 1462b1a76..c69ab6d9a 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -34,16 +34,15 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, - const Vec3f& source, const Vec3f& dir, - float radius, CursorType cursor_type, - EnforcerBlockerType new_state) + const Vec3f& source, float radius, + CursorType cursor_type, EnforcerBlockerType new_state, + const Transform3d& trafo) { assert(facet_start < m_orig_size_indices); - assert(is_approx(dir.norm(), 1.f)); - // Save current cursor center, squared radius and camera direction, - // so we don't have to pass it around. - m_cursor = {hit, source, dir, radius*radius, cursor_type}; + // Save current cursor center, squared radius and camera direction, so we don't + // have to pass it around. + m_cursor = Cursor(hit, source, radius, cursor_type, trafo); // In case user changed cursor size since last time, update triangle edge limit. if (m_old_cursor_radius != radius) { @@ -176,10 +175,24 @@ void TriangleSelector::split_triangle(int facet_idx) const double limit_squared = m_edge_limit_sqr; std::array& facet = tr->verts_idxs; - const stl_vertex* pts[3] = { &m_vertices[facet[0]].v, &m_vertices[facet[1]].v, &m_vertices[facet[2]].v}; - double sides[3] = { (*pts[2]-*pts[1]).squaredNorm(), - (*pts[0]-*pts[2]).squaredNorm(), - (*pts[1]-*pts[0]).squaredNorm() }; + std::array pts = { &m_vertices[facet[0]].v, + &m_vertices[facet[1]].v, + &m_vertices[facet[2]].v}; + std::array pts_transformed; // must stay in scope of pts !!! + + // In case the object is non-uniformly scaled, transform the + // points to world coords. + if (! m_cursor.uniform_scaling) { + for (size_t i=0; i sides; + sides = { (*pts[2]-*pts[1]).squaredNorm(), + (*pts[0]-*pts[2]).squaredNorm(), + (*pts[1]-*pts[0]).squaredNorm() }; std::vector sides_to_split; int side_to_keep = -1; @@ -204,38 +217,14 @@ void TriangleSelector::split_triangle(int facet_idx) } -// Calculate distance of a point from a line. -bool TriangleSelector::is_point_inside_cursor(const Vec3f& point) const -{ - Vec3f diff = m_cursor.center - point; - - if (m_cursor.type == CIRCLE) - return (diff - diff.dot(m_cursor.dir) * m_cursor.dir).squaredNorm() < m_cursor.radius_sqr; - else // SPHERE - return diff.squaredNorm() < m_cursor.radius_sqr; -} - // Is pointer in a triangle? bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const { - auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b, - const Vec3f& c, const Vec3f& d) -> bool { - return ((b-a).cross(c-a)).dot(d-a) > 0.; - }; - const Vec3f& p1 = m_vertices[m_triangles[facet_idx].verts_idxs[0]].v; const Vec3f& p2 = m_vertices[m_triangles[facet_idx].verts_idxs[1]].v; const Vec3f& p3 = m_vertices[m_triangles[facet_idx].verts_idxs[2]].v; - const Vec3f& q1 = m_cursor.center + m_cursor.dir; - const Vec3f q2 = m_cursor.center - m_cursor.dir; - - if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) { - bool pos = signed_volume_sign(q1,q2,p1,p2); - if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos) - return true; - } - return false; + return m_cursor.is_pointer_in_triangle(p1, p2, p3); } @@ -245,7 +234,13 @@ bool TriangleSelector::faces_camera(int facet) const { assert(facet < m_orig_size_indices); // The normal is cached in mesh->stl, use it. - return (m_mesh->stl.facet_start[facet].normal.dot(m_cursor.dir) < 0.); + Vec3f normal = m_mesh->stl.facet_start[facet].normal; + + if (! m_cursor.uniform_scaling) { + // Transform the normal into world coords. + normal = m_cursor.trafo_normal * normal; + } + return (normal.dot(m_cursor.dir) < 0.); } @@ -254,7 +249,7 @@ int TriangleSelector::vertices_inside(int facet_idx) const { int inside = 0; for (size_t i=0; i<3; ++i) { - if (is_point_inside_cursor(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v)) + if (m_cursor.is_mesh_point_inside(m_vertices[m_triangles[facet_idx].verts_idxs[i]].v)) ++inside; } return inside; @@ -264,9 +259,12 @@ int TriangleSelector::vertices_inside(int facet_idx) const // Is edge inside cursor? bool TriangleSelector::is_edge_inside_cursor(int facet_idx) const { - Vec3f pts[3]; - for (int i=0; i<3; ++i) + std::array pts; + for (int i=0; i<3; ++i) { pts[i] = m_vertices[m_triangles[facet_idx].verts_idxs[i]].v; + if (! m_cursor.uniform_scaling) + pts[i] = m_cursor.trafo * pts[i]; + } const Vec3f& p = m_cursor.center; @@ -690,6 +688,79 @@ void TriangleSelector::deserialize(const std::map> data) } +TriangleSelector::Cursor::Cursor( + const Vec3f& center_, const Vec3f& source_, float radius_world, + CursorType type_, const Transform3d& trafo_) + : center{center_}, + source{source_}, + type{type_}, + trafo{trafo_.cast()} +{ + Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor(); + if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) { + radius_sqr = std::pow(radius_world / sf(0), 2); + uniform_scaling = true; + } + else { + // In case that the transformation is non-uniform, all checks whether + // something is inside the cursor should be done in world coords. + // First transform center, source and dir in world coords and remember + // that we did this. + center = trafo * center; + source = trafo * source; + uniform_scaling = false; + radius_sqr = radius_world * radius_world; + trafo_normal = trafo.linear().inverse().transpose(); + } + + // Calculate dir, in whatever coords is appropriate. + dir = (center - source).normalized(); +} + + +// Is a point (in mesh coords) inside a cursor? +bool TriangleSelector::Cursor::is_mesh_point_inside(Vec3f point) const +{ + if (! uniform_scaling) + point = trafo * point; + + Vec3f diff = center - point; + + if (type == CIRCLE) + return (diff - diff.dot(dir) * dir).squaredNorm() < radius_sqr; + else // SPHERE + return diff.squaredNorm() < radius_sqr; +} + + + +// p1, p2, p3 are in mesh coords! +bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_, + const Vec3f& p2_, + const Vec3f& p3_) const +{ + const Vec3f& q1 = center + dir; + const Vec3f& q2 = center - dir; + + auto signed_volume_sign = [](const Vec3f& a, const Vec3f& b, + const Vec3f& c, const Vec3f& d) -> bool { + return ((b-a).cross(c-a)).dot(d-a) > 0.; + }; + + // In case the object is non-uniformly scaled, do the check in world coords. + const Vec3f& p1 = uniform_scaling ? p1_ : Vec3f(trafo * p1_); + const Vec3f& p2 = uniform_scaling ? p2_ : Vec3f(trafo * p2_); + const Vec3f& p3 = uniform_scaling ? p3_ : Vec3f(trafo * p3_); + + if (signed_volume_sign(q1,p1,p2,p3) != signed_volume_sign(q2,p1,p2,p3)) { + bool pos = signed_volume_sign(q1,q2,p1,p2); + if (signed_volume_sign(q1,q2,p2,p3) == pos && signed_volume_sign(q1,q2,p3,p1) == pos) + return true; + } + return false; +} + + } // namespace Slic3r diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 899539c8e..6f4ca29ac 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -32,10 +32,10 @@ public: void select_patch(const Vec3f& hit, // point where to start int facet_start, // facet that point belongs to const Vec3f& source, // camera position (mesh coords) - const Vec3f& dir, // direction of the ray (mesh coords) float radius, // radius of the cursor CursorType type, // current type of cursor - EnforcerBlockerType new_state); // enforcer or blocker? + EnforcerBlockerType new_state, // enforcer or blocker? + const Transform3d& trafo); // matrix to get from mesh to world // Get facets currently in the given state. indexed_triangle_set get_facets(EnforcerBlockerType state) const; @@ -129,11 +129,20 @@ protected: // Cache for cursor position, radius and direction. struct Cursor { + Cursor() = default; + Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world, + CursorType type_, const Transform3d& trafo_); + bool is_mesh_point_inside(Vec3f pt) const; + bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const; + Vec3f center; Vec3f source; Vec3f dir; float radius_sqr; CursorType type; + Transform3f trafo; + Transform3f trafo_normal; + bool uniform_scaling; }; Cursor m_cursor; @@ -142,7 +151,6 @@ protected: // Private functions: bool select_triangle(int facet_idx, EnforcerBlockerType type, bool recursive_call = false); - bool is_point_inside_cursor(const Vec3f& point) const; int vertices_inside(int facet_idx) const; bool faces_camera(int facet) const; void undivide_triangle(int facet_idx); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d90ad8a87..55fa7d274 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1670,8 +1670,9 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject } else { const GLGizmosManager& gm = get_gizmos_manager(); auto gizmo_type = gm.get_current_type(); - if (gizmo_type == GLGizmosManager::FdmSupports - || gizmo_type == GLGizmosManager::Seam) + if ( (gizmo_type == GLGizmosManager::FdmSupports + || gizmo_type == GLGizmosManager::Seam) + && ! vol->is_modifier) vol->force_neutral_color = true; else vol->force_native_color = true; @@ -5460,6 +5461,20 @@ void GLCanvas3D::_render_objects() const }); } + // In case a painting gizmo is open, it should render the painted triangles + // before transparent objects are rendered. Otherwise they would not be + // visible when inside modifier meshes etc. + { + const GLGizmosManager& gm = get_gizmos_manager(); + GLGizmosManager::EType type = gm.get_current_type(); + if (type == GLGizmosManager::FdmSupports + || type == GLGizmosManager::Seam) { + shader->stop_using(); + gm.render_painter_gizmo(); + shader->start_using(); + } + } + m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); shader->stop_using(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 3455a30d2..f11e6041a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -56,7 +56,7 @@ bool GLGizmoFdmSupports::on_init() -void GLGizmoFdmSupports::on_render() const +void GLGizmoFdmSupports::render_painter_gizmo() const { const Selection& selection = m_parent.get_selection(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 0c39992f0..fc9770787 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -13,14 +13,14 @@ public: GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} + void render_painter_gizmo() const override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; private: bool on_init() override; - void on_render() const override; - void on_render_for_picking() const override {} void update_model_object() const override; void update_from_model_object() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index c421e63de..f4e17d44c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -350,19 +350,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const Transform3d& trafo_matrix = trafo_matrices[m_rr.mesh_id]; - // Calculate how far can a point be from the line (in mesh coords). - // FIXME: The scaling of the mesh can be non-uniform. - const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); - const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; - const float limit = m_cursor_radius/avg_scaling; - // Calculate direction from camera to the hit (in mesh coords): Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); - Vec3f dir = (m_rr.hit - camera_pos).normalized(); assert(m_rr.mesh_id < int(m_triangle_selectors.size())); m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, m_rr.facet, camera_pos, - dir, limit, m_cursor_type, new_state); + m_cursor_radius, m_cursor_type, new_state, trafo_matrix); m_last_mouse_click = mouse_position; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 8a9e87124..2d4ec8ce8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -59,6 +59,8 @@ class GLGizmoPainterBase : public GLGizmoBase private: ObjectID m_old_mo_id; size_t m_old_volumes_size = 0; + virtual void on_render() const {} + virtual void on_render_for_picking() const {} public: GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -66,6 +68,12 @@ public: void set_painter_gizmo_data(const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + // Following function renders the triangles and cursor. Having this separated + // from usual on_render method allows to render them before transparent objects, + // so they can be seen inside them. The usual on_render is called after all + // volumes (including transparent ones) are rendered. + virtual void render_painter_gizmo() const = 0; + protected: void render_triangles(const Selection& selection) const; void render_cursor() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index b137dd5c1..ed4f2809c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -46,7 +46,7 @@ std::string GLGizmoSeam::on_get_name() const -void GLGizmoSeam::on_render() const +void GLGizmoSeam::render_painter_gizmo() const { const Selection& selection = m_parent.get_selection(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp index c3eb98c80..dfadec0da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -13,6 +13,8 @@ public: GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoPainterBase(parent, icon_filename, sprite_id) {} + void render_painter_gizmo() const override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; @@ -20,8 +22,6 @@ protected: private: bool on_init() override; - void on_render() const override; - void on_render_for_picking() const override {} void update_model_object() const override; void update_from_model_object() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a25d9105f..a34c7562e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -295,11 +295,9 @@ void ObjectClipper::on_update() if (has_hollowed) meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); - if (meshes.empty()) { + if (meshes.empty()) for (const ModelVolume* mv : mo->volumes) - if (mv->is_model_part()) - meshes.push_back(&mv->mesh()); - } + meshes.push_back(&mv->mesh()); if (meshes != m_old_meshes) { m_clippers.clear(); @@ -335,9 +333,6 @@ void ObjectClipper::render_cut() const size_t clipper_id = 0; for (const ModelVolume* mv : mo->volumes) { - if (! mv->is_model_part()) - continue; - Geometry::Transformation vol_trafo = mv->get_transformation(); Geometry::Transformation trafo = inst_trafo * vol_trafo; trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1087c64d5..54ae2de6e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -437,6 +437,19 @@ void GLGizmosManager::render_current_gizmo() const m_gizmos[m_current]->render(); } +void GLGizmosManager::render_painter_gizmo() const +{ + // This function shall only be called when current gizmo is + // derived from GLGizmoPainterBase. + + if (!m_enabled || m_current == Undefined) + return; + + auto* gizmo = dynamic_cast(get_current()); + assert(gizmo); // check the precondition + gizmo->render_painter_gizmo(); +} + void GLGizmosManager::render_current_gizmo_for_picking_pass() const { if (! m_enabled || m_current == Undefined) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 6b965525d..7f47167e9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -212,6 +212,7 @@ public: void render_current_gizmo() const; void render_current_gizmo_for_picking_pass() const; + void render_painter_gizmo() const; void render_overlay() const;