diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 992f4912a..000ddf8c9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; - if (is_point_clipped(support_point.pos.cast())) + if (is_mesh_point_clipped(support_point.pos.cast())) continue; // First decide about the color of the point. @@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) -bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const +bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { if (m_clipping_plane_distance == 0.f) return false; Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; - return m_clipping_plane->distance(transformed_point) < 0.; + return m_clipping_plane->is_point_clipped(transformed_point); } @@ -391,27 +391,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair hits; - std::vector normals; - m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals); - - // We must also take care of the clipping plane (if active) - unsigned i = 0; - if (m_clipping_plane_distance != 0.f) { - for (i=0; i())) - break; + Vec3f hit; + Vec3f normal; + if (m_mesh_raycaster->unproject_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); + return true; } - - if (i==hits.size() || (hits.size()-i) % 2 != 0) { - // All hits are either clipped, or there is an odd number of unclipped - // hits - meaning the nearest must be from inside the mesh. + else return false; - } - - // Calculate and return both the point and the facet normal. - pos_and_normal = std::make_pair(hits[i], normals[i]); - return true; } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. @@ -481,19 +469,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous std::vector points_inside; std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); for (size_t idx : points_idxs) - points_inside.push_back((trafo.get_matrix() * points[idx]).cast()); + points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, - [this](const Vec3f& pt) { return is_point_clipped(pt.cast()); })) + for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) { - const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point; - if (! is_point_clipped(support_point.pos.cast())) { - if (rectangle_status == GLSelectionRectangle::Deselect) - unselect_point(points_idxs[idx]); - else - select_point(points_idxs[idx]); - } + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); } return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index cf5245f19..15b2df80e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -125,7 +125,7 @@ private: mutable std::unique_ptr m_supports_clipper; std::vector get_config_options(const std::vector& keys) const; - bool is_point_clipped(const Vec3d& point) const; + bool is_mesh_point_clipped(const Vec3d& point) const; //void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index e559020e9..62a6813a6 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -152,8 +152,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const } -bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, - const Camera& camera, std::vector* positions, std::vector* normals) const +bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const { const std::array& viewport = camera.get_viewport(); const Transform3d& model_mat = camera.get_view_matrix(); @@ -179,25 +179,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - // Now stuff the points in the provided vector and calculate normals if asked about them: - if (positions != nullptr) { - positions->clear(); - if (normals != nullptr) - normals->clear(); - for (const igl::Hit& hit : hits) { - positions->push_back(m_AABB_wrapper->get_hit_pos(hit)); + unsigned i = 0; - if (normals != nullptr) - normals->push_back(m_AABB_wrapper->get_hit_normal(hit)); + // Remove points that are obscured or cut by the clipping plane + if (clipping_plane) { + for (i=0; iis_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast())) + break; + + if (i==hits.size() || (hits.size()-i) % 2 != 0) { + // All hits are either clipped, or there is an odd number of unclipped + // hits - meaning the nearest must be from inside the mesh. + return false; } } + // Now stuff the points in the provided vector and calculate normals if asked about them: + position = m_AABB_wrapper->get_hit_pos(hits[i]); + normal = m_AABB_wrapper->get_hit_normal(hits[i]); return true; } std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector& points, - std::function fn_ignore_hit) const + const ClippingPlane* clipping_plane) const { std::vector out; @@ -206,19 +211,24 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast() * direction_to_camera).normalized().eval(); Vec3f scaling = trafo.get_scaling_factor().cast(); direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2)); + const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast(); for (size_t i=0; iis_point_clipped(pt.cast())) + continue; + bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: std::vector hits; - // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. + // Offset the start of the ray by EPSILON to account for numerical inaccuracies. if (m_AABB_wrapper->m_AABB.intersect_ray( AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { + inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); + // If the closest hit facet normal points in the same direction as the ray, // we are looking through the mesh and should therefore discard the point: if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f) @@ -227,11 +237,12 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo // Eradicate all hits that the caller wants to ignore for (unsigned j=0; jget_hit_pos(hit))) { + if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast())) { hits.erase(hits.begin()+j); --j; } } + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. // Also, the threshold is in mesh coordinates, not in actual dimensions. if (! hits.empty()) diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index a2be2677b..e4c4c20d2 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -50,6 +50,7 @@ public: return (-get_normal().dot(pt) + m_data[3]); } + bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; } void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); } void set_offset(double offset) { m_data[3] = offset; } Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); } @@ -98,10 +99,10 @@ public: void set_camera(const Camera& camera); bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - std::vector* positions = nullptr, std::vector* normals = nullptr) const; + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const; std::vector get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, - const std::vector& points, std::function fn_ignore_hit) const; + const std::vector& points, const ClippingPlane* clipping_plane = nullptr) const; Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;