Visual Computing Library
Loading...
Searching...
No Matches
drawable_mesh.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_OPENGL2_DRAWABLE_DRAWABLE_MESH_H
24#define VCL_OPENGL2_DRAWABLE_DRAWABLE_MESH_H
25
26#include "mesh/mesh_render_vectors.h"
27
28#include <vclib/algorithms/mesh/stat/bounding_box.h>
29#include <vclib/render/drawable/abstract_drawable_mesh.h>
30
31#include <vclib/opengl2/drawable/draw_objects3.h>
32
33#ifdef _WIN32
34#include <windows.h>
35#endif
36
37#ifdef __APPLE__
38#include <OpenGL/gl.h>
39#else
40#include <GL/gl.h>
41#endif
42
43#include <iostream>
44
45namespace vcl {
46
47// From:
48// https://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
49inline void _check_gl_error(const char* file, int line)
50{
51 GLenum err(glGetError());
52
53 while (err != GL_NO_ERROR) {
54 std::string error;
55
56 switch (err) {
57 case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
58 case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
59 case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
60 case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
61#ifdef unix
62 case GL_INVALID_FRAMEBUFFER_OPERATION:
63 error = "INVALID_FRAMEBUFFER_OPERATION";
64 break;
65#endif
66 }
67
68 std::cerr << "GL_" << error.c_str() << " - " << file << ":" << line
69 << std::endl;
70 err = glGetError();
71 }
72}
73
79#define check_gl_error() _check_gl_error(__FILE__, __LINE__)
80
81template<MeshConcept MeshType>
82class DrawableMeshOpenGL2 : public AbstractDrawableMesh, public MeshType
83{
84 using MRI = MeshRenderInfo;
85
86 Box3d mBoundingBox;
87
89
90 std::vector<uint> mTextID;
91
92public:
93 DrawableMeshOpenGL2() = default;
94
95 DrawableMeshOpenGL2(const MeshType& mesh) :
96 AbstractDrawableMesh(mesh), MeshType(mesh)
97 {
98 updateBuffers();
99 mMRS.setDefaultSettingsFromCapability();
100 }
101
102 DrawableMeshOpenGL2(MeshType&& mesh) :
103 AbstractDrawableMesh(mesh), MeshType(std::move(mesh))
104 {
105 updateBuffers();
106 mMRS.setDefaultSettingsFromCapability();
107 }
108
109 ~DrawableMeshOpenGL2() = default;
110
111 void updateBuffers(
112 MRI::BuffersBitSet buffersToUpdate = MRI::BUFFERS_ALL) override
113 {
114 if constexpr (HasName<MeshType>) {
115 AbstractDrawableMesh::name() = MeshType::name();
116 }
117
119 if constexpr (vcl::HasBoundingBox<MeshType>) {
120 if (this->MeshType::boundingBox().isNull()) {
121 bbToInitialize = true;
122 }
123 else {
124 mBoundingBox =
125 this->MeshType::boundingBox().template cast<double>();
126 }
127 }
128
129 if (bbToInitialize) {
130 mBoundingBox = vcl::boundingBox(*this);
131 }
132
133 unbindTextures();
134 mMRD.update(*this, buffersToUpdate);
135 mMRS.setRenderCapabilityFrom(*this);
136 bindTextures();
137 }
138
139 std::string& name() override { return MeshType::name(); }
140
141 const std::string& name() const override { return MeshType::name(); }
142
143 void swap(DrawableMeshOpenGL2& other)
144 {
145 using std::swap;
146 AbstractDrawableMesh::swap(other);
147 MeshType::swap(other);
148 swap(mBoundingBox, other.mBoundingBox);
149 swap(mMRD, other.mMRD);
150 swap(mTextID, other.mTextID);
151 }
152
153 friend void swap(DrawableMeshOpenGL2& a, DrawableMeshOpenGL2& b)
154 {
155 a.swap(b);
156 }
157
158 // DrawableObject implementation
159
160 void init() override { bindTextures(); }
161
162 void draw(uint) const override
163 {
164 if (mMRS.isVisible()) {
165 if (mMRS.isWireframe(MRI::Wireframe::VISIBLE)) {
166 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
170 glDepthRange(0.0, 1.0);
171 renderPass();
173 }
174 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
175 if (mMRS.isSurface(MRI::Surface::SHADING_FLAT)) {
178 glDepthRange(0.01, 1.0);
179 renderPass();
180
183 glDepthRange(0.0, 1.0);
185 renderPass();
188 }
189 else if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
192 glDepthRange(0.01, 1.0);
193 renderPass();
194
197 glDepthRange(0.0, 1.0);
199 renderPass();
202 }
203 }
204 else {
208 glDepthRange(0.0, 1.0);
209 renderPass();
211 }
212 }
213 else { // no wireframe
214 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
216 renderPass();
217 }
218 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
219 if (mMRS.isSurface(MRI::Surface::SHADING_FLAT)) {
222 renderPass();
223 }
224 else if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
227 renderPass();
228 }
229 }
230 }
231 }
232 }
233
234 Box3d boundingBox() const override { return mBoundingBox; }
235
236 std::shared_ptr<DrawableObject> clone() const& override
237 {
238 return std::make_shared<DrawableMeshOpenGL2>(*this);
239 }
240
241 std::shared_ptr<DrawableObject> clone() && override
242 {
243 return std::make_shared<DrawableMeshOpenGL2>(std::move(*this));
244 }
245
246private:
247 void renderPass() const
248 {
249 uint nv = mMRD.vertexNumber();
250 uint nt = mMRD.triangleNumber();
251
252 const float* coords = mMRD.vertexBufferData();
253 const uint32_t* triangles = mMRD.triangleBufferData();
254 const float* vertexNormals = mMRD.vertexNormalBufferData();
255 const uint32_t* vertexColors = mMRD.vertexColorBufferData();
256 const float* triangleNormals = mMRD.triangleNormalBufferData();
257 const uint32_t* triangleColors = mMRD.triangleColorBufferData();
258 const float* vertTexCoords = mMRD.vertexTexCoordsBufferData();
259 const float* wedgTexCoords = mMRD.wedgeTexCoordsBufferData();
260
261 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
263 glVertexPointer(3, GL_FLOAT, 0, coords);
264
265 if (mMRS.isPoints(MRI::Points::COLOR_VERTEX)) {
268 }
269 else if (mMRS.isPoints(MRI::Points::COLOR_MESH)) {
270 glColor4fv(mMRD.meshColorBufferData());
271 }
272 else if (mMRS.isPoints(MRI::Points::COLOR_USER)) {
273 glColor4fv(mMRS.pointUserColorData());
274 }
275
276 glPointSize(mMRS.pointWidth());
277
278 glDrawArrays(GL_POINTS, 0, nv);
279
280 glDisableClientState(GL_COLOR_ARRAY);
281 glDisableClientState(GL_VERTEX_ARRAY);
282 }
283
284 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
285 // Old fashioned, verbose and slow rendering.
286 if (mMRS.isSurface(MRI::Surface::COLOR_FACE)) {
287 int n_tris = nt;
288 for (int tid = 0; tid < n_tris; ++tid) {
289 int tid_ptr = 3 * tid;
290 int vid0 = triangles[tid_ptr + 0];
291 int vid1 = triangles[tid_ptr + 1];
292 int vid2 = triangles[tid_ptr + 2];
293 int vid0_ptr = 3 * vid0;
294 int vid1_ptr = 3 * vid1;
295 int vid2_ptr = 3 * vid2;
296
297 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
298 glBegin(GL_TRIANGLES);
299 glColor4ubv((GLubyte*) &(triangleColors[tid]));
300 glNormal3fv(&(vertexNormals[vid0_ptr]));
301 glVertex3fv(&(coords[vid0_ptr]));
302 glNormal3fv(&(vertexNormals[vid1_ptr]));
303 glVertex3fv(&(coords[vid1_ptr]));
304 glNormal3fv(&(vertexNormals[vid2_ptr]));
305 glVertex3fv(&(coords[vid2_ptr]));
306 glEnd();
307 }
308 else {
309 glBegin(GL_TRIANGLES);
310 glColor4ubv((GLubyte*) &(triangleColors[tid]));
311 glNormal3fv(&(triangleNormals[tid_ptr]));
312 glVertex3fv(&(coords[vid0_ptr]));
313 glNormal3fv(&(triangleNormals[tid_ptr]));
314 glVertex3fv(&(coords[vid1_ptr]));
315 glNormal3fv(&(triangleNormals[tid_ptr]));
316 glVertex3fv(&(coords[vid2_ptr]));
317 glEnd();
318 }
319 }
320 }
321 else if (mMRS.isSurface(MRI::Surface::COLOR_VERTEX)) {
322 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
323 glEnableClientState(GL_VERTEX_ARRAY);
324 glVertexPointer(3, GL_FLOAT, 0, coords);
325
326 glEnableClientState(GL_NORMAL_ARRAY);
327 glNormalPointer(GL_FLOAT, 0, vertexNormals);
328
329 glEnableClientState(GL_COLOR_ARRAY);
330 glColorPointer(4, GL_UNSIGNED_BYTE, 0, vertexColors);
331
332 glDrawElements(
333 GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
334
335 glDisableClientState(GL_COLOR_ARRAY);
336 glDisableClientState(GL_NORMAL_ARRAY);
337 glDisableClientState(GL_VERTEX_ARRAY);
338 }
339 else {
340 glShadeModel(GL_SMOOTH);
341 int n_tris = nt;
342 for (int tid = 0; tid < n_tris; ++tid) {
343 int tid_ptr = 3 * tid;
344 int vid0 = triangles[tid_ptr + 0];
345 int vid1 = triangles[tid_ptr + 1];
346 int vid2 = triangles[tid_ptr + 2];
347 int vid0_ptr = 3 * vid0;
348 int vid1_ptr = 3 * vid1;
349 int vid2_ptr = 3 * vid2;
350
351 glBegin(GL_TRIANGLES);
352 glColor4ubv((GLubyte*) &(vertexColors[vid0]));
353 glNormal3fv(&(triangleNormals[tid_ptr]));
354 glVertex3fv(&(coords[vid0_ptr]));
355 glColor4ubv((GLubyte*) &(vertexColors[vid1]));
356 glNormal3fv(&(triangleNormals[tid_ptr]));
357 glVertex3fv(&(coords[vid1_ptr]));
358 glColor4ubv((GLubyte*) &(vertexColors[vid2]));
359 glNormal3fv(&(triangleNormals[tid_ptr]));
360 glVertex3fv(&(coords[vid2_ptr]));
361 glEnd();
362 }
363 }
364 }
365 else if (
366 mMRS.isSurface(MRI::Surface::COLOR_MESH) ||
367 mMRS.isSurface(MRI::Surface::COLOR_USER)) {
368 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
369 glEnableClientState(GL_VERTEX_ARRAY);
370 glVertexPointer(3, GL_FLOAT, 0, coords);
371
372 glEnableClientState(GL_NORMAL_ARRAY);
373 glNormalPointer(GL_FLOAT, 0, vertexNormals);
374
375 if (mMRS.isSurface(MRI::Surface::COLOR_MESH)) {
376 glColor4fv(mMRD.meshColorBufferData());
377 }
378 else {
379 glColor4ubv((GLubyte*) mMRS.surfaceUserColorData());
380 }
381
382 glDrawElements(
383 GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
384
385 glDisableClientState(GL_COLOR_ARRAY);
386 glDisableClientState(GL_NORMAL_ARRAY);
387 glDisableClientState(GL_VERTEX_ARRAY);
388 }
389 else {
390 if (mMRS.isSurface(MRI::Surface::COLOR_MESH)) {
391 glColor4fv(mMRD.meshColorBufferData());
392 }
393 else {
394 glColor4ubv((GLubyte*) mMRS.surfaceUserColorData());
395 }
396 int n_tris = nt;
397 for (int tid = 0; tid < n_tris; ++tid) {
398 int tid_ptr = 3 * tid;
399 int vid0 = triangles[tid_ptr + 0];
400 int vid1 = triangles[tid_ptr + 1];
401 int vid2 = triangles[tid_ptr + 2];
402 int vid0_ptr = 3 * vid0;
403 int vid1_ptr = 3 * vid1;
404 int vid2_ptr = 3 * vid2;
405
406 glBegin(GL_TRIANGLES);
407 glNormal3fv(&(triangleNormals[tid_ptr]));
408 glVertex3fv(&(coords[vid0_ptr]));
409 glNormal3fv(&(triangleNormals[tid_ptr]));
410 glVertex3fv(&(coords[vid1_ptr]));
411 glNormal3fv(&(triangleNormals[tid_ptr]));
412 glVertex3fv(&(coords[vid2_ptr]));
413 glEnd();
414 }
415 }
416 }
417 else if (mMRS.isSurface(MRI::Surface::COLOR_VERTEX_TEX)) {
418 glShadeModel(GL_SMOOTH);
419 int n_tris = nt;
420 for (int tid = 0; tid < n_tris; ++tid) {
421 int tid_ptr = 3 * tid;
422 int vid0 = triangles[tid_ptr + 0];
423 int vid1 = triangles[tid_ptr + 1];
424 int vid2 = triangles[tid_ptr + 2];
425 int vid0_ptr = 3 * vid0;
426 int vid1_ptr = 3 * vid1;
427 int vid2_ptr = 3 * vid2;
428 short texture =
429 mTextID[mMRD.vertexTextureIDsBufferData()[tid]];
430 glBindTexture(GL_TEXTURE_2D, texture);
431 glBegin(GL_TRIANGLES);
432 glColor4f(1, 1, 1, 1);
433 glTexCoord2f(
434 vertTexCoords[vid0 * 2 + 0],
435 vertTexCoords[vid0 * 2 + 1]);
436 glNormal3fv(&(vertexNormals[vid0_ptr]));
437 glVertex3fv(&(coords[vid0_ptr]));
438 glTexCoord2f(
439 vertTexCoords[vid1 * 2 + 0],
440 vertTexCoords[vid1 * 2 + 1]);
441 glNormal3fv(&(vertexNormals[vid1_ptr]));
442 glVertex3fv(&(coords[vid1_ptr]));
443 glTexCoord2f(
444 vertTexCoords[vid2 * 2 + 0],
445 vertTexCoords[vid2 * 2 + 1]);
446 glNormal3fv(&(vertexNormals[vid2_ptr]));
447 glVertex3fv(&(coords[vid2_ptr]));
448 glEnd();
449 glBindTexture(GL_TEXTURE_2D, 0);
450 }
451 }
452 else if (mMRS.isSurface(MRI::Surface::COLOR_WEDGE_TEX)) {
453 int n_tris = nt;
454 for (int tid = 0; tid < n_tris; ++tid) {
455 int tid_ptr = 3 * tid;
456 int vid0 = triangles[tid_ptr + 0];
457 int vid1 = triangles[tid_ptr + 1];
458 int vid2 = triangles[tid_ptr + 2];
459 int vid0_ptr = 3 * vid0;
460 int vid1_ptr = 3 * vid1;
461 int vid2_ptr = 3 * vid2;
462 short texture =
463 mTextID[mMRD.wedgeTextureIDsBufferData()[tid]];
464 glBindTexture(GL_TEXTURE_2D, texture);
465 glBegin(GL_TRIANGLES);
466 glColor4f(1, 1, 1, 1);
467 glTexCoord2f(
468 wedgTexCoords[vid0 * 2 + 0],
469 wedgTexCoords[vid0 * 2 + 1]);
470 glNormal3fv(&(vertexNormals[vid0_ptr]));
471 glVertex3fv(&(coords[vid0_ptr]));
472 glTexCoord2f(
473 wedgTexCoords[vid1 * 2 + 0],
474 wedgTexCoords[vid1 * 2 + 1]);
475 glNormal3fv(&(vertexNormals[vid1_ptr]));
476 glVertex3fv(&(coords[vid1_ptr]));
477 glTexCoord2f(
478 wedgTexCoords[vid2 * 2 + 0],
479 wedgTexCoords[vid2 * 2 + 1]);
480 glNormal3fv(&(vertexNormals[vid2_ptr]));
481 glVertex3fv(&(coords[vid2_ptr]));
482 glEnd();
483 glBindTexture(GL_TEXTURE_2D, 0);
484 }
485 }
486 }
487
488 if (mMRS.isWireframe(MRI::Wireframe::VISIBLE)) {
489 glEnableClientState(GL_VERTEX_ARRAY);
490 glVertexPointer(3, GL_FLOAT, 0, coords);
491
492 glLineWidth(mMRS.wireframeWidth());
493
494 if (mMRS.isWireframe(MRI::Wireframe::COLOR_MESH)) {
495 glColor4fv(mMRD.meshColorBufferData());
496 }
497 else {
498 glColor4fv(mMRS.wireframeUserColorData());
499 }
500
501 glDrawElements(GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
502
503 glDisableClientState(GL_VERTEX_ARRAY);
504 }
505 }
506
507 void bindTextures()
508 {
509 mTextID.resize(mMRD.textureNumber());
510 glEnable(GL_TEXTURE_2D);
511 glGenTextures(mMRD.textureNumber(), mTextID.data());
512
513 for (uint i = 0; i < mMRD.textureNumber(); i++) {
514 glBindTexture(GL_TEXTURE_2D, mTextID[i]);
515 glTexImage2D(
516 GL_TEXTURE_2D,
517 0,
518 GL_RGBA,
519 mMRD.textureSize(i).x(),
520 mMRD.textureSize(i).y(),
521 0,
522 GL_RGBA,
523 GL_UNSIGNED_BYTE,
524 mMRD.textureBufferData(i));
525 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
526 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
528 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
529 }
530 }
531
532 void unbindTextures()
533 {
534 if (mTextID.size() > 0) {
535 glDeleteTextures(mTextID.size(), mTextID.data());
536 mTextID.clear();
537 }
538 }
539};
540
541} // namespace vcl
542
543#endif // VCL_OPENGL2_DRAWABLE_DRAWABLE_MESH_H
Definition abstract_drawable_mesh.h:32
The BitSet class allows to treat an integral type as an array of booleans of a guaranteed size.
Definition bit_set.h:53
Definition drawable_mesh.h:83
std::shared_ptr< DrawableObject > clone() const &override
This member function is used to create a new copy of the DrawableObject. Each derived class must impl...
Definition drawable_mesh.h:236
const std::string & name() const override
Returns the name of the object.
Definition drawable_mesh.h:141
void draw(uint) const override
This member function must draw the object. It will be called at every frame.
Definition drawable_mesh.h:162
Box3d boundingBox() const override
This member function is used to find a good camera position to render object. It should return the th...
Definition drawable_mesh.h:234
std::string & name() override
Returns a reference of the name of the object, that allows to modify it.
Definition drawable_mesh.h:139
std::shared_ptr< DrawableObject > clone() &&override
This member function is used to create a new DrawableObject that is a moved from the current one....
Definition drawable_mesh.h:241
void init() override
This member function is called after the initialization of the Context. It must initialize and bind d...
Definition drawable_mesh.h:160
virtual const std::string & name() const
Returns the name of the object.
Definition drawable_object.h:153
The MeshRenderInfo class is a collection of rendering settings for a Mesh.
Definition mesh_render_info.h:61
bool isPoints(MeshRenderInfo::Points p) const
Returns whether the given points option is set.
Definition mesh_render_settings.h:226
bool isVisible() const
Returns whether the mesh is visible.
Definition mesh_render_settings.h:200
bool isSurface(MeshRenderInfo::Surface s) const
Returns whether the given surface option is set.
Definition mesh_render_settings.h:251
bool isWireframe(MeshRenderInfo::Wireframe w) const
Returns whether the given wireframe option is set.
Definition mesh_render_settings.h:271
A class representing a line segment in n-dimensional space. The class is parameterized by a PointConc...
Definition segment.h:43
Concept that is evaluated true if a Mesh has the BoundingBox component.
Definition per_mesh.h:60
Concept that checks if a Mesh has the Name component.
Definition per_mesh.h:95
auto boundingBox(const PointType &p)
Compute the bounding box of a single point.
Definition bounding_box.h:65