From 13604469fdd9a255f2d0790ee516215e44b7a26e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 7 Aug 2021 08:12:32 +0200 Subject: [PATCH] Painting gizmos and sinking objects: 1. Do not show what's below the bed when a gizmo is open 2. Triangulate the cut so people don't see inside 3. If regular clipping plane is used, the cuts are mutually clipped by one another 4. Painting itself ignores the hidden part of the object --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 64 ++++++++++++- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 + src/slic3r/GUI/MeshUtils.cpp | 94 ++++++++++++++++---- src/slic3r/GUI/MeshUtils.hpp | 6 ++ 5 files changed, 152 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 68f0f3f99..4f3e31bea 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 115a675ac..18ce9d73c 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 6f59dc95e..228f5b58c 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 073bb6f78..d889bc538 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 07b01e27f..ec6c337c0 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;