1 #include <GL/glew.h>
2
3 #if ENABLE_SMOOTH_NORMALS
4 #include <igl/per_face_normals.h>
5 #include <igl/per_corner_normals.h>
6 #include <igl/per_vertex_normals.h>
7 #endif // ENABLE_SMOOTH_NORMALS
8
9 #include "3DScene.hpp"
10 #include "GLShader.hpp"
11 #include "GUI_App.hpp"
12 #if ENABLE_ENVIRONMENT_MAP
13 #include "Plater.hpp"
14 #endif // ENABLE_ENVIRONMENT_MAP
15
16 #include "libslic3r/ExtrusionEntity.hpp"
17 #include "libslic3r/ExtrusionEntityCollection.hpp"
18 #include "libslic3r/Geometry.hpp"
19 #include "libslic3r/Print.hpp"
20 #include "libslic3r/SLAPrint.hpp"
21 #include "libslic3r/Slicing.hpp"
22 #include "slic3r/GUI/BitmapCache.hpp"
23 #include "libslic3r/Format/STL.hpp"
24 #include "libslic3r/Utils.hpp"
25 #include "libslic3r/AppConfig.hpp"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31
32 #include <boost/log/trivial.hpp>
33
34 #include <boost/filesystem/operations.hpp>
35 #include <boost/algorithm/string/predicate.hpp>
36
37 #include <boost/nowide/cstdio.hpp>
38
39 #include <Eigen/Dense>
40
41 #ifdef HAS_GLSAFE
glAssertRecentCallImpl(const char * file_name,unsigned int line,const char * function_name)42 void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name)
43 {
44 #if defined(NDEBUG) && ENABLE_OPENGL_ERROR_LOGGING
45 // In release mode, if OpenGL debugging was forced by ENABLE_OPENGL_ERROR_LOGGING, only show
46 // OpenGL errors if sufficiently high loglevel.
47 if (Slic3r::get_logging_level() < 5)
48 return;
49 #endif // ENABLE_OPENGL_ERROR_LOGGING
50
51 GLenum err = glGetError();
52 if (err == GL_NO_ERROR)
53 return;
54 const char* sErr = 0;
55 switch (err) {
56 case GL_INVALID_ENUM: sErr = "Invalid Enum"; break;
57 case GL_INVALID_VALUE: sErr = "Invalid Value"; break;
58 // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd
59 case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break;
60 case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break;
61 case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break;
62 case GL_OUT_OF_MEMORY: sErr = "Out Of Memory"; break;
63 default: sErr = "Unknown"; break;
64 }
65 BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr;
66 assert(false);
67 }
68 #endif // HAS_GLSAFE
69
70 namespace Slic3r {
71
72 #if ENABLE_SMOOTH_NORMALS
smooth_normals_corner(TriangleMesh & mesh,std::vector<stl_normal> & normals)73 static void smooth_normals_corner(TriangleMesh& mesh, std::vector<stl_normal>& normals)
74 {
75 mesh.repair();
76
77 using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
78 using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
79
80 std::vector<stl_normal> face_normals(mesh.stl.stats.number_of_facets);
81 for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
82 face_normals[i] = mesh.stl.facet_start[i].normal;
83 }
84
85 Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(),
86 Eigen::Index(mesh.its.vertices.size()), 3).cast<double>();
87 Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(),
88 Eigen::Index(mesh.its.indices.size()), 3);
89 Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(),
90 Eigen::Index(face_normals.size()), 3).cast<double>();
91 Eigen::MatrixXd out_normals;
92
93 igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals);
94
95 normals = std::vector<stl_normal>(mesh.its.vertices.size());
96 for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
97 for (size_t j = 0; j < 3; ++j) {
98 normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast<float>();
99 }
100 }
101 }
102
smooth_normals_vertex(TriangleMesh & mesh,std::vector<stl_normal> & normals)103 static void smooth_normals_vertex(TriangleMesh& mesh, std::vector<stl_normal>& normals)
104 {
105 mesh.repair();
106
107 using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
108 using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
109
110 Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(),
111 Eigen::Index(mesh.its.vertices.size()), 3).cast<double>();
112 Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(),
113 Eigen::Index(mesh.its.indices.size()), 3);
114 Eigen::MatrixXd out_normals;
115
116 // igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_UNIFORM, out_normals);
117 // igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_AREA, out_normals);
118 igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_ANGLE, out_normals);
119 // igl::per_vertex_normals(vertices, indices, igl::PER_VERTEX_NORMALS_WEIGHTING_TYPE_DEFAULT, out_normals);
120
121 normals = std::vector<stl_normal>(mesh.its.vertices.size());
122 for (size_t i = 0; i < static_cast<size_t>(out_normals.rows()); ++i) {
123 normals[i] = out_normals.row(i).cast<float>();
124 }
125 }
126 #endif // ENABLE_SMOOTH_NORMALS
127
128 #if ENABLE_SMOOTH_NORMALS
load_mesh_full_shading(const TriangleMesh & mesh,bool smooth_normals)129 void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh, bool smooth_normals)
130 #else
131 void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh)
132 #endif // ENABLE_SMOOTH_NORMALS
133 {
134 assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
135 assert(quad_indices.empty() && triangle_indices_size == 0);
136 assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
137
138 #if ENABLE_SMOOTH_NORMALS
139 if (smooth_normals) {
140 TriangleMesh new_mesh(mesh);
141 std::vector<stl_normal> normals;
142 smooth_normals_corner(new_mesh, normals);
143 // smooth_normals_vertex(new_mesh, normals);
144
145 this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 2 * new_mesh.its.vertices.size());
146 for (size_t i = 0; i < new_mesh.its.vertices.size(); ++i) {
147 const stl_vertex& v = new_mesh.its.vertices[i];
148 const stl_normal& n = normals[i];
149 this->push_geometry(v(0), v(1), v(2), n(0), n(1), n(2));
150 }
151
152 for (size_t i = 0; i < new_mesh.its.indices.size(); ++i) {
153 const stl_triangle_vertex_indices& idx = new_mesh.its.indices[i];
154 this->push_triangle(idx(0), idx(1), idx(2));
155 }
156 }
157 else {
158 #endif // ENABLE_SMOOTH_NORMALS
159 this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
160
161 unsigned int vertices_count = 0;
162 for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
163 const stl_facet& facet = mesh.stl.facet_start[i];
164 for (int j = 0; j < 3; ++j)
165 this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
166
167 this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
168 vertices_count += 3;
169 }
170 #if ENABLE_SMOOTH_NORMALS
171 }
172 #endif // ENABLE_SMOOTH_NORMALS
173 }
174
finalize_geometry(bool opengl_initialized)175 void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
176 {
177 assert(this->vertices_and_normals_interleaved_VBO_id == 0);
178 assert(this->triangle_indices_VBO_id == 0);
179 assert(this->quad_indices_VBO_id == 0);
180
181 if (! opengl_initialized) {
182 // Shrink the data vectors to conserve memory in case the data cannot be transfered to the OpenGL driver yet.
183 this->shrink_to_fit();
184 return;
185 }
186
187 if (! this->vertices_and_normals_interleaved.empty()) {
188 glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
189 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
190 glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
191 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
192 this->vertices_and_normals_interleaved.clear();
193 }
194 if (! this->triangle_indices.empty()) {
195 glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
196 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
197 glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
198 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
199 this->triangle_indices.clear();
200 }
201 if (! this->quad_indices.empty()) {
202 glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
203 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
204 glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
205 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
206 this->quad_indices.clear();
207 }
208 }
209
release_geometry()210 void GLIndexedVertexArray::release_geometry()
211 {
212 if (this->vertices_and_normals_interleaved_VBO_id) {
213 glsafe(::glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
214 this->vertices_and_normals_interleaved_VBO_id = 0;
215 }
216 if (this->triangle_indices_VBO_id) {
217 glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id));
218 this->triangle_indices_VBO_id = 0;
219 }
220 if (this->quad_indices_VBO_id) {
221 glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id));
222 this->quad_indices_VBO_id = 0;
223 }
224 this->clear();
225 }
226
render() const227 void GLIndexedVertexArray::render() const
228 {
229 assert(this->vertices_and_normals_interleaved_VBO_id != 0);
230 assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
231
232 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
233 glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
234 glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
235
236 glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
237 glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
238
239 // Render using the Vertex Buffer Objects.
240 if (this->triangle_indices_size > 0) {
241 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
242 glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
243 glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
244 }
245 if (this->quad_indices_size > 0) {
246 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
247 glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
248 glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
249 }
250
251 glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
252 glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
253
254 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
255 }
256
render(const std::pair<size_t,size_t> & tverts_range,const std::pair<size_t,size_t> & qverts_range) const257 void GLIndexedVertexArray::render(
258 const std::pair<size_t, size_t>& tverts_range,
259 const std::pair<size_t, size_t>& qverts_range) const
260 {
261 assert(this->vertices_and_normals_interleaved_VBO_id != 0);
262 assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
263
264 // Render using the Vertex Buffer Objects.
265 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
266 glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
267 glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
268
269 glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
270 glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
271
272 if (this->triangle_indices_size > 0) {
273 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
274 glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
275 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
276 }
277 if (this->quad_indices_size > 0) {
278 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
279 glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
280 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
281 }
282
283 glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
284 glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
285
286 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
287 }
288
289 const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
290 const float GLVolume::HOVER_SELECT_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
291 const float GLVolume::HOVER_DESELECT_COLOR[4] = { 1.0f, 0.75f, 0.75f, 1.0f };
292 const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
293 const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
294 const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f };
295 const float GLVolume::MODEL_COLOR[4][4] = {
296 { 1.0f, 1.0f, 0.0f, 1.f },
297 { 1.0f, 0.5f, 0.5f, 1.f },
298 { 0.5f, 1.0f, 0.5f, 1.f },
299 { 0.5f, 0.5f, 1.0f, 1.f }
300 };
301 const float GLVolume::SLA_SUPPORT_COLOR[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
302 const float GLVolume::SLA_PAD_COLOR[4] = { 0.0f, 0.2f, 0.0f, 1.0f };
303 const float GLVolume::NEUTRAL_COLOR[4] = { 0.9f, 0.9f, 0.9f, 1.0f };
304
GLVolume(float r,float g,float b,float a)305 GLVolume::GLVolume(float r, float g, float b, float a)
306 : m_transformed_bounding_box_dirty(true)
307 , m_sla_shift_z(0.0)
308 , m_transformed_convex_hull_bounding_box_dirty(true)
309 // geometry_id == 0 -> invalid
310 , geometry_id(std::pair<size_t, size_t>(0, 0))
311 , extruder_id(0)
312 , selected(false)
313 , disabled(false)
314 , printable(true)
315 , is_active(true)
316 , zoom_to_volumes(true)
317 , shader_outside_printer_detection_enabled(false)
318 , is_outside(false)
319 , hover(HS_None)
320 , is_modifier(false)
321 , is_wipe_tower(false)
322 , is_extrusion_path(false)
323 , force_transparent(false)
324 , force_native_color(false)
325 , force_neutral_color(false)
326 , tverts_range(0, size_t(-1))
327 , qverts_range(0, size_t(-1))
328 {
329 color[0] = r;
330 color[1] = g;
331 color[2] = b;
332 color[3] = a;
333 set_render_color(r, g, b, a);
334 }
335
set_render_color(float r,float g,float b,float a)336 void GLVolume::set_render_color(float r, float g, float b, float a)
337 {
338 render_color[0] = r;
339 render_color[1] = g;
340 render_color[2] = b;
341 render_color[3] = a;
342 }
343
set_render_color(const float * rgba,unsigned int size)344 void GLVolume::set_render_color(const float* rgba, unsigned int size)
345 {
346 ::memcpy((void*)render_color, (const void*)rgba, (size_t)(std::min((unsigned int)4, size) * sizeof(float)));
347 }
348
set_render_color()349 void GLVolume::set_render_color()
350 {
351 if (force_native_color || force_neutral_color)
352 {
353 if (is_outside && shader_outside_printer_detection_enabled)
354 set_render_color(OUTSIDE_COLOR, 4);
355 else {
356 if (force_native_color)
357 set_render_color(color, 4);
358 else
359 set_render_color(NEUTRAL_COLOR, 4);
360 }
361 }
362 else {
363 if (hover == HS_Select)
364 set_render_color(HOVER_SELECT_COLOR, 4);
365 else if (hover == HS_Deselect)
366 set_render_color(HOVER_DESELECT_COLOR, 4);
367 else if (selected)
368 set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
369 else if (disabled)
370 set_render_color(DISABLED_COLOR, 4);
371 else if (is_outside && shader_outside_printer_detection_enabled)
372 set_render_color(OUTSIDE_COLOR, 4);
373 else
374 set_render_color(color, 4);
375 }
376
377 if (!printable)
378 {
379 render_color[0] /= 4;
380 render_color[1] /= 4;
381 render_color[2] /= 4;
382 }
383
384 if (force_transparent)
385 render_color[3] = color[3];
386 }
387
set_color_from_model_volume(const ModelVolume * model_volume)388 void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume)
389 {
390 if (model_volume->is_modifier()) {
391 color[0] = 0.2f;
392 color[1] = 1.0f;
393 color[2] = 0.2f;
394 }
395 else if (model_volume->is_support_blocker()) {
396 color[0] = 1.0f;
397 color[1] = 0.2f;
398 color[2] = 0.2f;
399 }
400 else if (model_volume->is_support_enforcer()) {
401 color[0] = 0.2f;
402 color[1] = 0.2f;
403 color[2] = 1.0f;
404 }
405 color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
406 }
407
world_matrix() const408 Transform3d GLVolume::world_matrix() const
409 {
410 Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
411 m.translation()(2) += m_sla_shift_z;
412 return m;
413 }
414
is_left_handed() const415 bool GLVolume::is_left_handed() const
416 {
417 const Vec3d &m1 = m_instance_transformation.get_mirror();
418 const Vec3d &m2 = m_volume_transformation.get_mirror();
419 return m1.x() * m1.y() * m1.z() * m2.x() * m2.y() * m2.z() < 0.;
420 }
421
transformed_bounding_box() const422 const BoundingBoxf3& GLVolume::transformed_bounding_box() const
423 {
424 const BoundingBoxf3& box = bounding_box();
425 assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
426
427 if (m_transformed_bounding_box_dirty)
428 {
429 m_transformed_bounding_box = box.transformed(world_matrix());
430 m_transformed_bounding_box_dirty = false;
431 }
432
433 return m_transformed_bounding_box;
434 }
435
transformed_convex_hull_bounding_box() const436 const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
437 {
438 if (m_transformed_convex_hull_bounding_box_dirty)
439 m_transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
440 return m_transformed_convex_hull_bounding_box;
441 }
442
transformed_convex_hull_bounding_box(const Transform3d & trafo) const443 BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
444 {
445 return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
446 m_convex_hull->transformed_bounding_box(trafo) :
447 bounding_box().transformed(trafo);
448 }
449
set_range(double min_z,double max_z)450 void GLVolume::set_range(double min_z, double max_z)
451 {
452 this->qverts_range.first = 0;
453 this->qverts_range.second = this->indexed_vertex_array.quad_indices_size;
454 this->tverts_range.first = 0;
455 this->tverts_range.second = this->indexed_vertex_array.triangle_indices_size;
456 if (! this->print_zs.empty()) {
457 // The Z layer range is specified.
458 // First test whether the Z span of this object is not out of (min_z, max_z) completely.
459 if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) {
460 this->qverts_range.second = 0;
461 this->tverts_range.second = 0;
462 } else {
463 // Then find the lowest layer to be displayed.
464 size_t i = 0;
465 for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i);
466 if (i == this->print_zs.size()) {
467 // This shall not happen.
468 this->qverts_range.second = 0;
469 this->tverts_range.second = 0;
470 } else {
471 // Remember start of the layer.
472 this->qverts_range.first = this->offsets[i * 2];
473 this->tverts_range.first = this->offsets[i * 2 + 1];
474 // Some layers are above $min_z. Which?
475 for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++ i);
476 if (i < this->print_zs.size()) {
477 this->qverts_range.second = this->offsets[i * 2];
478 this->tverts_range.second = this->offsets[i * 2 + 1];
479 }
480 }
481 }
482 }
483 }
484
render() const485 void GLVolume::render() const
486 {
487 if (!is_active)
488 return;
489
490 if (this->is_left_handed())
491 glFrontFace(GL_CW);
492 glsafe(::glCullFace(GL_BACK));
493 glsafe(::glPushMatrix());
494 glsafe(::glMultMatrixd(world_matrix().data()));
495
496 this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
497
498 glsafe(::glPopMatrix());
499 if (this->is_left_handed())
500 glFrontFace(GL_CCW);
501 }
502
is_sla_support() const503 bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
is_sla_pad() const504 bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
505
load_object(const ModelObject * model_object,int obj_idx,const std::vector<int> & instance_idxs,const std::string & color_by,bool opengl_initialized)506 std::vector<int> GLVolumeCollection::load_object(
507 const ModelObject *model_object,
508 int obj_idx,
509 const std::vector<int> &instance_idxs,
510 const std::string &color_by,
511 bool opengl_initialized)
512 {
513 std::vector<int> volumes_idx;
514 for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
515 for (int instance_idx : instance_idxs)
516 volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized));
517 return volumes_idx;
518 }
519
load_object_volume(const ModelObject * model_object,int obj_idx,int volume_idx,int instance_idx,const std::string & color_by,bool opengl_initialized)520 int GLVolumeCollection::load_object_volume(
521 const ModelObject *model_object,
522 int obj_idx,
523 int volume_idx,
524 int instance_idx,
525 const std::string &color_by,
526 bool opengl_initialized)
527 {
528 const ModelVolume *model_volume = model_object->volumes[volume_idx];
529 const int extruder_id = model_volume->extruder_id();
530 const ModelInstance *instance = model_object->instances[instance_idx];
531 const TriangleMesh &mesh = model_volume->mesh();
532 float color[4];
533 memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
534 /* if (model_volume->is_support_blocker()) {
535 color[0] = 1.0f;
536 color[1] = 0.2f;
537 color[2] = 0.2f;
538 } else if (model_volume->is_support_enforcer()) {
539 color[0] = 0.2f;
540 color[1] = 0.2f;
541 color[2] = 1.0f;
542 }
543 color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */
544 color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
545 this->volumes.emplace_back(new GLVolume(color));
546 GLVolume& v = *this->volumes.back();
547 v.set_color_from_model_volume(model_volume);
548 #if ENABLE_SMOOTH_NORMALS
549 v.indexed_vertex_array.load_mesh(mesh, true);
550 #else
551 v.indexed_vertex_array.load_mesh(mesh);
552 #endif // ENABLE_SMOOTH_NORMALS
553 v.indexed_vertex_array.finalize_geometry(opengl_initialized);
554 v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
555 if (model_volume->is_model_part())
556 {
557 // GLVolume will reference a convex hull from model_volume!
558 v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
559 if (extruder_id != -1)
560 v.extruder_id = extruder_id;
561 }
562 v.is_modifier = !model_volume->is_model_part();
563 v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
564 v.set_instance_transformation(instance->get_transformation());
565 v.set_volume_transformation(model_volume->get_transformation());
566
567 return int(this->volumes.size() - 1);
568 }
569
570 // Load SLA auxiliary GLVolumes (for support trees or pad).
571 // This function produces volumes for multiple instances in a single shot,
572 // as some object specific mesh conversions may be expensive.
load_object_auxiliary(const SLAPrintObject * print_object,int obj_idx,const std::vector<std::pair<size_t,size_t>> & instances,SLAPrintObjectStep milestone,size_t timestamp,bool opengl_initialized)573 void GLVolumeCollection::load_object_auxiliary(
574 const SLAPrintObject *print_object,
575 int obj_idx,
576 // pairs of <instance_idx, print_instance_idx>
577 const std::vector<std::pair<size_t, size_t>>& instances,
578 SLAPrintObjectStep milestone,
579 // Timestamp of the last change of the milestone
580 size_t timestamp,
581 bool opengl_initialized)
582 {
583 assert(print_object->is_step_done(milestone));
584 Transform3d mesh_trafo_inv = print_object->trafo().inverse();
585 // Get the support mesh.
586 TriangleMesh mesh = print_object->get_mesh(milestone);
587 mesh.transform(mesh_trafo_inv);
588 // Convex hull is required for out of print bed detection.
589 TriangleMesh convex_hull = mesh.convex_hull_3d();
590 for (const std::pair<size_t, size_t>& instance_idx : instances) {
591 const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
592 this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
593 GLVolume& v = *this->volumes.back();
594 #if ENABLE_SMOOTH_NORMALS
595 v.indexed_vertex_array.load_mesh(mesh, true);
596 #else
597 v.indexed_vertex_array.load_mesh(mesh);
598 #endif // ENABLE_SMOOTH_NORMALS
599 v.indexed_vertex_array.finalize_geometry(opengl_initialized);
600 v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
601 v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
602 // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
603 if (&instance_idx == &instances.back())
604 v.set_convex_hull(std::move(convex_hull));
605 else
606 v.set_convex_hull(convex_hull);
607 v.is_modifier = false;
608 v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
609 v.set_instance_transformation(model_instance.get_transformation());
610 // Leave the volume transformation at identity.
611 // v.set_volume_transformation(model_volume->get_transformation());
612 }
613 }
614
load_wipe_tower_preview(int obj_idx,float pos_x,float pos_y,float width,float depth,float height,float rotation_angle,bool size_unknown,float brim_width,bool opengl_initialized)615 int GLVolumeCollection::load_wipe_tower_preview(
616 int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized)
617 {
618 if (depth < 0.01f)
619 return int(this->volumes.size() - 1);
620 if (height == 0.0f)
621 height = 0.1f;
622 Point origin_of_rotation(0.f, 0.f);
623 TriangleMesh mesh;
624 float color[4] = { 0.5f, 0.5f, 0.0f, 1.f };
625
626 // In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged:
627 if (size_unknown) {
628 color[0] = 0.9f;
629 color[1] = 0.6f;
630
631 depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway.
632 float min_width = 30.f;
633 // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front
634 // edge has y=0 and centerline of the back edge has y=depth:
635 Pointf3s points;
636 std::vector<Vec3i> facets;
637 float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
638 { 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
639 int out_facets_idx[][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
640 {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8},
641 {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} };
642 for (int i = 0; i < 16; ++i)
643 points.emplace_back(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]);
644 for (int i = 0; i < 28; ++i)
645 facets.emplace_back(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]);
646 TriangleMesh tooth_mesh(points, facets);
647
648 // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to
649 // the required width of the block. Than we can scale it precisely.
650 size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
651 for (size_t i = 0; i < n; ++i) {
652 mesh.merge(tooth_mesh);
653 tooth_mesh.translate(min_width, 0.f, 0.f);
654 }
655
656 mesh.scale(Vec3d(width / (n * min_width), 1.f, height)); // Scaling to proper width
657 }
658 else
659 mesh = make_cube(width, depth, height);
660
661 // We'll make another mesh to show the brim (fixed layer height):
662 TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
663 brim_mesh.translate(-brim_width, -brim_width, 0.f);
664 mesh.merge(brim_mesh);
665
666 this->volumes.emplace_back(new GLVolume(color));
667 GLVolume& v = *this->volumes.back();
668 v.indexed_vertex_array.load_mesh(mesh);
669 v.indexed_vertex_array.finalize_geometry(opengl_initialized);
670 v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
671 v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
672 v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
673 v.geometry_id.first = 0;
674 v.geometry_id.second = wipe_tower_instance_id().id;
675 v.is_wipe_tower = true;
676 v.shader_outside_printer_detection_enabled = !size_unknown;
677 return int(this->volumes.size() - 1);
678 }
679
new_toolpath_volume(const float * rgba,size_t reserve_vbo_floats)680 GLVolume* GLVolumeCollection::new_toolpath_volume(const float *rgba, size_t reserve_vbo_floats)
681 {
682 GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats);
683 out->is_extrusion_path = true;
684 return out;
685 }
686
new_nontoolpath_volume(const float * rgba,size_t reserve_vbo_floats)687 GLVolume* GLVolumeCollection::new_nontoolpath_volume(const float *rgba, size_t reserve_vbo_floats)
688 {
689 GLVolume *out = new GLVolume(rgba);
690 out->is_extrusion_path = false;
691 // Reserving number of vertices (3x position + 3x color)
692 out->indexed_vertex_array.reserve(reserve_vbo_floats / 6);
693 this->volumes.emplace_back(out);
694 return out;
695 }
696
volumes_to_render(const GLVolumePtrs & volumes,GLVolumeCollection::ERenderType type,const Transform3d & view_matrix,std::function<bool (const GLVolume &)> filter_func)697 GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
698 {
699 GLVolumeWithIdAndZList list;
700 list.reserve(volumes.size());
701
702 for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i)
703 {
704 GLVolume* volume = volumes[i];
705 bool is_transparent = (volume->render_color[3] < 1.0f);
706 if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
707 ((type == GLVolumeCollection::Transparent) && is_transparent) ||
708 (type == GLVolumeCollection::All)) &&
709 (! filter_func || filter_func(*volume)))
710 list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
711 }
712
713 if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
714 {
715 for (GLVolumeWithIdAndZ& volume : list)
716 {
717 volume.second.second = volume.first->bounding_box().transformed(view_matrix * volume.first->world_matrix()).max(2);
718 }
719
720 std::sort(list.begin(), list.end(),
721 [](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.second.second < v2.second.second; }
722 );
723 }
724 else if ((type == GLVolumeCollection::Opaque) && (list.size() > 1))
725 {
726 std::sort(list.begin(), list.end(),
727 [](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.first->selected && !v2.first->selected; }
728 );
729 }
730
731 return list;
732 }
733
render(GLVolumeCollection::ERenderType type,bool disable_cullface,const Transform3d & view_matrix,std::function<bool (const GLVolume &)> filter_func) const734 void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
735 {
736 GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
737 if (shader == nullptr)
738 return;
739
740 glsafe(::glEnable(GL_BLEND));
741 glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
742
743 glsafe(::glCullFace(GL_BACK));
744 if (disable_cullface)
745 glsafe(::glDisable(GL_CULL_FACE));
746
747 glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
748 glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
749
750 shader->set_uniform("print_box.min", m_print_box_min, 3);
751 shader->set_uniform("print_box.max", m_print_box_max, 3);
752 shader->set_uniform("z_range", m_z_range, 2);
753 shader->set_uniform("clipping_plane", m_clipping_plane, 4);
754 shader->set_uniform("slope.normal_z", m_slope.normal_z);
755
756 #if ENABLE_ENVIRONMENT_MAP
757 unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
758 bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
759 shader->set_uniform("use_environment_tex", use_environment_texture);
760 if (use_environment_texture)
761 glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
762 #endif // ENABLE_ENVIRONMENT_MAP
763 glcheck();
764
765 GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
766 for (GLVolumeWithIdAndZ& volume : to_render) {
767 volume.first->set_render_color();
768 shader->set_uniform("uniform_color", volume.first->render_color, 4);
769 shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
770 shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
771 shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
772 shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
773
774 volume.first->render();
775 }
776
777 #if ENABLE_ENVIRONMENT_MAP
778 if (use_environment_texture)
779 glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
780 #endif // ENABLE_ENVIRONMENT_MAP
781
782 glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
783 glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
784
785 glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
786 glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
787
788 if (disable_cullface)
789 glsafe(::glEnable(GL_CULL_FACE));
790
791 glsafe(::glDisable(GL_BLEND));
792 }
793
check_outside_state(const DynamicPrintConfig * config,ModelInstanceEPrintVolumeState * out_state)794 bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state)
795 {
796 if (config == nullptr)
797 return false;
798
799 const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
800 if (opt == nullptr)
801 return false;
802
803 BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
804 BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
805 // Allow the objects to protrude below the print bed
806 print_volume.min(2) = -1e10;
807 print_volume.min(0) -= BedEpsilon;
808 print_volume.min(1) -= BedEpsilon;
809 print_volume.max(0) += BedEpsilon;
810 print_volume.max(1) += BedEpsilon;
811
812 ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside;
813
814 bool contained_min_one = false;
815
816 for (GLVolume* volume : this->volumes)
817 {
818 if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
819 continue;
820
821 const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
822 bool contained = print_volume.contains(bb);
823
824 volume->is_outside = !contained;
825 if (!volume->printable)
826 continue;
827
828 if (contained)
829 contained_min_one = true;
830
831 if ((state == ModelInstancePVS_Inside) && volume->is_outside)
832 state = ModelInstancePVS_Fully_Outside;
833
834 if ((state == ModelInstancePVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb))
835 state = ModelInstancePVS_Partly_Outside;
836 }
837
838 if (out_state != nullptr)
839 *out_state = state;
840
841 return contained_min_one;
842 }
843
reset_outside_state()844 void GLVolumeCollection::reset_outside_state()
845 {
846 for (GLVolume* volume : this->volumes)
847 {
848 if (volume != nullptr)
849 volume->is_outside = false;
850 }
851 }
852
update_colors_by_extruder(const DynamicPrintConfig * config)853 void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config)
854 {
855 static const float inv_255 = 1.0f / 255.0f;
856
857 struct Color
858 {
859 std::string text;
860 unsigned char rgb[3];
861
862 Color()
863 : text("")
864 {
865 rgb[0] = 255;
866 rgb[1] = 255;
867 rgb[2] = 255;
868 }
869
870 void set(const std::string& text, unsigned char* rgb)
871 {
872 this->text = text;
873 ::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char));
874 }
875 };
876
877 if (config == nullptr)
878 return;
879
880 const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
881 if (extruders_opt == nullptr)
882 return;
883
884 const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
885 if (filamemts_opt == nullptr)
886 return;
887
888 unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
889 if (colors_count == 0)
890 return;
891
892 std::vector<Color> colors(colors_count);
893
894 unsigned char rgb[3];
895 for (unsigned int i = 0; i < colors_count; ++i)
896 {
897 const std::string& txt_color = config->opt_string("extruder_colour", i);
898 if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
899 {
900 colors[i].set(txt_color, rgb);
901 }
902 else
903 {
904 const std::string& txt_color = config->opt_string("filament_colour", i);
905 if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb))
906 colors[i].set(txt_color, rgb);
907 }
908 }
909
910 for (GLVolume* volume : volumes)
911 {
912 if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
913 continue;
914
915 int extruder_id = volume->extruder_id - 1;
916 if ((extruder_id < 0) || ((int)colors.size() <= extruder_id))
917 extruder_id = 0;
918
919 const Color& color = colors[extruder_id];
920 if (!color.text.empty())
921 {
922 for (int i = 0; i < 3; ++i)
923 {
924 volume->color[i] = (float)color.rgb[i] * inv_255;
925 }
926 }
927 }
928 }
929
get_current_print_zs(bool active_only) const930 std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const
931 {
932 // Collect layer top positions of all volumes.
933 std::vector<double> print_zs;
934 for (GLVolume *vol : this->volumes)
935 {
936 if (!active_only || vol->is_active)
937 append(print_zs, vol->print_zs);
938 }
939 std::sort(print_zs.begin(), print_zs.end());
940
941 // Replace intervals of layers with similar top positions with their average value.
942 int n = int(print_zs.size());
943 int k = 0;
944 for (int i = 0; i < n;) {
945 int j = i + 1;
946 coordf_t zmax = print_zs[i] + EPSILON;
947 for (; j < n && print_zs[j] <= zmax; ++ j) ;
948 print_zs[k ++] = (j > i + 1) ? (0.5 * (print_zs[i] + print_zs[j - 1])) : print_zs[i];
949 i = j;
950 }
951 if (k < n)
952 print_zs.erase(print_zs.begin() + k, print_zs.end());
953
954 return print_zs;
955 }
956
cpu_memory_used() const957 size_t GLVolumeCollection::cpu_memory_used() const
958 {
959 size_t memsize = sizeof(*this) + this->volumes.capacity() * sizeof(GLVolume);
960 for (const GLVolume *volume : this->volumes)
961 memsize += volume->cpu_memory_used();
962 return memsize;
963 }
964
gpu_memory_used() const965 size_t GLVolumeCollection::gpu_memory_used() const
966 {
967 size_t memsize = 0;
968 for (const GLVolume *volume : this->volumes)
969 memsize += volume->gpu_memory_used();
970 return memsize;
971 }
972
log_memory_info() const973 std::string GLVolumeCollection::log_memory_info() const
974 {
975 return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")";
976 }
977
can_export_to_obj(const GLVolume & volume)978 bool can_export_to_obj(const GLVolume& volume)
979 {
980 if (!volume.is_active || !volume.is_extrusion_path)
981 return false;
982
983 bool has_triangles = !volume.indexed_vertex_array.triangle_indices.empty() || (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) > 0);
984 bool has_quads = !volume.indexed_vertex_array.quad_indices.empty() || (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) > 0);
985
986 return has_triangles || has_quads;
987 }
988
has_toolpaths_to_export() const989 bool GLVolumeCollection::has_toolpaths_to_export() const
990 {
991 for (const GLVolume* volume : this->volumes)
992 {
993 if (can_export_to_obj(*volume))
994 return true;
995 }
996
997 return false;
998 }
999
1000 // caller is responsible for supplying NO lines with zero length
thick_lines_to_indexed_vertex_array(const Lines & lines,const std::vector<double> & widths,const std::vector<double> & heights,bool closed,double top_z,GLIndexedVertexArray & volume)1001 static void thick_lines_to_indexed_vertex_array(
1002 const Lines &lines,
1003 const std::vector<double> &widths,
1004 const std::vector<double> &heights,
1005 bool closed,
1006 double top_z,
1007 GLIndexedVertexArray &volume)
1008 {
1009 assert(! lines.empty());
1010 if (lines.empty())
1011 return;
1012
1013 #define LEFT 0
1014 #define RIGHT 1
1015 #define TOP 2
1016 #define BOTTOM 3
1017
1018 // right, left, top, bottom
1019 int idx_prev[4] = { -1, -1, -1, -1 };
1020 double bottom_z_prev = 0.;
1021 Vec2d b1_prev(Vec2d::Zero());
1022 Vec2d v_prev(Vec2d::Zero());
1023 int idx_initial[4] = { -1, -1, -1, -1 };
1024 double width_initial = 0.;
1025 double bottom_z_initial = 0.0;
1026 double len_prev = 0.0;
1027
1028 // loop once more in case of closed loops
1029 size_t lines_end = closed ? (lines.size() + 1) : lines.size();
1030 for (size_t ii = 0; ii < lines_end; ++ ii) {
1031 size_t i = (ii == lines.size()) ? 0 : ii;
1032 const Line &line = lines[i];
1033 double bottom_z = top_z - heights[i];
1034 double middle_z = 0.5 * (top_z + bottom_z);
1035 double width = widths[i];
1036
1037 bool is_first = (ii == 0);
1038 bool is_last = (ii == lines_end - 1);
1039 bool is_closing = closed && is_last;
1040
1041 Vec2d v = unscale(line.vector()).normalized();
1042 double len = unscale<double>(line.length());
1043
1044 Vec2d a = unscale(line.a);
1045 Vec2d b = unscale(line.b);
1046 Vec2d a1 = a;
1047 Vec2d a2 = a;
1048 Vec2d b1 = b;
1049 Vec2d b2 = b;
1050 {
1051 double dist = 0.5 * width; // scaled
1052 double dx = dist * v(0);
1053 double dy = dist * v(1);
1054 a1 += Vec2d(+dy, -dx);
1055 a2 += Vec2d(-dy, +dx);
1056 b1 += Vec2d(+dy, -dx);
1057 b2 += Vec2d(-dy, +dx);
1058 }
1059
1060 // calculate new XY normals
1061 Vec2d xy_right_normal = unscale(line.normal()).normalized();
1062
1063 int idx_a[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings
1064 int idx_b[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings
1065 int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6);
1066
1067 bool bottom_z_different = bottom_z_prev != bottom_z;
1068 bottom_z_prev = bottom_z;
1069
1070 if (!is_first && bottom_z_different)
1071 {
1072 // Found a change of the layer thickness -> Add a cap at the end of the previous segment.
1073 volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
1074 }
1075
1076 // Share top / bottom vertices if possible.
1077 if (is_first) {
1078 idx_a[TOP] = idx_last++;
1079 volume.push_geometry(a(0), a(1), top_z , 0., 0., 1.);
1080 } else {
1081 idx_a[TOP] = idx_prev[TOP];
1082 }
1083
1084 if (is_first || bottom_z_different) {
1085 // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z.
1086 idx_a[BOTTOM] = idx_last ++;
1087 volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.);
1088 idx_a[LEFT ] = idx_last ++;
1089 volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
1090 idx_a[RIGHT] = idx_last ++;
1091 volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
1092 }
1093 else {
1094 idx_a[BOTTOM] = idx_prev[BOTTOM];
1095 }
1096
1097 if (is_first) {
1098 // Start of the 1st line segment.
1099 width_initial = width;
1100 bottom_z_initial = bottom_z;
1101 memcpy(idx_initial, idx_a, sizeof(int) * 4);
1102 } else {
1103 // Continuing a previous segment.
1104 // Share left / right vertices if possible.
1105 double v_dot = v_prev.dot(v);
1106 // To reduce gpu memory usage, we try to reuse vertices
1107 // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
1108 // is longer than a fixed threshold.
1109 // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
1110 double len_threshold = 2.5;
1111
1112 // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
1113 bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
1114 if (sharp) {
1115 if (!bottom_z_different)
1116 {
1117 // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
1118 idx_a[RIGHT] = idx_last++;
1119 volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
1120 idx_a[LEFT] = idx_last++;
1121 volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
1122 if (cross2(v_prev, v) > 0.) {
1123 // Right turn. Fill in the right turn wedge.
1124 volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
1125 volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
1126 }
1127 else {
1128 // Left turn. Fill in the left turn wedge.
1129 volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
1130 volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
1131 }
1132 }
1133 }
1134 else
1135 {
1136 if (!bottom_z_different)
1137 {
1138 // The two successive segments are nearly collinear.
1139 idx_a[LEFT ] = idx_prev[LEFT];
1140 idx_a[RIGHT] = idx_prev[RIGHT];
1141 }
1142 }
1143 if (is_closing) {
1144 if (!sharp) {
1145 if (!bottom_z_different)
1146 {
1147 // Closing a loop with smooth transition. Unify the closing left / right vertices.
1148 memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6);
1149 memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6);
1150 volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end());
1151 // Replace the left / right vertex indices to point to the start of the loop.
1152 for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) {
1153 if (volume.quad_indices[u] == idx_prev[LEFT])
1154 volume.quad_indices[u] = idx_initial[LEFT];
1155 else if (volume.quad_indices[u] == idx_prev[RIGHT])
1156 volume.quad_indices[u] = idx_initial[RIGHT];
1157 }
1158 }
1159 }
1160 // This is the last iteration, only required to solve the transition.
1161 break;
1162 }
1163 }
1164
1165 // Only new allocate top / bottom vertices, if not closing a loop.
1166 if (is_closing) {
1167 idx_b[TOP] = idx_initial[TOP];
1168 } else {
1169 idx_b[TOP] = idx_last ++;
1170 volume.push_geometry(b(0), b(1), top_z , 0., 0., 1.);
1171 }
1172
1173 if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) {
1174 idx_b[BOTTOM] = idx_initial[BOTTOM];
1175 } else {
1176 idx_b[BOTTOM] = idx_last ++;
1177 volume.push_geometry(b(0), b(1), bottom_z, 0., 0., -1.);
1178 }
1179 // Generate new vertices for the end of this line segment.
1180 idx_b[LEFT ] = idx_last ++;
1181 volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
1182 idx_b[RIGHT ] = idx_last ++;
1183 volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
1184
1185 memcpy(idx_prev, idx_b, 4 * sizeof(int));
1186 bottom_z_prev = bottom_z;
1187 b1_prev = b1;
1188 v_prev = v;
1189 len_prev = len;
1190
1191 if (bottom_z_different && (closed || (!is_first && !is_last)))
1192 {
1193 // Found a change of the layer thickness -> Add a cap at the beginning of this segment.
1194 volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
1195 }
1196
1197 if (! closed) {
1198 // Terminate open paths with caps.
1199 if (is_first)
1200 volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
1201 // We don't use 'else' because both cases are true if we have only one line.
1202 if (is_last)
1203 volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
1204 }
1205
1206 // Add quads for a straight hollow tube-like segment.
1207 // bottom-right face
1208 volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]);
1209 // top-right face
1210 volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]);
1211 // top-left face
1212 volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]);
1213 // bottom-left face
1214 volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]);
1215 }
1216
1217 #undef LEFT
1218 #undef RIGHT
1219 #undef TOP
1220 #undef BOTTOM
1221 }
1222
1223 // caller is responsible for supplying NO lines with zero length
thick_lines_to_indexed_vertex_array(const Lines3 & lines,const std::vector<double> & widths,const std::vector<double> & heights,bool closed,GLIndexedVertexArray & volume)1224 static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
1225 const std::vector<double>& widths,
1226 const std::vector<double>& heights,
1227 bool closed,
1228 GLIndexedVertexArray& volume)
1229 {
1230 assert(!lines.empty());
1231 if (lines.empty())
1232 return;
1233
1234 #define LEFT 0
1235 #define RIGHT 1
1236 #define TOP 2
1237 #define BOTTOM 3
1238
1239 // left, right, top, bottom
1240 int idx_initial[4] = { -1, -1, -1, -1 };
1241 int idx_prev[4] = { -1, -1, -1, -1 };
1242 double z_prev = 0.0;
1243 double len_prev = 0.0;
1244 Vec3d n_right_prev = Vec3d::Zero();
1245 Vec3d n_top_prev = Vec3d::Zero();
1246 Vec3d unit_v_prev = Vec3d::Zero();
1247 double width_initial = 0.0;
1248
1249 // new vertices around the line endpoints
1250 // left, right, top, bottom
1251 Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
1252 Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
1253
1254 // loop once more in case of closed loops
1255 size_t lines_end = closed ? (lines.size() + 1) : lines.size();
1256 for (size_t ii = 0; ii < lines_end; ++ii)
1257 {
1258 size_t i = (ii == lines.size()) ? 0 : ii;
1259
1260 const Line3& line = lines[i];
1261 double height = heights[i];
1262 double width = widths[i];
1263
1264 Vec3d unit_v = unscale(line.vector()).normalized();
1265 double len = unscale<double>(line.length());
1266
1267 Vec3d n_top = Vec3d::Zero();
1268 Vec3d n_right = Vec3d::Zero();
1269
1270 if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1)))
1271 {
1272 // vertical segment
1273 n_top = Vec3d::UnitY();
1274 n_right = Vec3d::UnitX();
1275 if (line.a(2) < line.b(2))
1276 n_right = -n_right;
1277 }
1278 else
1279 {
1280 // horizontal segment
1281 n_right = unit_v.cross(Vec3d::UnitZ()).normalized();
1282 n_top = n_right.cross(unit_v).normalized();
1283 }
1284
1285 Vec3d rl_displacement = 0.5 * width * n_right;
1286 Vec3d tb_displacement = 0.5 * height * n_top;
1287 Vec3d l_a = unscale(line.a);
1288 Vec3d l_b = unscale(line.b);
1289
1290 a[RIGHT] = l_a + rl_displacement;
1291 a[LEFT] = l_a - rl_displacement;
1292 a[TOP] = l_a + tb_displacement;
1293 a[BOTTOM] = l_a - tb_displacement;
1294 b[RIGHT] = l_b + rl_displacement;
1295 b[LEFT] = l_b - rl_displacement;
1296 b[TOP] = l_b + tb_displacement;
1297 b[BOTTOM] = l_b - tb_displacement;
1298
1299 Vec3d n_bottom = -n_top;
1300 Vec3d n_left = -n_right;
1301
1302 int idx_a[4];
1303 int idx_b[4];
1304 int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6);
1305
1306 bool z_different = (z_prev != l_a(2));
1307 z_prev = l_b(2);
1308
1309 // Share top / bottom vertices if possible.
1310 if (ii == 0)
1311 {
1312 idx_a[TOP] = idx_last++;
1313 volume.push_geometry(a[TOP], n_top);
1314 }
1315 else
1316 idx_a[TOP] = idx_prev[TOP];
1317
1318 if ((ii == 0) || z_different)
1319 {
1320 // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z.
1321 idx_a[BOTTOM] = idx_last++;
1322 volume.push_geometry(a[BOTTOM], n_bottom);
1323 idx_a[LEFT] = idx_last++;
1324 volume.push_geometry(a[LEFT], n_left);
1325 idx_a[RIGHT] = idx_last++;
1326 volume.push_geometry(a[RIGHT], n_right);
1327 }
1328 else
1329 idx_a[BOTTOM] = idx_prev[BOTTOM];
1330
1331 if (ii == 0)
1332 {
1333 // Start of the 1st line segment.
1334 width_initial = width;
1335 ::memcpy(idx_initial, idx_a, sizeof(int) * 4);
1336 }
1337 else
1338 {
1339 // Continuing a previous segment.
1340 // Share left / right vertices if possible.
1341 double v_dot = unit_v_prev.dot(unit_v);
1342 bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0;
1343
1344 // To reduce gpu memory usage, we try to reuse vertices
1345 // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
1346 // is longer than a fixed threshold.
1347 // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
1348 double len_threshold = 2.5;
1349
1350 // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
1351 bool is_sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
1352 if (is_sharp)
1353 {
1354 // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
1355 idx_a[RIGHT] = idx_last++;
1356 volume.push_geometry(a[RIGHT], n_right);
1357 idx_a[LEFT] = idx_last++;
1358 volume.push_geometry(a[LEFT], n_left);
1359
1360 if (is_right_turn)
1361 {
1362 // Right turn. Fill in the right turn wedge.
1363 volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
1364 volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
1365 }
1366 else
1367 {
1368 // Left turn. Fill in the left turn wedge.
1369 volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
1370 volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
1371 }
1372 }
1373 else
1374 {
1375 // The two successive segments are nearly collinear.
1376 idx_a[LEFT] = idx_prev[LEFT];
1377 idx_a[RIGHT] = idx_prev[RIGHT];
1378 }
1379
1380 if (ii == lines.size())
1381 {
1382 if (!is_sharp)
1383 {
1384 // Closing a loop with smooth transition. Unify the closing left / right vertices.
1385 ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6, sizeof(float) * 6);
1386 ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6);
1387 volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end());
1388 // Replace the left / right vertex indices to point to the start of the loop.
1389 for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++u)
1390 {
1391 if (volume.quad_indices[u] == idx_prev[LEFT])
1392 volume.quad_indices[u] = idx_initial[LEFT];
1393 else if (volume.quad_indices[u] == idx_prev[RIGHT])
1394 volume.quad_indices[u] = idx_initial[RIGHT];
1395 }
1396 }
1397
1398 // This is the last iteration, only required to solve the transition.
1399 break;
1400 }
1401 }
1402
1403 // Only new allocate top / bottom vertices, if not closing a loop.
1404 if (closed && (ii + 1 == lines.size()))
1405 idx_b[TOP] = idx_initial[TOP];
1406 else
1407 {
1408 idx_b[TOP] = idx_last++;
1409 volume.push_geometry(b[TOP], n_top);
1410 }
1411
1412 if (closed && (ii + 1 == lines.size()) && (width == width_initial))
1413 idx_b[BOTTOM] = idx_initial[BOTTOM];
1414 else
1415 {
1416 idx_b[BOTTOM] = idx_last++;
1417 volume.push_geometry(b[BOTTOM], n_bottom);
1418 }
1419
1420 // Generate new vertices for the end of this line segment.
1421 idx_b[LEFT] = idx_last++;
1422 volume.push_geometry(b[LEFT], n_left);
1423 idx_b[RIGHT] = idx_last++;
1424 volume.push_geometry(b[RIGHT], n_right);
1425
1426 ::memcpy(idx_prev, idx_b, 4 * sizeof(int));
1427 n_right_prev = n_right;
1428 n_top_prev = n_top;
1429 unit_v_prev = unit_v;
1430 len_prev = len;
1431
1432 if (!closed)
1433 {
1434 // Terminate open paths with caps.
1435 if (i == 0)
1436 volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
1437
1438 // We don't use 'else' because both cases are true if we have only one line.
1439 if (i + 1 == lines.size())
1440 volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
1441 }
1442
1443 // Add quads for a straight hollow tube-like segment.
1444 // bottom-right face
1445 volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]);
1446 // top-right face
1447 volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]);
1448 // top-left face
1449 volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]);
1450 // bottom-left face
1451 volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]);
1452 }
1453
1454 #undef LEFT
1455 #undef RIGHT
1456 #undef TOP
1457 #undef BOTTOM
1458 }
1459
point_to_indexed_vertex_array(const Vec3crd & point,double width,double height,GLIndexedVertexArray & volume)1460 static void point_to_indexed_vertex_array(const Vec3crd& point,
1461 double width,
1462 double height,
1463 GLIndexedVertexArray& volume)
1464 {
1465 // builds a double piramid, with vertices on the local axes, around the point
1466
1467 Vec3d center = unscale(point);
1468
1469 double scale_factor = 1.0;
1470 double w = scale_factor * width;
1471 double h = scale_factor * height;
1472
1473 // new vertices ids
1474 int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6);
1475 int idxs[6];
1476 for (int i = 0; i < 6; ++i)
1477 {
1478 idxs[i] = idx_last + i;
1479 }
1480
1481 Vec3d displacement_x(w, 0.0, 0.0);
1482 Vec3d displacement_y(0.0, w, 0.0);
1483 Vec3d displacement_z(0.0, 0.0, h);
1484
1485 Vec3d unit_x(1.0, 0.0, 0.0);
1486 Vec3d unit_y(0.0, 1.0, 0.0);
1487 Vec3d unit_z(0.0, 0.0, 1.0);
1488
1489 // vertices
1490 volume.push_geometry(center - displacement_x, -unit_x); // idxs[0]
1491 volume.push_geometry(center + displacement_x, unit_x); // idxs[1]
1492 volume.push_geometry(center - displacement_y, -unit_y); // idxs[2]
1493 volume.push_geometry(center + displacement_y, unit_y); // idxs[3]
1494 volume.push_geometry(center - displacement_z, -unit_z); // idxs[4]
1495 volume.push_geometry(center + displacement_z, unit_z); // idxs[5]
1496
1497 // top piramid faces
1498 volume.push_triangle(idxs[0], idxs[2], idxs[5]);
1499 volume.push_triangle(idxs[2], idxs[1], idxs[5]);
1500 volume.push_triangle(idxs[1], idxs[3], idxs[5]);
1501 volume.push_triangle(idxs[3], idxs[0], idxs[5]);
1502
1503 // bottom piramid faces
1504 volume.push_triangle(idxs[2], idxs[0], idxs[4]);
1505 volume.push_triangle(idxs[1], idxs[2], idxs[4]);
1506 volume.push_triangle(idxs[3], idxs[1], idxs[4]);
1507 volume.push_triangle(idxs[0], idxs[3], idxs[4]);
1508 }
1509
thick_lines_to_verts(const Lines & lines,const std::vector<double> & widths,const std::vector<double> & heights,bool closed,double top_z,GLVolume & volume)1510 void _3DScene::thick_lines_to_verts(
1511 const Lines &lines,
1512 const std::vector<double> &widths,
1513 const std::vector<double> &heights,
1514 bool closed,
1515 double top_z,
1516 GLVolume &volume)
1517 {
1518 thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array);
1519 }
1520
thick_lines_to_verts(const Lines3 & lines,const std::vector<double> & widths,const std::vector<double> & heights,bool closed,GLVolume & volume)1521 void _3DScene::thick_lines_to_verts(const Lines3& lines,
1522 const std::vector<double>& widths,
1523 const std::vector<double>& heights,
1524 bool closed,
1525 GLVolume& volume)
1526 {
1527 thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array);
1528 }
1529
thick_point_to_verts(const Vec3crd & point,double width,double height,GLVolume & volume)1530 static void thick_point_to_verts(const Vec3crd& point,
1531 double width,
1532 double height,
1533 GLVolume& volume)
1534 {
1535 point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array);
1536 }
1537
extrusionentity_to_verts(const Polyline & polyline,float width,float height,float print_z,GLVolume & volume)1538 void _3DScene::extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume)
1539 {
1540 if (polyline.size() >= 2) {
1541 size_t num_segments = polyline.size() - 1;
1542 thick_lines_to_verts(polyline.lines(), std::vector<double>(num_segments, width), std::vector<double>(num_segments, height), false, print_z, volume);
1543 }
1544 }
1545
1546 // Fill in the qverts and tverts with quads and triangles for the extrusion_path.
extrusionentity_to_verts(const ExtrusionPath & extrusion_path,float print_z,GLVolume & volume)1547 void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume)
1548 {
1549 extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume);
1550 }
1551
1552 // Fill in the qverts and tverts with quads and triangles for the extrusion_path.
extrusionentity_to_verts(const ExtrusionPath & extrusion_path,float print_z,const Point & copy,GLVolume & volume)1553 void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume)
1554 {
1555 Polyline polyline = extrusion_path.polyline;
1556 polyline.remove_duplicate_points();
1557 polyline.translate(copy);
1558 Lines lines = polyline.lines();
1559 std::vector<double> widths(lines.size(), extrusion_path.width);
1560 std::vector<double> heights(lines.size(), extrusion_path.height);
1561 thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
1562 }
1563
1564 // Fill in the qverts and tverts with quads and triangles for the extrusion_loop.
extrusionentity_to_verts(const ExtrusionLoop & extrusion_loop,float print_z,const Point & copy,GLVolume & volume)1565 void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume)
1566 {
1567 Lines lines;
1568 std::vector<double> widths;
1569 std::vector<double> heights;
1570 for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) {
1571 Polyline polyline = extrusion_path.polyline;
1572 polyline.remove_duplicate_points();
1573 polyline.translate(copy);
1574 Lines lines_this = polyline.lines();
1575 append(lines, lines_this);
1576 widths.insert(widths.end(), lines_this.size(), extrusion_path.width);
1577 heights.insert(heights.end(), lines_this.size(), extrusion_path.height);
1578 }
1579 thick_lines_to_verts(lines, widths, heights, true, print_z, volume);
1580 }
1581
1582 // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path.
extrusionentity_to_verts(const ExtrusionMultiPath & extrusion_multi_path,float print_z,const Point & copy,GLVolume & volume)1583 void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume)
1584 {
1585 Lines lines;
1586 std::vector<double> widths;
1587 std::vector<double> heights;
1588 for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) {
1589 Polyline polyline = extrusion_path.polyline;
1590 polyline.remove_duplicate_points();
1591 polyline.translate(copy);
1592 Lines lines_this = polyline.lines();
1593 append(lines, lines_this);
1594 widths.insert(widths.end(), lines_this.size(), extrusion_path.width);
1595 heights.insert(heights.end(), lines_this.size(), extrusion_path.height);
1596 }
1597 thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
1598 }
1599
extrusionentity_to_verts(const ExtrusionEntityCollection & extrusion_entity_collection,float print_z,const Point & copy,GLVolume & volume)1600 void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume)
1601 {
1602 for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities)
1603 extrusionentity_to_verts(extrusion_entity, print_z, copy, volume);
1604 }
1605
extrusionentity_to_verts(const ExtrusionEntity * extrusion_entity,float print_z,const Point & copy,GLVolume & volume)1606 void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume)
1607 {
1608 if (extrusion_entity != nullptr) {
1609 auto *extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity);
1610 if (extrusion_path != nullptr)
1611 extrusionentity_to_verts(*extrusion_path, print_z, copy, volume);
1612 else {
1613 auto *extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity);
1614 if (extrusion_loop != nullptr)
1615 extrusionentity_to_verts(*extrusion_loop, print_z, copy, volume);
1616 else {
1617 auto *extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity);
1618 if (extrusion_multi_path != nullptr)
1619 extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, volume);
1620 else {
1621 auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
1622 if (extrusion_entity_collection != nullptr)
1623 extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume);
1624 else {
1625 throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()");
1626 }
1627 }
1628 }
1629 }
1630 }
1631 }
1632
polyline3_to_verts(const Polyline3 & polyline,double width,double height,GLVolume & volume)1633 void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume)
1634 {
1635 Lines3 lines = polyline.lines();
1636 std::vector<double> widths(lines.size(), width);
1637 std::vector<double> heights(lines.size(), height);
1638 thick_lines_to_verts(lines, widths, heights, false, volume);
1639 }
1640
point3_to_verts(const Vec3crd & point,double width,double height,GLVolume & volume)1641 void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume)
1642 {
1643 thick_point_to_verts(point, width, height, volume);
1644 }
1645
1646 } // namespace Slic3r
1647