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