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
This commit is contained in:
Lukas Matena 2021-08-07 08:12:32 +02:00
parent b174dacc85
commit 13604469fd
5 changed files with 152 additions and 17 deletions

View file

@ -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));

View file

@ -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<double>::max()));
std::vector<const TriangleMesh*> 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();

View file

@ -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<const TriangleMesh*> m_old_meshes;
std::vector<std::unique_ptr<MeshClipper>> m_clippers;
};

View file

@ -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<ExPolygons> neg_polys = slice_mesh_ex(m_negative_mesh->its, std::vector<float>{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<coord_t>::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; i<hits.size(); ++i)
if (! clipping_plane->is_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<hits.size(); ++i) {
Vec3d transformed_hit = trafo * hits[i].position();
if (transformed_hit.z() >= 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:

View file

@ -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<Vec2f> m_triangles2d;
GLIndexedVertexArray m_vertex_array;
bool m_triangles_valid = false;