1 #include "MeshUtils.hpp"
2
3 #include "libslic3r/Tesselate.hpp"
4 #include "libslic3r/TriangleMesh.hpp"
5
6 #include "slic3r/GUI/Camera.hpp"
7
8 #include <GL/glew.h>
9
10
11 namespace Slic3r {
12 namespace GUI {
13
set_plane(const ClippingPlane & plane)14 void MeshClipper::set_plane(const ClippingPlane& plane)
15 {
16 if (m_plane != plane) {
17 m_plane = plane;
18 m_triangles_valid = false;
19 }
20 }
21
22
23
set_mesh(const TriangleMesh & mesh)24 void MeshClipper::set_mesh(const TriangleMesh& mesh)
25 {
26 if (m_mesh != &mesh) {
27 m_mesh = &mesh;
28 m_triangles_valid = false;
29 m_triangles2d.resize(0);
30 m_tms.reset(nullptr);
31 }
32 }
33
34
35
set_transformation(const Geometry::Transformation & trafo)36 void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
37 {
38 if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
39 m_trafo = trafo;
40 m_triangles_valid = false;
41 m_triangles2d.resize(0);
42 }
43 }
44
45
46
render_cut()47 void MeshClipper::render_cut()
48 {
49 if (! m_triangles_valid)
50 recalculate_triangles();
51
52 if (m_vertex_array.has_VBOs())
53 m_vertex_array.render();
54 }
55
56
57
recalculate_triangles()58 void MeshClipper::recalculate_triangles()
59 {
60 if (! m_tms) {
61 m_tms.reset(new TriangleMeshSlicer);
62 m_tms->init(m_mesh, [](){});
63 }
64
65 const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
66 const Vec3f& scaling = m_trafo.get_scaling_factor().cast<float>();
67 // Calculate clipping plane normal in mesh coordinates.
68 Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
69 Vec3d up (up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
70 // Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
71 float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
72
73 // Now do the cutting
74 std::vector<ExPolygons> list_of_expolys;
75 m_tms->set_up_direction(up.cast<float>());
76 m_tms->slice(std::vector<float>{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){});
77 m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.);
78
79 // Rotate the cut into world coords:
80 Eigen::Quaterniond q;
81 q.setFromTwoVectors(Vec3d::UnitZ(), up);
82 Transform3d tr = Transform3d::Identity();
83 tr.rotate(q);
84 tr = m_trafo.get_matrix() * tr;
85
86 // to avoid z-fighting
87 height_mesh += 0.001f;
88
89 m_vertex_array.release_geometry();
90 for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) {
91 m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up);
92 m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up);
93 m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up);
94 size_t idx = it - m_triangles2d.cbegin();
95 m_vertex_array.push_triangle(idx, idx+1, idx+2);
96 }
97 m_vertex_array.finalize_geometry(true);
98
99 m_triangles_valid = true;
100 }
101
102
get_triangle_normal(size_t facet_idx) const103 Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
104 {
105 return m_normals[facet_idx];
106 }
107
line_from_mouse_pos(const Vec2d & mouse_pos,const Transform3d & trafo,const Camera & camera,Vec3d & point,Vec3d & direction) const108 void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
109 Vec3d& point, Vec3d& direction) const
110 {
111 const std::array<int, 4>& viewport = camera.get_viewport();
112 const Transform3d& model_mat = camera.get_view_matrix();
113 const Transform3d& proj_mat = camera.get_projection_matrix();
114
115 Vec3d pt1;
116 Vec3d pt2;
117 ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0., model_mat.data(), proj_mat.data(), viewport.data(), &pt1(0), &pt1(1), &pt1(2));
118 ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1., model_mat.data(), proj_mat.data(), viewport.data(), &pt2(0), &pt2(1), &pt2(2));
119
120 Transform3d inv = trafo.inverse();
121 pt1 = inv * pt1;
122 pt2 = inv * pt2;
123
124 point = pt1;
125 direction = pt2-pt1;
126 }
127
128
unproject_on_mesh(const Vec2d & mouse_pos,const Transform3d & trafo,const Camera & camera,Vec3f & position,Vec3f & normal,const ClippingPlane * clipping_plane,size_t * facet_idx) const129 bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
130 Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
131 size_t* facet_idx) const
132 {
133 Vec3d point;
134 Vec3d direction;
135 line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
136
137 std::vector<sla::IndexedMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
138
139 if (hits.empty())
140 return false; // no intersection found
141
142 unsigned i = 0;
143
144 // Remove points that are obscured or cut by the clipping plane
145 if (clipping_plane) {
146 for (i=0; i<hits.size(); ++i)
147 if (! clipping_plane->is_point_clipped(trafo * hits[i].position()))
148 break;
149
150 if (i==hits.size() || (hits.size()-i) % 2 != 0) {
151 // All hits are either clipped, or there is an odd number of unclipped
152 // hits - meaning the nearest must be from inside the mesh.
153 return false;
154 }
155 }
156
157 // Now stuff the points in the provided vector and calculate normals if asked about them:
158 position = hits[i].position().cast<float>();
159 normal = hits[i].normal().cast<float>();
160
161 if (facet_idx)
162 *facet_idx = hits[i].face();
163
164 return true;
165 }
166
167
get_unobscured_idxs(const Geometry::Transformation & trafo,const Camera & camera,const std::vector<Vec3f> & points,const ClippingPlane * clipping_plane) const168 std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
169 const ClippingPlane* clipping_plane) const
170 {
171 std::vector<unsigned> out;
172
173 const Transform3d& instance_matrix_no_translation_no_scaling = trafo.get_matrix(true,false,true);
174 Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
175 Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
176 Vec3f scaling = trafo.get_scaling_factor().cast<float>();
177 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));
178 const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
179
180 for (size_t i=0; i<points.size(); ++i) {
181 const Vec3f& pt = points[i];
182 if (clipping_plane && clipping_plane->is_point_clipped(pt.cast<double>()))
183 continue;
184
185 bool is_obscured = false;
186 // Cast a ray in the direction of the camera and look for intersection with the mesh:
187 std::vector<sla::IndexedMesh::hit_result> hits;
188 // Offset the start of the ray by EPSILON to account for numerical inaccuracies.
189 hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast<double>(),
190 direction_to_camera.cast<double>());
191
192
193 if (! hits.empty()) {
194 // If the closest hit facet normal points in the same direction as the ray,
195 // we are looking through the mesh and should therefore discard the point:
196 if (hits.front().normal().dot(direction_to_camera_mesh.cast<double>()) > 0)
197 is_obscured = true;
198
199 // Eradicate all hits that the caller wants to ignore
200 for (unsigned j=0; j<hits.size(); ++j) {
201 if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * hits[j].position())) {
202 hits.erase(hits.begin()+j);
203 --j;
204 }
205 }
206
207 // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
208 // Also, the threshold is in mesh coordinates, not in actual dimensions.
209 if (! hits.empty())
210 is_obscured = true;
211 }
212 if (! is_obscured)
213 out.push_back(i);
214 }
215 return out;
216 }
217
218
get_closest_point(const Vec3f & point,Vec3f * normal) const219 Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
220 {
221 int idx = 0;
222 Vec3d closest_point;
223 m_emesh.squared_distance(point.cast<double>(), idx, closest_point);
224 if (normal)
225 *normal = m_normals[idx];
226
227 return closest_point.cast<float>();
228 }
229
230
231
232 } // namespace GUI
233 } // namespace Slic3r
234