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 &copy, 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 &copy, 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 &copy, 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 &copy, 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 &copy, 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