GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/mesh/mesh_gfx.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 1095 0.0%
Functions: 0 79 0.0%
Branches: 0 760 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram_gfx/mesh/mesh_gfx.h>
41 #include <geogram_gfx/GLUP/GLUP_private.h>
42 #include <geogram_gfx/basic/GLSL.h>
43
44 #include <geogram/basic/logger.h>
45 #include <geogram/basic/command_line.h>
46
47
48 // TODO: implement attribute display for cell facets.
49 // TODO: use vertex arrays for attribute display for
50 // vertex attributes whenever possible.
51
52 namespace GEO {
53
54 MeshGfx::MeshGfx() {
55 show_mesh_ = true;
56 mesh_width_ = 1;
57 mesh_border_width_ = 2;
58 shrink_ = 0.0;
59 animate_ = false;
60 time_ = 0.0;
61 draw_cells_[MESH_TET] = true;
62 draw_cells_[MESH_HEX] = true;
63 draw_cells_[MESH_PRISM] = true;
64 draw_cells_[MESH_PYRAMID] = true;
65 draw_cells_[MESH_CONNECTOR] = true;
66 points_size_ = 1.0f;
67 set_points_color(0.0f, 1.0f, 0.0f);
68 set_mesh_color(0.0f, 0.0f, 0.0f);
69 set_surface_color(0.0f, 0.5f, 1.0f);
70 set_backface_surface_color(1.0f, 0.0f, 0.0f);
71 set_cells_color(0.9f, 0.9f, 0.9f);
72 cells_colors_by_type_ = false;
73 lighting_ = true;
74 picking_mode_ = MESH_NONE;
75 object_picking_id_ = NO_INDEX;
76 mesh_ = nullptr;
77 triangles_and_quads_ = true;
78 quads_ = true;
79 for(index_t type=0; type<MESH_NB_CELL_TYPES; ++type) {
80 has_cells_[type] = false;
81 }
82
83 buffer_objects_dirty_ = false;
84 attributes_buffer_objects_dirty_ = false;
85 long_vector_attribute_ = false;
86
87 vertices_VAO_ = 0;
88 edges_VAO_ = 0;
89 facets_VAO_ = 0;
90 cells_VAO_ = 0;
91
92 vertices_VBO_ = 0;
93 edge_indices_VBO_ = 0;
94 facet_indices_VBO_ = 0;
95 cell_indices_VBO_ = 0;
96 vertices_attribute_VBO_ = 0;
97
98 do_animation_ = false;
99
100 attribute_subelements_ = MESH_NONE;
101 attribute_min_ = 0.0;
102 attribute_max_ = 0.0;
103 attribute_texture_ = 0;
104 attribute_repeat_ = 1;
105 attribute_dim_ = 1;
106
107 ES_profile_ = false;
108 }
109
110 void MeshGfx::cleanup() {
111 if(vertices_VAO_ != 0) {
112 glupDeleteVertexArrays(1,&vertices_VAO_);
113 vertices_VAO_ = 0;
114 }
115
116 if(edges_VAO_ != 0) {
117 glupDeleteVertexArrays(1,&edges_VAO_);
118 edges_VAO_ = 0;
119 }
120
121 if(facets_VAO_ != 0) {
122 glupDeleteVertexArrays(1,&facets_VAO_);
123 facets_VAO_ = 0;
124 }
125
126 if(cells_VAO_ != 0) {
127 glupDeleteVertexArrays(1,&cells_VAO_);
128 cells_VAO_ = 0;
129 }
130
131 if(vertices_VBO_ != 0) {
132 glDeleteBuffers(1,&vertices_VBO_);
133 vertices_VBO_ = 0;
134 }
135
136 if(edge_indices_VBO_ != 0) {
137 glDeleteBuffers(1,&edge_indices_VBO_);
138 edge_indices_VBO_ = 0;
139 }
140
141 if(facet_indices_VBO_ != 0) {
142 glDeleteBuffers(1,&facet_indices_VBO_);
143 facet_indices_VBO_ = 0;
144 }
145
146 if(cell_indices_VBO_ != 0) {
147 glDeleteBuffers(1,&cell_indices_VBO_);
148 cell_indices_VBO_ = 0;
149 }
150
151 if(vertices_attribute_VBO_ != 0) {
152 glDeleteBuffers(1,&vertices_attribute_VBO_);
153 vertices_attribute_VBO_ = 0;
154 }
155 buffer_objects_dirty_ = true;
156 attributes_buffer_objects_dirty_ = true;
157 vertices_filter_.dirty = true;
158 edges_filter_.dirty = true;
159 facets_filter_.dirty = true;
160 cells_filter_.dirty = true;
161 vertices_selection_filter_.dirty = true;
162 }
163
164 MeshGfx::~MeshGfx() {
165 cleanup();
166 }
167
168 bool MeshGfx::can_use_array_mode(GLUPprimitive prim) const {
169 if(do_animation_) {
170 return false;
171 }
172
173 // Special case: GLUPES2 can use array mode for triangles, but
174 // not with mesh and not with facet shrink.
175 if(
176 prim == GLUP_TRIANGLES &&
177 ES_profile_ &&
178 (show_mesh_ || shrink_ != 0.0)
179 ) {
180 return false;
181 }
182
183 // Special case: GLUPES2 can use array mode for lines, but
184 // not if width > 1
185 if(
186 prim == GLUP_LINES &&
187 ES_profile_ &&
188 (mesh_width_ > 1)
189 ) {
190 return false;
191 }
192
193 if(!glupPrimitiveSupportsArrayMode(prim)) {
194 return false;
195 }
196
197 if(
198 attribute_subelements_ != MESH_NONE &&
199 attribute_subelements_ != MESH_VERTICES
200 ) {
201 return false;
202 }
203 #ifdef GEO_GL_NO_DOUBLES
204 // If there is an attribute bound, then return false,
205 // this will force switching to immediate mode.
206 if(attribute_subelements_ != MESH_NONE) {
207 return false;
208 }
209 #endif
210 if(long_vector_attribute_) {
211 return false;
212 }
213
214 // For now, texturing is only implemented in
215 // immediate mode (TODO: implement tex coords
216 // in vertex array objects).
217 if(attribute_dim_ > 1) {
218 return false;
219 }
220
221 return true;
222 }
223
224
225 /*********************************** vertices ***************/
226
227 void MeshGfx::draw_vertices_immediate_plain() {
228 draw_sequences(
229 mesh_->vertices,
230 [&](index_t begin_v, index_t end_v) {
231 glupBegin(GLUP_POINTS);
232 for(index_t v=begin_v; v<end_v; ++v) {
233 draw_vertex(v);
234 }
235 glupEnd();
236 }
237 );
238 }
239
240 void MeshGfx::draw_vertices_immediate_attrib() {
241 begin_attributes();
242 draw_sequences(
243 mesh_->vertices,
244 [&](index_t begin_v, index_t end_v) {
245 glupBegin(GLUP_POINTS);
246 for(index_t v=begin_v; v<end_v; ++v) {
247 draw_vertex_with_attribute(v);
248 }
249 glupEnd();
250 }
251 );
252 end_attributes();
253 }
254
255 void MeshGfx::draw_vertices_array() {
256 glupBindVertexArray(vertices_VAO_);
257 if(attribute_subelements_ == MESH_VERTICES) {
258 begin_attributes();
259 }
260 vertices_filter_.begin(mesh_->vertices.attributes());
261 glupDrawArrays(GLUP_POINTS, 0, GLUPsizei(mesh_->vertices.nb()));
262 vertices_filter_.end();
263 if(attribute_subelements_ == MESH_VERTICES) {
264 end_attributes();
265 }
266 glupBindVertexArray(0);
267 }
268
269 void MeshGfx::draw_vertices_selection() {
270 // Fast mode, using a single draw call, unselected vertices are
271 // filtered-out using hardware filtering. Also works for picking
272 // since IDs are correct (extracted from gl_PrimitiveID).
273 if(hw_filtering_supported()) {
274 if(
275 !Attribute<bool>::is_defined(
276 mesh_->vertices.attributes(), vertices_selection_
277 )
278 ) {
279 return;
280 }
281 glupBindVertexArray(vertices_VAO_);
282 vertices_selection_filter_.begin(mesh_->vertices.attributes());
283 glupDrawArrays(GLUP_POINTS, 0, GLUPsizei(mesh_->vertices.nb()));
284 vertices_selection_filter_.end();
285 glupBindVertexArray(0);
286 return;
287 }
288
289 // If hw filtering is not supported, use immediate mode.
290 // Note: picking ID would be incorrect, so ignore in picking mode
291 // (vertices can be picked anyway).
292 if(picking_mode_ != MESH_NONE) {
293 return;
294 }
295
296 Attribute<bool> v_selection;
297 v_selection.bind_if_is_defined(
298 mesh_->vertices.attributes(), vertices_selection_
299 );
300 if(!v_selection.is_bound()) {
301 return;
302 }
303 glupBegin(GLUP_POINTS);
304 for(index_t v: mesh_->vertices) {
305 if(v_selection[v]) {
306 draw_vertex(v);
307 }
308 }
309 glupEnd();
310 }
311
312 void MeshGfx::draw_vertices() {
313 if(mesh_ == nullptr || mesh_->vertices.nb() == 0) {
314 return;
315 }
316
317 set_GLUP_parameters();
318 set_GLUP_picking(MESH_VERTICES);
319 update_buffer_objects_if_needed();
320
321 glupSetColor4fv(GLUP_FRONT_COLOR, points_color_);
322 glupSetPointSize(points_size_ * 5.0f);
323
324 if(vertices_selection_ == "") {
325 if(
326 can_use_array_mode(GLUP_POINTS) && vertices_VAO_ != 0
327 ) {
328 draw_vertices_array();
329 } else {
330 if(attribute_subelements_ == MESH_VERTICES) {
331 draw_vertices_immediate_attrib();
332 } else {
333 draw_vertices_immediate_plain();
334 }
335 }
336 } else {
337 draw_vertices_selection();
338 }
339
340 glupDisable(GLUP_PICKING);
341 }
342
343 /*********************************** edges ***************/
344
345 void MeshGfx::draw_edges_array() {
346 glupBindVertexArray(edges_VAO_);
347 if(attribute_subelements_ == MESH_VERTICES) {
348 begin_attributes();
349 }
350 edges_filter_.begin(mesh_->edges.attributes());
351 glupDrawElements(
352 GLUP_LINES,
353 GLUPsizei(mesh_->edges.nb()*2),
354 GL_UNSIGNED_INT,
355 nullptr
356 );
357 edges_filter_.end();
358 if(attribute_subelements_ == MESH_VERTICES) {
359 end_attributes();
360 }
361 glupBindVertexArray(0);
362 }
363
364 void MeshGfx::draw_edges_immediate_plain() {
365 edges_filter_.begin(mesh_->edges.attributes(), false);
366 glupBegin(GLUP_LINES);
367 for(index_t e: mesh_->edges) {
368 if(!edges_filter_.test(e)) {
369 continue;
370 }
371 index_t v1 = mesh_->edges.vertex(e,0);
372 index_t v2 = mesh_->edges.vertex(e,1);
373 draw_vertex(v1);
374 draw_vertex(v2);
375 }
376 glupEnd();
377 edges_filter_.end();
378 }
379
380 void MeshGfx::draw_edges_immediate_attrib() {
381 edges_filter_.begin(mesh_->edges.attributes(), false);
382 begin_attributes();
383 if(attribute_subelements_ == MESH_VERTICES) {
384 glupBegin(GLUP_LINES);
385 for(index_t e: mesh_->edges) {
386 if(!edges_filter_.test(e)) {
387 continue;
388 }
389 index_t v1 = mesh_->edges.vertex(e,0);
390 index_t v2 = mesh_->edges.vertex(e,1);
391 draw_vertex_with_attribute(v1);
392 draw_vertex_with_attribute(v2);
393 }
394 glupEnd();
395 } else if(attribute_subelements_ == MESH_EDGES) {
396 glupBegin(GLUP_LINES);
397 for(index_t e: mesh_->edges) {
398 if(!edges_filter_.test(e)) {
399 continue;
400 }
401 index_t v1 = mesh_->edges.vertex(e,0);
402 index_t v2 = mesh_->edges.vertex(e,1);
403 draw_attribute_as_tex_coord(e);
404 draw_vertex(v1);
405 draw_vertex(v2);
406 }
407 glupEnd();
408 }
409 end_attributes();
410 edges_filter_.end();
411 }
412
413 void MeshGfx::draw_edges_picking_filter() {
414 index_t cur_picking_id = 0;
415 edges_filter_.begin(mesh_->edges.attributes(), false);
416 glupBegin(GLUP_LINES);
417 for(index_t e: mesh_->edges) {
418 if(!edges_filter_.test(e)) {
419 continue;
420 }
421 if(cur_picking_id != e) {
422 glupEnd();
423 glupBasePickingId(e);
424 cur_picking_id = e;
425 glupBegin(GLUP_LINES);
426 }
427 index_t v1 = mesh_->edges.vertex(e,0);
428 index_t v2 = mesh_->edges.vertex(e,1);
429 draw_vertex(v1);
430 draw_vertex(v2);
431 ++cur_picking_id;
432 }
433 glupEnd();
434 edges_filter_.end();
435 glupBasePickingId(0);
436 }
437
438
439 void MeshGfx::draw_edges() {
440 if(mesh_ == nullptr || mesh_->edges.nb() == 0) {
441 return;
442 }
443
444 set_GLUP_parameters();
445 // TODO: maybe reactivate if we implement nice shaded cylinders
446 glupDisable(GLUP_LIGHTING);
447 set_GLUP_picking(MESH_EDGES); // HERE
448 update_buffer_objects_if_needed();
449
450 glupSetColor4fv(GLUP_FRONT_COLOR, mesh_color_);
451 glupSetMeshWidth(GLUPint(mesh_width_));
452
453 if(
454 glupIsEnabled(GLUP_PICKING) &&
455 glupGetPickingMode() == GLUP_PICK_PRIMITIVE &&
456 edges_filter_.attribute_name != ""
457 ) {
458 draw_edges_picking_filter();
459 } else if(can_use_array_mode(GLUP_LINES) && edges_VAO_ != 0) {
460 draw_edges_array();
461 } else {
462 if(attribute_subelements_ == MESH_VERTICES ||
463 attribute_subelements_ == MESH_EDGES) {
464 draw_edges_immediate_attrib();
465 } else {
466 draw_edges_immediate_plain();
467 }
468 }
469 }
470
471 /******************************************************************/
472
473 void MeshGfx::draw_triangles() {
474 if(can_use_array_mode(GLUP_TRIANGLES) && facets_VAO_ != 0) {
475 draw_triangles_array();
476 } else {
477 if(
478 attribute_subelements_ == MESH_VERTICES ||
479 attribute_subelements_ == MESH_FACETS ||
480 attribute_subelements_ == MESH_FACET_CORNERS
481 ) {
482 draw_triangles_immediate_attrib();
483 } else {
484 draw_triangles_immediate_plain();
485 }
486 }
487 }
488
489 void MeshGfx::draw_triangles_array() {
490 glupBindVertexArray(facets_VAO_);
491 if(attribute_subelements_ == MESH_VERTICES) {
492 begin_attributes();
493 }
494 facets_filter_.begin(mesh_->facets.attributes());
495 glupDrawElements(
496 GLUP_TRIANGLES,
497 GLUPsizei(mesh_->facets.nb()*3),
498 GL_UNSIGNED_INT,
499 nullptr
500 );
501 facets_filter_.end();
502 if(attribute_subelements_ == MESH_VERTICES) {
503 end_attributes();
504 }
505 glupBindVertexArray(0);
506 }
507
508 void MeshGfx::draw_triangles_immediate_plain() {
509 // If filter is active, use generic code
510 if(facets_filter_.attribute_name != "") {
511 draw_sequences(
512 mesh_->facets,
513 [&](index_t begin_f, index_t end_f) {
514 glupBegin(GLUP_TRIANGLES);
515 for(index_t f=begin_f; f<end_f; ++f) {
516 draw_vertex(mesh_->facets.vertex(f,0));
517 draw_vertex(mesh_->facets.vertex(f,1));
518 draw_vertex(mesh_->facets.vertex(f,2));
519 }
520 glupEnd();
521 }
522 );
523 return;
524 }
525
526 // Optimized code for triangle surface with no attribute, single
527 // and double precision. Writes mesh data directly in GLUP buffers.
528 glupBegin(GLUP_TRIANGLES);
529 if(!do_animation_ && mesh_->vertices.dimension() >= 3) {
530 GLUP::Context* context = (GLUP::Context*)(glupCurrentContext());
531 GLUP::ImmediateState& state = context->immediate_state();
532 GLUP::ImmediateBuffer& buffer =
533 state.buffer[GLUP::GLUP_VERTEX_ATTRIBUTE];
534 if(mesh_->vertices.single_precision()) {
535 index_t t1 = 0;
536 while(t1 < mesh_->facets.nb()) {
537 index_t t2 = t1 + (state.max_current_vertex()/3);
538 t2 = std::min(t2, mesh_->facets.nb());
539 GLfloat* current_vertex = buffer.data();
540 for(index_t t=t1; t<t2; ++t) {
541 for(index_t lv=0; lv<3; ++lv) {
542 index_t v = mesh_->facets.vertex(t,lv);
543 const float* p = mesh_->vertices.
544 single_precision_point_ptr(v);
545 current_vertex[0] = p[0];
546 current_vertex[1] = p[1];
547 current_vertex[2] = p[2];
548 current_vertex[3] = 1.0f;
549 current_vertex += 4;
550 }
551 }
552 state.set_current_vertex(3*(t2-t1));
553 context->flush_immediate_buffers();
554 t1 = t2;
555 }
556 } else {
557 index_t t1 = 0;
558 while(t1 < mesh_->facets.nb()) {
559 index_t t2 = t1 + (state.max_current_vertex()/3);
560 t2 = std::min(t2, mesh_->facets.nb());
561 GLfloat* current_vertex = buffer.data();
562 for(index_t t=t1; t<t2; ++t) {
563 for(index_t lv=0; lv<3; ++lv) {
564 index_t v = mesh_->facets.vertex(t,lv);
565 const double* p = mesh_->vertices.point_ptr(v);
566 current_vertex[0] = float(p[0]);
567 current_vertex[1] = float(p[1]);
568 current_vertex[2] = float(p[2]);
569 current_vertex[3] = 1.0f;
570 current_vertex += 4;
571 }
572 }
573 state.set_current_vertex(3*(t2-t1));
574 context->flush_immediate_buffers();
575 t1 = t2;
576 }
577 }
578 } else {
579 for(index_t t: mesh_->facets) {
580 draw_vertex(mesh_->facets.vertex(t,0));
581 draw_vertex(mesh_->facets.vertex(t,1));
582 draw_vertex(mesh_->facets.vertex(t,2));
583 }
584 }
585 glupEnd();
586 }
587
588 void MeshGfx::draw_triangles_immediate_attrib() {
589 begin_attributes();
590 draw_sequences(
591 mesh_->facets,
592 [&](index_t begin_f, index_t end_f) {
593 glupBegin(GLUP_TRIANGLES);
594 for(index_t f=begin_f; f<end_f; ++f) {
595 for(index_t c: mesh_->facets.corners(f)) {
596 index_t v=mesh_->facet_corners.vertex(c);
597 draw_surface_vertex_with_attribute(v,f,c);
598 }
599 }
600 glupEnd();
601 }
602 );
603 end_attributes();
604 }
605
606 void MeshGfx::draw_quads() {
607 if(can_use_array_mode(GLUP_QUADS) && facets_VAO_ != 0) {
608 draw_quads_array();
609 } else {
610 if(
611 attribute_subelements_ == MESH_VERTICES ||
612 attribute_subelements_ == MESH_FACETS ||
613 attribute_subelements_ == MESH_FACET_CORNERS
614 ) {
615 draw_quads_immediate_attrib();
616 } else {
617 draw_quads_immediate_plain();
618 }
619 }
620 }
621
622 void MeshGfx::draw_quads_array() {
623 glupBindVertexArray(facets_VAO_);
624 if(attribute_subelements_ == MESH_VERTICES) {
625 begin_attributes();
626 }
627 facets_filter_.begin(mesh_->facets.attributes());
628 glupDrawElements(
629 GLUP_QUADS,
630 GLUPsizei(mesh_->facets.nb()*4),
631 GL_UNSIGNED_INT,
632 nullptr
633 );
634 facets_filter_.end();
635 if(attribute_subelements_ == MESH_VERTICES) {
636 end_attributes();
637 }
638 glupBindVertexArray(0);
639 }
640
641 void MeshGfx::draw_quads_immediate_plain() {
642 draw_sequences(
643 mesh_->facets,
644 [&](index_t begin_f, index_t end_f) {
645 glupBegin(GLUP_QUADS);
646 for(index_t q=begin_f; q<end_f; ++q) {
647 draw_vertex(mesh_->facets.vertex(q,0));
648 draw_vertex(mesh_->facets.vertex(q,1));
649 draw_vertex(mesh_->facets.vertex(q,2));
650 draw_vertex(mesh_->facets.vertex(q,3));
651 }
652 glupEnd();
653 }
654 );
655 }
656
657 void MeshGfx::draw_quads_immediate_attrib() {
658 begin_attributes();
659 draw_sequences(
660 mesh_->facets,
661 [&](index_t begin_f, index_t end_f) {
662 glupBegin(GLUP_QUADS);
663 for(index_t q=begin_f; q<end_f; ++q) {
664 for(index_t c: mesh_->facets.corners(q)) {
665 index_t v=mesh_->facet_corners.vertex(c);
666 draw_surface_vertex_with_attribute(v,q,c);
667 }
668 }
669 glupEnd();
670 }
671 );
672 end_attributes();
673 }
674
675 void MeshGfx::draw_triangles_and_quads() {
676
677 if(picking_mode_ != MESH_NONE) {
678 draw_polygons_plain();
679 return;
680 }
681
682 if(
683 can_use_array_mode(GLUP_TRIANGLES) &&
684 can_use_array_mode(GLUP_QUADS) &&
685 facets_VAO_ != 0
686 ) {
687 draw_triangles_and_quads_array();
688 } else {
689 if(
690 attribute_subelements_ == MESH_VERTICES ||
691 attribute_subelements_ == MESH_FACETS ||
692 attribute_subelements_ == MESH_FACET_CORNERS
693 ) {
694 draw_triangles_and_quads_immediate_attrib();
695 } else {
696 draw_triangles_and_quads_immediate_plain();
697 }
698 }
699 }
700
701 void MeshGfx::draw_triangles_and_quads_array() {
702
703 // Note: to go faster, here we could draw sequences of triangles
704 // and quads without taking filtering into account and do the
705 // filtering in hw (but well difference will not be so important).
706
707 glupBindVertexArray(facets_VAO_);
708 if(attribute_subelements_ == MESH_VERTICES) {
709 begin_attributes();
710 }
711
712 // draw triangles
713 draw_sequences_if(
714 mesh_->facets,
715 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 3); },
716 [&](index_t begin_f, index_t end_f) {
717 glupDrawElements(
718 GLUP_TRIANGLES,
719 GLUPsizei((end_f-begin_f)*3),
720 GL_UNSIGNED_INT,
721 (GLUPvoid*)(
722 mesh_->facets.corners_begin(begin_f) * sizeof(index_t)
723 )
724 );
725 }
726 );
727
728 // draw quads
729 draw_sequences_if(
730 mesh_->facets,
731 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 4); },
732 [&](index_t begin_f, index_t end_f) {
733 glupDrawElements(
734 GLUP_QUADS,
735 GLUPsizei((end_f-begin_f)*4),
736 GL_UNSIGNED_INT,
737 (GLUPvoid*)(
738 mesh_->facets.corners_begin(begin_f) * sizeof(index_t)
739 )
740 );
741 }
742 );
743
744 if(attribute_subelements_ == MESH_VERTICES) {
745 end_attributes();
746 }
747 glupBindVertexArray(0);
748 }
749
750 void MeshGfx::draw_triangles_and_quads_immediate_plain() {
751
752 // draw triangles
753 draw_sequences_if(
754 mesh_->facets,
755 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 3); },
756 [&](index_t begin_f, index_t end_f) {
757 glupBegin(GLUP_TRIANGLES);
758 for(index_t t = begin_f; t < end_f; ++t) {
759 draw_vertex(mesh_->facets.vertex(t,0));
760 draw_vertex(mesh_->facets.vertex(t,1));
761 draw_vertex(mesh_->facets.vertex(t,2));
762 }
763 glupEnd();
764 }
765 );
766
767 // draw quads
768 draw_sequences_if(
769 mesh_->facets,
770 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 4); },
771 [&](index_t begin_f, index_t end_f) {
772 glupBegin(GLUP_QUADS);
773 for(index_t q = begin_f; q < end_f; ++q) {
774 draw_vertex(mesh_->facets.vertex(q,0));
775 draw_vertex(mesh_->facets.vertex(q,1));
776 draw_vertex(mesh_->facets.vertex(q,2));
777 draw_vertex(mesh_->facets.vertex(q,3));
778 }
779 glupEnd();
780 }
781 );
782 }
783
784 void MeshGfx::draw_triangles_and_quads_immediate_attrib() {
785 begin_attributes();
786
787 // draw triangles
788 draw_sequences_if(
789 mesh_->facets,
790 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 3); },
791 [&](index_t begin_f, index_t end_f) {
792 glupBegin(GLUP_TRIANGLES);
793 for(index_t f = begin_f; f < end_f; ++f) {
794 for(index_t c: mesh_->facets.corners(f)) {
795 index_t v=mesh_->facet_corners.vertex(c);
796 draw_surface_vertex_with_attribute(v,f,c);
797 }
798 }
799 glupEnd();
800 }
801 );
802
803 // draw quads
804 draw_sequences_if(
805 mesh_->facets,
806 [&](index_t f) { return (mesh_->facets.nb_vertices(f) == 4); },
807 [&](index_t begin_f, index_t end_f) {
808 glupBegin(GLUP_QUADS);
809 for(index_t f = begin_f; f < end_f; ++f) {
810 for(index_t c: mesh_->facets.corners(f)) {
811 index_t v=mesh_->facet_corners.vertex(c);
812 draw_surface_vertex_with_attribute(v,f,c);
813 }
814 }
815 glupEnd();
816 }
817 );
818
819 end_attributes();
820 }
821
822 void MeshGfx::draw_polygons() {
823 if(
824 picking_mode_ == MESH_NONE && (
825 attribute_subelements_ == MESH_VERTICES ||
826 attribute_subelements_ == MESH_FACETS ||
827 attribute_subelements_ == MESH_FACET_CORNERS
828 )
829 ) {
830 draw_polygons_attrib();
831 } else {
832 draw_polygons_plain();
833 }
834 }
835
836 void MeshGfx::draw_polygons_plain() {
837 glupDisable(GLUP_DRAW_MESH);
838
839 // Using vertex colors to do the picking.
840 if(picking_mode_ != MESH_NONE) {
841 glupDisable(GLUP_PICKING);
842 glupDisable(GLUP_LIGHTING);
843 glupEnable(GLUP_VERTEX_COLORS);
844 }
845
846 facets_filter_.begin(mesh_->facets.attributes(),false);
847
848 glupBegin(GLUP_TRIANGLES);
849 bool picking_vertex_colors = false;
850 if(picking_mode_ != MESH_NONE) {
851 picking_vertex_colors = (
852 (picking_mode_ & MESH_FACETS) != 0 &&
853 object_picking_id_ == NO_INDEX
854 );
855 set_GLUP_vertex_color_from_picking_id(object_picking_id_);
856 }
857 for(index_t f: mesh_->facets) {
858 if(!facets_filter_.test(f)) {
859 continue;
860 }
861 if(picking_vertex_colors) {
862 set_GLUP_vertex_color_from_picking_id(f);
863 }
864 index_t v1 = mesh_->facets.vertex(f,0);
865 for(index_t lv=1; lv+1<mesh_->facets.nb_vertices(f); ++lv) {
866 index_t v2 = mesh_->facets.vertex(f,lv);
867 index_t v3 = mesh_->facets.vertex(f,lv+1);
868 draw_vertex(v1);
869 draw_vertex(v2);
870 draw_vertex(v3);
871 }
872 }
873 glupEnd();
874 glupDisable(GLUP_VERTEX_COLORS);
875
876 facets_filter_.end();
877
878 if(show_mesh_ && (picking_mode_ == MESH_NONE)) {
879 draw_surface_mesh_with_lines();
880 }
881 }
882
883 void MeshGfx::draw_polygons_attrib() {
884 begin_attributes();
885 facets_filter_.begin(mesh_->facets.attributes(),false);
886 glupDisable(GLUP_DRAW_MESH);
887 glupBegin(GLUP_TRIANGLES);
888 for(index_t f: mesh_->facets) {
889 if(!facets_filter_.test(f)) {
890 continue;
891 }
892 index_t c1 = mesh_->facets.corners_begin(f);
893 index_t v1 = mesh_->facet_corners.vertex(c1);
894 for(
895 index_t c2 = c1+1;
896 c2+1<mesh_->facets.corners_end(f); ++c2
897 ) {
898 index_t c3=c2+1;
899 index_t v2=mesh_->facet_corners.vertex(c2);
900 index_t v3=mesh_->facet_corners.vertex(c3);
901 draw_surface_vertex_with_attribute(v1,f,c1);
902 draw_surface_vertex_with_attribute(v2,f,c2);
903 draw_surface_vertex_with_attribute(v3,f,c3);
904 }
905 }
906 glupEnd();
907 facets_filter_.end();
908 end_attributes();
909 if(show_mesh_ && (picking_mode_ == MESH_NONE)) {
910 glupDisable(GLUP_VERTEX_COLORS);
911 draw_surface_mesh_with_lines();
912 }
913 }
914
915 void MeshGfx::draw_surface() {
916 if(mesh_ == nullptr || mesh_->facets.nb() == 0) {
917 return;
918 }
919 set_GLUP_parameters();
920 set_GLUP_picking(MESH_FACETS);
921 update_buffer_objects_if_needed();
922
923 glupSetCellsShrink(0.0f);
924
925 if(
926 attribute_subelements_ != MESH_NONE &&
927 !glupIsEnabled(GLUP_NORMAL_MAPPING)
928 ) {
929 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 1.0f, 1.0f, 1.0f);
930 } else {
931 glupSetColor4fv(GLUP_FRONT_COLOR, surface_color_);
932 glupSetColor4fv(GLUP_BACK_COLOR, backface_surface_color_);
933 }
934
935 if(mesh_->facets.are_simplices()) {
936 draw_triangles();
937 } else if(quads_) {
938 draw_quads();
939 } else if(triangles_and_quads_) {
940 draw_triangles_and_quads();
941 } else {
942 draw_polygons();
943 }
944 }
945
946 void MeshGfx::draw_surface_mesh_with_lines() {
947 facets_filter_.begin(mesh_->facets.attributes(),false);
948 glupSetMeshWidth(GLUPint(mesh_width_));
949 glupSetColor4fv(GLUP_FRONT_AND_BACK_COLOR, mesh_color_);
950 glupBegin(GLUP_LINES);
951 for(index_t f: mesh_->facets) {
952 if(!facets_filter_.test(f)) {
953 continue;
954 }
955 for(index_t c1: mesh_->facets.corners(f)) {
956 index_t c2 =
957 mesh_->facets.next_corner_around_facet(f,c1);
958 index_t v1 = mesh_->facet_corners.vertex(c1);
959 index_t v2 = mesh_->facet_corners.vertex(c2);
960 draw_vertex(v1);
961 draw_vertex(v2);
962 }
963 }
964 glupEnd();
965 facets_filter_.end();
966 }
967
968 void MeshGfx::draw_surface_borders() {
969 if(picking_mode_ != MESH_NONE) {
970 return;
971 }
972 facets_filter_.begin(mesh_->facets.attributes(),false);
973 set_GLUP_parameters();
974 glupDisable(GLUP_LIGHTING); // TODO: maybe reactivate if we implement nice shaded cylinders
975 glupSetColor4fv(GLUP_FRONT_COLOR, mesh_color_);
976 glupSetMeshWidth(GLUPint(mesh_border_width_));
977 glupBegin(GLUP_LINES);
978 for(index_t f: mesh_->facets) {
979 if(!facets_filter_.test(f)) {
980 continue;
981 }
982 for(index_t c1: mesh_->facets.corners(f)) {
983 if(mesh_->facet_corners.adjacent_facet(c1) == NO_FACET) {
984 index_t v1 = mesh_->facet_corners.vertex(c1);
985 index_t c2 = mesh_->facets.next_corner_around_facet(f,c1);
986 index_t v2 = mesh_->facet_corners.vertex(c2);
987 draw_vertex(v1);
988 draw_vertex(v2);
989 }
990 }
991 }
992 glupEnd();
993 facets_filter_.end();
994 glupDisable(GLUP_DRAW_MESH);
995 }
996
997 /***********************************************************************/
998
999 void MeshGfx::draw_tets() {
1000 if(!draw_cells_[MESH_TET]) {
1001 return;
1002 }
1003 glupSetColor4fv(GLUP_FRONT_AND_BACK_COLOR, cells_color_[MESH_TET]);
1004 if(can_use_array_mode(GLUP_TETRAHEDRA) && cells_VAO_ != 0) {
1005 draw_tets_array();
1006 } else {
1007 if(
1008 attribute_subelements_ == MESH_VERTICES ||
1009 attribute_subelements_ == MESH_CELLS ||
1010 attribute_subelements_ == MESH_CELL_FACETS ||
1011 attribute_subelements_ == MESH_CELL_CORNERS
1012 ) {
1013 draw_tets_immediate_attrib();
1014 } else {
1015 draw_tets_immediate_plain();
1016 }
1017 }
1018 }
1019
1020 void MeshGfx::draw_tets_array() {
1021 glupBindVertexArray(cells_VAO_);
1022 if(attribute_subelements_ == MESH_VERTICES) {
1023 begin_attributes();
1024 }
1025 cells_filter_.begin(mesh_->cells.attributes());
1026 glupDrawElements(
1027 GLUP_TETRAHEDRA,
1028 GLUPsizei(mesh_->cells.nb()*4),
1029 GL_UNSIGNED_INT,
1030 nullptr
1031 );
1032 cells_filter_.end();
1033 if(attribute_subelements_ == MESH_VERTICES) {
1034 end_attributes();
1035 }
1036 glupBindVertexArray(0);
1037 }
1038
1039 void MeshGfx::draw_tets_immediate_plain() {
1040
1041 // If filter is active, use generic code
1042 if(cells_filter_.attribute_name != "") {
1043 draw_sequences(
1044 mesh_->cells,
1045 [&](index_t begin_t, index_t end_t) {
1046 glupBegin(GLUP_TETRAHEDRA);
1047 for(index_t t=begin_t; t<end_t; ++t) {
1048 draw_vertex(mesh_->cells.vertex(t,0));
1049 draw_vertex(mesh_->cells.vertex(t,1));
1050 draw_vertex(mesh_->cells.vertex(t,2));
1051 draw_vertex(mesh_->cells.vertex(t,3));
1052 }
1053 glupEnd();
1054 }
1055 );
1056 return;
1057 }
1058
1059 // Optimized code for tet mesh with no attribute, single
1060 // and double precision. Writes mesh data directly in GLUP buffers.
1061 glupBegin(GLUP_TETRAHEDRA);
1062 if(!do_animation_ && mesh_->vertices.dimension() >= 3) {
1063 GLUP::Context* context = (GLUP::Context*)(glupCurrentContext());
1064 GLUP::ImmediateState& state = context->immediate_state();
1065 GLUP::ImmediateBuffer& buffer =
1066 state.buffer[GLUP::GLUP_VERTEX_ATTRIBUTE];
1067 if(mesh_->vertices.single_precision()) {
1068 index_t t1 = 0;
1069 while(t1 < mesh_->cells.nb()) {
1070 index_t t2 = t1 + (state.max_current_vertex()/4);
1071 t2 = std::min(t2, mesh_->cells.nb());
1072 GLfloat* current_vertex = buffer.data();
1073 for(index_t t=t1; t<t2; ++t) {
1074 for(index_t lv=0; lv<4; ++lv) {
1075 index_t v = mesh_->cells.vertex(t,lv);
1076 const float* p = mesh_->vertices.
1077 single_precision_point_ptr(v);
1078 current_vertex[0] = p[0];
1079 current_vertex[1] = p[1];
1080 current_vertex[2] = p[2];
1081 current_vertex[3] = 1.0f;
1082 current_vertex += 4;
1083 }
1084 }
1085 state.set_current_vertex(4*(t2-t1));
1086 context->flush_immediate_buffers();
1087 t1 = t2;
1088 }
1089 } else {
1090 index_t t1 = 0;
1091 while(t1 < mesh_->cells.nb()) {
1092 index_t t2 = t1 + (state.max_current_vertex()/4);
1093 t2 = std::min(t2, mesh_->cells.nb());
1094 GLfloat* current_vertex = buffer.data();
1095 for(index_t t=t1; t<t2; ++t) {
1096 for(index_t lv=0; lv<4; ++lv) {
1097 index_t v = mesh_->cells.vertex(t,lv);
1098 const double* p = mesh_->vertices.point_ptr(v);
1099 current_vertex[0] = float(p[0]);
1100 current_vertex[1] = float(p[1]);
1101 current_vertex[2] = float(p[2]);
1102 current_vertex[3] = 1.0f;
1103 current_vertex += 4;
1104 }
1105 }
1106 state.set_current_vertex(4*(t2-t1));
1107 context->flush_immediate_buffers();
1108 t1 = t2;
1109 }
1110 }
1111 } else {
1112 for(index_t t: mesh_->cells) {
1113 draw_vertex(mesh_->cells.vertex(t,0));
1114 draw_vertex(mesh_->cells.vertex(t,1));
1115 draw_vertex(mesh_->cells.vertex(t,2));
1116 draw_vertex(mesh_->cells.vertex(t,3));
1117 }
1118 }
1119 glupEnd();
1120 }
1121
1122 void MeshGfx::draw_tets_immediate_attrib() {
1123 begin_attributes();
1124 draw_sequences(
1125 mesh_->cells,
1126 [&](index_t begin_t, index_t end_t) {
1127 glupBegin(GLUP_TETRAHEDRA);
1128 for(index_t t=begin_t; t<end_t; ++t) {
1129 index_t c0 = 4*t;
1130 index_t v0 = mesh_->cells.vertex(t,0);
1131 index_t v1 = mesh_->cells.vertex(t,1);
1132 index_t v2 = mesh_->cells.vertex(t,2);
1133 index_t v3 = mesh_->cells.vertex(t,3);
1134 draw_volume_vertex_with_attribute(v0, t, c0);
1135 draw_volume_vertex_with_attribute(v1, t, c0+1);
1136 draw_volume_vertex_with_attribute(v2, t, c0+2);
1137 draw_volume_vertex_with_attribute(v3, t, c0+3);
1138 }
1139 glupEnd();
1140 }
1141 );
1142 end_attributes();
1143 }
1144
1145 static GLUPprimitive geogram_cell_to_glup[MESH_NB_CELL_TYPES] = {
1146 GLUP_TETRAHEDRA,
1147 GLUP_HEXAHEDRA,
1148 GLUP_PRISMS,
1149 GLUP_PYRAMIDS,
1150 GLUP_CONNECTORS
1151 };
1152
1153 void MeshGfx::draw_hybrid() {
1154 if(
1155 cells_VAO_ != 0 &&
1156 (!has_cells_[MESH_TET] || can_use_array_mode(GLUP_TETRAHEDRA)) &&
1157 (!has_cells_[MESH_HEX] || can_use_array_mode(GLUP_HEXAHEDRA) ) &&
1158 (!has_cells_[MESH_PRISM] || can_use_array_mode(GLUP_PRISMS) ) &&
1159 (!has_cells_[MESH_PYRAMID] || can_use_array_mode(GLUP_PYRAMIDS) ) &&
1160 (!has_cells_[MESH_CONNECTOR] || can_use_array_mode(GLUP_CONNECTORS))
1161 ) {
1162 draw_hybrid_array();
1163 } else {
1164 if(
1165 (
1166 picking_mode_ == MESH_NONE) && (
1167 attribute_subelements_ == MESH_VERTICES ||
1168 attribute_subelements_ == MESH_CELLS ||
1169 attribute_subelements_ == MESH_CELL_FACETS ||
1170 attribute_subelements_ == MESH_CELL_CORNERS
1171 )
1172 ) {
1173 draw_hybrid_immediate_attrib();
1174 } else {
1175 draw_hybrid_immediate_plain();
1176 }
1177 }
1178 }
1179
1180 void MeshGfx::draw_hybrid_array() {
1181
1182 // Note: to go faster, here we could draw sequences of primitives
1183 // without taking filtering into account and do the
1184 // filtering in hw (but well difference will not be so important).
1185
1186 glupBindVertexArray(cells_VAO_);
1187 if(attribute_subelements_ == MESH_VERTICES) {
1188 begin_attributes();
1189 }
1190
1191 for(index_t type=MESH_TET; type < MESH_NB_CELL_TYPES; ++type) {
1192 if(!draw_cells_[type] || !has_cells_[type]) {
1193 continue;
1194 }
1195
1196 if(attribute_subelements_ != MESH_VERTICES) {
1197 glupSetColor4fv(GLUP_FRONT_AND_BACK_COLOR, cells_color_[type]);
1198 }
1199
1200 GLUPprimitive glup_prim = geogram_cell_to_glup[type];
1201 index_t nb_vertices = mesh_->cells.cell_type_to_cell_descriptor(
1202 MeshCellType(type)
1203 ).nb_vertices;
1204
1205 draw_sequences_if(
1206 mesh_->cells,
1207 [&](index_t c) { return index_t(mesh_->cells.type(c))==type; },
1208 [&](index_t begin_c, index_t end_c) {
1209 glupDrawElements(
1210 glup_prim,
1211 GLUPsizei((end_c-begin_c)*nb_vertices),
1212 GL_UNSIGNED_INT,
1213 (GLUPvoid*)(
1214 mesh_->cells.corners_begin(begin_c)*sizeof(index_t)
1215 )
1216 );
1217 }
1218 );
1219 }
1220
1221 if(attribute_subelements_ == MESH_VERTICES) {
1222 end_attributes();
1223 }
1224 glupBindVertexArray(0);
1225 }
1226
1227 void MeshGfx::draw_hybrid_immediate_plain() {
1228 for(index_t type=MESH_TET; type < MESH_NB_CELL_TYPES; ++type) {
1229 if(!draw_cells_[type] || !has_cells_[type]) {
1230 continue;
1231 }
1232 glupSetColor4fv(GLUP_FRONT_AND_BACK_COLOR, cells_color_[type]);
1233 draw_sequences_if(
1234 mesh_->cells,
1235 [&](index_t c) { return index_t(mesh_->cells.type(c))==type; },
1236 [&](index_t begin_c, index_t end_c) {
1237 glupBegin(geogram_cell_to_glup[type]);
1238 for(index_t c=begin_c; c<end_c; ++c) {
1239 for(index_t lv=0;lv<mesh_->cells.nb_vertices(c);++lv) {
1240 draw_vertex(mesh_->cells.vertex(c,lv));
1241 }
1242 }
1243 glupEnd();
1244 }
1245 );
1246 }
1247 }
1248
1249 void MeshGfx::draw_hybrid_immediate_attrib() {
1250 begin_attributes();
1251 for(index_t type=MESH_TET; type < MESH_NB_CELL_TYPES; ++type) {
1252 if(!draw_cells_[type] || !has_cells_[type]) {
1253 continue;
1254 }
1255 draw_sequences_if(
1256 mesh_->cells,
1257 [&](index_t c) { return index_t(mesh_->cells.type(c))==type; },
1258 [&](index_t begin_c, index_t end_c) {
1259 glupBegin(geogram_cell_to_glup[type]);
1260 for(index_t c=begin_c; c<end_c; ++c) {
1261 index_t c0 = mesh_->cells.corners_begin(c);
1262 for(index_t lv=0;lv<mesh_->cells.nb_vertices(c);++lv) {
1263 draw_volume_vertex_with_attribute(
1264 mesh_->cells.vertex(c,lv),
1265 c,
1266 c0+lv
1267 );
1268 }
1269 }
1270 glupEnd();
1271 }
1272 );
1273 }
1274 end_attributes();
1275 }
1276
1277 void MeshGfx::draw_volume() {
1278 if(mesh_ == nullptr || mesh_->cells.nb() == 0) {
1279 return;
1280 }
1281 set_GLUP_parameters();
1282 set_GLUP_picking(MESH_CELLS);
1283 update_buffer_objects_if_needed();
1284 glupSetCellsShrink(GLUPfloat(shrink_));
1285
1286 if(mesh_->cells.are_simplices()) {
1287 draw_tets();
1288 } else {
1289 draw_hybrid();
1290 }
1291 glupSetCellsShrink(0.0f);
1292 }
1293
1294 void MeshGfx::set_mesh(const Mesh* mesh) {
1295 mesh_ = mesh;
1296 triangles_and_quads_ = true;
1297 quads_ = true;
1298 if(mesh_ != nullptr) {
1299 for(index_t f: mesh_->facets) {
1300 index_t nb = mesh_->facets.nb_vertices(f);
1301 if(nb != 3 && nb != 4) {
1302 triangles_and_quads_ = false;
1303 }
1304 if(nb != 4) {
1305 quads_ = false;
1306 }
1307 }
1308 for(index_t type=0; type<MESH_NB_CELL_TYPES; ++type) {
1309 has_cells_[type] = false;
1310 }
1311 for(index_t cell: mesh_->cells) {
1312 has_cells_[mesh_->cells.type(cell)] = true;
1313 }
1314 }
1315 buffer_objects_dirty_ = true;
1316 attributes_buffer_objects_dirty_ = true;
1317 vertices_filter_.dirty = true;
1318 edges_filter_.dirty = true;
1319 facets_filter_.dirty = true;
1320 cells_filter_.dirty = true;
1321 vertices_selection_filter_.dirty = true;
1322 }
1323
1324 void MeshGfx::set_GLUP_parameters() {
1325 if(show_mesh_) {
1326 glupEnable(GLUP_DRAW_MESH);
1327 } else {
1328 glupDisable(GLUP_DRAW_MESH);
1329 }
1330 glupSetColor4fv(GLUP_MESH_COLOR, mesh_color_);
1331 glupSetMeshWidth(GLUPint(mesh_width_));
1332 glupSetPointSize(points_size_);
1333 if(lighting_) {
1334 glupEnable(GLUP_LIGHTING);
1335 } else {
1336 glupDisable(GLUP_LIGHTING);
1337 }
1338
1339 do_animation_ =
1340 (animate_ && mesh_->vertices.dimension() >= 6);
1341
1342 ES_profile_ = !strcmp(glupCurrentProfileName(), "GLUPES2");
1343 }
1344
1345 void MeshGfx::set_GLUP_picking(MeshElementsFlags what) {
1346 if(picking_mode_ == MESH_NONE && object_picking_id_ == NO_INDEX) {
1347 glupDisable(GLUP_PICKING);
1348 } else {
1349 glupEnable(GLUP_PICKING);
1350 if(
1351 (object_picking_id_ == NO_INDEX) &&
1352 ((picking_mode_ & what) != 0)
1353 ) {
1354 glupPickingMode(GLUP_PICK_PRIMITIVE);
1355 } else {
1356 glupPickingMode(GLUP_PICK_CONSTANT);
1357 glupPickingId(object_picking_id_);
1358 }
1359 }
1360 }
1361
1362 void MeshGfx::set_GLUP_vertex_color_from_picking_id(index_t id) {
1363 GLubyte r = GLubyte( id & 255);
1364 GLubyte g = GLubyte((id >> 8) & 255);
1365 GLubyte b = GLubyte((id >> 16) & 255);
1366 GLubyte a = GLubyte((id >> 24) & 255);
1367 glupColor4f(
1368 GLfloat(r)/255.0f,
1369 GLfloat(g)/255.0f,
1370 GLfloat(b)/255.0f,
1371 GLfloat(a)/255.0f
1372 );
1373 }
1374
1375 void MeshGfx::bind_vertices_VBO() {
1376 glBindBuffer(GL_ARRAY_BUFFER, vertices_VBO_);
1377 glEnableVertexAttribArray(0);
1378
1379 GLint dim = GLint(std::min(3u, mesh_->vertices.dimension()));
1380
1381 if(mesh_->vertices.single_precision()) {
1382 GLsizei stride = GLsizei(
1383 mesh_->vertices.dimension() * sizeof(float)
1384 );
1385 glVertexAttribPointer(
1386 0, // Attribute 0
1387 dim, // nb coordinates per vertex
1388 GL_FLOAT, // input coordinates representation
1389 GL_FALSE, // do not normalize
1390 stride, // offset between two consecutive vertices
1391 nullptr // addr. relative to bound VBO
1392 );
1393 } else {
1394 #ifdef GEO_GL_NO_DOUBLES
1395 // Do nothing, because if a double attribute is bound,
1396 // then can_use_array_mode() returns 0, then we switch
1397 // to immediate mode.
1398 #else
1399 GLsizei stride = GLsizei(
1400 mesh_->vertices.dimension() * sizeof(double)
1401 );
1402 glVertexAttribPointer(
1403 0, // Attribute 0
1404 dim, // nb coordinates per vertex
1405 GL_DOUBLE, // input coordinates representation
1406 GL_FALSE, // do not normalize
1407 stride, // offset between two consecutive vertices
1408 nullptr // addr. relative to bound VBO
1409 );
1410 #endif
1411 }
1412 }
1413
1414
1415 void MeshGfx::update_buffer_objects_if_needed() {
1416 if(mesh_->vertices.nb() == 0) {
1417 return;
1418 }
1419
1420 if(!buffer_objects_dirty_) {
1421 update_attribute_buffer_objects_if_needed();
1422 return;
1423 }
1424
1425 if(mesh_->vertices.single_precision()) {
1426 size_t size = mesh_->vertices.nb() *
1427 mesh_->vertices.dimension() * sizeof(float);
1428 update_or_check_buffer_object(
1429 vertices_VBO_, GL_ARRAY_BUFFER,
1430 size, mesh_->vertices.single_precision_point_ptr(0),
1431 buffer_objects_dirty_
1432 );
1433 } else {
1434 size_t size = mesh_->vertices.nb() *
1435 mesh_->vertices.dimension() * sizeof(double);
1436 update_or_check_buffer_object(
1437 vertices_VBO_, GL_ARRAY_BUFFER,
1438 size, mesh_->vertices.point_ptr(0),
1439 buffer_objects_dirty_
1440 );
1441 }
1442
1443 if(vertices_VAO_ == 0) {
1444 glupGenVertexArrays(1, &vertices_VAO_);
1445 }
1446 glupBindVertexArray(vertices_VAO_);
1447 bind_vertices_VBO();
1448 glupBindVertexArray(0);
1449
1450 if(mesh_->edges.nb()) {
1451 update_or_check_buffer_object(
1452 edge_indices_VBO_, GL_ELEMENT_ARRAY_BUFFER,
1453 mesh_->edges.nb() * 2 * sizeof(int),
1454 mesh_->edges.vertex_index_ptr(0),
1455 buffer_objects_dirty_
1456 );
1457 if(edges_VAO_ == 0) {
1458 glupGenVertexArrays(1, &edges_VAO_);
1459 }
1460 glupBindVertexArray(edges_VAO_);
1461 bind_vertices_VBO();
1462 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edge_indices_VBO_);
1463 glupBindVertexArray(0);
1464 }
1465
1466 if(
1467 mesh_->facets.nb() != 0 &&
1468 (mesh_->facets.are_simplices() || triangles_and_quads_)
1469 ) {
1470 update_or_check_buffer_object(
1471 facet_indices_VBO_, GL_ELEMENT_ARRAY_BUFFER,
1472 mesh_->facet_corners.nb() * sizeof(int),
1473 mesh_->facet_corners.vertex_index_ptr(0),
1474 buffer_objects_dirty_
1475 );
1476 if(facets_VAO_ == 0) {
1477 glupGenVertexArrays(1, &facets_VAO_);
1478 }
1479 glupBindVertexArray(facets_VAO_);
1480 bind_vertices_VBO();
1481 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, facet_indices_VBO_);
1482 glupBindVertexArray(0);
1483 }
1484
1485 if(mesh_->cells.nb() != 0) {
1486 update_or_check_buffer_object(
1487 cell_indices_VBO_, GL_ELEMENT_ARRAY_BUFFER,
1488 mesh_->cell_corners.nb() * sizeof(int),
1489 mesh_->cell_corners.vertex_index_ptr(0),
1490 buffer_objects_dirty_
1491 );
1492 if(cells_VAO_ == 0) {
1493 glupGenVertexArrays(1, &cells_VAO_);
1494 }
1495 glupBindVertexArray(cells_VAO_);
1496 bind_vertices_VBO();
1497 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cell_indices_VBO_);
1498 glupBindVertexArray(0);
1499 }
1500
1501 buffer_objects_dirty_ = false;
1502 update_attribute_buffer_objects_if_needed();
1503 }
1504
1505 void MeshGfx::set_scalar_attribute(
1506 MeshElementsFlags subelements,
1507 const std::string& name,
1508 double attr_min, double attr_max,
1509 GLuint colormap_texture,
1510 index_t repeat
1511 ) {
1512 if(
1513 subelements != attribute_subelements_ ||
1514 attribute_name_ != name
1515 ) {
1516 attributes_buffer_objects_dirty_ = true;
1517 }
1518 attribute_subelements_ = subelements;
1519 attribute_name_ = name;
1520 attribute_min_ = attr_min;
1521 attribute_max_ = attr_max;
1522 attribute_repeat_ = repeat;
1523 attribute_texture_ = colormap_texture;
1524 attribute_dim_ = 1;
1525 attribute_texture_dim_ = 1;
1526
1527 const MeshSubElementsStore& mesh_subelements =
1528 mesh_->get_subelements_by_type(attribute_subelements_);
1529
1530 if(!ReadOnlyScalarAttributeAdapter::is_defined(
1531 mesh_subelements.attributes(), attribute_name_
1532 )
1533 ) {
1534 attribute_subelements_ = MESH_NONE;
1535 }
1536
1537 }
1538
1539 void MeshGfx::set_texturing(
1540 MeshElementsFlags subelements,
1541 const std::string& name,
1542 GLuint texture,
1543 index_t texture_dim,
1544 index_t repeat
1545 ) {
1546 if(
1547 subelements != attribute_subelements_ ||
1548 attribute_name_ != name
1549 ) {
1550 attributes_buffer_objects_dirty_ = true;
1551 }
1552 attribute_subelements_ = subelements;
1553 attribute_name_ = name;
1554 attribute_min_ = 0.0;
1555 attribute_max_ = 1.0;
1556 attribute_repeat_ = repeat;
1557 attribute_texture_ = texture;
1558 attribute_texture_dim_ = texture_dim;
1559
1560 const MeshSubElementsStore& mesh_subelements =
1561 mesh_->get_subelements_by_type(attribute_subelements_);
1562
1563 attribute_dim_ = 0;
1564 FOR(i,3) {
1565 tex_coord_attribute_[i].bind_if_is_defined(
1566 mesh_subelements.attributes(),
1567 attribute_name_ + "[" + String::to_string(i) + "]"
1568 );
1569 if(tex_coord_attribute_[i].is_bound()) {
1570 attribute_dim_ = i+1;
1571 tex_coord_attribute_[i].unbind();
1572 }
1573 }
1574 if(attribute_dim_ == 0) {
1575 attribute_subelements_ = MESH_NONE;
1576 }
1577 }
1578
1579 void MeshGfx::update_attribute_buffer_objects_if_needed() {
1580 if(mesh_->vertices.nb() == 0) {
1581 return;
1582 }
1583
1584 if(!attributes_buffer_objects_dirty_) {
1585 return;
1586 }
1587
1588 long_vector_attribute_ = false;
1589
1590 if(attribute_subelements_ == MESH_VERTICES) {
1591 scalar_attribute_.bind_if_is_defined(
1592 mesh_->vertices.attributes(), attribute_name_
1593 );
1594 if(scalar_attribute_.attribute_store()->dimension() > 4) {
1595 scalar_attribute_.unbind();
1596 long_vector_attribute_ = true;
1597 }
1598 }
1599
1600 if(scalar_attribute_.is_bound()) {
1601 size_t element_size =
1602 scalar_attribute_.attribute_store()->element_size();
1603 GLint dimension =
1604 GLint(scalar_attribute_.attribute_store()->dimension());
1605 // nb_items should be scalar_attribute_.size(), using capacity()
1606 // instead seemingly fixes a display bug (zero attribute on last
1607 // vertex). To be further investigated...
1608 index_t nb_items = scalar_attribute_.size();
1609 // ... or ... scalar_attribute_.attribute_store()->capacity();
1610 const void* data = scalar_attribute_.attribute_store()->data();
1611
1612 update_or_check_buffer_object(
1613 vertices_attribute_VBO_, GL_ARRAY_BUFFER,
1614 element_size*index_t(dimension)*nb_items,
1615 data,
1616 attributes_buffer_objects_dirty_
1617 );
1618
1619 bind_attribute_buffer_object(vertices_VAO_);
1620 bind_attribute_buffer_object(edges_VAO_);
1621 bind_attribute_buffer_object(facets_VAO_);
1622 bind_attribute_buffer_object(cells_VAO_);
1623
1624 scalar_attribute_.unbind();
1625 } else {
1626 unbind_attribute_buffer_object(vertices_VAO_);
1627 unbind_attribute_buffer_object(edges_VAO_);
1628 unbind_attribute_buffer_object(facets_VAO_);
1629 unbind_attribute_buffer_object(cells_VAO_);
1630 }
1631 attributes_buffer_objects_dirty_ = false;
1632 }
1633
1634 void MeshGfx::bind_attribute_buffer_object(GLuint VAO) {
1635 if(VAO == 0) {
1636 return;
1637 }
1638 size_t element_size =
1639 scalar_attribute_.attribute_store()->element_size();
1640 GLint dimension =
1641 GLint(scalar_attribute_.attribute_store()->dimension());
1642
1643 if(
1644 scalar_attribute_.element_type() ==
1645 ReadOnlyScalarAttributeAdapter::ET_VEC2) {
1646 dimension *= 2;
1647 element_size /= 2;
1648 } else if(scalar_attribute_.element_type() ==
1649 ReadOnlyScalarAttributeAdapter::ET_VEC3) {
1650 dimension *= 3;
1651 element_size /= 3;
1652 }
1653
1654 GLsizei stride = GLsizei(element_size) * dimension;
1655
1656 const GLvoid* offset = (const GLvoid*)(
1657 element_size * index_t(scalar_attribute_.element_index())
1658 );
1659
1660 glupBindVertexArray(VAO);
1661 glBindBuffer(GL_ARRAY_BUFFER, vertices_attribute_VBO_);
1662 glEnableVertexAttribArray(2); // 2 = tex coords
1663
1664 switch(scalar_attribute_.element_type()) {
1665 case ReadOnlyScalarAttributeAdapter::ET_UINT8:
1666 glVertexAttribPointer(
1667 2, dimension, GL_UNSIGNED_BYTE, GL_FALSE, stride, offset
1668 );
1669 break;
1670 case ReadOnlyScalarAttributeAdapter::ET_INT8:
1671 glVertexAttribPointer(
1672 2, dimension, GL_BYTE, GL_FALSE, stride, offset
1673 );
1674 break;
1675 case ReadOnlyScalarAttributeAdapter::ET_UINT32:
1676 glVertexAttribPointer(
1677 2, dimension, GL_UNSIGNED_INT, GL_FALSE, stride, offset
1678 );
1679 break;
1680 case ReadOnlyScalarAttributeAdapter::ET_INT32:
1681 glVertexAttribPointer(
1682 2, dimension, GL_INT, GL_FALSE, stride, offset
1683 );
1684 break;
1685 case ReadOnlyScalarAttributeAdapter::ET_FLOAT32:
1686 glVertexAttribPointer(
1687 2, dimension, GL_FLOAT, GL_FALSE, stride, offset
1688 );
1689 break;
1690 case ReadOnlyScalarAttributeAdapter::ET_FLOAT64:
1691 case ReadOnlyScalarAttributeAdapter::ET_VEC2:
1692 case ReadOnlyScalarAttributeAdapter::ET_VEC3:
1693 #ifdef GEO_GL_NO_DOUBLES
1694 // Do nothing, because if a double attribute is bound,
1695 // then can_use_array_mode() returns 0, then we switch
1696 // to immediate mode.
1697 #else
1698 glVertexAttribPointer(
1699 2, dimension, GL_DOUBLE, GL_FALSE, stride, offset
1700 );
1701 #endif
1702 break;
1703 case ReadOnlyScalarAttributeAdapter::ET_NONE:
1704 geo_assert_not_reached;
1705 }
1706 glupBindVertexArray(0);
1707 glBindBuffer(GL_ARRAY_BUFFER, 0);
1708 }
1709
1710 void MeshGfx::unbind_attribute_buffer_object(GLuint VAO) {
1711 if(VAO == 0) {
1712 return;
1713 }
1714 glupBindVertexArray(VAO);
1715 glDisableVertexAttribArray(2); // 2 = tex coords
1716 glupBindVertexArray(0);
1717 glBindBuffer(GL_ARRAY_BUFFER, 0);
1718 }
1719
1720 void MeshGfx::begin_attributes() {
1721 if(picking_mode_ != MESH_NONE) {
1722 return;
1723 }
1724 if(attribute_subelements_ == MESH_NONE) {
1725 return;
1726 }
1727 const MeshSubElementsStore& subelements =
1728 mesh_->get_subelements_by_type(attribute_subelements_);
1729
1730 if(attribute_dim_ == 1) {
1731 scalar_attribute_.bind_if_is_defined(
1732 subelements.attributes(), attribute_name_
1733 );
1734 if(!scalar_attribute_.is_bound()) {
1735 return;
1736 }
1737 } else {
1738 FOR(i,3) {
1739 tex_coord_attribute_[i].bind_if_is_defined(
1740 subelements.attributes(),
1741 attribute_name_+"["+String::to_string(i)+"]"
1742 );
1743 }
1744 if(!tex_coord_attribute_[0].is_bound()) {
1745 return;
1746 }
1747 }
1748
1749 glupEnable(GLUP_TEXTURING);
1750 glupTextureMode(GLUP_TEXTURE_REPLACE);
1751
1752 switch(attribute_texture_dim_) {
1753 case 1:
1754 glupTextureType(GLUP_TEXTURE_1D);
1755 glActiveTexture(GL_TEXTURE0 + GLUP_TEXTURE_1D_UNIT);
1756 glBindTexture(
1757 GLUP_TEXTURE_1D_TARGET, attribute_texture_
1758 );
1759 break;
1760 case 2:
1761 glupTextureType(GLUP_TEXTURE_2D);
1762 glActiveTexture(GL_TEXTURE0 + GLUP_TEXTURE_2D_UNIT);
1763 glBindTexture(
1764 GLUP_TEXTURE_2D_TARGET, attribute_texture_
1765 );
1766 break;
1767 case 3:
1768 glupTextureType(GLUP_TEXTURE_3D);
1769 glActiveTexture(GL_TEXTURE0 + GLUP_TEXTURE_3D_UNIT);
1770 glBindTexture(
1771 GLUP_TEXTURE_3D_TARGET, attribute_texture_
1772 );
1773 break;
1774 }
1775
1776 if(attribute_dim_ == 1) {
1777 // Setup a texture matrix that rescales attribute range
1778 // from [attribute_min_,attribute_max_] to [0,1]
1779 glupMapTexCoords1d(
1780 attribute_min_, attribute_max_, attribute_repeat_
1781 );
1782 } else {
1783 glupMatrixMode(GLUP_TEXTURE_MATRIX);
1784 glupLoadIdentity();
1785 if(attribute_repeat_ != 0) {
1786 glupScalef(
1787 float(attribute_repeat_),
1788 float(attribute_repeat_),
1789 float(attribute_repeat_)
1790 );
1791 }
1792 glupMatrixMode(GLUP_MODELVIEW_MATRIX);
1793 }
1794
1795 if(!glupIsEnabled(GLUP_NORMAL_MAPPING)) {
1796 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 1.0f, 1.0f, 1.0f);
1797 }
1798 }
1799
1800 void MeshGfx::end_attributes() {
1801 if(scalar_attribute_.is_bound()) {
1802 glupDisable(GLUP_TEXTURING);
1803 scalar_attribute_.unbind();
1804 }
1805 FOR(i,3) {
1806 if(tex_coord_attribute_[i].is_bound()) {
1807 if(i==0) {
1808 glupDisable(GLUP_TEXTURING);
1809 }
1810 tex_coord_attribute_[i].unbind();
1811 }
1812 }
1813 glupMatrixMode(GLUP_TEXTURE_MATRIX);
1814 glupLoadIdentity();
1815 glupMatrixMode(GLUP_MODELVIEW_MATRIX);
1816 }
1817
1818 /*********************************************/
1819
1820 void MeshGfx::set_filter(
1821 MeshElementsFlags subelements,
1822 const std::string& name
1823 ) {
1824 switch(subelements) {
1825 case MESH_VERTICES:
1826 vertices_filter_.attribute_name = name;
1827 vertices_filter_.dirty = true;
1828 if(name == "") {
1829 vertices_filter_.deallocate();
1830 }
1831 break;
1832 case MESH_EDGES:
1833 edges_filter_.attribute_name = name;
1834 edges_filter_.dirty = true;
1835 if(name == "") {
1836 edges_filter_.deallocate();
1837 }
1838 break;
1839 case MESH_FACETS:
1840 facets_filter_.attribute_name = name;
1841 facets_filter_.dirty = true;
1842 if(name == "") {
1843 facets_filter_.deallocate();
1844 }
1845 break;
1846 case MESH_CELLS:
1847 cells_filter_.attribute_name = name;
1848 cells_filter_.dirty = true;
1849 if(name == "") {
1850 cells_filter_.deallocate();
1851 }
1852 break;
1853 case MESH_ALL_ELEMENTS:
1854 set_filter(MESH_VERTICES, name);
1855 set_filter(MESH_FACETS, name);
1856 set_filter(MESH_CELLS, name);
1857 break;
1858 case MESH_NONE:
1859 case MESH_FACET_CORNERS:
1860 case MESH_CELL_CORNERS:
1861 case MESH_CELL_FACETS:
1862 case MESH_ALL_SUBELEMENTS:
1863 break;
1864 }
1865 }
1866
1867 void MeshGfx::unset_filters() {
1868 set_filter(MESH_ALL_ELEMENTS,"");
1869 }
1870
1871 bool MeshGfx::hw_filtering_supported() const {
1872
1873 // hardware primitive filtering is not supported by GLUPES2
1874 bool is_GLUPES2 = !strcmp(
1875 glupCurrentProfileName(),"GLUPES2"
1876 );
1877
1878 // It is also not supported by GLUP150 (but it should be).
1879 // It seems that VERTEX_GATHER mode does not work
1880 // with GLUP primitive filtering. TODO: debug it !
1881
1882 bool is_GLUP150 = !strcmp(
1883 glupCurrentProfileName(),"GLUP150"
1884 );
1885
1886 return !is_GLUPES2 && !is_GLUP150;
1887 }
1888
1889 /***********************************************************************/
1890
1891 MeshGfx::Filter::Filter() {
1892 VBO = 0;
1893 texture = 0;
1894 dirty = true;
1895 }
1896
1897 MeshGfx::Filter::~Filter() {
1898 deallocate();
1899 }
1900
1901 void MeshGfx::Filter::deallocate() {
1902 if(VBO != 0) {
1903 glDeleteBuffers(1,&VBO);
1904 VBO = 0;
1905 }
1906 if(texture != 0) {
1907 glDeleteTextures(1,&texture);
1908 texture = 0;
1909 }
1910 dirty = true;
1911 }
1912
1913 bool MeshGfx::Filter::begin(
1914 AttributesManager& attributes_manager,
1915 bool hw_primitive_filtering
1916 ) {
1917 if(attribute_name == "") {
1918 return false;
1919 }
1920 attribute.bind_if_is_defined(attributes_manager, attribute_name);
1921 if(!attribute.is_bound()) {
1922 return false;
1923 }
1924
1925 #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_ANDROID)
1926 geo_argused(hw_primitive_filtering); // not implemented yet
1927 #else
1928 if(hw_primitive_filtering) {
1929 if(dirty) {
1930 update_or_check_buffer_object(
1931 VBO, GL_ARRAY_BUFFER,
1932 attribute.size(),
1933 &(attribute[0]),
1934 dirty
1935 );
1936 if(texture == 0) {
1937 glGenTextures(1, &texture);
1938 }
1939 glActiveTexture(
1940 GL_TEXTURE0 + GLUP_TEXTURE_PRIMITIVE_FILTERING_UNIT
1941 );
1942 glBindTexture(GL_TEXTURE_BUFFER, texture);
1943 glTexBuffer(GL_TEXTURE_BUFFER, GL_R8, VBO);
1944 glBindTexture(GL_TEXTURE_BUFFER, 0);
1945 dirty = false;
1946 }
1947 glActiveTexture(
1948 GL_TEXTURE0 + GLUP_TEXTURE_PRIMITIVE_FILTERING_UNIT
1949 );
1950 glBindTexture(GL_TEXTURE_BUFFER, texture);
1951 glupEnable(GLUP_PRIMITIVE_FILTERING);
1952 }
1953 #endif
1954 return true;
1955 }
1956
1957 void MeshGfx::Filter::end() {
1958 #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_ANDROID)
1959 // not implemented yet
1960 #else
1961 if(glupIsEnabled(GLUP_PRIMITIVE_FILTERING)) {
1962 glupDisable(GLUP_PRIMITIVE_FILTERING);
1963 glActiveTexture(
1964 GL_TEXTURE0 + GLUP_TEXTURE_PRIMITIVE_FILTERING_UNIT
1965 );
1966 glBindTexture(GL_TEXTURE_BUFFER, 0);
1967 glActiveTexture(GL_TEXTURE0);
1968 }
1969 #endif
1970 if(attribute.is_bound()) {
1971 attribute.unbind();
1972 }
1973 }
1974
1975
1976 }
1977