Visual Computing Library  devel
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 swap(DrawableMeshOpenGL2& other)
112 {
113 using std::swap;
114 AbstractDrawableMesh::swap(other);
115 MeshType::swap(other);
116 swap(mBoundingBox, other.mBoundingBox);
117 swap(mMRD, other.mMRD);
118 swap(mTextID, other.mTextID);
119 }
120
121 friend void swap(DrawableMeshOpenGL2& a, DrawableMeshOpenGL2& b)
122 {
123 a.swap(b);
124 }
125
126 // AbstractDrawableMesh implementation
127
128 void updateBuffers(
129 MRI::BuffersBitSet buffersToUpdate = MRI::BUFFERS_ALL) override
130 {
131 if constexpr (HasName<MeshType>) {
132 AbstractDrawableMesh::name() = MeshType::name();
133 }
134
136 if constexpr (vcl::HasBoundingBox<MeshType>) {
137 if (this->MeshType::boundingBox().isNull()) {
138 bbToInitialize = true;
139 }
140 else {
141 mBoundingBox =
142 this->MeshType::boundingBox().template cast<double>();
143 }
144 }
145
146 if (bbToInitialize) {
147 mBoundingBox = vcl::boundingBox(*this);
148 }
149
150 unbindTextures();
151 mMRD.update(*this, buffersToUpdate);
152 mMRS.setRenderCapabilityFrom(*this);
153 bindTextures();
154 }
155
156 uint vertexNumber() const override { return MeshType::vertexNumber(); }
157
158 uint faceNumber() const override
159 {
160 if constexpr (HasFaces<MeshType>)
161 return MeshType::faceNumber();
162 else
163 return 0;
164 }
165
166 uint edgeNumber() const override
167 {
168 if constexpr (HasEdges<MeshType>)
169 return MeshType::edgeNumber();
170 else
171 return 0;
172 }
173
174 vcl::Matrix44d transformMatrix() const override
175 {
176 if constexpr (HasTransformMatrix<MeshType>) {
177 return MeshType::transformMatrix().template cast<double>();
178 }
179 else {
181 }
182 }
183
184 std::vector<std::string> textures() const override
185 {
186 std::vector<std::string> txs;
187 if constexpr (HasTexturePaths<MeshType>) {
188 txs.reserve(MeshType::textureNumber());
189 for (const auto& tpath : MeshType::texturePaths()) {
190 txs.push_back(tpath);
191 }
192 }
193 return txs;
194 }
195
196 // DrawableObject implementation
197
198 void init() override { bindTextures(); }
199
200 void draw(uint) const override
201 {
202 if (mMRS.isVisible()) {
203 if (mMRS.isWireframe(MRI::Wireframe::VISIBLE)) {
204 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
208 glDepthRange(0.0, 1.0);
209 renderPass();
211 }
212 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
213 if (mMRS.isSurface(MRI::Surface::SHADING_FLAT)) {
216 glDepthRange(0.01, 1.0);
217 renderPass();
218
221 glDepthRange(0.0, 1.0);
223 renderPass();
226 }
227 else if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
230 glDepthRange(0.01, 1.0);
231 renderPass();
232
235 glDepthRange(0.0, 1.0);
237 renderPass();
240 }
241 }
242 else {
246 glDepthRange(0.0, 1.0);
247 renderPass();
249 }
250 }
251 else { // no wireframe
252 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
254 renderPass();
255 }
256 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
257 if (mMRS.isSurface(MRI::Surface::SHADING_FLAT)) {
260 renderPass();
261 }
262 else if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
265 renderPass();
266 }
267 }
268 }
269 }
270 }
271
272 Box3d boundingBox() const override { return mBoundingBox; }
273
274 std::shared_ptr<DrawableObject> clone() const& override
275 {
276 return std::make_shared<DrawableMeshOpenGL2>(*this);
277 }
278
279 std::shared_ptr<DrawableObject> clone() && override
280 {
281 return std::make_shared<DrawableMeshOpenGL2>(std::move(*this));
282 }
283
284 std::string& name() override { return MeshType::name(); }
285
286 const std::string& name() const override { return MeshType::name(); }
287
288private:
289 void renderPass() const
290 {
291 uint nv = mMRD.vertexNumber();
292 uint nt = mMRD.triangleNumber();
293
294 const float* positions = mMRD.vertexBufferData();
295 const uint32_t* triangles = mMRD.triangleBufferData();
296 const float* vertexNormals = mMRD.vertexNormalBufferData();
297 const uint32_t* vertexColors = mMRD.vertexColorBufferData();
298 const float* triangleNormals = mMRD.triangleNormalBufferData();
299 const uint32_t* triangleColors = mMRD.triangleColorBufferData();
300 const float* vertTexCoords = mMRD.vertexTexCoordsBufferData();
301 const float* wedgTexCoords = mMRD.wedgeTexCoordsBufferData();
302
303 if (mMRS.isPoints(MRI::Points::VISIBLE)) {
305 glVertexPointer(3, GL_FLOAT, 0, positions);
306
307 if (mMRS.isPoints(MRI::Points::COLOR_VERTEX)) {
310 }
311 else if (mMRS.isPoints(MRI::Points::COLOR_MESH)) {
312 glColor4fv(mMRD.meshColorBufferData());
313 }
314 else if (mMRS.isPoints(MRI::Points::COLOR_USER)) {
315 glColor4fv(mMRS.pointUserColorData());
316 }
317
318 glPointSize(mMRS.pointWidth());
319
320 glDrawArrays(GL_POINTS, 0, nv);
321
322 glDisableClientState(GL_COLOR_ARRAY);
323 glDisableClientState(GL_VERTEX_ARRAY);
324 }
325
326 if (mMRS.isSurface(MRI::Surface::VISIBLE)) {
327 // Old fashioned, verbose and slow rendering.
328 if (mMRS.isSurface(MRI::Surface::COLOR_FACE)) {
329 int n_tris = nt;
330 for (int tid = 0; tid < n_tris; ++tid) {
331 int tid_ptr = 3 * tid;
332 int vid0 = triangles[tid_ptr + 0];
333 int vid1 = triangles[tid_ptr + 1];
334 int vid2 = triangles[tid_ptr + 2];
335 int vid0_ptr = 3 * vid0;
336 int vid1_ptr = 3 * vid1;
337 int vid2_ptr = 3 * vid2;
338
339 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
340 glBegin(GL_TRIANGLES);
341 glColor4ubv((GLubyte*) &(triangleColors[tid]));
342 glNormal3fv(&(vertexNormals[vid0_ptr]));
343 glVertex3fv(&(positions[vid0_ptr]));
344 glNormal3fv(&(vertexNormals[vid1_ptr]));
345 glVertex3fv(&(positions[vid1_ptr]));
346 glNormal3fv(&(vertexNormals[vid2_ptr]));
347 glVertex3fv(&(positions[vid2_ptr]));
348 glEnd();
349 }
350 else {
351 glBegin(GL_TRIANGLES);
352 glColor4ubv((GLubyte*) &(triangleColors[tid]));
353 glNormal3fv(&(triangleNormals[tid_ptr]));
354 glVertex3fv(&(positions[vid0_ptr]));
355 glNormal3fv(&(triangleNormals[tid_ptr]));
356 glVertex3fv(&(positions[vid1_ptr]));
357 glNormal3fv(&(triangleNormals[tid_ptr]));
358 glVertex3fv(&(positions[vid2_ptr]));
359 glEnd();
360 }
361 }
362 }
363 else if (mMRS.isSurface(MRI::Surface::COLOR_VERTEX)) {
364 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
365 glEnableClientState(GL_VERTEX_ARRAY);
366 glVertexPointer(3, GL_FLOAT, 0, positions);
367
368 glEnableClientState(GL_NORMAL_ARRAY);
369 glNormalPointer(GL_FLOAT, 0, vertexNormals);
370
371 glEnableClientState(GL_COLOR_ARRAY);
372 glColorPointer(4, GL_UNSIGNED_BYTE, 0, vertexColors);
373
374 glDrawElements(
375 GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
376
377 glDisableClientState(GL_COLOR_ARRAY);
378 glDisableClientState(GL_NORMAL_ARRAY);
379 glDisableClientState(GL_VERTEX_ARRAY);
380 }
381 else {
382 glShadeModel(GL_SMOOTH);
383 int n_tris = nt;
384 for (int tid = 0; tid < n_tris; ++tid) {
385 int tid_ptr = 3 * tid;
386 int vid0 = triangles[tid_ptr + 0];
387 int vid1 = triangles[tid_ptr + 1];
388 int vid2 = triangles[tid_ptr + 2];
389 int vid0_ptr = 3 * vid0;
390 int vid1_ptr = 3 * vid1;
391 int vid2_ptr = 3 * vid2;
392
393 glBegin(GL_TRIANGLES);
394 glColor4ubv((GLubyte*) &(vertexColors[vid0]));
395 glNormal3fv(&(triangleNormals[tid_ptr]));
396 glVertex3fv(&(positions[vid0_ptr]));
397 glColor4ubv((GLubyte*) &(vertexColors[vid1]));
398 glNormal3fv(&(triangleNormals[tid_ptr]));
399 glVertex3fv(&(positions[vid1_ptr]));
400 glColor4ubv((GLubyte*) &(vertexColors[vid2]));
401 glNormal3fv(&(triangleNormals[tid_ptr]));
402 glVertex3fv(&(positions[vid2_ptr]));
403 glEnd();
404 }
405 }
406 }
407 else if (
408 mMRS.isSurface(MRI::Surface::COLOR_MESH) ||
409 mMRS.isSurface(MRI::Surface::COLOR_USER)) {
410 if (mMRS.isSurface(MRI::Surface::SHADING_SMOOTH)) {
411 glEnableClientState(GL_VERTEX_ARRAY);
412 glVertexPointer(3, GL_FLOAT, 0, positions);
413
414 glEnableClientState(GL_NORMAL_ARRAY);
415 glNormalPointer(GL_FLOAT, 0, vertexNormals);
416
417 if (mMRS.isSurface(MRI::Surface::COLOR_MESH)) {
418 glColor4fv(mMRD.meshColorBufferData());
419 }
420 else {
421 glColor4ubv((GLubyte*) mMRS.surfaceUserColorData());
422 }
423
424 glDrawElements(
425 GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
426
427 glDisableClientState(GL_COLOR_ARRAY);
428 glDisableClientState(GL_NORMAL_ARRAY);
429 glDisableClientState(GL_VERTEX_ARRAY);
430 }
431 else {
432 if (mMRS.isSurface(MRI::Surface::COLOR_MESH)) {
433 glColor4fv(mMRD.meshColorBufferData());
434 }
435 else {
436 glColor4ubv((GLubyte*) mMRS.surfaceUserColorData());
437 }
438 int n_tris = nt;
439 for (int tid = 0; tid < n_tris; ++tid) {
440 int tid_ptr = 3 * tid;
441 int vid0 = triangles[tid_ptr + 0];
442 int vid1 = triangles[tid_ptr + 1];
443 int vid2 = triangles[tid_ptr + 2];
444 int vid0_ptr = 3 * vid0;
445 int vid1_ptr = 3 * vid1;
446 int vid2_ptr = 3 * vid2;
447
448 glBegin(GL_TRIANGLES);
449 glNormal3fv(&(triangleNormals[tid_ptr]));
450 glVertex3fv(&(positions[vid0_ptr]));
451 glNormal3fv(&(triangleNormals[tid_ptr]));
452 glVertex3fv(&(positions[vid1_ptr]));
453 glNormal3fv(&(triangleNormals[tid_ptr]));
454 glVertex3fv(&(positions[vid2_ptr]));
455 glEnd();
456 }
457 }
458 }
459 else if (mMRS.isSurface(MRI::Surface::COLOR_VERTEX_TEX)) {
460 glShadeModel(GL_SMOOTH);
461 int n_tris = nt;
462 for (int tid = 0; tid < n_tris; ++tid) {
463 int tid_ptr = 3 * tid;
464 int vid0 = triangles[tid_ptr + 0];
465 int vid1 = triangles[tid_ptr + 1];
466 int vid2 = triangles[tid_ptr + 2];
467 int vid0_ptr = 3 * vid0;
468 int vid1_ptr = 3 * vid1;
469 int vid2_ptr = 3 * vid2;
470 short texture =
471 mTextID[mMRD.vertexTextureIDsBufferData()[tid]];
472 glBindTexture(GL_TEXTURE_2D, texture);
473 glBegin(GL_TRIANGLES);
474 glColor4f(1, 1, 1, 1);
475 glTexCoord2f(
476 vertTexCoords[vid0 * 2 + 0],
477 vertTexCoords[vid0 * 2 + 1]);
478 glNormal3fv(&(vertexNormals[vid0_ptr]));
479 glVertex3fv(&(positions[vid0_ptr]));
480 glTexCoord2f(
481 vertTexCoords[vid1 * 2 + 0],
482 vertTexCoords[vid1 * 2 + 1]);
483 glNormal3fv(&(vertexNormals[vid1_ptr]));
484 glVertex3fv(&(positions[vid1_ptr]));
485 glTexCoord2f(
486 vertTexCoords[vid2 * 2 + 0],
487 vertTexCoords[vid2 * 2 + 1]);
488 glNormal3fv(&(vertexNormals[vid2_ptr]));
489 glVertex3fv(&(positions[vid2_ptr]));
490 glEnd();
491 glBindTexture(GL_TEXTURE_2D, 0);
492 }
493 }
494 else if (mMRS.isSurface(MRI::Surface::COLOR_WEDGE_TEX)) {
495 int n_tris = nt;
496 for (int tid = 0; tid < n_tris; ++tid) {
497 int tid_ptr = 3 * tid;
498 int vid0 = triangles[tid_ptr + 0];
499 int vid1 = triangles[tid_ptr + 1];
500 int vid2 = triangles[tid_ptr + 2];
501 int vid0_ptr = 3 * vid0;
502 int vid1_ptr = 3 * vid1;
503 int vid2_ptr = 3 * vid2;
504 short texture =
505 mTextID[mMRD.wedgeTextureIDsBufferData()[tid]];
506 glBindTexture(GL_TEXTURE_2D, texture);
507 glBegin(GL_TRIANGLES);
508 glColor4f(1, 1, 1, 1);
509 glTexCoord2f(
510 wedgTexCoords[vid0 * 2 + 0],
511 wedgTexCoords[vid0 * 2 + 1]);
512 glNormal3fv(&(vertexNormals[vid0_ptr]));
513 glVertex3fv(&(positions[vid0_ptr]));
514 glTexCoord2f(
515 wedgTexCoords[vid1 * 2 + 0],
516 wedgTexCoords[vid1 * 2 + 1]);
517 glNormal3fv(&(vertexNormals[vid1_ptr]));
518 glVertex3fv(&(positions[vid1_ptr]));
519 glTexCoord2f(
520 wedgTexCoords[vid2 * 2 + 0],
521 wedgTexCoords[vid2 * 2 + 1]);
522 glNormal3fv(&(vertexNormals[vid2_ptr]));
523 glVertex3fv(&(positions[vid2_ptr]));
524 glEnd();
525 glBindTexture(GL_TEXTURE_2D, 0);
526 }
527 }
528 }
529
530 if (mMRS.isWireframe(MRI::Wireframe::VISIBLE)) {
531 glEnableClientState(GL_VERTEX_ARRAY);
532 glVertexPointer(3, GL_FLOAT, 0, positions);
533
534 glLineWidth(mMRS.wireframeWidth());
535
536 if (mMRS.isWireframe(MRI::Wireframe::COLOR_MESH)) {
537 glColor4fv(mMRD.meshColorBufferData());
538 }
539 else {
540 glColor4fv(mMRS.wireframeUserColorData());
541 }
542
543 glDrawElements(GL_TRIANGLES, nt * 3, GL_UNSIGNED_INT, triangles);
544
545 glDisableClientState(GL_VERTEX_ARRAY);
546 }
547 }
548
549 void bindTextures()
550 {
551 mTextID.resize(mMRD.textureNumber());
552 glEnable(GL_TEXTURE_2D);
553 glGenTextures(mMRD.textureNumber(), mTextID.data());
554
555 for (uint i = 0; i < mMRD.textureNumber(); i++) {
556 glBindTexture(GL_TEXTURE_2D, mTextID[i]);
557 glTexImage2D(
558 GL_TEXTURE_2D,
559 0,
560 GL_RGBA,
561 mMRD.textureSize(i).x(),
562 mMRD.textureSize(i).y(),
563 0,
564 GL_RGBA,
565 GL_UNSIGNED_BYTE,
566 mMRD.textureBufferData(i));
567 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
568 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
569 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
570 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
571 }
572 }
573
574 void unbindTextures()
575 {
576 if (mTextID.size() > 0) {
577 glDeleteTextures(mTextID.size(), mTextID.data());
578 mTextID.clear();
579 }
580 }
581};
582
583} // namespace vcl
584
585#endif // VCL_OPENGL2_DRAWABLE_DRAWABLE_MESH_H
The AbstractDrawableMesh class is the base class for all the drawable meshes in the VCLib render syst...
Definition abstract_drawable_mesh.h:41
The BitSet class allows to treat an integral type as an array of booleans of a guaranteed size.
Definition bit_set.h:52
Box()
The Empty constructor of a box, initializes a null box.
Definition box.h:65
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:274
const std::string & name() const override
Returns the name of the object.
Definition drawable_mesh.h:286
void draw(uint) const override
This member function must draw the object. It will be called at every frame.
Definition drawable_mesh.h:200
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:272
std::string & name() override
Returns a reference of the name of the object, that allows to modify it.
Definition drawable_mesh.h:284
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:279
void init() override
This member function is called after the initialization of the Context. It must initialize and bind d...
Definition drawable_mesh.h:198
virtual const std::string & name() const
Returns the name of the object.
Definition drawable_object.h:164
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:225
bool isVisible() const
Returns whether the mesh is visible.
Definition mesh_render_settings.h:199
bool isSurface(MeshRenderInfo::Surface s) const
Returns whether the given surface option is set.
Definition mesh_render_settings.h:250
bool isWireframe(MeshRenderInfo::Wireframe w) const
Returns whether the given wireframe option is set.
Definition mesh_render_settings.h:270
Concept that is evaluated true if a Mesh has the BoundingBox component.
Definition mesh_requirements.h:53
HasEdges concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition edge_container.h:1064
HasFaces concepts is satisfied when at least one of its template types is (or inherits from) a vcl::m...
Definition face_container.h:1389
Concept that checks if a Mesh has the Name component.
Definition mesh_requirements.h:88
Concept that checks if a Mesh has the TexturePaths component.
Definition mesh_requirements.h:105
Concept that checks if a Mesh has the TransformMatrix component.
Definition mesh_requirements.h:114
auto boundingBox(const PointType &p)
Compute the bounding box of a single point.
Definition bounding_box.h:59