GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/GLUP/GLUP_marching_cells.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 166 0.0%
Functions: 0 9 0.0%
Branches: 0 156 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/GLUP/GLUP_marching_cells.h>
41 #include <geogram_gfx/GLUP/GLUP_private.h>
42 #include <geogram_gfx/basic/GLSL.h>
43 #include <geogram/basic/string.h>
44
45 #ifdef GEO_GL_140
46 namespace {
47 using namespace GLUP;
48 /**
49 * \brief Sets an entry in an array with specified stride.
50 * \param[in] array the address of the first element of the array
51 * \param[in] stride number of bytes between two consecutive elements
52 * of the array
53 * \param[in] i index of the element to be set
54 * \param[in] value value of the element ot be set
55 */
56 template <class T> inline void set_array_item(
57 void* array, size_t stride, index_t i, T value
58 ) {
59 *reinterpret_cast<T*>(Memory::pointer(array) + (i * stride)) = value;
60 }
61 }
62 #endif
63
64
65 namespace GLUP {
66 using namespace GEO;
67
68 /*******************************************************************/
69
70 MarchingCell::MarchingCell(GLUPprimitive prim) {
71 UBO_ = 0;
72 elements_VBO_ = 0;
73 desc_ = nullptr;
74 switch(prim) {
75 case GLUP_TETRAHEDRA:
76 desc_ =
77 &MeshCellsStore::cell_type_to_cell_descriptor(MESH_TET);
78 uniform_binding_point_ = 2;
79 break;
80 case GLUP_HEXAHEDRA:
81 desc_ =
82 &MeshCellsStore::cell_type_to_cell_descriptor(MESH_HEX);
83 uniform_binding_point_ = 3;
84 break;
85 case GLUP_PRISMS:
86 desc_ =
87 &MeshCellsStore::cell_type_to_cell_descriptor(MESH_PRISM);
88 uniform_binding_point_ = 4;
89 break;
90 case GLUP_PYRAMIDS:
91 desc_ =
92 &MeshCellsStore::cell_type_to_cell_descriptor(MESH_PYRAMID);
93 uniform_binding_point_ = 5;
94 break;
95 case GLUP_CONNECTORS:
96 desc_ =
97 &MeshCellsStore::cell_type_to_cell_descriptor(MESH_CONNECTOR);
98 uniform_binding_point_ = 6;
99 break;
100 case GLUP_POINTS:
101 case GLUP_LINES:
102 case GLUP_THICK_LINES:
103 case GLUP_TRIANGLES:
104 case GLUP_QUADS:
105 case GLUP_SPHERES:
106 case GLUP_NB_PRIMITIVES:
107 geo_assert_not_reached;
108 }
109
110 index_t nb_v = desc_->nb_vertices;
111
112 for(index_t i=0; i<64; ++i) {
113 vv_to_e_[i] = index_t(-1);
114 }
115
116 for(index_t e=0; e<desc_->nb_edges; ++e) {
117 index_t v1 = desc_->edge_vertex[e][0];
118 index_t v2 = desc_->edge_vertex[e][1];
119 vv_to_e_[v1*nb_v+v2] = e;
120 vv_to_e_[v2*nb_v+v1] = e;
121 }
122
123 nb_vertices_ = desc_->nb_vertices;
124 nb_configs_ = 1u << nb_vertices_;
125 nb_edges_ = desc_->nb_edges;
126 edge_ = new index_t[nb_edges_*2];
127 config_ = new index_t[nb_configs_*nb_edges_];
128 config_size_ = new index_t[nb_configs_];
129
130 for(index_t e=0; e<nb_edges_; ++e) {
131 edge_[2*e] = desc_->edge_vertex[e][0];
132 edge_[2*e+1] = desc_->edge_vertex[e][1];
133 }
134
135 max_config_size_=0;
136 for(index_t config=0; config<nb_configs_; ++config) {
137 compute_config(config);
138 // It only happens for connectors.
139 if(config_size_[config] < 3) {
140 config_size_[config] = 0;
141 }
142 max_config_size_=std::max(max_config_size_,config_size(config));
143 }
144
145 GLSL_uniform_state_declaration_ = std::string() +
146 " const int cell_nb_vertices = " +
147 String::to_string(nb_vertices()) + ";\n"
148 " const int cell_nb_edges = " +
149 String::to_string(nb_edges()) + ";\n"
150 " const int cell_nb_configs = " +
151 String::to_string(nb_configs()) + ";\n"
152 " const int cell_max_config_size = " +
153 String::to_string(max_config_size()) + ";\n" +
154 " layout(shared) \n"
155 " uniform MarchingCellStateBlock { \n"
156 " int config_size[cell_nb_configs]; \n"
157 " int config[cell_nb_configs*cell_max_config_size]; \n"
158 " } MarchingCell; \n"
159 " int config_size(in int i) { \n"
160 " return MarchingCell.config_size[i]; \n"
161 " } \n"
162 " int config_edge(in int i, in int j) { \n"
163 " return MarchingCell.config[i*cell_max_config_size+j]; \n"
164 " } \n"
165 ;
166
167 GLSL_compute_intersections_ = std::string() +
168 " vec4 isect_point_clip_space[cell_nb_edges]; \n"
169 " vec4 isect_color[cell_nb_edges]; \n"
170 " vec4 isect_tex_coord[cell_nb_edges]; \n"
171 " void compute_intersection(in int i, in int v1, in int v2) { \n"
172 " vec4 p1 = vertex_clip_space_in(v1); \n"
173 " vec4 p2 = vertex_clip_space_in(v2); \n"
174 " float t = -dot(p1, GLUP.clip_clip_plane); \n"
175 " float d = dot(p2-p1, GLUP.clip_clip_plane); \n"
176 " if(abs(d) < 1e-6) { t = 0.5; } else { t /= d; }\n"
177 " isect_point_clip_space[i] = mix(p1,p2,t); \n"
178 " if(glupIsEnabled(GLUP_VERTEX_COLORS)) { \n"
179 " isect_color[i] = mix( \n"
180 " color_in(v1), color_in(v2), t \n"
181 " ); \n"
182 " } \n"
183 " if(glupIsEnabled(GLUP_TEXTURING)) { \n"
184 " isect_tex_coord[i] = mix( \n"
185 " tex_coord_in(v1), tex_coord_in(v2), t \n"
186 " ); \n"
187 " } \n"
188 " } \n"
189 " void compute_intersections() { \n"
190 ;
191
192
193 for(index_t e=0; e<nb_edges(); ++e) {
194 GLSL_compute_intersections_ +=
195 " compute_intersection(" +
196 String::to_string(e) + "," +
197 String::to_string(edge_vertex(e,0)) + "," +
198 String::to_string(edge_vertex(e,1)) + ");\n" ;
199 }
200
201 GLSL_compute_intersections_ +=
202 " } \n"
203 ;
204 }
205
206 MarchingCell::~MarchingCell() {
207 delete[] edge_;
208 delete[] config_;
209 delete[] config_size_;
210 if(UBO_ != 0) {
211 glDeleteBuffers(1, &UBO_);
212 UBO_ = 0;
213 }
214 if(elements_VBO_ != 0) {
215 glDeleteBuffers(1, &elements_VBO_);
216 elements_VBO_ = 0;
217 }
218 }
219
220 void MarchingCell::compute_config(index_t config) {
221 config_size_[config] = 0;
222 index_t* config_out = config_ + config * nb_edges_;
223 if(config_is_ambiguous(config)) {
224 return;
225 }
226 index_t first_f = index_t(-1);
227 index_t f = index_t(-1);
228 index_t lv = index_t(-1);
229 if(!get_first_edge(first_f,lv,config)) {
230 return;
231 }
232 f = first_f;
233 do {
234 geo_debug_assert(config_size_[config] < nb_edges_);
235 config_out[config_size_[config]] = edge(f,lv);
236 ++config_size_[config];
237 do {
238 move_to_next(f,lv);
239 } while(!edge_is_intersected(f,lv,config));
240 move_to_opposite(f,lv);
241 } while(f != first_f);
242 }
243
244 void MarchingCell::move_to_opposite(index_t& f, index_t& lv) {
245 index_t v = destination_vertex(f, lv);
246 index_t e = edge(f, lv);
247 geo_debug_assert(
248 desc_->edge_adjacent_facet[e][0] == f ||
249 desc_->edge_adjacent_facet[e][1] == f
250 );
251 if(desc_->edge_adjacent_facet[e][0] == f) {
252 f = desc_->edge_adjacent_facet[e][1];
253 } else {
254 f = desc_->edge_adjacent_facet[e][0];
255 }
256 for(lv = 0; lv < desc_->nb_vertices_in_facet[f]; ++lv) {
257 if(desc_->facet_vertex[f][lv] == v) {
258 return;
259 }
260 }
261 geo_assert_not_reached;
262 }
263
264 bool MarchingCell::config_is_ambiguous(index_t config) {
265 for(index_t f=0; f<desc_->nb_facets; ++f) {
266 index_t nb_isect_in_f = 0;
267 for(index_t lv=0; lv<desc_->nb_vertices_in_facet[f]; ++lv) {
268 if(edge_is_intersected(f, lv, config)) {
269 ++nb_isect_in_f;
270 }
271 }
272 if(nb_isect_in_f > 2) {
273 return true;
274 }
275 }
276 return false;
277 }
278
279 bool MarchingCell::get_first_edge(index_t& f, index_t& lv, index_t config) {
280 for(f=0; f<desc_->nb_facets; ++f) {
281 for(lv=0; lv<desc_->nb_vertices_in_facet[f]; ++lv) {
282 if(edge_is_intersected(f, lv, config)) {
283 return true;
284 }
285 }
286 }
287 return false;
288 }
289
290
291
292
293 GLuint MarchingCell::create_UBO() {
294
295 #ifdef GEO_GL_140
296
297 // Create a program that uses the UBO
298
299 #if defined(GEO_OS_ANDROID)
300 static const char* shader_source_header_ =
301 "#version 300 es\n"
302 "precision highp float;\n";
303 #elif defined(GEO_OS_APPLE)
304 static const char* shader_source_header_ =
305 "#version 150\n";
306 #else
307 static const char* shader_source_header_ =
308 "#version 150 core\n";
309 #endif
310
311 // This program is stupid, it is only meant to make sure
312 // that all variables in the UBO are used (else some
313 // GLSL compilers optimize-it out and we can no-longer
314 // query variable offsets from it)
315 static const char* vertex_shader_source_ =
316 "in vec3 position; \n"
317 "void main() { \n"
318 " gl_Position.x = float(MarchingCell.config_size[0]); \n"
319 " gl_Position.y = float(MarchingCell.config[0]); \n"
320 " gl_Position.z = float(MarchingCell.config[1]); \n"
321 " gl_Position.w = float(MarchingCell.config[1]); \n"
322 "} \n"
323 ;
324
325 static const char* fragment_shader_source_ =
326 "out vec4 colorOut; \n"
327 "void main() { \n"
328 " colorOut = vec4(1.0, 1.0, 1.0, 1.0); \n"
329 "} \n";
330
331 GLuint vertex_shader = GLSL::compile_shader(
332 GL_VERTEX_SHADER,
333 shader_source_header_,
334 GLSL_uniform_state_declaration(),
335 vertex_shader_source_,
336 nullptr
337 );
338
339 GLuint fragment_shader = GLSL::compile_shader(
340 GL_FRAGMENT_SHADER,
341 shader_source_header_,
342 fragment_shader_source_,
343 nullptr
344 );
345
346 GLuint program = GLSL::create_program_from_shaders(
347 vertex_shader,
348 fragment_shader,
349 nullptr
350 );
351
352 // Get UBO size and offsets
353
354 GLuint UBO_index =
355 glGetUniformBlockIndex(program, "MarchingCellStateBlock");
356
357 if(UBO_index == GL_INVALID_INDEX) {
358 Logger::err("GLUP")
359 << "MarchingCellsStateBlock"
360 << ":did not find uniform state variable"
361 << std::endl;
362 throw GLSL::GLSLCompileError();
363 }
364
365 glUniformBlockBinding(
366 program, UBO_index, uniform_binding_point_
367 );
368
369 GLint uniform_buffer_size;
370
371 glGetActiveUniformBlockiv(
372 program, UBO_index,
373 GL_UNIFORM_BLOCK_DATA_SIZE,
374 &uniform_buffer_size
375 );
376
377
378 // Create UBO
379
380 Memory::byte* UBO_data = new Memory::byte[size_t(uniform_buffer_size)];
381 Memory::clear(UBO_data, size_t(uniform_buffer_size));
382
383 glGenBuffers(1, &UBO_);
384 glBindBuffer(GL_UNIFORM_BUFFER, UBO_);
385
386 glBindBufferBase(
387 GL_UNIFORM_BUFFER,
388 uniform_binding_point_,
389 UBO_
390 );
391
392
393 // Get variable offsets and array strides
394
395 GLint config_size_offset = GLSL::get_uniform_variable_offset(
396 program, "MarchingCellStateBlock.config_size[0]"
397 );
398
399 GLint config_offset = GLSL::get_uniform_variable_offset(
400 program, "MarchingCellStateBlock.config[0]"
401 );
402
403 // Note: array strides may differ from one OpenGL vendor to another,
404 // for instance, for an array of ints,
405 // with NVidia, stride = 4
406 // with Intel, stride = 16
407 // (by quiering, the following code works on both).
408
409 size_t config_size_stride = GLSL::get_uniform_variable_array_stride(
410 program, "MarchingCellStateBlock.config_size[0]"
411 );
412
413 size_t config_stride = GLSL::get_uniform_variable_array_stride(
414 program, "MarchingCellStateBlock.config[0]"
415 );
416
417 void* config_size_ptr = (UBO_data + config_size_offset);
418 void* config_ptr = (UBO_data + config_offset);
419
420 for(index_t i=0; i<nb_configs(); ++i) {
421 set_array_item(
422 config_size_ptr, config_size_stride, i, config_size(i)
423 );
424 for(index_t j=0; j<config_size(i); ++j) {
425 set_array_item(
426 config_ptr, config_stride,
427 i*max_config_size()+j, config_edges(i)[j]
428 );
429 }
430 }
431
432 glBufferData(
433 GL_UNIFORM_BUFFER,
434 uniform_buffer_size,
435 UBO_data,
436 GL_STATIC_DRAW
437 );
438
439 glBindBuffer(GL_UNIFORM_BUFFER, 0);
440
441 // Delete temporary UBO data
442 delete[] UBO_data;
443
444 // Delete program and shaders
445 glDeleteShader(vertex_shader);
446 glDeleteShader(fragment_shader);
447 glDeleteProgram(program);
448
449
450 #endif
451 return UBO_;
452 }
453
454 GLuint MarchingCell::create_elements_VBO() {
455 glGenBuffers(1, &elements_VBO_);
456 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elements_VBO_);
457 index_t nb_elements = nb_configs() * max_config_size();
458 Numeric::uint8* indices = new Numeric::uint8[nb_elements];
459 for(index_t i=0; i<nb_configs(); ++i) {
460 for(index_t j=0; j<config_size(i); ++j) {
461 indices[i*max_config_size()+j] =
462 Numeric::uint8(config_edges(i)[j]);
463 }
464 }
465 glBufferData(
466 GL_ELEMENT_ARRAY_BUFFER,
467 GLsizeiptr(sizeof(Numeric::uint8) * nb_elements),
468 indices, GL_STATIC_DRAW
469 );
470 delete[] indices;
471 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
472 return elements_VBO_;
473 }
474
475 void MarchingCell::bind_uniform_state(GLuint program) {
476 #ifndef GEO_GL_140
477 geo_argused(program);
478 #else
479 GLuint UBO_index = glGetUniformBlockIndex(
480 program, "MarchingCellStateBlock"
481 );
482 if(UBO_index != GL_INVALID_INDEX) {
483 glUniformBlockBinding(
484 program, UBO_index, uniform_binding_point_
485 );
486 } else {
487 Logger::warn("GLUP")
488 << "MarchingCellStateBlock not found" << std::endl;
489 }
490 #endif
491 }
492
493 }
494