diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 68f0f3f993..4f3e31beaa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -67,8 +67,8 @@ void GLGizmoFdmSupports::render_painter_gizmo() const glsafe(::glEnable(GL_DEPTH_TEST)); render_triangles(selection); - m_c->object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); render_cursor(); glsafe(::glDisable(GL_BLEND)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 115a675aca..18ce9d73c4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -150,6 +150,30 @@ void InstancesHider::on_update() canvas->toggle_model_objects_visibility(false); canvas->toggle_model_objects_visibility(true, mo, active_inst); canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + canvas->set_use_clipping_planes(true); + // Some objects may be sinking, do not show whatever is below the bed. + canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), 0.)); + canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), std::numeric_limits::max())); + + + std::vector meshes; + for (const ModelVolume* mv : mo->volumes) + meshes.push_back(&mv->mesh()); + + if (meshes != m_old_meshes) { + m_clippers.clear(); + for (const TriangleMesh* mesh : meshes) { + m_clippers.emplace_back(new MeshClipper); + if (mo->get_instance_min_z(active_inst) < SINKING_Z_THRESHOLD) + m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), 0.)); + else { + m_clippers.back()->set_plane(ClippingPlane::ClipsNothing()); + m_clippers.back()->set_limiting_plane(ClippingPlane::ClipsNothing()); + } + m_clippers.back()->set_mesh(*mesh); + } + m_old_meshes = meshes; + } } else canvas->toggle_model_objects_visibility(true); @@ -158,6 +182,9 @@ void InstancesHider::on_update() void InstancesHider::on_release() { get_pool()->get_canvas()->toggle_model_objects_visibility(true); + get_pool()->get_canvas()->set_use_clipping_planes(false); + m_old_meshes.clear(); + m_clippers.clear(); } void InstancesHider::show_supports(bool show) { @@ -167,6 +194,38 @@ void InstancesHider::show_supports(bool show) { } } +void InstancesHider::render_cut() const +{ + const SelectionInfo* sel_info = get_pool()->selection_info(); + const ModelObject* mo = sel_info->model_object(); + Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + + size_t clipper_id = 0; + for (const ModelVolume* mv : mo->volumes) { + 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())); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_transformation(trafo); + const ObjectClipper* obj_clipper = get_pool()->object_clipper(); + if (obj_clipper->is_valid() && obj_clipper->get_clipping_plane() + && obj_clipper->get_position() != 0.) { + ClippingPlane clp = *get_pool()->object_clipper()->get_clipping_plane(); + clp.set_normal(-clp.get_normal()); + clipper->set_limiting_plane(clp); + } else + clipper->set_limiting_plane(ClippingPlane::ClipsNothing()); + + glsafe(::glPushMatrix()); + glsafe(::glColor3f(0.8f, 0.3f, 0.0f)); + clipper->render_cut(); + glsafe(::glPopMatrix()); + + ++clipper_id; + } +} + void HollowedMesh::on_update() @@ -348,6 +407,7 @@ void ObjectClipper::render_cut() const const SelectionInfo* sel_info = get_pool()->selection_info(); const ModelObject* mo = sel_info->model_object(); Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + const bool sinking = mo->bounding_box().min.z() < SINKING_Z_THRESHOLD; size_t clipper_id = 0; for (const ModelVolume* mv : mo->volumes) { @@ -358,7 +418,9 @@ void ObjectClipper::render_cut() const auto& clipper = m_clippers[clipper_id]; clipper->set_plane(*m_clp); clipper->set_transformation(trafo); - + clipper->set_limiting_plane(sinking ? + ClippingPlane(Vec3d::UnitZ(), 0.) + : ClippingPlane::ClipsNothing()); glsafe(::glPushMatrix()); glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); clipper->render_cut(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 6f59dc95ed..228f5b58c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -180,6 +180,7 @@ public: void show_supports(bool show); bool are_supports_shown() const { return m_show_supports; } + void render_cut() const; protected: void on_update() override; @@ -187,6 +188,8 @@ protected: private: bool m_show_supports = false; + std::vector m_old_meshes; + std::vector> m_clippers; }; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 073bb6f78d..d889bc538b 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -24,6 +24,15 @@ void MeshClipper::set_plane(const ClippingPlane& plane) } +void MeshClipper::set_limiting_plane(const ClippingPlane& plane) +{ + if (m_limiting_plane != plane) { + m_limiting_plane = plane; + m_triangles_valid = false; + } +} + + void MeshClipper::set_mesh(const TriangleMesh& mesh) { @@ -89,18 +98,71 @@ void MeshClipper::recalculate_triangles() std::vector neg_polys = slice_mesh_ex(m_negative_mesh->its, std::vector{height_mesh}, slicing_params); list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front()); } - - m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); - // Rotate the cut into world coords: + // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d::UnitZ(), up); Transform3d tr = Transform3d::Identity(); tr.rotate(q); tr = m_trafo.get_matrix() * tr; + height_mesh += 0.001f; // to avoid z-fighting - // to avoid z-fighting - height_mesh += 0.001f; + if (m_limiting_plane != ClippingPlane::ClipsNothing()) + { + // Now remove whatever ended up below the limiting plane (e.g. sinking objects). + // First transform the limiting plane from world to mesh coords. + // Note that inverse of tr transforms the plane from world to horizontal. + Vec3d normal_old = m_limiting_plane.get_normal().normalized(); + Vec3d normal_new = (tr.matrix().block<3,3>(0,0).transpose() * normal_old).normalized(); + + // normal_new should now be the plane normal in mesh coords. To find the offset, + // transform a point and set offset so it belongs to the transformed plane. + Vec3d pt = Vec3d::Zero(); + double plane_offset = m_limiting_plane.get_data()[3]; + if (std::abs(normal_old.z()) > 0.5) // normal is normalized, at least one of the coords if larger than sqrt(3)/3 = 0.57 + pt.z() = - plane_offset / normal_old.z(); + else if (std::abs(normal_old.y()) > 0.5) + pt.y() = - plane_offset / normal_old.y(); + else + pt.x() = - plane_offset / normal_old.x(); + pt = tr.inverse() * pt; + double offset = -(normal_new.dot(pt)); + + if (std::abs(normal_old.dot(m_plane.get_normal().normalized())) > 0.99) { + // The cuts are parallel, show all or nothing. + if (offset < height_mesh) + list_of_expolys.front().clear(); + } else { + // The cut is a horizontal plane defined by z=height_mesh. + // ax+by+e=0 is the line of intersection with the limiting plane. + // Normalized so a^2 + b^2 = 1. + double len = std::hypot(normal_new.x(), normal_new.y()); + if (len == 0.) + return; + double a = normal_new.x() / len; + double b = normal_new.y() / len; + double e = (normal_new.z() * height_mesh + offset) / len; + if (b == 0.) + return; + + // We need a half-plane to limit the cut. Get angle of the intersecting line. + double angle = std::atan(-a/b); + if (b > 0) // select correct half-plane + angle += M_PI; + + // We'll take a big rectangle above x-axis and rotate and translate + // it so it lies on our line. This will be the figure to subtract + // from the cut. The coordinates must not overflow after the transform, + // make the rectangle a bit smaller. + coord_t size = (std::numeric_limits::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; + ExPolygons ep {ExPolygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; + ep.front().rotate(angle); + ep.front().translate(scale_(-e * a), scale_(-e * b)); + list_of_expolys.front() = diff_ex(list_of_expolys.front(), ep.front()); + } + } + + m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); m_vertex_array.release_geometry(); for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { @@ -159,17 +221,19 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& unsigned i = 0; - // Remove points that are obscured or cut by the clipping plane - if (clipping_plane) { - for (i=0; iis_point_clipped(trafo * hits[i].position())) - break; + // Remove points that are obscured or cut by the clipping plane. + // Also, remove anything below the bed (sinking objects). + for (i=0; i= 0. && + (! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit))) + 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; - } + 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: diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 07b01e27f6..ec6c337c06 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -71,6 +71,11 @@ public: // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); + // In case the object is clipped by two planes (e.g. in case of sinking + // objects), this will be used to clip the triagnulated cut. + // Pass ClippingPlane::ClipsNothing to turn this off. + void set_limiting_plane(const ClippingPlane& plane); + // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. void set_mesh(const TriangleMesh& mesh); @@ -92,6 +97,7 @@ private: const TriangleMesh* m_mesh = nullptr; const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; + ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array; bool m_triangles_valid = false;