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