1 #include "Engine.hpp"
2 #include <libslic3r/Utils.hpp>
3 #include <libslic3r/SLAPrint.hpp>
4 
5 #include <GL/glew.h>
6 
7 #include <boost/log/trivial.hpp>
8 
9 #ifndef NDEBUG
10 #define HAS_GLSAFE
11 #endif
12 
13 #ifdef HAS_GLSAFE
14 extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name);
glAssertRecentCall()15 inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); }
16 #define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
17 #define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
18 
glAssertRecentCallImpl(const char * file_name,unsigned int line,const char * function_name)19 void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name)
20 {
21     GLenum err = glGetError();
22     if (err == GL_NO_ERROR)
23         return;
24     const char *sErr = 0;
25     switch (err) {
26     case GL_INVALID_ENUM:       sErr = "Invalid Enum";      break;
27     case GL_INVALID_VALUE:      sErr = "Invalid Value";     break;
28     // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd
29     case GL_INVALID_OPERATION:  sErr = "Invalid Operation"; break;
30     case GL_STACK_OVERFLOW:     sErr = "Stack Overflow";    break;
31     case GL_STACK_UNDERFLOW:    sErr = "Stack Underflow";   break;
32     case GL_OUT_OF_MEMORY:      sErr = "Out Of Memory";     break;
33     default:                    sErr = "Unknown";           break;
34     }
35     BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr;
36     assert(false);
37 }
38 
39 #else
glAssertRecentCall()40 inline void glAssertRecentCall() { }
41 #define glsafe(cmd) cmd
42 #define glcheck()
43 #endif
44 
45 namespace Slic3r { namespace GL {
46 
47 Scene::Scene() = default;
48 Scene::~Scene() = default;
49 
render_scene()50 void CSGDisplay::render_scene()
51 {
52     GLfloat color[] = {1.f, 1.f, 0.f, 0.f};
53     glsafe(::glColor4fv(color));
54 
55     if (m_csgsettings.is_enabled()) {
56         OpenCSG::render(m_scene_cache.primitives_csg);
57         glDepthFunc(GL_EQUAL);
58     }
59 
60     for (auto& p : m_scene_cache.primitives_csg) p->render();
61     if (m_csgsettings.is_enabled()) glDepthFunc(GL_LESS);
62 
63     for (auto& p : m_scene_cache.primitives_free) p->render();
64 
65     glFlush();
66 }
67 
set_print(uqptr<SLAPrint> && print)68 void Scene::set_print(uqptr<SLAPrint> &&print)
69 {
70     m_print = std::move(print);
71 
72     // Notify displays
73     call(&Listener::on_scene_updated, m_listeners, *this);
74 }
75 
get_bounding_box() const76 BoundingBoxf3 Scene::get_bounding_box() const
77 {
78     return m_print->model().bounding_box();
79 }
80 
clear()81 void CSGDisplay::SceneCache::clear()
82 {
83     primitives_csg.clear();
84     primitives_free.clear();
85     primitives.clear();
86 }
87 
add_mesh(const TriangleMesh & mesh)88 shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
89 {
90     auto p = std::make_shared<Primitive>();
91     p->load_mesh(mesh);
92     primitives.emplace_back(p);
93     primitives_free.emplace_back(p.get());
94     return p;
95 }
96 
add_mesh(const TriangleMesh & mesh,OpenCSG::Operation o,unsigned c)97 shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
98                                                    OpenCSG::Operation  o,
99                                                    unsigned            c)
100 {
101     auto p = std::make_shared<Primitive>(o, c);
102     p->load_mesh(mesh);
103     primitives.emplace_back(p);
104     primitives_csg.emplace_back(p.get());
105     return p;
106 }
107 
push_geometry(float x,float y,float z,float nx,float ny,float nz)108 void IndexedVertexArray::push_geometry(float x, float y, float z, float nx, float ny, float nz)
109 {
110     assert(this->vertices_and_normals_interleaved_VBO_id == 0);
111     if (this->vertices_and_normals_interleaved_VBO_id != 0)
112         return;
113 
114     if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
115         this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
116     this->vertices_and_normals_interleaved.emplace_back(nx);
117     this->vertices_and_normals_interleaved.emplace_back(ny);
118     this->vertices_and_normals_interleaved.emplace_back(nz);
119     this->vertices_and_normals_interleaved.emplace_back(x);
120     this->vertices_and_normals_interleaved.emplace_back(y);
121     this->vertices_and_normals_interleaved.emplace_back(z);
122 
123     this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
124 }
125 
push_triangle(int idx1,int idx2,int idx3)126 void IndexedVertexArray::push_triangle(int idx1, int idx2, int idx3) {
127     assert(this->vertices_and_normals_interleaved_VBO_id == 0);
128     if (this->vertices_and_normals_interleaved_VBO_id != 0)
129         return;
130 
131     if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
132         this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
133     this->triangle_indices.emplace_back(idx1);
134     this->triangle_indices.emplace_back(idx2);
135     this->triangle_indices.emplace_back(idx3);
136     this->triangle_indices_size = this->triangle_indices.size();
137 }
138 
load_mesh(const TriangleMesh & mesh)139 void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
140 {
141     assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
142     assert(quad_indices.empty() && triangle_indices_size == 0);
143     assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
144 
145     this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
146 
147     int vertices_count = 0;
148     for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
149         const stl_facet &facet = mesh.stl.facet_start[i];
150         for (int j = 0; j < 3; ++j)
151             this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
152 
153                 this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
154         vertices_count += 3;
155     }
156 }
157 
finalize_geometry()158 void IndexedVertexArray::finalize_geometry()
159 {
160     assert(this->vertices_and_normals_interleaved_VBO_id == 0);
161     assert(this->triangle_indices_VBO_id == 0);
162     assert(this->quad_indices_VBO_id == 0);
163 
164     if (!this->vertices_and_normals_interleaved.empty()) {
165         glsafe(
166             ::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
167         glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
168                               this->vertices_and_normals_interleaved_VBO_id));
169         glsafe(
170             ::glBufferData(GL_ARRAY_BUFFER,
171                            GLsizeiptr(
172                                this->vertices_and_normals_interleaved.size() *
173                                4),
174                            this->vertices_and_normals_interleaved.data(),
175                            GL_STATIC_DRAW));
176         glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
177         this->vertices_and_normals_interleaved.clear();
178     }
179     if (!this->triangle_indices.empty()) {
180         glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
181         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
182                               this->triangle_indices_VBO_id));
183         glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
184                               GLsizeiptr(this->triangle_indices.size() * 4),
185                               this->triangle_indices.data(), GL_STATIC_DRAW));
186         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
187         this->triangle_indices.clear();
188     }
189     if (!this->quad_indices.empty()) {
190         glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
191         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
192                               this->quad_indices_VBO_id));
193         glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
194                               GLsizeiptr(this->quad_indices.size() * 4),
195                               this->quad_indices.data(), GL_STATIC_DRAW));
196         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
197         this->quad_indices.clear();
198     }
199 }
200 
release_geometry()201 void IndexedVertexArray::release_geometry()
202 {
203     if (this->vertices_and_normals_interleaved_VBO_id) {
204         glsafe(
205             ::glDeleteBuffers(1,
206                               &this->vertices_and_normals_interleaved_VBO_id));
207         this->vertices_and_normals_interleaved_VBO_id = 0;
208     }
209     if (this->triangle_indices_VBO_id) {
210         glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id));
211         this->triangle_indices_VBO_id = 0;
212     }
213     if (this->quad_indices_VBO_id) {
214         glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id));
215         this->quad_indices_VBO_id = 0;
216     }
217     this->clear();
218 }
219 
render() const220 void IndexedVertexArray::render() const
221 {
222     assert(this->vertices_and_normals_interleaved_VBO_id != 0);
223     assert(this->triangle_indices_VBO_id != 0 ||
224            this->quad_indices_VBO_id != 0);
225 
226     glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
227                           this->vertices_and_normals_interleaved_VBO_id));
228     glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float),
229                              reinterpret_cast<const void *>(3 * sizeof(float))));
230     glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
231 
232     glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
233     glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
234 
235     // Render using the Vertex Buffer Objects.
236     if (this->triangle_indices_size > 0) {
237         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
238                               this->triangle_indices_VBO_id));
239         glsafe(::glDrawElements(GL_TRIANGLES,
240                                 GLsizei(this->triangle_indices_size),
241                                 GL_UNSIGNED_INT, nullptr));
242         glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
243     }
244     if (this->quad_indices_size > 0) {
245         glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
246                               this->quad_indices_VBO_id));
247         glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size),
248                                 GL_UNSIGNED_INT, nullptr));
249         glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
250     }
251 
252     glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
253     glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
254 
255     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
256 }
257 
clear()258 void IndexedVertexArray::clear() {
259     this->vertices_and_normals_interleaved.clear();
260     this->triangle_indices.clear();
261     this->quad_indices.clear();
262     vertices_and_normals_interleaved_size = 0;
263     triangle_indices_size = 0;
264     quad_indices_size = 0;
265 }
266 
shrink_to_fit()267 void IndexedVertexArray::shrink_to_fit() {
268     this->vertices_and_normals_interleaved.shrink_to_fit();
269     this->triangle_indices.shrink_to_fit();
270     this->quad_indices.shrink_to_fit();
271 }
272 
render()273 void Volume::render()
274 {
275     glsafe(::glPushMatrix());
276     glsafe(::glMultMatrixd(m_trafo.get_matrix().data()));
277     m_geom.render();
278     glsafe(::glPopMatrix());
279 }
280 
clear_screen()281 void Display::clear_screen()
282 {
283     glViewport(0, 0, GLsizei(m_size.x()), GLsizei(m_size.y()));
284     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
285 }
286 
~Display()287 Display::~Display()
288 {
289     OpenCSG::freeResources();
290 }
291 
set_active(long width,long height)292 void Display::set_active(long width, long height)
293 {
294     if (!m_initialized) {
295         glewInit();
296         m_initialized = true;
297     }
298 
299     // gray background
300     glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
301 
302     // Enable two OpenGL lights
303     GLfloat light_diffuse[]   = { 1.0f,  1.0f,  0.0f,  1.0f};  // White diffuse light
304     GLfloat light_position0[] = {-1.0f, -1.0f, -1.0f,  0.0f};  // Infinite light location
305     GLfloat light_position1[] = { 1.0f,  1.0f,  1.0f,  0.0f};  // Infinite light location
306 
307     glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
308     glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
309     glEnable(GL_LIGHT0);
310     glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
311     glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
312     glEnable(GL_LIGHT1);
313     glEnable(GL_LIGHTING);
314     glEnable(GL_NORMALIZE);
315 
316     // Use depth buffering for hidden surface elimination
317     glEnable(GL_DEPTH_TEST);
318     glEnable(GL_STENCIL_TEST);
319 
320     set_screen_size(width, height);
321 }
322 
set_screen_size(long width,long height)323 void Display::set_screen_size(long width, long height)
324 {
325     if (m_size.x() != width || m_size.y() != height)
326         m_camera->set_screen(width, height);
327 
328     m_size = {width, height};
329 }
330 
repaint()331 void Display::repaint()
332 {
333     clear_screen();
334 
335     m_camera->view();
336     render_scene();
337 
338     m_fps_counter.update();
339 
340     swap_buffers();
341 }
342 
on_scene_updated(const Scene & scene)343 void Controller::on_scene_updated(const Scene &scene)
344 {
345     const SLAPrint *print = scene.get_print();
346     if (!print) return;
347 
348     auto bb = scene.get_bounding_box();
349     double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z());
350     m_wheel_pos = long(2 * d);
351 
352     call_cameras(&Camera::set_zoom, m_wheel_pos);
353     call(&Display::on_scene_updated, m_displays, scene);
354 }
355 
on_scroll(long v,long d,MouseInput::WheelAxis)356 void Controller::on_scroll(long v, long d, MouseInput::WheelAxis /*wa*/)
357 {
358     m_wheel_pos += v / d;
359 
360     call_cameras(&Camera::set_zoom, m_wheel_pos);
361     call(&Display::repaint, m_displays);
362 }
363 
on_moved_to(long x,long y)364 void Controller::on_moved_to(long x, long y)
365 {
366     if (m_left_btn) {
367         call_cameras(&Camera::rotate, (Vec2i{x, y} - m_mouse_pos).cast<float>());
368         call(&Display::repaint, m_displays);
369     }
370 
371     m_mouse_pos = {x, y};
372 }
373 
apply_csgsettings(const CSGSettings & settings)374 void CSGDisplay::apply_csgsettings(const CSGSettings &settings)
375 {
376     using namespace OpenCSG;
377 
378     bool needupdate = m_csgsettings.get_convexity() != settings.get_convexity();
379 
380     m_csgsettings = settings;
381     setOption(AlgorithmSetting, m_csgsettings.get_algo());
382     setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo());
383     setOption(DepthBoundsOptimization, m_csgsettings.get_optimization());
384 
385     if (needupdate) {
386         for (OpenCSG::Primitive * p : m_scene_cache.primitives_csg)
387             if (p->getConvexity() > 1)
388                 p->setConvexity(m_csgsettings.get_convexity());
389     }
390 }
391 
on_scene_updated(const Scene & scene)392 void CSGDisplay::on_scene_updated(const Scene &scene)
393 {
394     const SLAPrint *print = scene.get_print();
395     if (!print) return;
396 
397     m_scene_cache.clear();
398 
399     for (const SLAPrintObject *po : print->objects()) {
400         const ModelObject *mo = po->model_object();
401         TriangleMesh msh = mo->raw_mesh();
402 
403         sla::DrainHoles holedata = mo->sla_drain_holes;
404 
405         for (const ModelInstance *mi : mo->instances) {
406 
407             TriangleMesh mshinst = msh;
408             auto interior = po->hollowed_interior_mesh();
409             interior.transform(po->trafo().inverse());
410 
411             mshinst.merge(interior);
412             mshinst.require_shared_vertices();
413 
414             mi->transform_mesh(&mshinst);
415 
416             auto bb = mshinst.bounding_box();
417             auto center = bb.center().cast<float>();
418             mshinst.translate(-center);
419 
420             mshinst.require_shared_vertices();
421             m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
422                                    m_csgsettings.get_convexity());
423         }
424 
425         for (const sla::DrainHole &holept : holedata) {
426             TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
427             holemesh.require_shared_vertices();
428             m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
429         }
430     }
431 
432     repaint();
433 }
434 
view()435 void Camera::view()
436 {
437     glMatrixMode(GL_MODELVIEW);
438     glLoadIdentity();
439     gluLookAt(0.0, m_zoom, 0.0,  /* eye is at (0,zoom,0) */
440               m_referene.x(), m_referene.y(), m_referene.z(),
441               0.0, 0.0, 1.0); /* up is in positive Y direction */
442 
443     // TODO Could have been set in prevoius gluLookAt in first argument
444     glRotatef(m_rot.y(), 1.0, 0.0, 0.0);
445     glRotatef(m_rot.x(), 0.0, 0.0, 1.0);
446 
447     if (m_clip_z > 0.) {
448         GLdouble plane[] = {0., 0., 1., m_clip_z};
449         glClipPlane(GL_CLIP_PLANE0, plane);
450         glEnable(GL_CLIP_PLANE0);
451     } else {
452         glDisable(GL_CLIP_PLANE0);
453     }
454 }
455 
set_screen(long width,long height)456 void PerspectiveCamera::set_screen(long width, long height)
457 {
458     // Setup the view of the CSG shape
459     glMatrixMode(GL_PROJECTION);
460     glLoadIdentity();
461     gluPerspective(45.0, width / double(height), .1, 200.0);
462     glMatrixMode(GL_MODELVIEW);
463 }
464 
enable_multisampling(bool e)465 bool enable_multisampling(bool e)
466 {
467     if (!e) { glDisable(GL_MULTISAMPLE); return false; }
468 
469     GLint is_ms_context;
470     glGetIntegerv(GL_SAMPLE_BUFFERS, &is_ms_context);
471 
472     if (is_ms_context) { glEnable(GL_MULTISAMPLE); return true; }
473     else return false;
474 }
475 
476 MouseInput::Listener::~Listener() = default;
477 
update()478 void FpsCounter::update()
479 {
480     ++m_frames;
481 
482     TimePoint msec = Clock::now();
483 
484     double seconds_window = to_sec(msec - m_window);
485     m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window);
486 
487     if (to_sec(msec - m_last) >= m_resolution) {
488         m_last = msec;
489         for (auto &l : m_listeners) l(m_fps);
490     }
491 
492     if (seconds_window >= m_window_size) {
493         m_frames = 0;
494         m_window = msec;
495     }
496 }
497 
498 }} // namespace Slic3r::GL
499