GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/GLUP/GLUP_context_ES.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 407 0.0%
Functions: 0 33 0.0%
Branches: 0 270 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_context_ES.h>
41 #include <geogram_gfx/basic/GLSL.h>
42 #include <geogram/basic/string.h>
43 #include <geogram/basic/command_line.h>
44
45 #ifdef GEO_OS_EMSCRIPTEN
46 #include <emscripten.h>
47 #endif
48
49 #ifdef GEO_GL_ES2
50
51 /*
52 * Notes:
53 * J'ai reussi a corriger le bug de picking de primitives WEBGL: ce sont les
54 * int-s encodes dans des float-s qui font un peu n'importe quoi. En ajoutant
55 * 0.5 au primitive_id a la sortie du vertex shader, ca regle le probleme.
56 *
57 * Il faudra peut-etre quand meme encoder les valeurs de picking de l'etat
58 * sous forme de vec4 plutot qu'en int, parceque je ne suis pas sur que ces
59 * ints puissent stocker plus que 65535 (mais le fait que je voie du bleu
60 * quand on met plein de primitive tend a montrer que ca marche quand meme...)
61 *
62 * Il manque encore le picking en mode "slice cell"
63 *
64 * Les triangles n'ont pas besoin d'indices d'elements je pense ...
65 *
66 * Slice cells:
67 * ============
68 * Pour implanter le mode "slice cells", je me demande s'il vaut mieux
69 * - avoir un tout petit buffer, et faire un flush par cellule ?
70 * - tout assembler dans des gros buffers, et faire un flush tous les
71 * 65K sommets ?
72 * -> essayer le premier (plus simple a programmer), et si \c{c}a rame
73 * faire le deuxi\`eme:
74 * Ca a l'air de marcher pas trop mal.
75 * Rem: le ELEMENT_ARRAY_BUFFER est en entiers 32 bits, certaines archis
76 * (telephones etc...) peuvent avoir besoin d'un ELEMENT_ARRAY_BUFFER
77 * 16 bits (ce qu'on pourrait assez facilement avoir...)
78 *
79 * texture 3D:
80 * ===========
81 * OpenGL ES 2.0 n'a pas de textures 3D, mais on peut les emuler avec des
82 * textures 2D:
83 * https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences
84 * mais existe peut-etre une extension (a tester)
85 *
86 * The shaders
87 * ===========
88 * The shaders can be compiled either in OpenGL ES, GLSL 1.3 or GLSL 1.5.
89 * OpenGL ES mode is used by Emscripten.
90 * GLSL 1.3 mode is used by desktop OpenGL on:
91 * - Linux Intel i915
92 * - Linux NVidia
93 * - Windows
94 * GLSL 1.5 mode is used by destop OpenGL on MacOSX (MacOSX supports 1.5
95 * but does not support 1.3)
96 *
97 * TODO: In the shaders, in/out declarations are selected by #ifdef GL_ES,
98 * instead of testing GLSL version, is it normal ?
99 */
100
101 namespace {
102 using namespace GEO;
103
104 /**
105 * \brief Creates the source of a GLSL function that
106 * gets mesh texture coordinates.
107 * \details This function is necessary because we do not have
108 * a way of specifying uniform constant arrays in OpenGL ES 2.0
109 * \param[in] tex_coords an array of 4*\p nb_tex_coords floats
110 * \param[in] nb_tex_coords number of vec4s that define texture coordinates
111 * \return a std::string with the source of the shader
112 */
113 std::string create_mesh_tex_coord_lookup_function(
114 GLUPfloat* tex_coords, index_t nb_tex_coords
115 ) {
116 std::ostringstream output;
117 output <<
118 "vec4 get_mesh_tex_coord(in int vertex_id) { \n"
119 " int idx = glup_mod(vertex_id," << nb_tex_coords << ");\n";
120 for(index_t i=0; i<nb_tex_coords; ++i) {
121 output << " if(idx == " << i << ") {";
122 output << " return vec4("
123 << tex_coords[4*i ] << ","
124 << tex_coords[4*i+1] << ","
125 << tex_coords[4*i+2] << ","
126 << tex_coords[4*i+3] << "); }\n";
127 }
128 // Will never get here, but some GLSL compilers will not
129 // accept a function that may not return a value.
130 output << " return vec4(0.0, 0.0, 0.0, 0.0);\n";
131 output << "}\n";
132 return output.str();
133 }
134
135 /**
136 * \brief Creates the source of a GLSL function that
137 * gets mesh texture coordinates.
138 * \details The size of the texture coordinates array is
139 * automatically computed.
140 * \param[in] tex an array of texture coordinates
141 * \return a pointer to a string with the sources of
142 * the shader.
143 */
144
145 #define mesh_tex_coord_lookup(tex) \
146 create_mesh_tex_coord_lookup_function( \
147 tex, index_t(sizeof(tex) / (4*sizeof(float))) \
148 )
149 }
150
151 namespace GLUP {
152
153 extern bool vertex_array_emulate;
154
155 void Context_ES2::begin(GLUPprimitive primitive) {
156 if(primitive == GLUP_THICK_LINES) {
157 // Thick lines use the normal vertex attribute to store the second
158 // extremity of each segment.
159 immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].enable();
160 }
161 Context::begin(primitive);
162 if(primitive == GLUP_THICK_LINES) {
163 // Thick lines need to replace line segments with quads.
164 // The additional vertices are generated in
165 // flush()_immediate_buffers (but we need twice the
166 // room in the buffer, so we flush when the buffer is half-full).
167 immediate_state_.begin(GLUP_THICK_LINES, IMMEDIATE_BUFFER_SIZE/2);
168 }
169 }
170
171 void Context_ES2::end() {
172 Context::end();
173 }
174
175 const char* Context_ES2::profile_name() const {
176 return "GLUPES2";
177 }
178
179 bool Context_ES2::primitive_supports_array_mode(GLUPprimitive prim) const {
180 // Note: points, spheres, lines with width 1
181 // and triangles without mesh can support array mode,
182 // but this will not work with picking, therefore it is disabled.
183 // In Android it is enabled. Additional test (triangles without mesh
184 // and line width 1) are done in MeshGfx.
185 if(does_not_need_picking_) {
186 return
187 (prim == GLUP_POINTS ||
188 prim == GLUP_SPHERES ||
189 prim == GLUP_LINES ||
190 prim == GLUP_TRIANGLES);
191 }
192 return false;
193 }
194
195 /**
196 * \brief Computes the number of elements in the buffer for
197 * a given primitive.
198 * \param[in] nb_vertices_per_glup_primitive number of vertices
199 * per primitive
200 * \param[in] nb_elements_per_glup_primitive number of elements
201 * per primitive
202 * \return total number of elements required to draw all the
203 * primitives in the buffer.
204 */
205 inline index_t nb_elements(
206 index_t nb_vertices_per_glup_primitive,
207 index_t nb_elements_per_glup_primitive
208 ) {
209 index_t nb_glup_primitives =
210 IMMEDIATE_BUFFER_SIZE / nb_vertices_per_glup_primitive;
211 return (
212 nb_glup_primitives * nb_elements_per_glup_primitive
213 );
214 }
215
216
217 void Context_ES2::setup() {
218
219 bool extension_standard_derivatives = false;
220 bool extension_vertex_array_object = false;
221
222 #ifdef GEO_OS_EMSCRIPTEN
223
224 /**
225 * \brief Tests whether a WebGL extension is supported.
226 * \details WebGL requires getExtension() to be called for initializing
227 * the extension (if supported, this returns an extension object,
228 * else this returns null).
229 * \param[in] ext the name of the extension surrounded by single quotes
230 * \retval true if the extension is supported
231 * \retval false otherwise
232 */
233 #define WEBGL_EXTENSION_SUPPORTED(ext) \
234 EM_ASM_INT({return ( \
235 Module.ctx.getExtension(ext) == null ? 0 : 1 \
236 );},0) != 0
237
238
239 extension_standard_derivatives = WEBGL_EXTENSION_SUPPORTED(
240 'OES_standard_derivatives'
241 );
242
243 extension_vertex_array_object = WEBGL_EXTENSION_SUPPORTED(
244 'OES_vertex_array_object'
245 );
246
247 #else
248 //TODO: it seems that standard derivatives and vertex array
249 //object extension names are not prefixed with GL_OES_ in
250 //OpenGL ES mode (but they are in WebGL, oh my....)
251
252 if(use_ES_profile_) {
253 extension_standard_derivatives =
254 extension_is_supported("GL_OES_standard_derivatives");
255
256 // ES profile can be artificially set to true for non-NVidia
257 // GPUs, see constructor.
258 extension_vertex_array_object =
259 extension_is_supported("GL_OES_vertex_array_object") ||
260 extension_is_supported("GL_ARB_vertex_array_object") ;
261
262 } else {
263 extension_standard_derivatives = true;
264 extension_vertex_array_object = true;
265 }
266 #endif
267
268 // Switch-on GLUP's emulation for Vertex Array
269 // Objects if they are not supported.
270 if(!extension_vertex_array_object) {
271 GLUP::vertex_array_emulate = true;
272 }
273
274 Logger::out("GLUP")
275 << "OES_standard_derivatives: "
276 << extension_standard_derivatives
277 << std::endl;
278
279 Logger::out("GLUP")
280 << "vertex_array_object: "
281 << extension_vertex_array_object
282 << std::endl;
283
284 create_CPU_side_uniform_buffer();
285
286
287 /************** VAOs and VBOs for whole cells clipping ******/
288
289 // Compute the maximum number of elements required to draw
290 // the vertices in a buffer, for all types of volumetric
291 // primitives.
292 index_t max_nb_elements = 0;
293 max_nb_elements = std::max(max_nb_elements, nb_elements(4,12));
294 max_nb_elements = std::max(max_nb_elements, nb_elements(8,36));
295 max_nb_elements = std::max(max_nb_elements, nb_elements(6,24));
296 max_nb_elements = std::max(max_nb_elements, nb_elements(5,18));
297 max_nb_elements = std::max(max_nb_elements, nb_elements(4,12));
298
299 nb_clip_cells_elements_ = max_nb_elements;
300 clip_cells_elements_ = new Numeric::uint16[nb_clip_cells_elements_];
301
302 glupGenVertexArrays(1,&clip_cells_VAO_);
303 glupBindVertexArray(clip_cells_VAO_);
304
305 bind_immediate_state_buffers_to_VAO();
306
307 glGenBuffers(1, &clip_cells_elements_VBO_);
308 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, clip_cells_elements_VBO_);
309
310 update_buffer_object(
311 clip_cells_elements_VBO_,
312 GL_ELEMENT_ARRAY_BUFFER,
313 max_nb_elements * sizeof(Numeric::uint32),
314 nullptr // no need to copy the buffer, it will be overwritten after.
315 );
316
317 glupBindVertexArray(0);
318
319 // Shaders in GLSL 2.0 do not support gl_VertexID, we need to
320 // emulate it.
321 create_vertex_id_VBO();
322
323 /************** VAOs and VBOs for sliced cells clipping ******/
324
325 max_nb_elements = 6;
326 index_t max_nb_vertices = 12;
327
328 glupGenVertexArrays(1,&sliced_cells_VAO_);
329 glupBindVertexArray(sliced_cells_VAO_);
330
331 glGenBuffers(1, &sliced_cells_elements_VBO_);
332 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sliced_cells_elements_VBO_);
333
334 update_buffer_object(
335 sliced_cells_elements_VBO_,
336 GL_ELEMENT_ARRAY_BUFFER,
337 max_nb_elements * sizeof(Numeric::uint16),
338 nullptr // no need to copy the buffer, it will be overwritten after.
339 );
340
341 for(index_t i=0; i<4; ++i) {
342 glGenBuffers(1, &sliced_cells_vertex_attrib_VBO_[i]);
343 glBindBuffer(GL_ARRAY_BUFFER, sliced_cells_vertex_attrib_VBO_[i]);
344
345 update_buffer_object(
346 sliced_cells_vertex_attrib_VBO_[i],
347 GL_ARRAY_BUFFER,
348 max_nb_vertices * sizeof(Numeric::float32) * 4,
349 nullptr // no need to copy the buffer, it will be overwritten.
350 );
351
352 glVertexAttribPointer(
353 i,
354 4,
355 GL_FLOAT,
356 GL_FALSE,
357 0,
358 nullptr
359 );
360 }
361 glEnableVertexAttribArray(0);
362 glupBindVertexArray(0);
363 }
364
365 Context_ES2::Context_ES2() :
366 nb_clip_cells_elements_(0),
367 clip_cells_elements_(nullptr),
368 clip_cells_elements_VBO_(0),
369 clip_cells_VAO_(0),
370 sliced_cells_elements_VBO_(0),
371 sliced_cells_VAO_(0),
372 vertex_id_VBO_bound_(false) {
373 for(index_t i=0; i<4; ++i) {
374 sliced_cells_vertex_attrib_VBO_[i] = 0;
375 }
376 GLSL_version_ = GLSL::supported_language_version();
377 #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_ANDROID)
378 use_ES_profile_ = true;
379 Logger::out("GLUP") << "ES2 context, supported GLSL version = " << GLSL_version_
380 << std::endl;
381 #endif
382 // GLUP_THICK_LINES are rendered as quads, with four vertices. In this
383 // profile, we have no geometry shader and we need to do the job on our
384 // own in flush_immediate_buffers().
385 nb_vertices_per_primitive_[GLUP_THICK_LINES] = 4;
386
387 // If application does not need picking, then array mode is supported
388 // for points, lines, spheres, triangles.
389 // We force that for android (for now).
390 #ifdef GEO_OS_ANDROID
391 does_not_need_picking_ = true;
392 #else
393 does_not_need_picking_ = CmdLine::get_arg_bool(
394 "dbg:gfx:GLUPES2:does_not_need_picking"
395 );
396 #endif
397 }
398
399 Context_ES2::~Context_ES2() {
400 glDeleteBuffers(1,&clip_cells_elements_VBO_);
401 glupDeleteVertexArrays(1, &clip_cells_VAO_);
402 for(index_t i=0; i<4; ++i) {
403 glDeleteBuffers(1, &sliced_cells_vertex_attrib_VBO_[i]);
404 }
405 glupDeleteVertexArrays(1, &sliced_cells_VAO_);
406 delete[] clip_cells_elements_;
407 }
408
409 Memory::pointer Context_ES2::get_state_variable_address(
410 const char* name
411 ) {
412 geo_assert(
413 variable_to_offset_.find(name) != variable_to_offset_.end()
414 );
415 return uniform_buffer_data_ + variable_to_offset_[name];
416 }
417
418 void Context_ES2::do_update_uniform_buffer() {
419 GEO_CHECK_GL();
420
421 if(!uniform_buffer_dirty_) {
422 return;
423 }
424
425 GEO_CHECK_GL();
426
427 if(matrices_dirty_) {
428 update_matrices();
429 }
430
431 GEO_CHECK_GL();
432
433 if(lighting_dirty_) {
434 update_lighting();
435 }
436
437 GEO_CHECK_GL();
438
439 glUseProgram(latest_program_);
440
441 GEO_CHECK_GL();
442
443 if(latest_program_ != 0) {
444 copy_uniform_state_to_current_program();
445 }
446
447 GEO_CHECK_GL();
448
449 uniform_buffer_dirty_ = false;
450 }
451
452 void Context_ES2::copy_uniform_state_to_current_program() {
453 GLint loc;
454
455 // Matrices (in vertex shader).
456 {
457 loc = glGetUniformLocation(
458 latest_program_, "GLUP_VS.modelviewprojection_matrix"
459 );
460 glUniformMatrix4fv(
461 loc, 1, GL_FALSE,
462 uniform_state_.modelviewprojection_matrix.get_pointer()
463 );
464 loc = glGetUniformLocation(
465 latest_program_, "GLUP_VS.modelview_matrix"
466 );
467 glUniformMatrix4fv(
468 loc, 1, GL_FALSE,
469 uniform_state_.modelview_matrix.get_pointer()
470 );
471 loc = glGetUniformLocation(
472 latest_program_, "GLUP_VS.projection_matrix"
473 );
474 glUniformMatrix4fv(
475 loc, 1, GL_FALSE,
476 uniform_state_.projection_matrix.get_pointer()
477 );
478 loc = glGetUniformLocation(
479 latest_program_, "GLUP_VS.texture_matrix"
480 );
481 glUniformMatrix4fv(
482 loc, 1, GL_FALSE,
483 uniform_state_.texture_matrix.get_pointer()
484 );
485 loc = glGetUniformLocation(
486 latest_program_, "GLUP_VS.normal_matrix"
487 );
488
489 // Normal matrix is stored in state buffer with padding.
490 float normal_matrix[9];
491 for(index_t i=0; i<3; ++i) {
492 for(index_t j=0; j<3; ++j) {
493 normal_matrix[i*3+j] =
494 uniform_state_.normal_matrix.get_pointer()[i*4+j];
495 }
496 }
497
498 glUniformMatrix3fv(loc, 1, GL_FALSE, normal_matrix);
499
500
501 loc = glGetUniformLocation(
502 latest_program_, "GLUP_VS.inverse_modelview_matrix"
503 );
504 glUniformMatrix4fv(
505 loc, 1, GL_FALSE,
506 uniform_state_.inverse_modelview_matrix.get_pointer()
507 );
508 loc = glGetUniformLocation(
509 latest_program_, "GLUP_VS.inverse_projection_matrix"
510 );
511 glUniformMatrix4fv(
512 loc, 1, GL_FALSE,
513 uniform_state_.inverse_projection_matrix.get_pointer()
514 );
515 loc = glGetUniformLocation(
516 latest_program_, "GLUP_VS.inverse_modelviewprojection_matrix"
517 );
518 glUniformMatrix4fv(
519 loc, 1, GL_FALSE,
520 uniform_state_.inverse_modelviewprojection_matrix.get_pointer()
521 );
522
523 loc = glGetUniformLocation(
524 latest_program_, "GLUP_VS.viewport"
525 );
526 GLint viewport[4];
527 glGetIntegerv(GL_VIEWPORT, viewport);
528 glUniform4f(
529 loc,
530 float(viewport[0]),
531 float(viewport[1]),
532 float(viewport[2]),
533 float(viewport[3])
534 );
535 }
536
537 // Matrices (in fragment shader)
538 {
539 loc = glGetUniformLocation(
540 latest_program_, "GLUP.texture_matrix"
541 );
542 glUniformMatrix4fv(
543 loc, 1, GL_FALSE,
544 uniform_state_.texture_matrix.get_pointer()
545 );
546 loc = glGetUniformLocation(
547 latest_program_, "GLUP.modelviewprojection_matrix"
548 );
549 glUniformMatrix4fv(
550 loc, 1, GL_FALSE,
551 uniform_state_.modelviewprojection_matrix.get_pointer()
552 );
553 loc = glGetUniformLocation(
554 latest_program_, "GLUP.inverse_modelviewprojection_matrix"
555 );
556 glUniformMatrix4fv(
557 loc, 1, GL_FALSE,
558 uniform_state_.inverse_modelviewprojection_matrix.get_pointer()
559 );
560
561 loc = glGetUniformLocation(
562 latest_program_, "GLUP.normal_matrix"
563 );
564 // Normal matrix is stored in state buffer with padding.
565 float normal_matrix[9];
566 for(index_t i=0; i<3; ++i) {
567 for(index_t j=0; j<3; ++j) {
568 normal_matrix[i*3+j] =
569 uniform_state_.normal_matrix.get_pointer()[i*4+j];
570 }
571 }
572 glUniformMatrix3fv(loc, 1, GL_FALSE, normal_matrix);
573
574 loc = glGetUniformLocation(
575 latest_program_, "GLUP.viewport"
576 );
577 GLint viewport[4];
578 glGetIntegerv(GL_VIEWPORT, viewport);
579 glUniform4f(
580 loc,
581 float(viewport[0]),
582 float(viewport[1]),
583 float(viewport[2]),
584 float(viewport[3])
585 );
586 }
587
588 // Points (in vertex shader).
589 {
590 loc = glGetUniformLocation(
591 latest_program_, "GLUP_VS.point_size"
592 );
593 glUniform1f(loc, uniform_state_.point_size.get());
594 }
595
596
597 // Lighting.
598 {
599 loc = glGetUniformLocation(
600 latest_program_, "GLUP.light_vector"
601 );
602 glUniform3fv(
603 loc, 1, uniform_state_.light_vector.get_pointer()
604 );
605 loc = glGetUniformLocation(
606 latest_program_, "GLUP.light_half_vector"
607 );
608 glUniform3fv(
609 loc, 1, uniform_state_.light_half_vector.get_pointer()
610 );
611 }
612
613 // All the toggles.
614 for(index_t i=0; i<uniform_state_.toggle.size(); ++i) {
615 loc = glGetUniformLocation(
616 latest_program_,
617 ("GLUP." + uniform_state_.toggle[i].name()).c_str()
618 );
619 glUniform1i(
620 loc, GLint(uniform_state_.toggle[i].get())
621 );
622 }
623
624 // Colors.
625 if(!uniform_state_.toggle[GLUP_VERTEX_COLORS].get()) {
626 for(index_t i=0; i<uniform_state_.color.size(); ++i) {
627 loc = glGetUniformLocation(
628 latest_program_,
629 ("GLUP." + uniform_state_.color[i].name()).c_str()
630 );
631 glUniform4fv(
632 loc, 1, uniform_state_.color[i].get_pointer()
633 );
634 }
635 }
636
637 // Mesh.
638 if(uniform_state_.toggle[GLUP_DRAW_MESH].get()) {
639 loc = glGetUniformLocation(latest_program_, "GLUP.mesh_width");
640 glUniform1f(loc, uniform_state_.mesh_width.get());
641 }
642
643 loc = glGetUniformLocation(latest_program_, "GLUP_VS.mesh_width");
644 glUniform1f(loc, uniform_state_.mesh_width.get());
645
646 // Texturing.
647 if(uniform_state_.toggle[GLUP_TEXTURING].get()) {
648 loc = glGetUniformLocation(
649 latest_program_, "GLUP.texture_mode"
650 );
651 glUniform1i(loc, uniform_state_.texture_mode.get());
652 loc = glGetUniformLocation(
653 latest_program_, "GLUP.texture_type"
654 );
655 glUniform1i(loc, uniform_state_.texture_type.get());
656 }
657
658 // Cell shrink
659 loc = glGetUniformLocation(latest_program_, "GLUP.cells_shrink");
660 glUniform1f(loc, uniform_state_.cells_shrink.get());
661
662 // Picking
663 if(uniform_state_.toggle[GLUP_PICKING].get()) {
664 loc = glGetUniformLocation(
665 latest_program_, "GLUP.picking_mode"
666 );
667 glUniform1i(loc, uniform_state_.picking_mode.get());
668 loc = glGetUniformLocation(
669 latest_program_, "GLUP.picking_id"
670 );
671 glUniform1i(loc, uniform_state_.picking_id.get());
672 loc = glGetUniformLocation(
673 latest_program_, "GLUP.base_picking_id"
674 );
675 glUniform1i(loc, uniform_state_.base_picking_id.get());
676 }
677
678 // Clipping.
679 if(uniform_state_.toggle[GLUP_CLIPPING].get()) {
680 loc = glGetUniformLocation(
681 latest_program_, "GLUP.clipping_mode"
682 );
683 glUniform1i(loc, uniform_state_.clipping_mode.get());
684 loc = glGetUniformLocation(
685 latest_program_, "GLUP.clip_plane"
686 );
687 glUniform4fv(loc, 1, uniform_state_.clip_plane.get_pointer());
688 loc = glGetUniformLocation(
689 latest_program_, "GLUP_VS.world_clip_plane"
690 );
691 glUniform4fv(
692 loc, 1, uniform_state_.world_clip_plane.get_pointer()
693 );
694 loc = glGetUniformLocation(
695 latest_program_, "GLUP.world_clip_plane"
696 );
697 glUniform4fv(
698 loc, 1, uniform_state_.world_clip_plane.get_pointer()
699 );
700 }
701
702 // alpha discard.
703 if(uniform_state_.toggle[GLUP_ALPHA_DISCARD].get()) {
704 loc = glGetUniformLocation(
705 latest_program_, "GLUP.alpha_threshold"
706 );
707 glUniform1f(loc, uniform_state_.alpha_threshold.get());
708 }
709
710 // specular
711 {
712 loc = glGetUniformLocation(
713 latest_program_, "GLUP.specular"
714 );
715 glUniform1f(loc, uniform_state_.specular.get());
716 }
717 }
718
719 void Context_ES2::update_base_picking_id(GLint new_value) {
720 if(uniform_state_.toggle[GLUP_PICKING].get()) {
721 uniform_state_.base_picking_id.set(new_value);
722 if(latest_program_ != 0) {
723 GEO_CHECK_GL();
724 GLint loc = glGetUniformLocation(
725 latest_program_, "GLUP.base_picking_id"
726 );
727 GEO_CHECK_GL();
728 glUseProgram(latest_program_);
729 glUniform1i(loc, new_value);
730 GEO_CHECK_GL();
731 }
732 }
733 }
734
735
736 /*****************************************************************************/
737
738 void Context_ES2::setup_GLUP_POINTS() {
739 set_primitive_info(
740 GLUP_POINTS, GL_POINTS,
741 GLSL::compile_program_with_includes_no_link(
742 this,
743 "//stage GL_VERTEX_SHADER\n"
744 "//import <GLUPES/points_vertex_shader.h>\n",
745 "//stage GL_FRAGMENT_SHADER\n"
746 "//import <GLUPES/spheres_fragment_shader.h>\n"
747 )
748 );
749 }
750
751 void Context_ES2::setup_GLUP_LINES() {
752 set_primitive_info(
753 GLUP_LINES, GL_LINES,
754 GLSL::compile_program_with_includes_no_link(
755 this,
756 "//stage GL_VERTEX_SHADER\n"
757 "//import <GLUPES/vertex_shader.h>\n",
758 "//stage GL_FRAGMENT_SHADER\n"
759 "//import <GLUPES/lines_fragment_shader.h>\n"
760 )
761 );
762 }
763
764 void Context_ES2::setup_GLUP_THICK_LINES() {
765 // GLUP_THICK_LINES are rendered as quads,
766 // Only immediate mode is supported.
767 // the line segments given by the user are transformed into quads
768 // in flush_immediate_buffers().
769
770 // For each quad, we draw two triangles
771 static index_t element_indices[6] = {
772 0, 1, 2,
773 2, 1, 3
774 };
775
776 set_primitive_info_immediate_index_mode(
777 primitive_source_, GL_TRIANGLES,
778 GLSL::compile_program_with_includes_no_link(
779 this,
780 "//stage GL_VERTEX_SHADER\n"
781 "//import <GLUPES/thick_lines_vertex_shader.h>\n",
782 "//stage GL_FRAGMENT_SHADER\n"
783 "//import <GLUPES/thick_lines_fragment_shader.h>\n"
784 ),
785 index_t(sizeof(element_indices)/sizeof(index_t)),
786 element_indices
787 );
788
789 // The number of vertices per thick line segment is adapted in two
790 // other places in the code:
791 // - in Context_ES2::begin(), the maximum index for the immediate
792 // buffers is set to half-buffer (so that we have enough room to
793 // generate the additional vertices to render quads).
794 // - in Context_ES2::flush_immediate_buffers(), the immediate buffers
795 // are reshuffled, in order to create quads, with
796 // their four vertices set to the first vertex of each segment,
797 // and "normals" set the second vertex of each segment.
798 //
799 // The vertex shader uses the primitive Id to know which vertex
800 // goes where.
801 //
802 // Note: since the GLUP_NORMAL_ATTRIBUTE is used to store the second
803 // vertex of each segment, it needs to be bound. It is done in
804 // Context_ES2::begin() (that calls then Context::begin()).
805 }
806
807 void Context_ES2::setup_primitive_generic(
808 index_t nb_elements_per_primitive,
809 index_t* element_indices
810 ) {
811
812 set_primitive_info_immediate_index_mode(
813 primitive_source_, GL_TRIANGLES,
814 GLSL::compile_program_with_includes_no_link(
815 this,
816 "//stage GL_VERTEX_SHADER\n"
817 "//import <GLUPES/vertex_shader.h>\n",
818 "//stage GL_FRAGMENT_SHADER\n"
819 "//import <GLUPES/fragment_shader.h>\n"
820 ),
821 nb_elements_per_primitive, element_indices
822 );
823 }
824
825 void Context_ES2::setup_GLUP_TRIANGLES() {
826 static index_t element_indices[3] = {
827 0, 1, 2
828 };
829 setup_primitive_generic(
830 index_t(sizeof(element_indices)/sizeof(index_t)),
831 element_indices
832 );
833 }
834
835 void Context_ES2::setup_GLUP_QUADS() {
836 static index_t element_indices[6] = {
837 0, 1, 2,
838 0, 2, 3
839 };
840 setup_primitive_generic(
841 index_t(sizeof(element_indices)/sizeof(index_t)),
842 element_indices
843 );
844 }
845
846 void Context_ES2::setup_GLUP_TETRAHEDRA() {
847 static index_t element_indices[12] = {
848 1,3,2,
849 0,2,3,
850 3,1,0,
851 0,1,2
852 };
853 setup_primitive_generic(
854 index_t(sizeof(element_indices)/sizeof(index_t)),
855 element_indices
856 );
857 }
858
859 void Context_ES2::setup_GLUP_HEXAHEDRA() {
860 static index_t element_indices[36] = {
861 0,2,6, 0,6,4,
862 3,1,5, 3,5,7,
863 1,0,4, 1,4,5,
864 2,3,7, 2,7,6,
865 1,3,2, 1,2,0,
866 4,6,7, 4,7,5
867 };
868 setup_primitive_generic(
869 index_t(sizeof(element_indices)/sizeof(index_t)),
870 element_indices
871 );
872 }
873
874 void Context_ES2::setup_GLUP_PRISMS() {
875 static index_t element_indices[24] = {
876 0,1,2,
877 3,5,4,
878 0,3,4, 0,4,1,
879 0,2,5, 0,5,3,
880 1,4,5, 1,5,2
881 };
882 setup_primitive_generic(
883 index_t(sizeof(element_indices)/sizeof(index_t)),
884 element_indices
885 );
886 }
887
888 void Context_ES2::setup_GLUP_PYRAMIDS() {
889 static index_t element_indices[18] = {
890 0,1,2, 0,2,3,
891 0,4,1,
892 0,3,4,
893 2,4,3,
894 2,1,4
895 };
896 setup_primitive_generic(
897 index_t(sizeof(element_indices)/sizeof(index_t)),
898 element_indices
899 );
900 }
901
902 void Context_ES2::setup_GLUP_CONNECTORS() {
903 static index_t element_indices[12] = {
904 0,1,2, 0,2,3,
905 2,1,0,
906 3,2,0
907 };
908 setup_primitive_generic(
909 index_t(sizeof(element_indices)/sizeof(index_t)),
910 element_indices
911 );
912 }
913
914 void Context_ES2::setup_GLUP_SPHERES() {
915 set_primitive_info(
916 GLUP_SPHERES, GL_POINTS,
917 GLSL::compile_program_with_includes_no_link(
918 this,
919 "//stage GL_VERTEX_SHADER\n"
920 "//import <GLUPES/spheres_vertex_shader.h>\n",
921 "//stage GL_FRAGMENT_SHADER\n"
922 "//import <GLUPES/spheres_fragment_shader.h>\n"
923 )
924 );
925 }
926
927 bool Context_ES2::cell_by_cell_clipping() const {
928 if(!uniform_state_.toggle[GLUP_CLIPPING].get()) {
929 return false;
930 }
931
932 if(
933 immediate_state_.primitive() != GLUP_TETRAHEDRA &&
934 immediate_state_.primitive() != GLUP_HEXAHEDRA &&
935 immediate_state_.primitive() != GLUP_PRISMS &&
936 immediate_state_.primitive() != GLUP_PYRAMIDS &&
937 immediate_state_.primitive() != GLUP_CONNECTORS
938 ) {
939 return false;
940 }
941
942 return (
943 uniform_state_.clipping_mode.get() == GLUP_CLIP_WHOLE_CELLS ||
944 uniform_state_.clipping_mode.get() == GLUP_CLIP_STRADDLING_CELLS
945 );
946 }
947
948 bool Context_ES2::sliced_cells_clipping() const {
949 if(!uniform_state_.toggle[GLUP_CLIPPING].get()) {
950 return false;
951 }
952
953 if(
954 immediate_state_.primitive() != GLUP_TETRAHEDRA &&
955 immediate_state_.primitive() != GLUP_HEXAHEDRA &&
956 immediate_state_.primitive() != GLUP_PRISMS &&
957 immediate_state_.primitive() != GLUP_PYRAMIDS &&
958 immediate_state_.primitive() != GLUP_CONNECTORS
959 ) {
960 return false;
961 }
962
963 return (
964 uniform_state_.clipping_mode.get() == GLUP_CLIP_SLICE_CELLS
965 );
966 }
967
968 void Context_ES2::flush_immediate_buffers_with_cell_by_cell_clipping() {
969 index_t cur_vertex = 0;
970 index_t cur_element_out = 0;
971 index_t nb_vertices_per_cell =
972 nb_vertices_per_primitive_[immediate_state_.primitive()];
973 index_t nb_elements_per_cell = primitive_info_[
974 immediate_state_.primitive()].nb_elements_per_primitive;
975
976 // Assemble the list of indices (elements) that correspond
977 // to the vertices of the visible cells.
978 while(cur_vertex < immediate_state_.nb_vertices()) {
979 if(!cell_is_clipped(cur_vertex)) {
980 for(index_t lei=0; lei < nb_elements_per_cell; ++lei) {
981 geo_debug_assert(
982 cur_element_out < nb_clip_cells_elements_
983 );
984 clip_cells_elements_[cur_element_out] =
985 Numeric::uint16(
986 cur_vertex + primitive_info_[
987 immediate_state_.primitive()
988 ].primitive_elements[lei]
989 );
990 ++cur_element_out;
991 }
992 }
993 cur_vertex += nb_vertices_per_cell;
994 }
995
996 glupBindVertexArray(clip_cells_VAO_);
997
998 // Stream the values in all bound buffers (and attach
999 // them to the VAO).
1000 for(index_t i=0; i<ImmediateState::NB_IMMEDIATE_BUFFERS; ++i) {
1001 if(immediate_state_.buffer[i].is_enabled()) {
1002 glEnableVertexAttribArray(i);
1003 if(immediate_state_.buffer[i].VBO() != 0) {
1004 stream_buffer_object(
1005 immediate_state_.buffer[i].VBO(),
1006 GL_ARRAY_BUFFER,
1007 immediate_state_.buffer[i].size_in_bytes(),
1008 immediate_state_.buffer[i].data()
1009 );
1010 }
1011 }
1012 }
1013
1014 if(
1015 vertex_id_VBO_ != 0 && ((
1016 immediate_state_.primitive() >= GLUP_TRIANGLES &&
1017 uniform_state_.toggle[GLUP_DRAW_MESH].get()
1018 ) || uniform_state_.toggle[GLUP_PICKING].get())
1019 ) {
1020 if(!vertex_id_VBO_bound_) {
1021 glEnableVertexAttribArray(GLUP_VERTEX_ID_ATTRIBUTE);
1022 glBindBuffer(
1023 GL_ARRAY_BUFFER, vertex_id_VBO_
1024 );
1025 glVertexAttribPointer(
1026 GLUP_VERTEX_ID_ATTRIBUTE,
1027 1, // 1 component per attribute
1028 GL_UNSIGNED_SHORT, // components are 16 bits integers
1029 GL_FALSE, // do not normalize
1030 0, // stride
1031 nullptr // pointer (relative to bound VBO beginning)
1032 );
1033 vertex_id_VBO_bound_ = true;
1034 }
1035 } else {
1036 if(vertex_id_VBO_bound_) {
1037 vertex_id_VBO_bound_ = false;
1038 glDisableVertexAttribArray(GLUP_VERTEX_ID_ATTRIBUTE);
1039 }
1040 }
1041
1042 // Stream the indices into the elements VBO.
1043 stream_buffer_object(
1044 clip_cells_elements_VBO_,
1045 GL_ELEMENT_ARRAY_BUFFER,
1046 cur_element_out * sizeof(Numeric::uint16),
1047 clip_cells_elements_
1048 );
1049
1050 glDrawElements(
1051 primitive_info_[immediate_state_.primitive()].GL_primitive,
1052 GLsizei(cur_element_out),
1053 GL_UNSIGNED_SHORT,
1054 nullptr
1055 );
1056
1057 glupBindVertexArray(0);
1058 glDisableVertexAttribArray(GLUP_VERTEX_ID_ATTRIBUTE);
1059 immediate_state_.reset();
1060 }
1061
1062 void Context_ES2::flush_immediate_buffers_with_sliced_cells_clipping() {
1063
1064 MarchingCell* marching_cell = nullptr;
1065 switch(immediate_state_.primitive()) {
1066 case GLUP_TETRAHEDRA:
1067 marching_cell = &marching_tet_;
1068 break;
1069 case GLUP_HEXAHEDRA:
1070 marching_cell = &marching_hex_;
1071 break;
1072 case GLUP_PRISMS:
1073 marching_cell = &marching_prism_;
1074 break;
1075 case GLUP_PYRAMIDS:
1076 marching_cell = &marching_pyramid_;
1077 break;
1078 case GLUP_CONNECTORS:
1079 marching_cell = &marching_connector_;
1080 break;
1081 case GLUP_POINTS:
1082 case GLUP_LINES:
1083 case GLUP_THICK_LINES:
1084 case GLUP_TRIANGLES:
1085 case GLUP_QUADS:
1086 case GLUP_SPHERES:
1087 case GLUP_NB_PRIMITIVES:
1088 geo_assert_not_reached;
1089 }
1090
1091 glupBindVertexArray(sliced_cells_VAO_);
1092
1093 index_t v0=0;
1094 while(v0 < immediate_state_.nb_vertices()) {
1095 index_t config = get_config(v0, marching_cell->nb_vertices());
1096
1097 // Compute all the intersection vertices (plus their
1098 // attributes if enabled).
1099 for(index_t i=0; i<marching_cell->config_size(config); ++i) {
1100 index_t e = marching_cell->config_edges(config)[i];
1101 compute_intersection(
1102 v0+marching_cell->edge_vertex(e,0),
1103 v0+marching_cell->edge_vertex(e,1),
1104 e
1105 );
1106 }
1107
1108 if(marching_cell->config_size(config) != 0) {
1109
1110 stream_buffer_object(
1111 sliced_cells_elements_VBO_,
1112 GL_ELEMENT_ARRAY_BUFFER,
1113 marching_cell->max_config_size() * sizeof(Numeric::uint32),
1114 marching_cell->config_edges(config)
1115 );
1116
1117 stream_buffer_object(
1118 sliced_cells_vertex_attrib_VBO_[0],
1119 GL_ARRAY_BUFFER,
1120 12 * sizeof(Numeric::float32) * 4,
1121 &isect_vertex_attribute_[0][0]
1122 );
1123
1124 for(index_t i=1; i<4; ++i) {
1125 if(immediate_state_.buffer[i].is_enabled()) {
1126 glEnableVertexAttribArray(i);
1127 stream_buffer_object(
1128 sliced_cells_vertex_attrib_VBO_[i],
1129 GL_ARRAY_BUFFER,
1130 12 * sizeof(Numeric::float32) * 4,
1131 &isect_vertex_attribute_[i][0]
1132 );
1133 } else {
1134 glDisableVertexAttribArray(i);
1135 }
1136 }
1137
1138 glDrawElements(
1139 GL_TRIANGLE_FAN,
1140 GLsizei(marching_cell->config_size(config)),
1141 GL_UNSIGNED_INT,
1142 nullptr
1143 );
1144 }
1145
1146 v0 += marching_cell->nb_vertices();
1147 }
1148
1149 glupBindVertexArray(0);
1150 immediate_state_.reset();
1151 }
1152
1153 void Context_ES2::flush_immediate_buffers() {
1154 if(immediate_state_.primitive() == GLUP_THICK_LINES) {
1155 // Make sure we have enough room to double the number of
1156 // vertices in immediate buffers
1157 geo_assert(
1158 immediate_state_.nb_vertices() <= IMMEDIATE_BUFFER_SIZE/2
1159 );
1160
1161 // input:
1162 // (v1 v2), (v1 v2), (v1 v2) in vertex buffer
1163 //
1164 // output:
1165 // (v1 v1 v1 v1) (v1 v1 v1 v1) in vertex buffer
1166 // (v2 v2 v2 v2) (v2 v2 v2 v2) in normal buffer
1167 //
1168 // Each line segment becomes four identical vertices. Vertex
1169 // shader distinguished them based on (vertex_id % 4).
1170
1171 for(index_t v=0; v<immediate_state_.nb_vertices(); ++v) {
1172 index_t from = immediate_state_.nb_vertices()-v-1;
1173 index_t to = 2*from;
1174 immediate_state_.copy_element(to,from);
1175 }
1176
1177 for(index_t v=0; v<immediate_state_.nb_vertices(); ++v) {
1178 index_t from = 2*v;
1179 index_t to = 2*v+1;
1180 immediate_state_.copy_element(to,from);
1181 }
1182
1183 // This doubles immediate_state_.nb_vertices()
1184 // (and does nothing else)
1185 immediate_state_.reset(immediate_state_.nb_vertices()*2);
1186
1187 for(index_t v1=0; v1<immediate_state_.nb_vertices(); v1+=4) {
1188 index_t v2=v1+1;
1189 index_t v3=v1+2;
1190 index_t v4=v1+3;
1191
1192 // copy second extremity to normal attribute
1193 // of the four vertices
1194 immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].copy(
1195 v1,
1196 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE],
1197 v3
1198 );
1199 immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].copy(
1200 v2,
1201 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE],
1202 v3
1203 );
1204 immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].copy(
1205 v3,
1206 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE],
1207 v3
1208 );
1209 immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].copy(
1210 v4,
1211 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE],
1212 v3
1213 );
1214
1215 // copy first extremity to vertex attribute of the four vertices
1216 // (three in fact, first one is already there)
1217 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE].copy(v2,v1);
1218 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE].copy(v3,v1);
1219 immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE].copy(v4,v1);
1220 }
1221 }
1222 classify_vertices_in_immediate_buffers();
1223 shrink_cells_in_immediate_buffers();
1224 if(cell_by_cell_clipping()) {
1225 flush_immediate_buffers_with_cell_by_cell_clipping();
1226 } else if(sliced_cells_clipping()) {
1227 flush_immediate_buffers_with_sliced_cells_clipping();
1228 } else {
1229 Context::flush_immediate_buffers();
1230 }
1231 }
1232
1233
1234 void Context_ES2::get_primitive_pseudo_file(
1235 std::vector<GLSL::Source>& sources
1236 ) {
1237 Context::get_primitive_pseudo_file(sources);
1238
1239 static GLUPfloat tri_tex_coords[12] = {
1240 1.0f, 0.0f, 0.0f, 0.0f,
1241 0.0f, 1.0f, 0.0f, 0.0f,
1242 0.0f, 0.0f, 1.0f, 0.0f
1243 };
1244
1245 static GLUPfloat quad_tex_coords[16] = {
1246 0.0f, 0.0f, 1.0f, 1.0f,
1247 1.0f, 0.0f, 0.0f, 1.0f,
1248 1.0f, 1.0f, 0.0f, 0.0f,
1249 0.0f, 1.0f, 1.0f, 0.0f,
1250 };
1251
1252 static GLUPfloat tet_tex_coords[16] = {
1253 1.0f, 0.0f, 0.0f, 0.0f,
1254 0.0f, 1.0f, 0.0f, 0.0f,
1255 0.0f, 0.0f, 1.0f, 0.0f,
1256 0.0f, 0.0f, 0.0f, 1.0f
1257 };
1258
1259 static GLUPfloat hex_tex_coords[32] = {
1260 0.0f, 0.0f, 0.0f, 0.0f,
1261 0.0f, 0.0f, 1.0f, 0.0f,
1262 0.0f, 1.0f, 0.0f, 0.0f,
1263 0.0f, 1.0f, 1.0f, 0.0f,
1264 1.0f, 0.0f, 0.0f, 0.0f,
1265 1.0f, 0.0f, 1.0f, 0.0f,
1266 1.0f, 1.0f, 0.0f, 0.0f,
1267 1.0f, 1.0f, 1.0f, 0.0f,
1268 };
1269
1270 static GLUPfloat prism_tex_coords[24] = {
1271 1.0f, 0.0f, 0.0f, 0.0f,
1272 0.0f, 1.0f, 0.0f, 0.0f,
1273 0.0f, 0.0f, 1.0f, 0.0f,
1274 1.0f, 0.0f, 0.0f, 1.0f,
1275 0.0f, 1.0f, 0.0f, 1.0f,
1276 0.0f, 0.0f, 1.0f, 1.0f
1277 };
1278
1279
1280 switch(primitive_source_) {
1281 case GLUP_TRIANGLES:
1282 sources.push_back(mesh_tex_coord_lookup(tri_tex_coords));
1283 break;
1284 case GLUP_QUADS:
1285 sources.push_back(mesh_tex_coord_lookup(quad_tex_coords));
1286 break;
1287 case GLUP_TETRAHEDRA:
1288 sources.push_back(mesh_tex_coord_lookup(tet_tex_coords));
1289 break;
1290 case GLUP_HEXAHEDRA:
1291 sources.push_back(mesh_tex_coord_lookup(hex_tex_coords));
1292 break;
1293 case GLUP_PRISMS:
1294 sources.push_back(mesh_tex_coord_lookup(prism_tex_coords));
1295 break;
1296
1297 case GLUP_POINTS:
1298 case GLUP_LINES:
1299 case GLUP_THICK_LINES:
1300 case GLUP_PYRAMIDS:
1301 case GLUP_CONNECTORS:
1302 case GLUP_SPHERES:
1303 sources.push_back(
1304 "#define GLUP_NO_MESH_TEX_COORDS\n"
1305 "vec4 get_mesh_tex_coord(in int vertex_id) {\n"
1306 " return vec4(0.0, 0.0, 0.0, 0.0);\n"
1307 "}\n"
1308 );
1309 break;
1310
1311 case GLUP_NB_PRIMITIVES:
1312 geo_assert_not_reached;
1313 }
1314 }
1315
1316 void Context_ES2::get_vertex_shader_preamble_pseudo_file(
1317 std::vector<GLSL::Source>& sources
1318 ) {
1319 if(use_ES_profile_) {
1320 if(GLSL_version_ >= 3.0) {
1321 sources.push_back(
1322 "#version 300 es\n"
1323 "#define GLUP_VERTEX_SHADER \n"
1324 );
1325 } else {
1326 sources.push_back(
1327 "#version 100\n"
1328 "#define GLUP_VERTEX_SHADER \n"
1329 );
1330 }
1331 } else {
1332 sources.push_back(
1333 #ifdef GEO_OS_APPLE
1334 "#version 150 core \n"
1335 #else
1336 "#version 130 \n"
1337 #endif
1338 "#define GLUP_VERTEX_SHADER \n"
1339 );
1340 }
1341 }
1342
1343
1344 void Context_ES2::get_fragment_shader_preamble_pseudo_file(
1345 std::vector<GLSL::Source>& sources
1346 ) {
1347 if(use_ES_profile_) {
1348 #if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_ANDROID)
1349 if(GLSL_version_ >= 3.0) {
1350 sources.push_back("#version 300 es\n");
1351 } else {
1352 sources.push_back("#version 100\n");
1353 }
1354 #endif
1355 sources.push_back(
1356 "#define GLUP_FRAGMENT_SHADER \n"
1357 "#extension GL_OES_standard_derivatives : enable \n"
1358 "#extension GL_EXT_frag_depth : enable \n"
1359 "#extension GL_OES_texture3D : enable \n"
1360 "#ifndef GL_OES_texture3D \n"
1361 " #define GLUP_NO_TEXTURE_3D \n"
1362 "#endif \n"
1363 "precision highp float; \n"
1364 "#ifdef GL_FRAGMENT_PRECISION_HIGH \n"
1365 " precision highp int; \n"
1366 "#endif \n"
1367 );
1368 } else {
1369 sources.push_back(
1370 #ifdef GEO_OS_APPLE
1371 "#version 150 core \n"
1372 #else
1373 "#version 130 \n"
1374 #endif
1375 "#define GLUP_FRAGMENT_SHADER \n"
1376 );
1377 }
1378 }
1379
1380 void Context_ES2::get_toggles_pseudo_file(
1381 std::vector<GLSL::Source>& sources
1382 ) {
1383
1384 // In GLUPES2, the state is split into vertex shader
1385 // and fragment shader state. The toggles are in the fragment
1386 // shader state, therefore the vertex shader can only know the
1387 // statically defined toggles. For the other ones, we
1388 // "conservatively" assume that they are on (on the worst case,
1389 // this means the vertex shader will copy more attributes than
1390 // needed).
1391
1392 std::string toggle_maybe_enabled_source =
1393 "bool glupIsEnabled(in int toggle) {\n";
1394
1395 for(index_t i=0; i<uniform_state_.toggle.size(); ++i) {
1396 std::string toggle_var_name = uniform_state_.toggle[i].name();
1397 std::string toggle_name = toggle_var_name;
1398 size_t pos = toggle_name.find("_enabled");
1399 geo_assert(pos != std::string::npos);
1400 toggle_name = "GLUP_" +
1401 String::to_uppercase(toggle_name.substr(0,pos));
1402
1403 std::string test =
1404 " if(toggle=="+toggle_name + ") {\n";
1405
1406 toggle_maybe_enabled_source += test;
1407
1408 if(toggles_source_undetermined_ & (1 << i)) {
1409 toggle_maybe_enabled_source += " return true;\n";
1410 } else {
1411 if(toggles_source_state_ & (1 << i)) {
1412 toggle_maybe_enabled_source +=
1413 " return true;\n";
1414 } else {
1415 toggle_maybe_enabled_source +=
1416 " return false;\n";
1417 }
1418 }
1419 toggle_maybe_enabled_source += " }\n";
1420 }
1421
1422 toggle_maybe_enabled_source += " return false; \n}\n";
1423
1424 sources.push_back("#ifdef GLUP_VERTEX_SHADER\n");
1425 sources.push_back(toggle_maybe_enabled_source);
1426 sources.push_back("#else\n");
1427 Context::get_toggles_pseudo_file(sources);
1428 sources.push_back("#endif\n");
1429 }
1430 }
1431
1432 #endif
1433