GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/GLUP/GLUP.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 820 0.0%
Functions: 0 134 0.0%
Branches: 0 402 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.h>
41 #include <geogram_gfx/GLUP/GLUP_context_GLSL.h>
42 #include <geogram_gfx/GLUP/GLUP_context_ES.h>
43 #include <geogram_gfx/basic/GLSL.h>
44 #include <geogram/basic/logger.h>
45 #include <geogram/basic/file_system.h>
46 #include <geogram/basic/command_line.h>
47
48 #ifdef GEO_OS_EMSCRIPTEN
49 #include <emscripten.h>
50 #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
51 #endif
52
53 #ifdef GEO_OS_ANDROID
54 # pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
55 #endif
56
57
58 /*****************************************************************************/
59
60 namespace {
61 using namespace GEO;
62 void downgrade_message() {
63 static bool done = false;
64 if(!done) {
65 done = true;
66 Logger::out("GLUP")
67 << "Something happened, trying to fix (by downgrading)"
68 << std::endl;
69 Logger::out("GLUP")
70 << "If this does not work, try the following options:"
71 << std::endl;
72 Logger::out("GLUP")
73 << " (1) make sure your OpenGL driver is up to date"
74 << std::endl;
75 Logger::out("GLUP")
76 << " (2) create a file named \'"
77 << CmdLine::get_config_file_name() << "\' in "
78 << std::endl;
79 Logger::out("GLUP")
80 << " your home directory ("
81 << FileSystem::home_directory() << ")"
82 << std::endl;
83 Logger::out("GLUP") << " with: " << std::endl;
84 Logger::out("GLUP")
85 << " gfx:GLUP_profile=GLUPES2"
86 << std::endl;
87 }
88 }
89 }
90
91 /*****************************************************************************/
92
93 namespace GLUP {
94 using namespace GEO;
95
96 extern GLUP_API Context* current_context_;
97 Context* current_context_ = nullptr;
98
99 static std::set<Context*> all_contexts_;
100 static bool initialized_ = false;
101 static void cleanup() {
102 #ifdef GEO_OS_ANDROID
103 // Note: removal of context from all_contexts_
104 // is done in glupDeleteContext() (not in Context
105 // destructor), so we can direcly iterate on all_contexts_
106 // and delete.
107 // TODO: check that GLUP contexts are really destroyed *before*
108 // the OpenGL context. (yes it is, because if I output
109 // something to the logger here, it appears in the app's temrinal).
110 for(auto ctxt: all_contexts_) {
111 delete ctxt;
112 }
113 all_contexts_.clear();
114 return;
115 #endif
116
117 // Note: remaining contexts are not deallocated here because:
118 // (1) there should be no remaining context (it is Application's
119 // job to deallocate them).
120 // (2) when cleanup() is called, Application's delete_window() was
121 // called before, as well as glfwDeleteWindow() / glfwTerminate()
122 // so the OpenGL context is destroyed (and it is not legal to destroy
123 // it before the OpenGL objects in the GLUP context)
124 if(all_contexts_.size() != 0) {
125 Logger::warn("GLUP") << "Some GLUP contexts were not deallocated"
126 << std::endl;
127 Logger::warn("GLUP") << "App\'s GL_terminate() probably forgot to"
128 << std::endl;
129 Logger::warn("GLUP") << "call SimpleApplication\'s GL_terminate()"
130 << std::endl;
131 Logger::warn("GLUP") << "(needs to be at then end of the function)"
132 << std::endl;
133 }
134 }
135
136 }
137
138 /*****************************************************************************/
139
140 const char* glupUniformStateDeclaration() {
141 GEO_CHECK_GL();
142 return GLUP::current_context_->uniform_state_declaration();
143 }
144
145 GLUPuint glupCompileShader(
146 GLUPenum target, GLUPprimitive primitive, const char* source
147 ) {
148 GEO_CHECK_GL();
149 GLUP::current_context_->setup_shaders_source_for_primitive(primitive);
150 // First param: ignored.
151 // Second param: all toggle states are unknown.
152 GLUP::current_context_->setup_shaders_source_for_toggles(0,~0);
153 GEO_CHECK_GL();
154 GLUPuint result = GLSL::compile_shader_with_includes(
155 target, source, GLUP::current_context_
156 );
157 GEO_CHECK_GL();
158 return result;
159 }
160
161 namespace {
162 /**
163 * \brief Converts a comment //stage GL_VERTEX_SHADER into
164 * the associated GLUPenum value.
165 * \param[in,out] comment_str on entry, a pointer to the comment.
166 * on exit, the next line. A '\0' terminator is inserted in
167 * the string (replaces the '\n' that ends the line).
168 * \return the target or 0 if an error was encountered.
169 */
170 static GLUPenum parse_target(char*& comment_str) {
171 char* p1 = comment_str + 8;
172 char* p2 = strchr(p1, '\n');
173 if(p2 == nullptr) {
174 Logger::err("GLSL")
175 << "Missing CR in //stage GL_xxxxx declaration"
176 << std::endl;
177 return 0;
178 }
179 std::string stage_str(p1, size_t(p2-p1));
180
181 GLenum stage = 0;
182 if(stage_str == "GL_VERTEX_SHADER") {
183 stage = GL_VERTEX_SHADER;
184 } else if(stage_str == "GL_FRAGMENT_SHADER") {
185 stage = GL_FRAGMENT_SHADER;
186 }
187
188 #ifndef GEO_OS_EMSCRIPTEN
189 else if(stage_str == "GL_GEOMETRY_SHADER") {
190 stage = GL_GEOMETRY_SHADER;
191 } else if(stage_str == "GL_TESS_CONTROL_SHADER") {
192 stage = GL_TESS_CONTROL_SHADER;
193 } else if(stage_str == "GL_TESS_EVALUATION_SHADER") {
194 stage = GL_TESS_EVALUATION_SHADER;
195 }
196 #endif
197 else {
198 Logger::err("GLSL") << stage_str << ": unknown stage"
199 << std::endl;
200 }
201 *comment_str = '\0';
202 comment_str = p2+1;
203 return stage;
204 }
205
206 /**
207 * \brief Converts a comment //primitive GLUP_QUADS into
208 * the associated GLUPprimitive value.
209 * \param[in] comment_str a pointer to the comment.
210 * \return the primitive
211 */
212 static GLUPprimitive parse_primitive(const char* comment_str) {
213 char* p1 = strstr(const_cast<char*>(comment_str), "//primitive");
214 if(p1 == nullptr) {
215 return GLUP_TRIANGLES; // default
216 }
217 p1 += 12;
218 char* p2 = strchr(p1, '\n');
219 if(p2 == nullptr) {
220 Logger::err("GLSL")
221 << "Missing CR in //primitive GLUP_xxxxx declaration"
222 << std::endl;
223 return GLUP_TRIANGLES;
224 }
225 std::string primitive_str(p1, size_t(p2-p1));
226 for(int i=0; i<GLUP_NB_PRIMITIVES; ++i) {
227 if(
228 primitive_str ==
229 GLUP::Context::glup_primitive_name(GLUPprimitive(i))
230 ) {
231 return GLUPprimitive(i);
232 }
233 }
234 return GLUP_TRIANGLES; // default
235 }
236
237 }
238
239 GLUPuint glupCompileProgram(const char* source_in) {
240 std::string source(source_in);
241 std::vector<const char*> sources;
242 std::vector<GLUPenum> targets;
243 std::vector<GLuint> shaders;
244
245 char* p = const_cast<char*>(source.c_str());
246
247 GLUPprimitive primitive = parse_primitive(source_in);
248
249 bool has_vertex_shader = false;
250 for(
251 p = strstr(p,"//stage");
252 (p != nullptr) && (*p != '\0');
253 p = strstr(p,"//stage ")
254 ) {
255 GLUPenum target = parse_target(p);
256 if(target == 0) {
257 return 0;
258 }
259 sources.push_back(p);
260 targets.push_back(target);
261 has_vertex_shader = has_vertex_shader || (target == GL_VERTEX_SHADER);
262 }
263
264 // Use default vertex shader if no vertex shader was specified.
265 if(!has_vertex_shader) {
266 if(!strcmp(glupCurrentProfileName(),"GLUPES2")) {
267 targets.push_back(GL_VERTEX_SHADER);
268 sources.push_back("//import <GLUPES/vertex_shader.h>\n");
269 } else if(
270 !strcmp(glupCurrentProfileName(),"GLUP140") ||
271 !strcmp(glupCurrentProfileName(),"GLUP150") ||
272 !strcmp(glupCurrentProfileName(),"GLUP440")
273 ) {
274 targets.push_back(GL_VERTEX_SHADER);
275 sources.push_back("//import <GLUPGLSL/vertex_shader.h>\n");
276 }
277 }
278
279 GLuint program = 0;
280 try {
281 for(index_t i=0; i<index_t(sources.size()); ++i) {
282 GLUPuint shader =
283 glupCompileShader(targets[i], primitive, sources[i]);
284 if(shader == 0) {
285 #ifdef GEO_OS_EMSCRIPTEN
286 return 0;
287 #else
288 throw(GLSL::GLSLCompileError());
289 #endif
290 }
291 shaders.push_back(shader);
292 }
293
294 program = glCreateProgram();
295
296 for(index_t i=0; i<index_t(shaders.size()); ++i) {
297 glAttachShader(program, shaders[i]);
298 }
299
300 GEO_CHECK_GL();
301 glBindAttribLocation(program, GLUP::GLUP_VERTEX_ATTRIBUTE, "vertex_in");
302 glBindAttribLocation(program, GLUP::GLUP_COLOR_ATTRIBUTE, "color_in");
303 glBindAttribLocation(
304 program, GLUP::GLUP_TEX_COORD_ATTRIBUTE, "tex_coord_in"
305 );
306 glBindAttribLocation(program, GLUP::GLUP_NORMAL_ATTRIBUTE, "normal_in");
307
308 GEO_CHECK_GL();
309 GLSL::link_program(program);
310 GEO_CHECK_GL();
311 GLUP::current_context_->bind_uniform_state(program);
312 GEO_CHECK_GL();
313 } catch(...) {
314 if(program != 0) {
315 Logger::err("GLSL") << "Could not compile program"
316 << std::endl;
317 glDeleteProgram(program);
318 program = 0;
319 }
320 }
321
322 // Shaders are reference-counted, now they are attached
323 // to the program.
324 for(index_t i=0; i<index_t(shaders.size()); ++i) {
325 glDeleteShader(shaders[i]);
326 }
327
328 return program;
329 }
330
331 void glupBindUniformState(GLUPuint program) {
332 GEO_CHECK_GL();
333 GLUP::current_context_->bind_uniform_state(program);
334 GEO_CHECK_GL();
335 }
336
337
338 #if !defined(GEO_OS_EMSCRIPTEN) && \
339 !defined(GEO_OS_APPLE) && \
340 !defined(GEO_OS_ANDROID)
341
342 /**
343 * \brief Tests whether tessellation shaders are supported by OpenGL.
344 * \details Some drivers may declare to be OpenGL 4.5 compliant whereas
345 * they do not have tesselation shader (for instance, I have an old
346 * NVidia quadro that does that...)
347 * \retval true if tessellation shaders are supported
348 * \retval false otherwise
349 */
350 static bool supports_tessellation_shader() {
351 GEO_CHECK_GL();
352
353 #ifndef GEO_GL_150
354 return false;
355 #else
356 bool result = true;
357
358 // Note: I experienced crashes with glPatchParameterfv() with
359 // the OpenGL es profile, so I'm deactivating it if detected.
360 if(GEO::CmdLine::get_arg("gfx:GL_profile") == "ES") {
361 GEO::Logger::out("GLUP")
362 << "Deactivating tesselation shader under OpenGL ES profile"
363 << std::endl;
364 return false;
365 }
366
367 GLuint s_handle = glCreateShader(GL_TESS_CONTROL_SHADER);
368 result = result && (s_handle != 0);
369 if (s_handle != 0) {
370 glDeleteShader(s_handle);
371 }
372
373 // Clear OpenGL error flag.
374 while(glGetError() != GL_NO_ERROR) {
375 }
376
377 return result;
378 #endif
379 }
380
381 #endif
382
383 GLUPcontext glupCreateContext() {
384
385
386 if(!GLUP::initialized_) {
387 GLUP::initialized_ = true;
388 atexit(GLUP::cleanup);
389 }
390
391
392 GEO_CHECK_GL();
393
394 std::string GLUP_profile = GEO::CmdLine::get_arg("gfx:GLUP_profile");
395 GLUP::Context* result = nullptr;
396
397 if(GLUP_profile == "auto") {
398
399 // emscripten, Apple, Android -> use GLUPES2
400 #if defined(GEO_OS_EMSCRIPTEN)||defined(GEO_OS_APPLE)||defined(GEO_OS_ANDROID)
401 GLUP_profile = "GLUPES2";
402 # if defined(GEO_OS_ANDROID)
403 GLint max_element_index;
404 glGetIntegerv(GL_MAX_ELEMENT_INDEX, &max_element_index);
405 GLint max_elements_indices;
406 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &max_elements_indices);
407 GLint max_elements_vertices;
408 glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices);
409 Logger::out("GLES") << "max element index:" << max_element_index
410 << " max elements indices:" << max_elements_indices
411 << " max elements vertices:" << max_elements_vertices
412 << std::endl;
413 # endif
414 #else
415 // Windows, Linux -> if board is not NVidia, use GLUPES2
416 const char* vendor = (const char*)glGetString(GL_VENDOR);
417 if(!strstr(vendor, "NVIDIA")) {
418 GLUP_profile = "GLUPES2";
419 }
420 // If we still don't know (Windows, Linux with an NVidia), decide
421 // based on supported GLSL version
422 if(GLUP_profile == "auto") {
423 GEO_CHECK_GL();
424 double GLSL_version = GEO::GLSL::supported_language_version();
425 GEO_CHECK_GL();
426 if (GLSL_version >= 4.4) {
427 GEO_CHECK_GL();
428 if (!supports_tessellation_shader()) {
429 GEO::Logger::out("GLUP")
430 << "GLSL version >= 4.4 but tessellation unsupported"
431 << std::endl;
432 downgrade_message();
433 GEO::Logger::out("GLUP") << "Downgrading to GLUP 150..."
434 << std::endl;
435 GLSL_version = 1.5;
436 }
437 GEO_CHECK_GL();
438 }
439
440 if(GLSL_version >= 4.4) {
441 GLUP_profile = "GLUP440";
442 } else if(GLSL_version >= 1.5) {
443 GLUP_profile = "GLUP150";
444 } else if(GLSL_version >= 1.4) {
445 GLUP_profile = "GLUP140";
446 } else {
447 GLUP_profile = "GLUPES2";
448 }
449 }
450 #endif
451 }
452
453
454 GEO::Logger::out("GLUP") << "Using " << GLUP_profile << " profile"
455 << std::endl;
456
457 #ifdef GEO_GL_440
458 if(GLUP_profile == "GLUP440") {
459 try {
460 result = new GLUP::Context_GLSL440;
461 result->setup();
462 } catch(...) {
463 GEO::Logger::warn("GLUP")
464 << "Caught an exception in GLUP440, downgrading to GLUP150"
465 << std::endl;
466 downgrade_message();
467 GLUP_profile = "GLUP150";
468 delete result;
469 result = nullptr;
470 }
471 }
472 #endif
473
474 #ifdef GEO_GL_150
475 if(GLUP_profile == "GLUP150") {
476 try {
477 result = new GLUP::Context_GLSL150;
478 result->setup();
479 } catch(...) {
480 GEO::Logger::warn("GLUP")
481 << "Caught an exception in GLUP150, downgrading to GLUP140"
482 << std::endl;
483 downgrade_message();
484 GLUP_profile = "GLUP140";
485 delete result;
486 result = nullptr;
487 }
488 }
489 #endif
490
491 #ifdef GEO_GL_140
492 if(GLUP_profile == "GLUP140") {
493 try {
494 result = new GLUP::Context_GLSL140;
495 result->setup();
496 } catch(...) {
497 GEO::Logger::warn("GLUP")
498 << "Caught an exception in GLUP140, downgrading to GLUPES2"
499 << std::endl;
500 downgrade_message();
501 GLUP_profile = "GLUPES2";
502 delete result;
503 result = nullptr;
504 }
505 }
506 #endif
507
508 #ifdef GEO_GL_ES2
509 if(GLUP_profile == "GLUPES2") {
510 try {
511 result = new GLUP::Context_ES2;
512 result->setup();
513 } catch(...) {
514 GEO::Logger::warn("GLUP")
515 << "Caught an exception in GLUPES2"
516 << std::endl;
517 downgrade_message();
518 delete result;
519 result = nullptr;
520 }
521 }
522 #endif
523
524 if(result == nullptr) {
525 GEO::Logger::err("GLUP") << "Could not create context"
526 << std::endl;
527 } else {
528 GLUP::all_contexts_.insert(result);
529 }
530
531 return result;
532 }
533
534 void glupDeleteContext(GLUPcontext context_in) {
535 GEO_CHECK_GL();
536
537 GLUP::Context* context =
538 reinterpret_cast<GLUP::Context*>(context_in);
539
540 auto it = GLUP::all_contexts_.find(context);
541 geo_assert(it != GLUP::all_contexts_.end());
542 GLUP::all_contexts_.erase(it);
543
544 if(GLUP::current_context_ == context) {
545 GLUP::current_context_ = nullptr;
546 }
547 delete context;
548 }
549
550
551 GLUPcontext glupCurrentContext() {
552 // Note: we cannot check GL state if OpenGL
553 // is not initialized.
554 if(GLUP::current_context_ != nullptr) {
555 GEO_CHECK_GL();
556 }
557 return GLUP::current_context_;
558 }
559
560 const char* glupCurrentProfileName() {
561 GEO_CHECK_GL();
562 return GLUP::current_context_->profile_name();
563 }
564
565 void glupMakeCurrent(GLUPcontext context) {
566 GEO_CHECK_GL();
567 GLUP::current_context_ = reinterpret_cast<GLUP::Context*>(context);
568 }
569
570 GLUPboolean glupPrimitiveSupportsArrayMode(GLUPprimitive prim) {
571 GEO_CHECK_GL();
572 return GLUP::current_context_->primitive_supports_array_mode(prim) ?
573 GL_TRUE : GL_FALSE ;
574 }
575
576 /****************** Enable / Disable ***************************/
577
578 namespace GLUP {
579 extern bool vertex_array_emulate;
580 }
581
582 void glupEnable(GLUPtoggle toggle) {
583 GEO_CHECK_GL();
584 GLUP::current_context_->uniform_state().toggle[toggle].set(GL_TRUE);
585 }
586
587 void glupDisable(GLUPtoggle toggle) {
588 GEO_CHECK_GL();
589 GLUP::current_context_->uniform_state().toggle[toggle].set(GL_FALSE);
590 }
591
592 GLUPboolean glupIsEnabled(GLUPtoggle toggle) {
593 GEO_CHECK_GL();
594 return GLUP::current_context_->uniform_state().toggle[toggle].get();
595 }
596
597 /********************** Texturing ******************************/
598
599 void glupTextureType(GLUPtextureType type) {
600 GEO_CHECK_GL();
601 GLUP::current_context_->uniform_state().texture_type.set(type);
602 }
603
604 GLUPtextureType glupGetTextureType() {
605 GEO_CHECK_GL();
606 return GLUPtextureType(
607 GLUP::current_context_->uniform_state().texture_type.get()
608 );
609 }
610
611 void glupTextureMode(GLUPtextureMode mode) {
612 GEO_CHECK_GL();
613 GLUP::current_context_->uniform_state().texture_mode.set(mode);
614 }
615
616 GLUPtextureMode glupGetTextureMode() {
617 GEO_CHECK_GL();
618 return GLUPtextureMode(
619 GLUP::current_context_->uniform_state().texture_mode.get()
620 );
621 }
622
623 /****************** Drawing state ******************************/
624
625 void glupSetColor4fv(GLUPcolor color, const GLUPfloat* rgba) {
626 GEO_CHECK_GL();
627 if(color == GLUP_FRONT_AND_BACK_COLOR) {
628 glupSetColor4fv(GLUP_FRONT_COLOR, rgba);
629 glupSetColor4fv(GLUP_BACK_COLOR, rgba);
630 } else {
631 GLUP::current_context_->uniform_state().color[color].set(rgba);
632 }
633 }
634
635 void glupGetColor4fv(GLUPcolor color, float* rgba) {
636 GEO_CHECK_GL();
637 geo_assert(color != GLUP_FRONT_AND_BACK_COLOR);
638 GLUP::current_context_->uniform_state().color[color].get(rgba);
639 }
640
641 void glupSetColor3fv(GLUPcolor color, const GLUPfloat* rgba) {
642 GEO_CHECK_GL();
643 glupSetColor4f(color, rgba[0], rgba[1], rgba[2], 1.0);
644 }
645
646 void glupSetColor4f(
647 GLUPcolor color, GLUPfloat r, GLUPfloat g, GLUPfloat b, GLUPfloat a
648 ) {
649 GEO_CHECK_GL();
650 if(color == GLUP_FRONT_AND_BACK_COLOR) {
651 glupSetColor4f(GLUP_FRONT_COLOR, r, g, b, a);
652 glupSetColor4f(GLUP_BACK_COLOR, r, g, b, a);
653 } else {
654 GLUPfloat* ptr =
655 GLUP::current_context_->uniform_state().color[color].get_pointer();
656 ptr[0] = r;
657 ptr[1] = g;
658 ptr[2] = b;
659 ptr[3] = a;
660 }
661 }
662
663 void glupSetColor3f(GLUPcolor color, GLUPfloat r, GLUPfloat g, GLUPfloat b) {
664 GEO_CHECK_GL();
665 glupSetColor4f(color, r, g, b, 1.0f);
666 }
667
668 void glupSetColor4dv(GLUPcolor color, const GLUPdouble* rgba) {
669 GEO_CHECK_GL();
670 glupSetColor4f(
671 color,
672 GLUPfloat(rgba[0]),
673 GLUPfloat(rgba[1]),
674 GLUPfloat(rgba[2]),
675 GLUPfloat(rgba[3])
676 );
677 }
678
679 void glupSetColor3dv(GLUPcolor color, const GLUPdouble* rgba) {
680 GEO_CHECK_GL();
681 glupSetColor4f(
682 color,
683 GLUPfloat(rgba[0]),
684 GLUPfloat(rgba[1]),
685 GLUPfloat(rgba[2]),
686 1.0f
687 );
688 }
689
690 void glupSetColor4d(
691 GLUPcolor color, GLUPdouble r, GLUPdouble g, GLUPdouble b, GLUPdouble a
692 ) {
693 GEO_CHECK_GL();
694 glupSetColor4f(
695 color,
696 GLUPfloat(r),
697 GLUPfloat(g),
698 GLUPfloat(b),
699 GLUPfloat(a)
700 );
701 }
702
703 void glupSetColor3d(
704 GLUPcolor color, GLUPdouble r, GLUPdouble g, GLUPdouble b
705 ) {
706 GEO_CHECK_GL();
707 glupSetColor4f(
708 color,
709 GLUPfloat(r),
710 GLUPfloat(g),
711 GLUPfloat(b),
712 1.0f
713 );
714 }
715
716 void glupLightVector3f(GLUPfloat x, GLUPfloat y, GLUPfloat z) {
717 GEO_CHECK_GL();
718 GLUPfloat* ptr =
719 GLUP::current_context_->uniform_state().light_vector.get_pointer();
720 ptr[0] = x;
721 ptr[1] = y;
722 ptr[2] = z;
723 GLUP::current_context_->flag_lighting_as_dirty();
724 }
725
726 void glupLightVector3fv(GLUPfloat* xyz) {
727 GEO_CHECK_GL();
728 GLUP::current_context_->uniform_state().light_vector.set(xyz);
729 GLUP::current_context_->flag_lighting_as_dirty();
730 }
731
732 void glupGetLightVector3fv(GLUPfloat* xyz) {
733 GEO_CHECK_GL();
734 GLUPfloat* ptr =
735 GLUP::current_context_->uniform_state().light_vector.get_pointer();
736 xyz[0] = ptr[0];
737 xyz[1] = ptr[1];
738 xyz[2] = ptr[2];
739 }
740
741 void glupSetPointSize(GLUPfloat size) {
742 GEO_CHECK_GL();
743 GLUP::current_context_->uniform_state().point_size.set(size);
744 }
745
746 GLUPfloat glupGetPointSize() {
747 GEO_CHECK_GL();
748 return GLUP::current_context_->uniform_state().point_size.get();
749 }
750
751 void glupSetMeshWidth(GLUPint width) {
752 GEO_CHECK_GL();
753 GLUP::current_context_->uniform_state().mesh_width.set(GLfloat(width));
754 }
755
756 GLUPint glupGetMeshWidth() {
757 GEO_CHECK_GL();
758 return GLUPint(GLUP::current_context_->uniform_state().mesh_width.get());
759 }
760
761 void glupSetCellsShrink(GLUPfloat x) {
762 GEO_CHECK_GL();
763 x = std::min(x, 1.0f);
764 x = std::max(x, 0.0f);
765 GLUP::current_context_->uniform_state().cells_shrink.set(x);
766 }
767
768 GLUPfloat glupGetCellsShrink() {
769 GEO_CHECK_GL();
770 return GLUP::current_context_->uniform_state().cells_shrink.get();
771 }
772
773 void glupSetAlphaThreshold(GLUPfloat x) {
774 GEO_CHECK_GL();
775 GLUP::current_context_->uniform_state().alpha_threshold.set(x);
776 }
777
778 GLUPfloat glupGetAlphaThreshold() {
779 GEO_CHECK_GL();
780 return GLUP::current_context_->uniform_state().alpha_threshold.get();
781 }
782
783 void glupSetSpecular(GLUPfloat x) {
784 GEO_CHECK_GL();
785 GLUP::current_context_->uniform_state().specular.set(x);
786 }
787
788 GLUPfloat glupGetSpecular() {
789 GEO_CHECK_GL();
790 return GLUP::current_context_->uniform_state().specular.get();
791 }
792
793
794 /****************** Picking ******************************/
795
796 void glupPickingMode(GLUPpickingMode mode) {
797 GEO_CHECK_GL();
798 GLUP::current_context_->uniform_state().picking_mode.set(mode);
799 }
800
801 GLUPpickingMode glupGetPickingMode() {
802 GEO_CHECK_GL();
803 return GLUPpickingMode(
804 GLUP::current_context_->uniform_state().picking_mode.get()
805 );
806 }
807
808 void glupPickingId(GLUPuint64 id) {
809 // TODO: uint64
810 GEO_CHECK_GL();
811 GLUP::current_context_->uniform_state().picking_id.set(GLint(id));
812 }
813
814 GLUPuint64 glupGetPickingId() {
815 // TODO: uint64
816 GEO_CHECK_GL();
817 return GLUPuint64(
818 GLUP::current_context_->uniform_state().picking_id.get()
819 );
820 }
821
822 void glupBasePickingId(GLUPuint64 id) {
823 // TODO: uint64
824 GEO_CHECK_GL();
825 GLUP::current_context_->uniform_state().base_picking_id.set(GLint(id));
826 }
827
828 GLUPuint64 glupGetBasePickingId() {
829 // TODO: uint64
830 GEO_CHECK_GL();
831 return GLUPuint64(
832 GLUP::current_context_->uniform_state().base_picking_id.get()
833 );
834 }
835
836 /****************** Clipping ******************************/
837
838 void glupClipMode(GLUPclipMode mode) {
839 GEO_CHECK_GL();
840 GLUP::current_context_->uniform_state().clipping_mode.set(mode);
841 }
842
843 GLUPclipMode glupGetClipMode() {
844 GEO_CHECK_GL();
845 return GLUPclipMode(
846 GLUP::current_context_->uniform_state().clipping_mode.get()
847 );
848 }
849
850 void glupClipPlane(const GLUPdouble* eqn_in) {
851 GEO_CHECK_GL();
852
853 const GLdouble* modelviewd =
854 GLUP::current_context_->get_matrix(GLUP_MODELVIEW_MATRIX);
855
856 GLfloat modelview[16];
857 GLUP::copy_vector(modelview, modelviewd, 16);
858
859 GLfloat modelview_invert[16];
860 if(!GLUP::invert_matrix(modelview_invert,modelview)) {
861 GEO::Logger::warn("GLUP") << "Singular ModelView matrix"
862 << std::endl;
863 GLUP::show_matrix(modelview);
864 }
865 GLfloat* state_world_clip_plane =
866 GLUP::current_context_->uniform_state().world_clip_plane.get_pointer();
867 GLfloat* state_clip_plane =
868 GLUP::current_context_->uniform_state().clip_plane.get_pointer();
869 for(GEO::index_t i=0; i<4; ++i) {
870 state_world_clip_plane[i] = float(eqn_in[i]);
871 }
872 GLUP::mult_matrix_vector(
873 state_clip_plane,modelview_invert,state_world_clip_plane
874 );
875 // TODO? clip_clip_plane update ?
876 }
877
878 void glupGetClipPlane(GLUPdouble* eqn) {
879 GEO_CHECK_GL();
880
881 const GLfloat* ptr =
882 GLUP::current_context_->uniform_state().clip_plane.get_pointer();
883 eqn[0] = GLdouble(ptr[0]);
884 eqn[1] = GLdouble(ptr[1]);
885 eqn[2] = GLdouble(ptr[2]);
886 eqn[3] = GLdouble(ptr[3]);
887 }
888
889 /******************* Matrices ***************************/
890
891
892 void glupMatrixMode(GLUPmatrix matrix) {
893 GEO_CHECK_GL();
894 GLUP::current_context_->set_matrix_mode(matrix);
895 }
896
897 GLUPmatrix glupGetMatrixMode() {
898 GEO_CHECK_GL();
899 return GLUP::current_context_->get_matrix_mode();
900 }
901
902 void glupPushMatrix() {
903 GEO_CHECK_GL();
904 GLUP::current_context_->push_matrix();
905 }
906
907 void glupPopMatrix() {
908 GEO_CHECK_GL();
909 GLUP::current_context_->pop_matrix();
910 }
911
912 void glupGetMatrixdv(GLUPmatrix matrix, GLUPdouble* ptr) {
913 GEO_CHECK_GL();
914 for(GEO::index_t i=0; i<16; ++i) {
915 ptr[i] = GLUPdouble(
916 GLUP::current_context_->get_matrix(matrix)[i]
917 );
918 }
919 }
920
921 void glupGetMatrixfv(GLUPmatrix matrix, GLUPfloat* ptr) {
922 GEO_CHECK_GL();
923 for(GEO::index_t i=0; i<16; ++i) {
924 ptr[i] = GLUPfloat(
925 GLUP::current_context_->get_matrix(matrix)[i]
926 );
927 }
928 }
929
930 void glupLoadIdentity() {
931 GEO_CHECK_GL();
932 GLUP::current_context_->load_identity();
933 }
934
935 void glupLoadMatrixf(const GLUPfloat* M) {
936 GEO_CHECK_GL();
937 GLUP::current_context_->load_matrix(M);
938 }
939
940 void glupLoadMatrixd(const GLUPdouble* M) {
941 GEO_CHECK_GL();
942 GLUP::current_context_->load_matrix(M);
943 }
944
945 void glupMultMatrixf(const GLUPfloat* M) {
946 GEO_CHECK_GL();
947 GLdouble Md[16];
948 for(GEO::index_t i=0; i<16; ++i) {
949 Md[i] = GLdouble(M[i]);
950 }
951 glupMultMatrixd(Md);
952 }
953
954 void glupMultMatrixd(const GLUPdouble* M) {
955 GEO_CHECK_GL();
956 GLUP::current_context_->mult_matrix(M);
957 }
958
959 void glupTranslated(GLUPdouble x, GLUPdouble y, GLUPdouble z) {
960 GEO_CHECK_GL();
961
962 GLdouble M[16];
963
964 M[4*0+0] = 1.0;
965 M[4*0+1] = 0.0;
966 M[4*0+2] = 0.0;
967 M[4*0+3] = x;
968
969 M[4*1+0] = 0.0;
970 M[4*1+1] = 1.0;
971 M[4*1+2] = 0.0;
972 M[4*1+3] = y;
973
974 M[4*2+0] = 0.0;
975 M[4*2+1] = 0.0;
976 M[4*2+2] = 1.0;
977 M[4*2+3] = z;
978
979 M[4*3+0] = 0.0;
980 M[4*3+1] = 0.0;
981 M[4*3+2] = 0.0;
982 M[4*3+3] = 1.0;
983
984 GLUP::transpose_matrix(M);
985
986 glupMultMatrixd(M);
987 }
988
989 void glupTranslatef(GLUPfloat x, GLUPfloat y, GLUPfloat z) {
990 GEO_CHECK_GL();
991 glupTranslated(GLUPdouble(x), GLUPdouble(y), GLUPdouble(z));
992 }
993
994 void glupScaled(GLUPdouble sx, GLUPdouble sy, GLUPdouble sz) {
995 GEO_CHECK_GL();
996
997 GLdouble M[16];
998
999 M[4*0+0] = sx;
1000 M[4*0+1] = 0.0;
1001 M[4*0+2] = 0.0;
1002 M[4*0+3] = 0.0;
1003
1004 M[4*1+0] = 0.0;
1005 M[4*1+1] = sy;
1006 M[4*1+2] = 0.0;
1007 M[4*1+3] = 0.0;
1008
1009 M[4*2+0] = 0.0;
1010 M[4*2+1] = 0.0;
1011 M[4*2+2] = sz;
1012 M[4*2+3] = 0.0;
1013
1014 M[4*3+0] = 0.0;
1015 M[4*3+1] = 0.0;
1016 M[4*3+2] = 0.0;
1017 M[4*3+3] = 1.0;
1018
1019 glupMultMatrixd(M);
1020 }
1021
1022 void glupScalef(GLUPfloat sx, GLUPfloat sy, GLUPfloat sz) {
1023 GEO_CHECK_GL();
1024 glupScaled(GLUPdouble(sx), GLUPdouble(sy), GLUPdouble(sz));
1025 }
1026
1027 void glupRotated(
1028 GLUPdouble angle, GLUPdouble x, GLUPdouble y, GLUPdouble z
1029 ) {
1030 GEO_CHECK_GL();
1031
1032 GLUPdouble l = 1.0 / ::sqrt(x*x+y*y+z*z);
1033 x *= l;
1034 y *= l;
1035 z *= l;
1036 GLUPdouble s = ::sin(angle * M_PI / 180.0);
1037 GLUPdouble c = ::cos(angle * M_PI / 180.0);
1038 GLUPdouble M[16];
1039
1040 M[4*0+0] = x*x*(1.0-c)+c;
1041 M[4*0+1] = x*y*(1.0-c)-z*s;
1042 M[4*0+2] = x*z*(1.0-c)+y*s;
1043 M[4*0+3] = 0.0;
1044
1045 M[4*1+0] = y*x*(1.0-c)+z*s;
1046 M[4*1+1] = y*y*(1.0-c)+c;
1047 M[4*1+2] = y*z*(1.0-c)-x*s;
1048 M[4*1+3] = 0.0;
1049
1050 M[4*2+0] = z*x*(1.0-c)-y*s;
1051 M[4*2+1] = z*y*(1.0-c)+x*s;
1052 M[4*2+2] = z*z*(1.0-c)+c;
1053 M[4*2+3] = 0.0;
1054
1055 M[4*3+0] = 0.0;
1056 M[4*3+1] = 0.0;
1057 M[4*3+2] = 0.0;
1058 M[4*3+3] = 1.0;
1059
1060 GLUP::transpose_matrix(M);
1061
1062 glupMultMatrixd(M);
1063 }
1064
1065 void glupRotatef(
1066 GLUPfloat angle, GLUPfloat x, GLUPfloat y, GLUPfloat z
1067 ) {
1068 GEO_CHECK_GL();
1069 glupRotated(GLdouble(angle), GLdouble(x), GLdouble(y), GLdouble(z));
1070 }
1071
1072
1073 void glupOrtho(
1074 GLUPdouble left, GLUPdouble right,
1075 GLUPdouble bottom, GLUPdouble top,
1076 GLUPdouble nearVal, GLUPdouble farVal
1077 ) {
1078 GEO_CHECK_GL();
1079
1080 GLdouble M[16];
1081
1082 GLdouble tx = -(right+left)/(right-left);
1083 GLdouble ty = -(top+bottom)/(top-bottom);
1084 GLdouble tz = -(farVal+nearVal)/(farVal-nearVal);
1085
1086 M[4*0+0] = 2.0 / (right-left);
1087 M[4*0+1] = 0.0;
1088 M[4*0+2] = 0.0;
1089 M[4*0+3] = tx;
1090
1091 M[4*1+0] = 0.0;
1092 M[4*1+1] = 2.0 / (top-bottom);
1093 M[4*1+2] = 0.0;
1094 M[4*1+3] = ty;
1095
1096 M[4*2+0] = 0.0;
1097 M[4*2+1] = 0.0;
1098 M[4*2+2] = -2.0 / (farVal - nearVal);
1099 M[4*2+3] = tz;
1100
1101 M[4*3+0] = 0.0;
1102 M[4*3+1] = 0.0;
1103 M[4*3+2] = 0.0;
1104 M[4*3+3] = 1.0;
1105
1106 GLUP::transpose_matrix(M);
1107 glupMultMatrixd(M);
1108 }
1109
1110 void glupOrtho2D(
1111 GLUPdouble left, GLUPdouble right, GLUPdouble bottom, GLUPdouble top
1112 ) {
1113 GEO_CHECK_GL();
1114
1115 glupOrtho(left, right, bottom, top, -1.0, 1.0);
1116 }
1117
1118 void glupFrustum(
1119 GLUPdouble left, GLUPdouble right,
1120 GLUPdouble bottom, GLUPdouble top,
1121 GLUPdouble nearVal, GLUPdouble farVal
1122 ) {
1123 GEO_CHECK_GL();
1124
1125 GLdouble M[16];
1126
1127 GLdouble A = (right + left) / (right - left);
1128 GLdouble B = (top + bottom) / (top - bottom);
1129 GLdouble C = -(farVal + nearVal) / (farVal - nearVal);
1130 GLdouble D = -2.0*farVal*nearVal / (farVal - nearVal);
1131
1132 M[4*0+0] = 2.0 * nearVal / (right - left);
1133 M[4*0+1] = 0.0;
1134 M[4*0+2] = A;
1135 M[4*0+3] = 0.0;
1136
1137 M[4*1+0] = 0.0;
1138 M[4*1+1] = 2.0 * nearVal / (top - bottom);
1139 M[4*1+2] = B;
1140 M[4*1+3] = 0.0;
1141
1142 M[4*2+0] = 0.0;
1143 M[4*2+1] = 0.0;
1144 M[4*2+2] = C;
1145 M[4*2+3] = D;
1146
1147 M[4*3+0] = 0.0;
1148 M[4*3+1] = 0.0;
1149 M[4*3+2] = -1.0;
1150 M[4*3+3] = 0.0;
1151
1152 GLUP::transpose_matrix(M);
1153 glupMultMatrixd(M);
1154 }
1155
1156 void glupPerspective(
1157 GLUPdouble fovy, GLUPdouble aspect,
1158 GLUPdouble zNear, GLUPdouble zFar
1159 ) {
1160 GEO_CHECK_GL();
1161
1162 GLdouble M[16];
1163
1164 double f = 1.0 / tan(fovy * M_PI / 180.0);
1165
1166 M[4*0+0] = f / aspect;
1167 M[4*0+1] = 0.0;
1168 M[4*0+2] = 0.0;
1169 M[4*0+3] = 0.0;
1170
1171 M[4*1+0] = 0.0;
1172 M[4*1+1] = f;
1173 M[4*1+2] = 0.0;
1174 M[4*1+3] = 0.0;
1175
1176 M[4*2+0] = 0.0;
1177 M[4*2+1] = 0.0;
1178 M[4*2+2] = (zFar+zNear)/(zNear-zFar);
1179 M[4*2+3] = 2.0*zFar*zNear/(zNear-zFar);
1180
1181 M[4*3+0] = 0.0;
1182 M[4*3+1] = 0.0;
1183 M[4*3+2] = -1.0;
1184 M[4*3+3] = 0.0;
1185
1186 GLUP::transpose_matrix(M);
1187 glupMultMatrixd(M);
1188 }
1189
1190 GLUPint glupProject(
1191 GLUPdouble objx, GLUPdouble objy, GLUPdouble objz,
1192 const GLUPdouble modelMatrix[16],
1193 const GLUPdouble projMatrix[16],
1194 const GLUPint viewport[4],
1195 GLUPdouble* winx, GLUPdouble* winy, GLUPdouble* winz
1196 ) {
1197 GEO_CHECK_GL();
1198
1199 double in[4];
1200 double out[4];
1201
1202 in[0]=objx;
1203 in[1]=objy;
1204 in[2]=objz;
1205 in[3]=1.0;
1206
1207 GLUP::mult_transpose_matrix_vector(out, modelMatrix, in);
1208 GLUP::mult_transpose_matrix_vector(in, projMatrix, out);
1209
1210 if (in[3] == 0.0) {
1211 return(GL_FALSE);
1212 }
1213 in[0] /= in[3];
1214 in[1] /= in[3];
1215 in[2] /= in[3];
1216
1217 // Map x, y and z to range 0-1 */
1218 in[0] = in[0] * 0.5 + 0.5;
1219 in[1] = in[1] * 0.5 + 0.5;
1220 in[2] = in[2] * 0.5 + 0.5;
1221
1222 // Map x,y to viewport
1223 in[0] = in[0] * viewport[2] + viewport[0];
1224 in[1] = in[1] * viewport[3] + viewport[1];
1225
1226 *winx=in[0];
1227 *winy=in[1];
1228 *winz=in[2];
1229 return(GL_TRUE);
1230 }
1231
1232 GLUPboolean glupUnProject(
1233 GLUPdouble winx, GLUPdouble winy, GLUPdouble winz,
1234 const GLUPdouble modelMatrix[16],
1235 const GLUPdouble projMatrix[16],
1236 const GLUPint viewport[4],
1237 GLUPdouble *objx, GLUPdouble *objy, GLUPdouble *objz
1238 ) {
1239 GEO_CHECK_GL();
1240
1241 double modelviewproject[16];
1242 double modelviewproject_inv[16];
1243 GLUP::mult_matrices(modelviewproject, modelMatrix, projMatrix);
1244 if(!GLUP::invert_matrix(modelviewproject_inv, modelviewproject)) {
1245 return GL_FALSE;
1246 }
1247
1248 double in[4];
1249 in[0] = winx;
1250 in[1] = winy;
1251 in[2] = winz;
1252 in[3] = 1.0;
1253
1254 // Invert viewport transform
1255 in[0] = (in[0] - double(viewport[0])) / double(viewport[2]);
1256 in[1] = (in[1] - double(viewport[1])) / double(viewport[3]);
1257
1258 // Map to [-1, 1]
1259 in[0] = in[0] * 2.0 - 1.0;
1260 in[1] = in[1] * 2.0 - 1.0;
1261 in[2] = in[2] * 2.0 - 1.0;
1262
1263 double out[4];
1264 GLUP::mult_transpose_matrix_vector(out, modelviewproject_inv, in);
1265
1266 if(out[3] == 0.0) {
1267 return GL_FALSE;
1268 }
1269
1270 *objx = out[0] / out[3];
1271 *objy = out[1] / out[3];
1272 *objz = out[2] / out[3];
1273
1274 return GL_TRUE;
1275 }
1276
1277 GLUPboolean glupInvertMatrixfv(
1278 GLUPfloat Minvert[16],
1279 const GLUPfloat M[16]
1280 ) {
1281 GEO_CHECK_GL();
1282
1283 return GLUP::invert_matrix(Minvert, M);
1284 }
1285
1286 GLUPboolean glupInvertMatrixdv(
1287 GLUPdouble Minvert[16],
1288 const GLUPdouble M[16]
1289 ) {
1290 GEO_CHECK_GL();
1291
1292 return GLUP::invert_matrix(Minvert, M);
1293 }
1294
1295
1296
1297 /******************* Drawing ***************************/
1298
1299 static inline void convert_primitive(GLUPprimitive& primitive) {
1300 if(primitive == GLUP_LINES && glupGetMeshWidth() > 1) {
1301 primitive = GLUP_THICK_LINES;
1302 }
1303 }
1304
1305 void glupDrawArrays(
1306 GLUPprimitive primitive, GLUPint first, GLUPsizei count
1307 ) {
1308 convert_primitive(primitive);
1309 GEO_CHECK_GL();
1310 GLUP::current_context_->draw_arrays(
1311 primitive, first, count
1312 );
1313 GEO_CHECK_GL();
1314 }
1315
1316 void glupDrawElements(
1317 GLUPprimitive primitive, GLUPsizei count,
1318 GLUPenum type, const GLUPvoid* indices
1319 ) {
1320 convert_primitive(primitive);
1321 GEO_CHECK_GL();
1322 GLUP::current_context_->draw_elements(
1323 primitive, count, type, indices
1324 );
1325 GEO_CHECK_GL();
1326 }
1327
1328 void glupBegin(GLUPprimitive primitive) {
1329 convert_primitive(primitive);
1330 GEO_CHECK_GL();
1331 GLUP::current_context_->begin(primitive);
1332 GEO_CHECK_GL();
1333 }
1334
1335 void glupEnd() {
1336 GEO_CHECK_GL();
1337 GLUP::current_context_->end();
1338 GEO_CHECK_GL();
1339 }
1340
1341 void glupVertex2fv(const GLUPfloat* xy) {
1342 GEO_CHECK_GL();
1343 GLUP::current_context_->immediate_vertex(xy[0], xy[1]);
1344 }
1345
1346 void glupVertex3fv(const GLUPfloat* xyz) {
1347 GEO_CHECK_GL();
1348 GLUP::current_context_->immediate_vertex(xyz[0], xyz[1], xyz[2]);
1349 }
1350
1351 void glupVertex4fv(const GLUPfloat* xyzw) {
1352 GEO_CHECK_GL();
1353 GLUP::current_context_->immediate_vertex(
1354 xyzw[0], xyzw[1], xyzw[2], xyzw[3]
1355 );
1356 }
1357
1358 void glupVertex2dv(const GLUPdouble* xy) {
1359 GEO_CHECK_GL();
1360 GLUP::current_context_->immediate_vertex(
1361 GLfloat(xy[0]),
1362 GLfloat(xy[1])
1363 );
1364 }
1365
1366 void glupVertex3dv(const GLUPdouble* xyz) {
1367 GEO_CHECK_GL();
1368 GLUP::current_context_->immediate_vertex(
1369 GLfloat(xyz[0]),
1370 GLfloat(xyz[1]),
1371 GLfloat(xyz[2])
1372 );
1373 }
1374
1375 void glupVertex4dv(const GLUPdouble* xyzw) {
1376 GEO_CHECK_GL();
1377 GLUP::current_context_->immediate_vertex(
1378 GLfloat(xyzw[0]),
1379 GLfloat(xyzw[1]),
1380 GLfloat(xyzw[2]),
1381 GLfloat(xyzw[3])
1382 );
1383 }
1384
1385 void glupVertex2f(GLUPfloat x, GLUPfloat y) {
1386 GEO_CHECK_GL();
1387 GLUP::current_context_->immediate_vertex(x,y);
1388 }
1389
1390 void glupVertex3f(GLUPfloat x, GLUPfloat y, GLUPfloat z) {
1391 GEO_CHECK_GL();
1392 GLUP::current_context_->immediate_vertex(x,y,z);
1393 }
1394
1395 void glupVertex4f(GLUPfloat x, GLUPfloat y, GLUPfloat z, GLUPfloat w) {
1396 GEO_CHECK_GL();
1397 GLUP::current_context_->immediate_vertex(x,y,z,w);
1398 }
1399
1400 void glupVertex2d(GLUPdouble x, GLUPdouble y) {
1401 GEO_CHECK_GL();
1402 GLUP::current_context_->immediate_vertex(
1403 GLfloat(x),
1404 GLfloat(y)
1405 );
1406 }
1407
1408 void glupVertex3d(GLUPdouble x, GLUPdouble y, GLUPdouble z) {
1409 GEO_CHECK_GL();
1410 GLUP::current_context_->immediate_vertex(
1411 GLfloat(x),
1412 GLfloat(y),
1413 GLfloat(z)
1414 );
1415 }
1416
1417 void glupVertex4d(GLUPdouble x, GLUPdouble y, GLUPdouble z, GLUPdouble w) {
1418 GEO_CHECK_GL();
1419 GLUP::current_context_->immediate_vertex(
1420 GLfloat(x),
1421 GLfloat(y),
1422 GLfloat(z),
1423 GLfloat(w)
1424 );
1425 }
1426
1427 void glupColor3fv(const GLUPfloat* rgb) {
1428 GEO_CHECK_GL();
1429 GLUP::current_context_->immediate_color(rgb[0], rgb[1], rgb[2]);
1430 }
1431
1432 void glupColor4fv(const GLUPfloat* rgba) {
1433 GEO_CHECK_GL();
1434 GLUP::current_context_->immediate_color(rgba[0], rgba[1], rgba[2], rgba[3]);
1435 }
1436
1437 void glupColor3dv(const GLUPdouble* rgb) {
1438 GEO_CHECK_GL();
1439 GLUP::current_context_->immediate_color(
1440 GLfloat(rgb[0]),
1441 GLfloat(rgb[1]),
1442 GLfloat(rgb[2])
1443 );
1444 }
1445
1446 void glupColor4dv(const GLUPdouble* rgba) {
1447 GEO_CHECK_GL();
1448 GLUP::current_context_->immediate_color(
1449 GLfloat(rgba[0]),
1450 GLfloat(rgba[1]),
1451 GLfloat(rgba[2]),
1452 GLfloat(rgba[3])
1453 );
1454 }
1455
1456 void glupColor3f(GLUPfloat r, GLUPfloat g, GLUPfloat b) {
1457 GEO_CHECK_GL();
1458 GLUP::current_context_->immediate_color(r, g, b);
1459 }
1460
1461 void glupColor4f(GLUPfloat r, GLUPfloat g, GLUPfloat b, GLUPfloat a) {
1462 GEO_CHECK_GL();
1463 GLUP::current_context_->immediate_color(r, g, b, a);
1464 }
1465
1466 void glupColor3d(GLUPdouble r, GLUPdouble g, GLUPdouble b) {
1467 GEO_CHECK_GL();
1468 GLUP::current_context_->immediate_color(
1469 GLfloat(r),
1470 GLfloat(g),
1471 GLfloat(b)
1472 );
1473 }
1474
1475 void glupColor4d(GLUPdouble r, GLUPdouble g, GLUPdouble b, GLUPdouble a) {
1476 GEO_CHECK_GL();
1477 GLUP::current_context_->immediate_color(
1478 GLfloat(r),
1479 GLfloat(g),
1480 GLfloat(b),
1481 GLfloat(a)
1482 );
1483 }
1484
1485 void glupTexCoord2fv(const GLUPfloat* st) {
1486 GEO_CHECK_GL();
1487 GLUP::current_context_->immediate_tex_coord(st[0], st[1]);
1488 }
1489
1490 void glupTexCoord3fv(const GLUPfloat* stu) {
1491 GEO_CHECK_GL();
1492 GLUP::current_context_->immediate_tex_coord(stu[0], stu[1], stu[2]);
1493 }
1494
1495 void glupTexCoord4fv(const GLUPfloat* stuv) {
1496 GEO_CHECK_GL();
1497 GLUP::current_context_->immediate_tex_coord(
1498 stuv[0], stuv[1], stuv[2], stuv[3]
1499 );
1500 }
1501
1502 void glupTexCoord2dv(const GLUPdouble* st) {
1503 GEO_CHECK_GL();
1504 GLUP::current_context_->immediate_tex_coord(
1505 GLfloat(st[0]),
1506 GLfloat(st[1])
1507 );
1508 }
1509
1510 void glupTexCoord3dv(const GLUPdouble* stu) {
1511 GEO_CHECK_GL();
1512 GLUP::current_context_->immediate_tex_coord(
1513 GLfloat(stu[0]),
1514 GLfloat(stu[1]),
1515 GLfloat(stu[2])
1516 );
1517 }
1518
1519 void glupTexCoord4dv(const GLUPdouble* stuv) {
1520 GEO_CHECK_GL();
1521 GLUP::current_context_->immediate_tex_coord(
1522 GLfloat(stuv[0]),
1523 GLfloat(stuv[1]),
1524 GLfloat(stuv[2]),
1525 GLfloat(stuv[3])
1526 );
1527 }
1528
1529 void glupTexCoord1f(GLUPfloat s) {
1530 GEO_CHECK_GL();
1531 GLUP::current_context_->immediate_tex_coord(s);
1532 }
1533
1534 void glupTexCoord2f(GLUPfloat s, GLUPfloat t) {
1535 GEO_CHECK_GL();
1536 GLUP::current_context_->immediate_tex_coord(s,t);
1537 }
1538
1539 void glupTexCoord3f(GLUPfloat s, GLUPfloat t, GLUPfloat u) {
1540 GEO_CHECK_GL();
1541 GLUP::current_context_->immediate_tex_coord(s,t,u);
1542 }
1543
1544 void glupTexCoord4f(GLUPfloat s, GLUPfloat t, GLUPfloat u, GLUPfloat v) {
1545 GEO_CHECK_GL();
1546 GLUP::current_context_->immediate_tex_coord(s,t,u,v);
1547 }
1548
1549 void glupTexCoord1d(GLUPdouble s) {
1550 GEO_CHECK_GL();
1551 GLUP::current_context_->immediate_tex_coord(
1552 GLfloat(s)
1553 );
1554 }
1555
1556 void glupTexCoord2d(GLUPdouble s, GLUPdouble t) {
1557 GEO_CHECK_GL();
1558 GLUP::current_context_->immediate_tex_coord(
1559 GLfloat(s),
1560 GLfloat(t)
1561 );
1562 }
1563
1564 void glupTexCoord3d(GLUPdouble s, GLUPdouble t, GLUPdouble u) {
1565 GEO_CHECK_GL();
1566 GLUP::current_context_->immediate_tex_coord(
1567 GLfloat(s),
1568 GLfloat(t),
1569 GLfloat(u)
1570 );
1571 }
1572
1573 void glupTexCoord4d(GLUPdouble s, GLUPdouble t, GLUPdouble u, GLUPdouble v) {
1574 GEO_CHECK_GL();
1575 GLUP::current_context_->immediate_tex_coord(
1576 GLfloat(s),
1577 GLfloat(t),
1578 GLfloat(u),
1579 GLfloat(v)
1580 );
1581 }
1582
1583
1584 void glupNormal3fv(GLUPfloat* xyz) {
1585 GEO_CHECK_GL();
1586 GLUP::current_context_->immediate_normal(
1587 xyz[0],xyz[1],xyz[2]
1588 );
1589 }
1590
1591 void glupNormal3f(GLUPfloat x, GLUPfloat y, GLUPfloat z) {
1592 GEO_CHECK_GL();
1593 GLUP::current_context_->immediate_normal(
1594 x,y,z
1595 );
1596 }
1597
1598 void glupNormal3dv(GLUPdouble* xyz) {
1599 GEO_CHECK_GL();
1600 GLUP::current_context_->immediate_normal(
1601 GLfloat(xyz[0]),
1602 GLfloat(xyz[1]),
1603 GLfloat(xyz[2])
1604 );
1605 }
1606
1607 void glupNormal3d(GLUPdouble x, GLUPdouble y, GLUPdouble z) {
1608 GEO_CHECK_GL();
1609 GLUP::current_context_->immediate_normal(
1610 GLfloat(x),
1611 GLfloat(y),
1612 GLfloat(z)
1613 );
1614 }
1615
1616 void glupUseProgram(GLUPuint program) {
1617 GEO_CHECK_GL();
1618 GLUP::current_context_->set_user_program(program);
1619 }
1620
1621
1622
1623 /****************************************************************************/
1624
1625 namespace GLUP {
1626
1627 static GLUPuint vertex_array_binding = 0;
1628 static GLint max_vertex_attrib = 0;
1629
1630 /**
1631 * \brief If true, then GLUP uses its own implementation
1632 * of Vertex Array Object.
1633 * \details This is for instance required
1634 * when using GLUP with Emscripten, in a brower that does
1635 * not have the Vertex Array Object extension (see
1636 * GLUP_context_GLES.cpp)
1637 */
1638 bool vertex_array_emulate = false;
1639
1640 /**
1641 * \brief Stores the state of a vertex attribute
1642 * binding.
1643 * \details Used to implement emulated vertex array
1644 * objects.
1645 * \see VertexArrayObject.
1646 */
1647 class VertexAttribBinding {
1648
1649 public:
1650
1651 /**
1652 * \brief VertexAttribBinding constructor.
1653 */
1654 VertexAttribBinding() :
1655 enabled(GL_FALSE),
1656 size(0),
1657 type(GL_FLOAT),
1658 normalized(GL_FALSE),
1659 stride(0),
1660 pointer(nullptr),
1661 buffer_binding(0) {
1662 }
1663
1664 /**
1665 * \brief Copies the binding of a vertex attribute
1666 * from OpenGL state to this VertexAttribBinding.
1667 * \param[in] index the index of the attribute
1668 */
1669 void copy_from_GL(GLuint index) {
1670
1671 // From the spec, glGetVertexAttribiv is supposed
1672 // to return 4 values always, even if we are only
1673 // interested in the first one, I guess it is needed
1674 // to read in a buffer with enough space for 4 values.
1675 GLint buff[4];
1676
1677 glGetVertexAttribiv(
1678 index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, buff
1679 );
1680 enabled = buff[0];
1681
1682 // With some webbrowsers, querying a vertex attrib array
1683 // that is not enabled returns the default values instead
1684 // of the actual values.
1685 if(!enabled) {
1686 glEnableVertexAttribArray(index);
1687 }
1688
1689 glGetVertexAttribiv(
1690 index, GL_VERTEX_ATTRIB_ARRAY_SIZE, buff
1691 );
1692 size = buff[0];
1693
1694 glGetVertexAttribiv(
1695 index, GL_VERTEX_ATTRIB_ARRAY_TYPE, buff
1696 );
1697 type = buff[0];
1698
1699 glGetVertexAttribiv(
1700 index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, buff
1701 );
1702 normalized = buff[0];
1703
1704 glGetVertexAttribiv(
1705 index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, buff
1706 );
1707 stride = buff[0];
1708
1709 glGetVertexAttribPointerv(
1710 index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &pointer
1711 );
1712
1713 glGetVertexAttribiv(
1714 index, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, buff
1715 );
1716 buffer_binding = buff[0];
1717
1718 if(!enabled) {
1719 glDisableVertexAttribArray(index);
1720 }
1721 }
1722
1723 /**
1724 * \brief Copies the binding stored in this VertexAttribBinding
1725 * into OpenGL.
1726 * \param[in] index the index of the attribute where the binding
1727 * should be copied.
1728 */
1729 void copy_to_GL(GLuint index) {
1730 if(enabled) {
1731 glEnableVertexAttribArray(index);
1732 } else {
1733 glDisableVertexAttribArray(index);
1734 }
1735 glBindBuffer(GL_ARRAY_BUFFER, GLuint(buffer_binding));
1736 if(buffer_binding != 0 || pointer != nullptr ) {
1737 glVertexAttribPointer(
1738 index, size, GLenum(type),
1739 GLboolean(normalized), GLsizei(stride), pointer
1740 );
1741 }
1742 }
1743
1744 /**
1745 * \brief Resets all the stored bindings to default
1746 * values.
1747 */
1748 void reset() {
1749 enabled=GL_FALSE;
1750 size=0;
1751 type=GL_FLOAT;
1752 normalized=GL_FALSE;
1753 stride=0;
1754 pointer=nullptr;
1755 buffer_binding=0;
1756 }
1757
1758 private:
1759 GLint enabled;
1760 GLint size;
1761 GLint type;
1762 GLint normalized;
1763 GLint stride;
1764 GLvoid* pointer;
1765 GLint buffer_binding;
1766 };
1767
1768 /**
1769 * \brief Emulates vertex array objects if not supported
1770 * by OpenGL implementation.
1771 */
1772 class VertexArrayObject {
1773 public:
1774 /**
1775 * \brief The maximum number of vertex attributes
1776 * that we save in a VAO.
1777 * \details For GLUP, only 4 are needed. Can be
1778 * increased if need be.
1779 */
1780 enum {MAX_VERTEX_ATTRIB = 4};
1781
1782 /**
1783 * \brief VertexArrayObject constructor.
1784 */
1785 VertexArrayObject() :
1786 element_array_buffer_binding_(0) {
1787 if(max_vertex_attrib == 0) {
1788 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attrib);
1789 max_vertex_attrib = std::min(
1790 max_vertex_attrib, GLint(MAX_VERTEX_ATTRIB)
1791 );
1792 }
1793 }
1794
1795 /**
1796 * \brief Binds this VertexArrayObject.
1797 * \details This copies the stored element array and vertex attribute
1798 * bindings to OpenGL.
1799 */
1800 void bind() {
1801 glBindBuffer(
1802 GL_ELEMENT_ARRAY_BUFFER, GLuint(element_array_buffer_binding_)
1803 );
1804 for(GLint i=0; i<max_vertex_attrib; ++i) {
1805 attrib_binding_[i].copy_to_GL(GLuint(i));
1806 }
1807 }
1808
1809 /**
1810 * \brief Unbinds this VertexArrayObject.
1811 * \details This copies the currently bound element array and
1812 * vertex attribute bindings from OpenGL to this VertexArrayObject.
1813 */
1814 void unbind() {
1815 // Note: In Emscripten, glGetIntegerv() does not suffer from the
1816 // same bug as glGetVertexAttribiv(
1817 // ..., GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, ...
1818 // ), therefore there is no special case here.
1819 glGetIntegerv(
1820 GL_ELEMENT_ARRAY_BUFFER_BINDING,
1821 &element_array_buffer_binding_
1822 );
1823 for(GLint i=0; i<max_vertex_attrib; ++i) {
1824 attrib_binding_[i].copy_from_GL(GLuint(i));
1825 }
1826 }
1827
1828 /**
1829 * \brief Resets all the stored bindings to default
1830 * values.
1831 */
1832 void reset() {
1833 element_array_buffer_binding_ = 0;
1834 for(GLint i=0; i<max_vertex_attrib; ++i) {
1835 attrib_binding_[i].reset();
1836 }
1837 }
1838
1839 private:
1840 VertexAttribBinding attrib_binding_[MAX_VERTEX_ATTRIB];
1841 GLint element_array_buffer_binding_;
1842 };
1843
1844
1845 /**
1846 * \brief Manages the emulated vertex array objects.
1847 */
1848 class VertexArrayObjectAllocator {
1849 public:
1850 /**
1851 * \brief VertexArrayObjectAllocator constructor.
1852 */
1853 VertexArrayObjectAllocator() {
1854 // Create the dummy slot 0.
1855 slots_.push_back(Slot());
1856 }
1857
1858 /**
1859 * \brief VertexArrayObjectAllocator destructor.
1860 */
1861 ~VertexArrayObjectAllocator() {
1862 for(index_t i=0; i<slots_.size(); ++i) {
1863 if(slots_[i].VAO != nullptr) {
1864 delete slots_[i].VAO;
1865 slots_[i].VAO = nullptr;
1866 }
1867 }
1868 }
1869
1870 /**
1871 * \brief Creates a new vertex array object.
1872 * \return the index of the newly created vertex
1873 * array object.
1874 */
1875 index_t new_VAO() {
1876 index_t result = 0;
1877 if(first_free_ != 0) {
1878 result = first_free_;
1879 first_free_ = slots_[first_free_].next;
1880 slots_[result].VAO->reset();
1881 } else {
1882 slots_.push_back(Slot());
1883 result = index_t(slots_.size()-1);
1884 slots_[result].VAO = new VertexArrayObject();
1885 }
1886 return result;
1887 }
1888
1889 /**
1890 * \brief Deletes a vertex array object.
1891 * \details Vertex array objects are recycled internally.
1892 * \param[in] VAOindex the index of the vertex
1893 * array object to delete.
1894 */
1895 void delete_VAO(index_t VAOindex) {
1896 slots_[VAOindex].next = first_free_;
1897 first_free_ = VAOindex;
1898 }
1899
1900 /**
1901 * \brief Gets a vertex array object by index.
1902 * \param[in] VAOindex the index of the vertex array object.
1903 * \return a pointer to the vertex array object.
1904 */
1905 VertexArrayObject* get_VAO(index_t VAOindex) {
1906 return slots_[VAOindex].VAO;
1907 }
1908
1909 private:
1910
1911 /**
1912 * \brief The information attached to each vertex
1913 * array object index.
1914 */
1915 struct Slot {
1916 /**
1917 * \brief Slot constructor.
1918 */
1919 Slot() :
1920 VAO(nullptr), next(0) {
1921 }
1922
1923 /**
1924 * \brief A pointer to the internal representation.
1925 */
1926 VertexArrayObject* VAO;
1927
1928 /**
1929 * \brief The index of the next free element, used
1930 * to have constant-time allocation and deallocation.
1931 */
1932 index_t next;
1933 };
1934
1935 vector<Slot> slots_;
1936
1937 /**
1938 * \brief The head of the free list.
1939 */
1940 GLUPuint first_free_;
1941 };
1942
1943 static VertexArrayObjectAllocator VAO_allocator;
1944
1945 }
1946
1947 // TODO1: ArraysOES()/Arrays() switch based on OpenGL profile
1948 // (runtime) rather than GEO_OS_EMSCRIPTEN macro (compile-time)
1949
1950 void glupGenVertexArrays(GLUPsizei n, GLUPuint* arrays) {
1951 if(GLUP::vertex_array_emulate) {
1952 for(GLUPsizei i=0; i<n; ++i) {
1953 arrays[i] = GLUP::VAO_allocator.new_VAO();
1954 }
1955
1956 } else {
1957 #ifdef GEO_OS_EMSCRIPTEN
1958 glGenVertexArraysOES(n, arrays);
1959 #else
1960 if(!glGenVertexArrays) {
1961 GLUP::vertex_array_emulate = true;
1962 glupGenVertexArrays(n,arrays);
1963 return;
1964 }
1965 glGenVertexArrays(n, arrays);
1966 #endif
1967 }
1968 }
1969
1970 void glupDeleteVertexArrays(GLUPsizei n, const GLUPuint *arrays) {
1971 if(GLUP::vertex_array_emulate) {
1972 for(GLUPsizei i=0; i<n; ++i) {
1973 GLUP::VAO_allocator.delete_VAO(arrays[i]);
1974 }
1975 } else {
1976 #if defined(GEO_OS_EMSCRIPTEN)
1977 glDeleteVertexArraysOES(n, arrays);
1978 #else
1979 if(!glDeleteVertexArrays) {
1980 GLUP::vertex_array_emulate = true;
1981 glupDeleteVertexArrays(n,arrays);
1982 return;
1983 }
1984 glDeleteVertexArrays(n, arrays);
1985 #endif
1986 }
1987 }
1988
1989 void glupBindVertexArray(GLUPuint array) {
1990 if(GLUP::vertex_array_emulate) {
1991 if(array != GLUP::vertex_array_binding) {
1992 if(GLUP::vertex_array_binding != 0) {
1993 GLUP::VAO_allocator.get_VAO(
1994 GLUP::vertex_array_binding
1995 )->unbind();
1996 }
1997 GLUP::vertex_array_binding = array;
1998 if(GLUP::vertex_array_binding != 0) {
1999 GLUP::VAO_allocator.get_VAO(
2000 GLUP::vertex_array_binding
2001 )->bind();
2002 }
2003 }
2004 } else {
2005 #if defined(GEO_OS_EMSCRIPTEN)
2006 glBindVertexArrayOES(array);
2007 #else
2008 if(!glBindVertexArray) {
2009 GLUP::vertex_array_emulate = true;
2010 glupBindVertexArray(array);
2011 return;
2012 }
2013 glBindVertexArray(array);
2014 #endif
2015 GLUP::vertex_array_binding = array;
2016 }
2017 }
2018
2019 GLUPuint glupGetVertexArrayBinding() {
2020 return GLUP::vertex_array_binding;
2021 }
2022
2023 /****************************************************************************/
2024