| 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/basic/GL.h> | ||
| 41 | #include <geogram_gfx/basic/GLSL.h> | ||
| 42 | #include <geogram_gfx/GLUP/GLUP.h> | ||
| 43 | #include <geogram_gfx/GLUP/GLUP_context.h> | ||
| 44 | #include <geogram/basic/logger.h> | ||
| 45 | #include <geogram/basic/command_line.h> | ||
| 46 | |||
| 47 | namespace { | ||
| 48 | using namespace GEO; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * \brief Converts a mat4 into a matrix for OpenGL. | ||
| 52 | * \param[in] m a const reference to the matrix | ||
| 53 | * \return a const pointer to the converted matrix, | ||
| 54 | * stored as a static array of 16 doubles. | ||
| 55 | * \note The matrix is transposed before being sent to OpenGL | ||
| 56 | * because Geogram uses the convention with column | ||
| 57 | * vectors and OpenGL the convention with row vectors | ||
| 58 | * to represent the transformed points. | ||
| 59 | */ | ||
| 60 | ✗ | const GLdouble* convert_matrix( | |
| 61 | const mat4& m | ||
| 62 | ) { | ||
| 63 | static double result[16] ; | ||
| 64 | ✗ | index_t k = 0 ; | |
| 65 | ✗ | for(index_t i=0; i<4; i++) { | |
| 66 | ✗ | for(index_t j=0; j<4; j++) { | |
| 67 | ✗ | result[k] = m(i,j) ; | |
| 68 | ✗ | k++ ; | |
| 69 | } | ||
| 70 | } | ||
| 71 | ✗ | return result ; | |
| 72 | } | ||
| 73 | |||
| 74 | GLuint quad_VAO = 0; | ||
| 75 | GLuint quad_vertices_VBO = 0; | ||
| 76 | GLuint quad_tex_coords_VBO = 0; | ||
| 77 | GLuint quad_program = 0; | ||
| 78 | GLuint quad_program_BW = 0; | ||
| 79 | GLuint quad_program_DEPTH = 0; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * \brief Creates the VAO, VBOs and program used to draw | ||
| 83 | * a single textured quad. | ||
| 84 | * \details We often have to do that, for instance for blitting | ||
| 85 | * a FrameBufferObject onto the screen (or onto another FrameBufferObject) | ||
| 86 | */ | ||
| 87 | ✗ | void create_quad_VAO_and_program() { | |
| 88 | // All that stuff just to draw a single textured square, | ||
| 89 | // the new OpenGL is really painful !!! | ||
| 90 | // (one could use glBegin()/glVertex()/glEnd() instead, but | ||
| 91 | // this would not work with pure Core OpenGL profile and | ||
| 92 | // neither with OpenGL ES !! | ||
| 93 | |||
| 94 | |||
| 95 | // Will be drawn with a triangle strip (supported in both | ||
| 96 | // OpenGL and OpenGL ES) | ||
| 97 | |||
| 98 | static GLfloat coords[4][2] = { | ||
| 99 | {-1.0f, -1.0f}, | ||
| 100 | { 1.0f, -1.0f}, | ||
| 101 | {-1.0f, 1.0f}, | ||
| 102 | { 1.0f, 1.0f} | ||
| 103 | }; | ||
| 104 | |||
| 105 | static GLfloat tex_coords[4][2] = { | ||
| 106 | { 0.0f, 0.0f}, | ||
| 107 | { 1.0f, 0.0f}, | ||
| 108 | { 0.0f, 1.0f}, | ||
| 109 | { 1.0f, 1.0f} | ||
| 110 | }; | ||
| 111 | |||
| 112 | ✗ | update_buffer_object( | |
| 113 | quad_vertices_VBO, GL_ARRAY_BUFFER, | ||
| 114 | sizeof(GL_FLOAT)*2*4, coords | ||
| 115 | ); | ||
| 116 | ✗ | update_buffer_object( | |
| 117 | quad_tex_coords_VBO, GL_ARRAY_BUFFER, | ||
| 118 | sizeof(GL_FLOAT)*2*4, tex_coords | ||
| 119 | ); | ||
| 120 | ✗ | glupGenVertexArrays(1, &quad_VAO); | |
| 121 | ✗ | glupBindVertexArray(quad_VAO); | |
| 122 | |||
| 123 | ✗ | glBindBuffer(GL_ARRAY_BUFFER, quad_vertices_VBO); | |
| 124 | ✗ | glEnableVertexAttribArray(0); | |
| 125 | ✗ | glVertexAttribPointer( | |
| 126 | 0, // Attribute 0 | ||
| 127 | 2, // nb coordinates per vertex | ||
| 128 | GL_FLOAT, // input coordinates representation | ||
| 129 | GL_FALSE, // do not normalize | ||
| 130 | 0, // offset between two consecutive vertices (0 = packed) | ||
| 131 | nullptr // addr. relative to bound VBO | ||
| 132 | ); | ||
| 133 | |||
| 134 | ✗ | glBindBuffer(GL_ARRAY_BUFFER, quad_tex_coords_VBO); | |
| 135 | ✗ | glEnableVertexAttribArray(1); | |
| 136 | ✗ | glVertexAttribPointer( | |
| 137 | 1, // Attribute 1 | ||
| 138 | 2, // nb coordinates per vertex | ||
| 139 | GL_FLOAT, // input coordinates representation | ||
| 140 | GL_FALSE, // do not normalize | ||
| 141 | 0, // offset between two consecutive vertices (0 = packed) | ||
| 142 | nullptr // addr. relative to bound VBO | ||
| 143 | ); | ||
| 144 | |||
| 145 | ✗ | glupBindVertexArray(0); | |
| 146 | ✗ | glBindBuffer(GL_ARRAY_BUFFER, 0); | |
| 147 | |||
| 148 | #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_ANDROID) | ||
| 149 | #define NO_DEPTH_SHADER | ||
| 150 | |||
| 151 | static const char* vshader_source = | ||
| 152 | "#version 100 \n" | ||
| 153 | "attribute vec2 vertex_in; \n" | ||
| 154 | "attribute vec2 tex_coord_in; \n" | ||
| 155 | "varying vec2 tex_coord; \n" | ||
| 156 | "void main() { \n" | ||
| 157 | " tex_coord = tex_coord_in; \n" | ||
| 158 | " gl_Position = vec4(vertex_in, 0.0, 1.0); \n" | ||
| 159 | "} \n" | ||
| 160 | ; | ||
| 161 | |||
| 162 | static const char* fshader_source = | ||
| 163 | "#version 100 \n" | ||
| 164 | "precision mediump float; \n" | ||
| 165 | "varying vec2 tex_coord; \n" | ||
| 166 | "uniform sampler2D tex; \n" | ||
| 167 | "void main() { \n" | ||
| 168 | " gl_FragColor = texture2D( \n" | ||
| 169 | " tex, tex_coord \n" | ||
| 170 | " ); \n" | ||
| 171 | "} \n" | ||
| 172 | ; | ||
| 173 | |||
| 174 | static const char* fshader_BW_source = | ||
| 175 | "#version 100 \n" | ||
| 176 | "precision mediump float; \n" | ||
| 177 | "varying vec2 tex_coord; \n" | ||
| 178 | "uniform sampler2D tex; \n" | ||
| 179 | "void main() { \n" | ||
| 180 | " gl_FragColor = vec4(texture2D( \n" | ||
| 181 | " tex, tex_coord \n" | ||
| 182 | " ).xxx,1.0); \n" | ||
| 183 | "} \n" | ||
| 184 | ; | ||
| 185 | |||
| 186 | #else | ||
| 187 | |||
| 188 | static const char* vshader_source = | ||
| 189 | # ifdef GEO_OS_APPLE | ||
| 190 | "#version 330 \n" | ||
| 191 | # else | ||
| 192 | "#version 130 \n" | ||
| 193 | # endif | ||
| 194 | "in vec2 vertex_in; \n" | ||
| 195 | "in vec2 tex_coord_in; \n" | ||
| 196 | "out vec2 tex_coord; \n" | ||
| 197 | "void main() { \n" | ||
| 198 | " tex_coord = tex_coord_in; \n" | ||
| 199 | " gl_Position = vec4(vertex_in, 0.0, 1.0); \n" | ||
| 200 | "} \n" | ||
| 201 | ; | ||
| 202 | |||
| 203 | static const char* fshader_source = | ||
| 204 | # ifdef GEO_OS_APPLE | ||
| 205 | "#version 330 \n" | ||
| 206 | # else | ||
| 207 | "#version 130 \n" | ||
| 208 | # endif | ||
| 209 | "out vec4 frag_color ; \n" | ||
| 210 | "in vec2 tex_coord; \n" | ||
| 211 | "uniform sampler2D tex; \n" | ||
| 212 | "void main() { \n" | ||
| 213 | " frag_color = texture( \n" | ||
| 214 | " tex, tex_coord \n" | ||
| 215 | " ); \n" | ||
| 216 | "} \n" | ||
| 217 | ; | ||
| 218 | |||
| 219 | static const char* fshader_BW_source = | ||
| 220 | # ifdef GEO_OS_APPLE | ||
| 221 | "#version 330 \n" | ||
| 222 | # else | ||
| 223 | "#version 130 \n" | ||
| 224 | # endif | ||
| 225 | "out vec4 frag_color ; \n" | ||
| 226 | "in vec2 tex_coord; \n" | ||
| 227 | "uniform sampler2D tex; \n" | ||
| 228 | "void main() { \n" | ||
| 229 | " frag_color = vec4(texture( \n" | ||
| 230 | " tex, tex_coord \n" | ||
| 231 | " ).xxx,1.0); \n" | ||
| 232 | "} \n" | ||
| 233 | ; | ||
| 234 | |||
| 235 | // NOTE: does not seem to work ! | ||
| 236 | static const char* fshader_DEPTH_source = | ||
| 237 | # ifdef GEO_OS_APPLE | ||
| 238 | "#version 330 \n" | ||
| 239 | # else | ||
| 240 | "#version 130 \n" | ||
| 241 | # endif | ||
| 242 | "in vec2 tex_coord; \n" | ||
| 243 | "uniform sampler2D tex; \n" | ||
| 244 | "void main() { \n" | ||
| 245 | " float z = texture(tex,tex_coord).r; \n" | ||
| 246 | " gl_FragDepth = \n" | ||
| 247 | " (1.0-z)*gl_DepthRange.near + z*gl_DepthRange.far; \n" | ||
| 248 | "} \n" | ||
| 249 | ; | ||
| 250 | #endif | ||
| 251 | |||
| 252 | ✗ | GLuint vshader = GLSL::compile_shader( | |
| 253 | GL_VERTEX_SHADER, vshader_source, nullptr | ||
| 254 | ); | ||
| 255 | |||
| 256 | ✗ | GLuint fshader = GLSL::compile_shader( | |
| 257 | GL_FRAGMENT_SHADER, fshader_source, nullptr | ||
| 258 | ); | ||
| 259 | |||
| 260 | ✗ | GLuint fshader_BW = GLSL::compile_shader( | |
| 261 | GL_FRAGMENT_SHADER, fshader_BW_source, nullptr | ||
| 262 | ); | ||
| 263 | |||
| 264 | #ifndef NO_DEPTH_SHADER | ||
| 265 | ✗ | GLuint fshader_DEPTH = GLSL::compile_shader( | |
| 266 | GL_FRAGMENT_SHADER, fshader_DEPTH_source, nullptr | ||
| 267 | ); | ||
| 268 | #endif | ||
| 269 | |||
| 270 | ✗ | quad_program = GLSL::create_program_from_shaders_no_link( | |
| 271 | vshader, fshader, nullptr | ||
| 272 | ); | ||
| 273 | |||
| 274 | ✗ | quad_program_BW = GLSL::create_program_from_shaders_no_link( | |
| 275 | vshader, fshader_BW, nullptr | ||
| 276 | ); | ||
| 277 | |||
| 278 | #ifndef NO_DEPTH_SHADER | ||
| 279 | ✗ | quad_program_DEPTH = GLSL::create_program_from_shaders_no_link( | |
| 280 | vshader, fshader_DEPTH, nullptr | ||
| 281 | ); | ||
| 282 | #endif | ||
| 283 | |||
| 284 | ✗ | glBindAttribLocation(quad_program, 0, "vertex_in"); | |
| 285 | ✗ | glBindAttribLocation(quad_program, 1, "tex_coord_in"); | |
| 286 | ✗ | GLSL::link_program(quad_program); | |
| 287 | |||
| 288 | |||
| 289 | ✗ | glBindAttribLocation(quad_program_BW, 0, "vertex_in"); | |
| 290 | ✗ | glBindAttribLocation(quad_program_BW, 1, "tex_coord_in"); | |
| 291 | ✗ | GLSL::link_program(quad_program_BW); | |
| 292 | ✗ | GLSL::set_program_uniform_by_name(quad_program_BW, "tex", 0); | |
| 293 | |||
| 294 | #ifndef NO_DEPTH_SHADER | ||
| 295 | ✗ | glBindAttribLocation(quad_program_DEPTH, 0, "vertex_in"); | |
| 296 | ✗ | glBindAttribLocation(quad_program_DEPTH, 1, "tex_coord_in"); | |
| 297 | ✗ | GLSL::link_program(quad_program_DEPTH); | |
| 298 | ✗ | GLSL::set_program_uniform_by_name(quad_program_DEPTH, "tex", 0); | |
| 299 | #endif | ||
| 300 | ✗ | glDeleteShader(vshader); | |
| 301 | ✗ | glDeleteShader(fshader); | |
| 302 | ✗ | glDeleteShader(fshader_BW); | |
| 303 | |||
| 304 | #ifndef NO_DEPTH_SHADER | ||
| 305 | ✗ | glDeleteShader(fshader_DEPTH); | |
| 306 | #endif | ||
| 307 | ✗ | } | |
| 308 | |||
| 309 | ✗ | const char* error_string(GLenum error_code) { | |
| 310 | ✗ | const char* result = nullptr; | |
| 311 | ✗ | switch(error_code) { | |
| 312 | ✗ | case GL_NO_ERROR: | |
| 313 | ✗ | result = "no error"; | |
| 314 | ✗ | break; | |
| 315 | ✗ | case GL_INVALID_ENUM: | |
| 316 | ✗ | result = "invalid enum"; | |
| 317 | ✗ | break; | |
| 318 | ✗ | case GL_INVALID_VALUE: | |
| 319 | ✗ | result = "invalid value"; | |
| 320 | ✗ | break; | |
| 321 | ✗ | case GL_INVALID_OPERATION: | |
| 322 | ✗ | result = "invalid operation"; | |
| 323 | ✗ | break; | |
| 324 | ✗ | case GL_INVALID_FRAMEBUFFER_OPERATION: | |
| 325 | ✗ | result = "invalid framebuffer operation"; | |
| 326 | ✗ | break; | |
| 327 | ✗ | case GL_OUT_OF_MEMORY: | |
| 328 | ✗ | result = "out of memory"; | |
| 329 | ✗ | break; | |
| 330 | /* | ||
| 331 | case GL_STACK_UNDERFLOW: | ||
| 332 | result = "stack underflow"; | ||
| 333 | break; | ||
| 334 | case GL_STACK_OVERFLOW: | ||
| 335 | result = "stack over"; | ||
| 336 | break; | ||
| 337 | */ | ||
| 338 | ✗ | default: | |
| 339 | ✗ | result = "unknown errorcode"; | |
| 340 | } | ||
| 341 | ✗ | return result; | |
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | namespace GEO { | ||
| 346 | |||
| 347 | namespace GL { | ||
| 348 | |||
| 349 | ✗ | void initialize() { | |
| 350 | ✗ | } | |
| 351 | |||
| 352 | ✗ | void terminate() { | |
| 353 | ✗ | if(quad_program != 0) { | |
| 354 | ✗ | glDeleteProgram(quad_program); | |
| 355 | ✗ | quad_program = 0; | |
| 356 | } | ||
| 357 | ✗ | if(quad_program_BW != 0) { | |
| 358 | ✗ | glDeleteProgram(quad_program_BW); | |
| 359 | ✗ | quad_program_BW = 0; | |
| 360 | } | ||
| 361 | ✗ | if(quad_program_DEPTH != 0) { | |
| 362 | ✗ | glDeleteProgram(quad_program_DEPTH); | |
| 363 | ✗ | quad_program_DEPTH = 0; | |
| 364 | } | ||
| 365 | ✗ | if(quad_VAO != 0) { | |
| 366 | ✗ | glupDeleteVertexArrays(1, &quad_VAO); | |
| 367 | ✗ | quad_VAO = 0; | |
| 368 | } | ||
| 369 | ✗ | if(quad_vertices_VBO != 0) { | |
| 370 | ✗ | glDeleteBuffers(1, &quad_vertices_VBO); | |
| 371 | ✗ | quad_vertices_VBO = 0; | |
| 372 | } | ||
| 373 | ✗ | if(quad_tex_coords_VBO != 0) { | |
| 374 | ✗ | glDeleteBuffers(1, &quad_tex_coords_VBO); | |
| 375 | ✗ | quad_tex_coords_VBO = 0; | |
| 376 | } | ||
| 377 | ✗ | } | |
| 378 | } | ||
| 379 | |||
| 380 | ✗ | void glupMapTexCoords1d(double minval, double maxval, index_t mult) { | |
| 381 | ✗ | glupMatrixMode(GLUP_TEXTURE_MATRIX); | |
| 382 | GLUPdouble M[16]; | ||
| 383 | ✗ | Memory::clear(M, sizeof(GLUPdouble)*16); | |
| 384 | ✗ | double d = maxval - minval; | |
| 385 | ✗ | if(::fabs(d) < 1e-10) { | |
| 386 | ✗ | d = 0.0; | |
| 387 | } else { | ||
| 388 | ✗ | d = 1.0 / d; | |
| 389 | } | ||
| 390 | ✗ | d *= double(std::max(mult, 1u)); | |
| 391 | ✗ | M[0] = d; | |
| 392 | ✗ | M[12] = -d*minval; | |
| 393 | ✗ | M[15] = 1.0; | |
| 394 | ✗ | glupLoadMatrixd(M); | |
| 395 | ✗ | glupMatrixMode(GLUP_MODELVIEW_MATRIX); | |
| 396 | ✗ | } | |
| 397 | |||
| 398 | ✗ | void glupLoadMatrix(const mat4& m) { | |
| 399 | ✗ | glupLoadMatrixd(convert_matrix(m)); | |
| 400 | ✗ | } | |
| 401 | |||
| 402 | ✗ | void glupMultMatrix(const mat4& m) { | |
| 403 | ✗ | glupMultMatrixd(convert_matrix(m)); | |
| 404 | ✗ | } | |
| 405 | |||
| 406 | |||
| 407 | ✗ | GLint64 get_size_of_bound_buffer_object(GLenum target) { | |
| 408 | |||
| 409 | #ifdef GLUP_DEBUG | ||
| 410 | { | ||
| 411 | GLint buffer = 0; | ||
| 412 | switch(target) { | ||
| 413 | case GL_ARRAY_BUFFER: | ||
| 414 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &buffer); | ||
| 415 | break; | ||
| 416 | case GL_ELEMENT_ARRAY_BUFFER: | ||
| 417 | glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &buffer); | ||
| 418 | break; | ||
| 419 | default: | ||
| 420 | geo_assert_not_reached; | ||
| 421 | break; | ||
| 422 | } | ||
| 423 | geo_assert(buffer != 0); | ||
| 424 | } | ||
| 425 | #endif | ||
| 426 | |||
| 427 | ✗ | GLint64 result=0; | |
| 428 | |||
| 429 | #ifdef GEO_GL_440 | ||
| 430 | |||
| 431 | static bool init = false; | ||
| 432 | static bool use_glGetBufferParameteri64v = false; | ||
| 433 | |||
| 434 | // Note: there is a version of glGetBufferParameteriv that uses | ||
| 435 | // 64 bit parameters. Since array data larger than 4Gb will be | ||
| 436 | // common place, it is this version that should be used. However, | ||
| 437 | // it is not supported by the Intel driver (therefore we fallback | ||
| 438 | // to the standard 32 bits version if such a driver is detected). | ||
| 439 | // It is not implemented by Gallium either... Oh well, for now | ||
| 440 | // I deactivate it if the driver is not NVIDIA. | ||
| 441 | |||
| 442 | ✗ | if(!init) { | |
| 443 | ✗ | init = true; | |
| 444 | ✗ | const char* vendor = (const char*)glGetString(GL_VENDOR); | |
| 445 | ✗ | use_glGetBufferParameteri64v = ( | |
| 446 | ✗ | strlen(vendor) >= 6 && !strncmp(vendor, "NVIDIA", 6) && | |
| 447 | ✗ | (glGetBufferParameteri64v != nullptr) | |
| 448 | ); | ||
| 449 | // Does not seem to be implemented under OpenGL ES | ||
| 450 | ✗ | if(CmdLine::get_arg("gfx:GL_profile") == "ES") { | |
| 451 | ✗ | use_glGetBufferParameteri64v = false; | |
| 452 | } | ||
| 453 | } | ||
| 454 | ✗ | if(use_glGetBufferParameteri64v) { | |
| 455 | ✗ | glGetBufferParameteri64v(target,GL_BUFFER_SIZE,&result); | |
| 456 | } else | ||
| 457 | #endif | ||
| 458 | { | ||
| 459 | ✗ | GLint result32=0; | |
| 460 | ✗ | glGetBufferParameteriv(target,GL_BUFFER_SIZE,&result32); | |
| 461 | ✗ | result = GLint64(result32); | |
| 462 | } | ||
| 463 | ✗ | return result; | |
| 464 | } | ||
| 465 | |||
| 466 | |||
| 467 | ✗ | void update_buffer_object( | |
| 468 | GLuint& buffer_id, GLenum target, size_t new_size, const void* data | ||
| 469 | ) { | ||
| 470 | ✗ | if(new_size == 0) { | |
| 471 | ✗ | if(buffer_id != 0) { | |
| 472 | ✗ | glDeleteBuffers(1, &buffer_id); | |
| 473 | ✗ | buffer_id = 0; | |
| 474 | } | ||
| 475 | ✗ | return; | |
| 476 | } | ||
| 477 | |||
| 478 | ✗ | GLint64 size = 0; | |
| 479 | ✗ | if(buffer_id == 0) { | |
| 480 | ✗ | glGenBuffers(1, &buffer_id); | |
| 481 | ✗ | glBindBuffer(target, buffer_id); | |
| 482 | } else { | ||
| 483 | ✗ | glBindBuffer(target, buffer_id); | |
| 484 | ✗ | size = get_size_of_bound_buffer_object(target); | |
| 485 | } | ||
| 486 | |||
| 487 | ✗ | if(new_size == size_t(size)) { | |
| 488 | ✗ | glBufferSubData(target, 0, GLsizeiptr(size), data); | |
| 489 | } else { | ||
| 490 | ✗ | glBufferData( | |
| 491 | target, GLsizeiptr(new_size), data, GL_STATIC_DRAW | ||
| 492 | ); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | ✗ | void stream_buffer_object( | |
| 497 | GLuint& buffer_id, GLenum target, size_t new_size, const void* data | ||
| 498 | ) { | ||
| 499 | ✗ | if(new_size == 0) { | |
| 500 | ✗ | if(buffer_id != 0) { | |
| 501 | ✗ | glDeleteBuffers(1, &buffer_id); | |
| 502 | ✗ | buffer_id = 0; | |
| 503 | } | ||
| 504 | ✗ | return; | |
| 505 | } | ||
| 506 | |||
| 507 | ✗ | GLint64 size = 0; | |
| 508 | ✗ | if(buffer_id == 0) { | |
| 509 | ✗ | glGenBuffers(1, &buffer_id); | |
| 510 | ✗ | glBindBuffer(target, buffer_id); | |
| 511 | } else { | ||
| 512 | ✗ | glBindBuffer(target, buffer_id); | |
| 513 | ✗ | size = get_size_of_bound_buffer_object(target); | |
| 514 | } | ||
| 515 | |||
| 516 | ✗ | if(new_size == size_t(size)) { | |
| 517 | // Binding nullptr makes the GPU-side allocated buffer "orphan", | ||
| 518 | // if there was a rendering operation currently using it, then | ||
| 519 | // it can safely continue. | ||
| 520 | ✗ | glBufferData(target, GLsizeiptr(size), nullptr, GL_STREAM_DRAW); | |
| 521 | // And here we bind a fresh new block of GPU-side memory. | ||
| 522 | // See https://www.opengl.org/wiki/Buffer_Object_Streaming | ||
| 523 | ✗ | glBufferData(target, GLsizeiptr(size), data, GL_STREAM_DRAW); | |
| 524 | } else { | ||
| 525 | ✗ | glBufferData( | |
| 526 | target, GLsizeiptr(new_size), data, GL_STATIC_DRAW | ||
| 527 | ); | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | |||
| 532 | ✗ | void update_or_check_buffer_object( | |
| 533 | GLuint& buffer_id, GLenum target, size_t new_size, const void* data, | ||
| 534 | bool update | ||
| 535 | ) { | ||
| 536 | ✗ | if(update) { | |
| 537 | ✗ | update_buffer_object(buffer_id, target, new_size, data); | |
| 538 | } else { | ||
| 539 | ✗ | glBindBuffer(target, buffer_id); | |
| 540 | ✗ | if(new_size != size_t(get_size_of_bound_buffer_object(target))) { | |
| 541 | ✗ | Logger::warn("OpenGL") | |
| 542 | ✗ | << "Buffer Object does not have the expected size." | |
| 543 | ✗ | << std::endl; | |
| 544 | ✗ | Logger::warn("OpenGL") | |
| 545 | << "An object was probably changed " | ||
| 546 | ✗ | << "without notifying/updating the graphics." | |
| 547 | ✗ | << std::endl; | |
| 548 | ✗ | Logger::warn("OpenGL") | |
| 549 | ✗ | << "Forcing Buffer Object update." | |
| 550 | ✗ | << std::endl; | |
| 551 | ✗ | update_buffer_object(buffer_id, target, new_size, data); | |
| 552 | } | ||
| 553 | } | ||
| 554 | ✗ | } | |
| 555 | |||
| 556 | ✗ | void check_gl(const char* file, int line, bool warning_only) { | |
| 557 | |||
| 558 | ✗ | GLenum error_code = glGetError() ; | |
| 559 | ✗ | bool has_opengl_errors = false ; | |
| 560 | ✗ | while(error_code != GL_NO_ERROR) { | |
| 561 | ✗ | has_opengl_errors = true ; | |
| 562 | ✗ | if(warning_only) { | |
| 563 | ✗ | Logger::warn("OpenGL") | |
| 564 | ✗ | << file << ":" << line << " " | |
| 565 | << error_string(error_code) | ||
| 566 | ✗ | << " (ignored)" | |
| 567 | ✗ | << std::endl; | |
| 568 | } else { | ||
| 569 | ✗ | Logger::err("OpenGL") | |
| 570 | ✗ | << file << ":" << line << " " | |
| 571 | ✗ | << error_string(error_code) | |
| 572 | ✗ | << std::endl; | |
| 573 | } | ||
| 574 | ✗ | error_code = glGetError() ; | |
| 575 | } | ||
| 576 | ✗ | geo_argused(has_opengl_errors); | |
| 577 | ✗ | } | |
| 578 | |||
| 579 | ✗ | void clear_gl_error_flags(const char* file, int line) { | |
| 580 | #ifdef GEO_DEBUG | ||
| 581 | ✗ | check_gl(file,line,true); | |
| 582 | #else | ||
| 583 | geo_argused(file); | ||
| 584 | geo_argused(line); | ||
| 585 | while(glGetError() != GL_NO_ERROR); | ||
| 586 | #endif | ||
| 587 | ✗ | } | |
| 588 | |||
| 589 | ✗ | void draw_unit_textured_quad(TexturedQuadMode mode) { | |
| 590 | ✗ | if(quad_VAO == 0) { | |
| 591 | ✗ | create_quad_VAO_and_program(); | |
| 592 | } | ||
| 593 | ✗ | GLint current_program = 0; | |
| 594 | ✗ | glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program); | |
| 595 | ✗ | if(current_program == 0) { | |
| 596 | ✗ | switch(mode) { | |
| 597 | ✗ | case TEX_QUAD_RGBA: | |
| 598 | ✗ | glUseProgram(quad_program); | |
| 599 | ✗ | break; | |
| 600 | ✗ | case TEX_QUAD_RRR1: | |
| 601 | ✗ | glUseProgram(quad_program_BW); | |
| 602 | ✗ | break; | |
| 603 | ✗ | case TEX_QUAD_DEPTH: | |
| 604 | ✗ | glUseProgram(quad_program_DEPTH); | |
| 605 | ✗ | break; | |
| 606 | } | ||
| 607 | } | ||
| 608 | ✗ | glupBindVertexArray(quad_VAO); | |
| 609 | ✗ | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
| 610 | ✗ | glupBindVertexArray(0); | |
| 611 | ✗ | if(current_program == 0) { | |
| 612 | ✗ | glUseProgram(0); | |
| 613 | } | ||
| 614 | ✗ | } | |
| 615 | |||
| 616 | /****************************************************************/ | ||
| 617 | |||
| 618 | |||
| 619 | ✗ | static int htoi(char digit) { | |
| 620 | ✗ | if(digit >= '0' && digit <= '9') { | |
| 621 | ✗ | return digit - '0'; | |
| 622 | } | ||
| 623 | ✗ | if(digit >= 'a' && digit <= 'f') { | |
| 624 | ✗ | return digit - 'a' + 10; | |
| 625 | } | ||
| 626 | ✗ | if(digit >= 'A' && digit <= 'F') { | |
| 627 | ✗ | return digit - 'A' + 10; | |
| 628 | } | ||
| 629 | ✗ | fprintf(stderr, "xpm: unknown digit\n"); | |
| 630 | ✗ | return 0; | |
| 631 | } | ||
| 632 | |||
| 633 | /* The colormap. */ | ||
| 634 | static unsigned char i2r[1024]; | ||
| 635 | static unsigned char i2g[1024]; | ||
| 636 | static unsigned char i2b[1024]; | ||
| 637 | static unsigned char i2a[1024]; | ||
| 638 | |||
| 639 | /* | ||
| 640 | * Converts a two-digit XPM color code into | ||
| 641 | * a color index. | ||
| 642 | */ | ||
| 643 | static int char_to_index[256][256]; | ||
| 644 | |||
| 645 | ✗ | void glTexImage2Dxpm(char const* const* xpm_data) { | |
| 646 | int width, height, nb_colors, chars_per_pixel; | ||
| 647 | ✗ | int line = 0; | |
| 648 | ✗ | int color = 0; | |
| 649 | ✗ | int key1 = 0, key2 = 0; | |
| 650 | const char* colorcode; | ||
| 651 | int x, y; | ||
| 652 | unsigned char* rgba; | ||
| 653 | unsigned char* pixel; | ||
| 654 | |||
| 655 | ✗ | sscanf( | |
| 656 | ✗ | xpm_data[line], "%6d%6d%6d%6d", | |
| 657 | &width, &height, &nb_colors, &chars_per_pixel | ||
| 658 | ); | ||
| 659 | ✗ | line++; | |
| 660 | ✗ | if(nb_colors > 1024) { | |
| 661 | ✗ | fprintf(stderr, "xpm with more than 1024 colors\n"); | |
| 662 | ✗ | return; | |
| 663 | } | ||
| 664 | ✗ | if(chars_per_pixel != 1 && chars_per_pixel != 2) { | |
| 665 | ✗ | fprintf(stderr, "xpm with more than 2 chars per pixel\n"); | |
| 666 | ✗ | return; | |
| 667 | } | ||
| 668 | ✗ | for(color = 0; color < nb_colors; color++) { | |
| 669 | int r, g, b; | ||
| 670 | int none ; | ||
| 671 | |||
| 672 | ✗ | key1 = xpm_data[line][0]; | |
| 673 | ✗ | key2 = (chars_per_pixel == 2) ? xpm_data[line][1] : 0; | |
| 674 | ✗ | colorcode = strstr(xpm_data[line], "c #"); | |
| 675 | ✗ | none = 0; | |
| 676 | ✗ | if(colorcode == nullptr) { | |
| 677 | ✗ | colorcode = "c #000000"; | |
| 678 | ✗ | if(strstr(xpm_data[line], "None") != nullptr) { | |
| 679 | ✗ | none = 1; | |
| 680 | } else { | ||
| 681 | ✗ | fprintf( | |
| 682 | stderr, "unknown xpm color entry (replaced with black)\n" | ||
| 683 | ); | ||
| 684 | } | ||
| 685 | } | ||
| 686 | ✗ | colorcode += 3; | |
| 687 | |||
| 688 | ✗ | if(strlen(colorcode) == 12) { | |
| 689 | ✗ | r = 16 * htoi(colorcode[0]) + htoi(colorcode[1]); | |
| 690 | ✗ | g = 16 * htoi(colorcode[4]) + htoi(colorcode[5]); | |
| 691 | ✗ | b = 16 * htoi(colorcode[8]) + htoi(colorcode[9]); | |
| 692 | } else { | ||
| 693 | ✗ | r = 16 * htoi(colorcode[0]) + htoi(colorcode[1]); | |
| 694 | ✗ | g = 16 * htoi(colorcode[2]) + htoi(colorcode[3]); | |
| 695 | ✗ | b = 16 * htoi(colorcode[4]) + htoi(colorcode[5]); | |
| 696 | } | ||
| 697 | |||
| 698 | ✗ | i2r[color] = (unsigned char) r; | |
| 699 | ✗ | i2g[color] = (unsigned char) g; | |
| 700 | ✗ | i2b[color] = (unsigned char) b; | |
| 701 | ✗ | if(none) { | |
| 702 | ✗ | i2a[color] = 0; | |
| 703 | } else { | ||
| 704 | ✗ | i2a[color] = 255; | |
| 705 | } | ||
| 706 | ✗ | char_to_index[key1][key2] = color; | |
| 707 | ✗ | line++; | |
| 708 | } | ||
| 709 | ✗ | rgba = (unsigned char*) malloc((size_t) (width * height * 4)); | |
| 710 | ✗ | pixel = rgba; | |
| 711 | ✗ | for(y = 0; y < height; y++) { | |
| 712 | ✗ | for(x = 0; x < width; x++) { | |
| 713 | ✗ | if(chars_per_pixel == 2) { | |
| 714 | ✗ | key1 = xpm_data[line][2 * x]; | |
| 715 | ✗ | key2 = xpm_data[line][2 * x + 1]; | |
| 716 | } else { | ||
| 717 | ✗ | key1 = xpm_data[line][x]; | |
| 718 | ✗ | key2 = 0; | |
| 719 | } | ||
| 720 | ✗ | color = char_to_index[key1][key2]; | |
| 721 | ✗ | pixel[0] = i2r[color]; | |
| 722 | ✗ | pixel[1] = i2g[color]; | |
| 723 | ✗ | pixel[2] = i2b[color]; | |
| 724 | ✗ | pixel[3] = i2a[color]; | |
| 725 | ✗ | pixel += 4; | |
| 726 | } | ||
| 727 | ✗ | line++; | |
| 728 | } | ||
| 729 | |||
| 730 | ✗ | glTexImage2D( | |
| 731 | GL_TEXTURE_2D, 0, | ||
| 732 | GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba | ||
| 733 | ); | ||
| 734 | #ifndef __EMSCRIPTEN__ | ||
| 735 | ✗ | glGenerateMipmap(GL_TEXTURE_2D); | |
| 736 | #endif | ||
| 737 | ✗ | free(rgba); | |
| 738 | } | ||
| 739 | |||
| 740 | |||
| 741 | } | ||
| 742 |