| 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 |