FreeWRL / FreeX3D 4.3.0
Component_Lighting.c
1/*
2
3
4X3D Lighting Component
5
6*/
7
8/****************************************************************************
9 This file is part of the FreeWRL/FreeX3D Distribution.
10
11 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12
13 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25****************************************************************************/
26
27
28
29#include <config.h>
30#include <system.h>
31#include <display.h>
32#include <internal.h>
33
34#include <libFreeWRL.h>
35
36#include "../vrml_parser/Structs.h"
37#include "../main/headers.h"
38#include "../opengl/OpenGL_Utils.h"
39#include "../opengl/Frustum.h"
40#include "RenderFuncs.h"
41//#include "../opengl/OpenGL_Utils.h"
42#include "LinearAlgebra.h"
43#include "Polyrep.h"
44#include "Component_Shape.h"
45
46typedef struct pComponent_Lighting {
47 Stack* genshadow_stack;
48 Stack* light_stack;
49}*ppComponent_Lighting;
50
51static void* Component_Lighting_constructor() {
52 void* v = MALLOCV(sizeof(struct pComponent_Lighting));
53 memset(v, 0, sizeof(struct pComponent_Lighting));
54 return v;
55}
56
57// iglobal.c loves to call this one.
58void Component_Lighting_init(struct tComponent_Lighting* t) {
59 //public
60 //private
61 t->prv = Component_Lighting_constructor();
62 {
63 ppComponent_Lighting p = (ppComponent_Lighting)t->prv;
64 p->genshadow_stack = newStack(usehit);
65 p->light_stack = newStack(usehit);
66 }
67}
68
69// iglobal.c loves to call this one.
70void Component_Lighting_clear(struct tComponent_Lighting* t) {
71 //public
72 //private
73 {
74 ppComponent_Lighting p = (ppComponent_Lighting)t->prv;
75 deleteVector(usehit, p->genshadow_stack);
76 deleteVector(usehit, p->light_stack);
77 }
78}
79
80//ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
81void get_view_matrix(double* savePosOri, double* saveView);
82void set_debug_quad_near_farplane(float nearplane, float farplane);
83void lightTable_clear();
84void lightTable_push(usehit tuple);
85void lightTable_pop();
86int lightTable_count();
87usehit* lightTable_item(int i);
88void generate_shadowmap_2D(usehit uhit, int index);
89int make_or_get_depth_buffer(int index, struct X3D_Node* node);
90
91
92//LIGHT TABLE
93void lightTable_clear() {
94 //called once per frame, before the search for global=true projectors
95 //will clear any global=true projectors from last frame
96 ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
97 clearStack(p->light_stack);
98}
99void lightTable_push(usehit tuple) {
100 //called when we find a global=true, on=true light and
101 //called in sib_prep for a global=false, on=true light
102 ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
103 stack_push(usehit, p->light_stack, tuple);
104
105}
106void lightTable_pop() {
107 //called in sib_fin for a global=false, on=true light
108 ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
109 if (p->light_stack->n < 1)
110 printf("ouch from lightTable_pop()\n");
111 stack_pop(usehit, p->light_stack);
112}
113int lightTable_count() {
114 ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
115 return p->light_stack->n;
116}
117usehit *lightTable_item(int i) {
118 ppComponent_Lighting p = (ppComponent_Lighting)gglobal()->Component_Lighting.prv;
119 return vector_get_ptr(usehit,p->light_stack,i);
120}
121int lightTable_node_use_count(struct X3D_Node *node) {
122 int count = 0;
123 for (int i = 0; i < lightTable_count(); i++) {
124 if (lightTable_item(i)->node == node) count++;
125 }
126 return count;
127}
128
129
130// a specialization of InternalRep - see PolyRep.h
132 int itype; //=5, 0 PointRep 1 LineRep 2 PolyRep 3 MeshRep 4 TextureRep 5 LightRep 6 ProjectorRep
133 //depth section
134 Stack* depth_buffer_stack;
135 int size;
136 double matproj[16];
137 double matview[16];
138 //light section
139 void* lightbuf;
140 int ilightbuf; //opengl uniform buffer index
141
142};
143
144void* set_LightRep(void* _lightrep)
145{
146 struct X3D_LightRep* lightrep = _lightrep;
147 if (!_lightrep) {
148 _lightrep = MALLOC(struct X3D_LightRep*, sizeof(struct X3D_LightRep));
149 memset(_lightrep, 0, sizeof(struct X3D_LightRep));
150 lightrep = (struct X3D_LightRep*)_lightrep;
151 lightrep->itype = 5;
152 lightrep->ilightbuf = -1;
153 lightrep->size = 1024; //size of shadow image, or for pointlight, size of each of 6 sides of cubemap
154 //lightrep->idepthtexture = -1;
155 }
156 return lightrep;
157}
158void delete_LightRep(void* _lightrep) {
159 //call during node deletion > unRegisterX3DAnyNode > delete_geomrep
160 if (_lightrep) {
161 struct X3D_LightRep* lightrep = _lightrep;
162 if (lightrep->depth_buffer_stack) {
163 //Q. are the texture nodes it points to registered and deleted separately?
164 // here we assume so
165 deleteStack(struct X3D_Node*, lightrep->depth_buffer_stack);
166 }
167 FREE_IF_NZ(_lightrep);
168 }
169}
170
171// PointLight Shadowmap phases/stages
172// cubemap Stage I - creating empty cubemap and 6 side textures and fbo
173// cubemap Stage II - rendering the 6 sides of cubemap to fill with images
174// cubemap Stage III - sending to shader for sampling cubemap
175//GCM generated cube map texture method:
176// - Stage II uses 6 individual textures
177//non-GCM method:
178// - uses cubemap in Stage II, specificie which side is attached to FBO for rendering
179static int gcm_method_used = 0;
180int gcm_method() {
181 return gcm_method_used;
182}
183
184#define RETURN_IF_LIGHT_STATE_NOT_US \
185 if (renderstate()->render_light== VF_globalLight) { \
186 if (!node->global) return;\
187 /* printf ("and this is a global light\n"); */\
188 } else if (node->global || renderstate()->render_depth || !renderstate()->render_geom ) return; \
189 /* else printf ("and this is a local light\n"); */
190
191
192void projPerspective(double fovy, double aspect, double zNear, double zFar, double* matrix);
193void projOrtho(double left, double right, double bottom, double top,
194 double nearZ, double farZ, double* matrix);
195void projLookAt(double eyex, double eyey, double eyez,
196 double centerx, double centery, double centerz,
197 double upx, double upy, double upz, double* matrix);
198double* matrix_lookAtd(double* eye3, double* center3, double* up3, double* matrix) {
199 //gluLookAt convention:
200 // eye - the viewpoint
201 // center - any point along the ray to the scene, typically a point on the geometry in the scene to look at
202 // up - which way is up in the viewing volume
203 projLookAt(eye3[0], eye3[1], eye3[2], center3[0], center3[1], center3[2], up3[0], up3[1], up3[2], matrix);
204 return matrix;
205}
206double* matrix_lookAtfd(float* eye3, float* center3, float* up3, double* matrix) {
207 double eyed[3], centerd[3], upd[3];
208 float2double(eyed, eye3, 3);
209 float2double(centerd, center3, 3);
210 float2double(upd, up3, 3);
211 matrix_lookAtd(eyed, centerd, upd, matrix);
212 return matrix;
213}
214void compile_DirectionalLight (struct X3D_DirectionalLight *node) {
215 struct point_XYZ vec;
216
217
218 MARK_NODE_COMPILED;
219}
220void mesa_Ortho(GLDOUBLE left, GLDOUBLE right, GLDOUBLE bottom, GLDOUBLE top, GLDOUBLE nearZ, GLDOUBLE farZ, GLDOUBLE* m);
221void render_DirectionalLight0(struct X3D_Node* parent, struct X3D_DirectionalLight* node) {
222
223 // if we are doing global lighting, is this one for us?
224 RETURN_IF_LIGHT_STATE_NOT_US
225
226 COMPILE_IF_REQUIRED;
227
228 if (node->on) {
229 //both global on VF_GlobalLight on children->render, and local on prep_sibAffectors come in here
230 usehit uhit;
231 uhit.node = X3D_NODE(node);
232 uhit.userdata = parent;
233 fw_glGetDoublev(GL_MODELVIEW_MATRIX, uhit.mvm);
234 if (node->shadows) {
235 //prepare local view matrix (from node.location, node.direction which aren't included in modelview matrix)
236 // and projection matrix, both of which are stable / same between DEF and USE instances of a directionallight
237 node->_intern = set_LightRep(node->_intern);
238 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
239 //lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
240 //for up vector in theory we need a few cross products to ensure its at least orthogonal to direction
241 //- up is somewhat arbitrary -spotlight is symmetrical about direction vector-
242 // but must be consistent between depth texture rendering and shader sampling
243 float up[3], center[3], location[3];
244 vecset3f(up, 0.0f, 1.0f, 0.0f);
245 vecset3f(location, 0.0f, 0.0f, 0.0f);
246 vecadd3f(center, location, node->direction.c);
247 matrix_lookAtfd(location, center, up, lightrep->matview);
248
249 //for ortho, we will scale in render_directionalLight to parent extent
250 //so shadow map covers siblings affected
251 float eout6[6], ein6[6];
252 double ed[6];
253 if (node->global) {
254 extent6f_copy(ein6, rootNode()->_extent);
255 }
256 else {
257 extent6f_copy(ein6, parent->_extent);
258 }
259 if (!extent6f_isSet(ein6) ) {
260 float e6[6], scale3[3];
261 extent6f_constructor(e6, -1.0f, 1.0f, -1.0f, 1.0f, .1f, 15.0f); //something for the first frame
262 extent6f_scale3f(eout6, e6, vecset3f(scale3, 8.0f, 4.0f, 1.0f));
263 }
264 else {
265 float e6[6], scale3[3];
266 extent6f_mattransform4d(e6, ein6, lightrep->matview);
267 extent6f_scale3f(eout6, e6, vecset3f(scale3, 1.01f, 1.01f, 1.01f));
268
269 }
270 int method = 1;
271 if (method == 0) {
272 //extent6f_printf(eout6); printf("e6\n");
273 float2double(ed, eout6, 6);
274 //projOrtho(ed[1],ed[0],ed[3],ed[2], .1, ed[4], lightrep->matproj); only half or 1/4 the area
275 mesa_Ortho(ed[1], ed[0], ed[3], ed[2], .1, ed[4], lightrep->matproj);
276
277 set_debug_quad_near_farplane(eout6[4], eout6[5]);
278 }
279 else if (method == 1) {
280 //convert extent in lightview space into an equivalent transform
281 //and transform it back to light space, to append to mvm
282 float size[3];
283 double mate[16], matel[16], dcenter[3], dsize[3], matviewInv[16], mvm2[16], matev[16], matevinv[16];
284 extent6f2bbox(eout6, center, size);
285 //extent6f_printf(eout6); printf("extent in lightview space\n");
286 //vecprint3fb("center", center, "\n");
287 //vecprint3fb("size", size, "\n");
288 matidentity4d(mate);
289 mattranslate4d(mate, float2double(dcenter, center, 3));
290 matscale4d(mate, float2double(dsize, size, 3));
291 //printmatrix2(mate, "center and size mat in lightview space");
292
293 //printmatrix2(matevinv, "center and size in light space");
294 matcopy(uhit.extra, mate);
295 //do the following in generate_shadowmap_2D and shadow section of sendLightInfo2, with mate = uhit.extra
296 // matmultiplyAFFINE(mvm2, uhit.extra, uhit.mvm);
297 //matcopy(uhit.mvm, mvm2);
298 mesa_Ortho(-.5,.5,-.5,.5, .001,.5, lightrep->matproj);
299
300 set_debug_quad_near_farplane(.0, .5);
301
302 }
303 int nuse = lightTable_node_use_count(X3D_NODE(node));
304 uhit.ivalue = make_or_get_depth_buffer(nuse, X3D_NODE(node));
305 generate_shadowmap_2D(uhit, 0);
306
307 }
308 lightTable_push(uhit);
309 }
310}
311void render_DirectionalLight(struct X3D_DirectionalLight* node) {
312 render_DirectionalLight0(NULL, node);
313}
314/* global lights are done before the rendering of geometry */
315void prep_DirectionalLight (struct X3D_DirectionalLight *node) {
316 if (!renderstate()->render_light) return;
317 render_DirectionalLight(node);
318}
319
320static struct X3D_DirectionalLight* headlight = NULL;
321void push_headlight() {
322 if (!headlight) {
323 headlight = createNewX3DNode(NODE_DirectionalLight);
324 compile_DirectionalLight(headlight);
325 vecset3f(headlight->direction.c, 0.0f, 0.0f, -1.0f);
326 headlight->global = TRUE;
327 headlight->on = TRUE;
328 }
329 {
330 // like render_DirectionalLight(headlight);
331 // except identity mvm (headlight pose is identity in avatar coordinates)
332 // and no compile_shadowMap / no shadowmaps for headlight (shadows would be obscured anyway)
333 struct X3D_DirectionalLight* node =headlight;
334 RETURN_IF_LIGHT_STATE_NOT_US
335 usehit uhit;
336 uhit.node = X3D_NODE(headlight);
337 matidentity4d(uhit.mvm);
338 lightTable_push(uhit);
339 }
340}
341void render_headlight() {
342 //how set renderflags so it knows its VF_GlobalLight?
343 //call from render_hier on VF_globalLight pass, before scenegraph render
344 if (fwl_get_headlight()) //checks if Viewer requests headlight
345 push_headlight();
346}
347
348
349void compile_PointLight(struct X3D_PointLight* node) {
350 if (node->shadows) {
351 //prepare local view matrix (from node.location, which isn't included in modelview matrix)
352 // and projection matrix, both of which are stable / same between DEF and USE instances of a Pointlight
353 node->_intern = set_LightRep(node->_intern);
354 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
355 set_debug_quad_near_farplane(.5f, node->radius);
356
357 //lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
358 //for up vector in theory we need a few cross products to ensure its at least orthogonal to direction
359 //- up is somewhat arbitrary -spotlight is symmetrical about direction vector-
360 // but must be consistent between depth texture rendering and shader sampling
361 float up[3], direction[3], center[3];
362 vecset3f(up, 0.0f, 1.0f, 0.0f);
363 vecset3f(direction, 0.0f, 0.0f, 1.0f);
364 vecadd3f(center, node->location.c, direction);
365 matrix_lookAtfd(node->location.c, center, up, lightrep->matview);
366 matidentity4d(lightrep->matproj);
367 }
368 MARK_NODE_COMPILED;
369}
370
371void generate_shadowmap_cube(usehit uhit, int index);
372void render_PointLight0(struct X3D_Node* parent, struct X3D_PointLight* node) {
373 float ft;
374
375 /* if we are doing global lighting, is this one for us? */
376 RETURN_IF_LIGHT_STATE_NOT_US
377
378 COMPILE_IF_REQUIRED;
379
380 if (node->on) {
381 //both global on VF_GlobalLight on children->render, and local on prep_sibAffectors come in here
382 usehit uhit;
383 uhit.node = X3D_NODE(node);
384 uhit.userdata = parent;
385 fw_glGetDoublev(GL_MODELVIEW_MATRIX, uhit.mvm);
386 if (node->shadows) {
387 int nuse = lightTable_node_use_count(X3D_NODE(node));
388 uhit.ivalue = make_or_get_depth_buffer(nuse, X3D_NODE(node));
389 generate_shadowmap_cube(uhit, uhit.ivalue);
390 /* Finished rendering CubeMap, set it back for normal textures */
391 getAppearanceProperties()->cubeFace = 0;
392
393
394 }
395 lightTable_push(uhit);
396 }
397}
398void render_PointLight(struct X3D_PointLight* node) {
399 render_PointLight0(NULL, node);
400}
401/* pointLights are done before the rendering of geometry */
402void prep_PointLight (struct X3D_PointLight *node) {
403
404 if (!renderstate()->render_light) return;
405 /* this will be a global light here... */
406 render_PointLight(node);
407}
408
409
410/*
411// NOT IMPLEMENTED as of June 2022, would buffer lights so don't send every child_shape
412//UNIFORM BUFFER
413//https ://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)
414//-about ¾ down Buffer backed
415//https ://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
416//-25 % down Interface blocks > Uniform blocks, Lights mentioned.
417
418static int light_buf = 0;
419int light_buffering() {
420 return light_buf;
421}
422struct Lightbuf {
423 //std140 padding required so GPU padding using std140 is aligned
424 //static part of light, stored in gl buffer
425 float color[4]; //padded so consistently ends on vec4 boundary on CPU side, can read as vec3 on GPU side
426 float location[4];
427 float halfVector[4];
428 float direction[4];
429 float attenuations[4];
430 float matview[16]; //contains .location and .direction +up please transpose for shader
431 float matproj[16]; //shadow map > contains frustum projection, please transpose for shader
432 float ambient; //the rest take up 4 bytes each on GPU and CPU, no padding needed except at end
433 float intensity;
434 float spotBeamWidth;
435 float spotCutoff;
436 float lightRadius;
437 float shadowIntensity;
438 bool shadows;
439 int depthmap;
440 int lighttype;
441 int pad1;
442 int pad2;
443 int pad3;
444};
445static int lightpose_buf = 0;
446int lightpose_buffering() {
447 return lightpose_buf;
448}
449struct LightPose {
450 //std140 padding required so GPU padding using std140 is aligned
451 //dynamic part of light, specifically modelview matrix aka visit transform
452 // can be different for each render_ visit
453 // --can have multiple DEF/USE references to a light, or parent can be DEF/USEd--
454 // and typically re-sent to shader every child_shape affected, unless buffered
455 // lifespan of a visit transform is maximum 1 frame, so when lightTable cleared, should be cleared.
456 float modelview[16]; //visit transform: to transform .location .direction into viewpoint / eye space
457 float eye2frustum[16]; //for shadow maps, transforms from viewpoint to shadow map frustum
458 int lightbuf; //opengl uniform buffer index pointing to a struct Lightbuf
459 int lightbufIndex; //index of bound LightBufs in shader.
460 int pad2;
461};
462*/
463void compile_SpotLight (struct X3D_SpotLight *node) {
464 if (node->shadows) {
465 //prepare local view matrix (from node.location, node.direction which aren't included in modelview matrix)
466 // and projection matrix, both of which are stable / same between DEF and USE instances of a spotlight
467 node->_intern = set_LightRep(node->_intern);
468 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
469 projPerspective(node->cutOffAngle * (180.0 / PI) * 2.0, 1.0, .5, (double)node->radius, lightrep->matproj);
470 set_debug_quad_near_farplane(.5f, node->radius);
471
472 //for up vector in theory we need a few cross products to ensure its at least orthogonal to direction
473 //- up is somewhat arbitrary -spotlight is symmetrical about direction vector-
474 // but must be consistent between depth texture rendering and shader sampling
475 float up[3], center[3];
476 vecset3f(up, 0.0f, 1.0f, 0.0f);
477 vecadd3f(center, node->location.c, node->direction.c);
478 matrix_lookAtfd(node->location.c, center, up, lightrep->matview);
479 }
480 /* NOT IMPLEMENTED as of June 2022
481 if (light_buffering()) {
482 //static / infrequently changing part of light
483 //.on and .global aren't included, they are implied in the list sent in sendLightInfo() to shader
484 //modelview matrix isn't included: DEF/USE instances each have a different LightPose
485 node->_intern = set_LightRep(node->_intern);
486 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
487 if (!lightrep->lightbuf) {
488 lightrep->lightbuf = malloc(sizeof(struct Lightbuf));
489 memset(lightrep->lightbuf, 0, sizeof(struct Lightbuf));
490 }
491 struct Lightbuf* lightbuf = (struct Lightbuf*)lightrep->lightbuf;
492 //fill out lightbuf from node fields
493 veccopy3f(lightbuf->color, node->color.c);
494 veccopy3f(lightbuf->location, node->location.c);
495 veccopy3f(lightbuf->direction, node->direction.c);
496 veccopy3f(lightbuf->attenuations, node->attenuation.c);
497 lightbuf->ambient = node->ambientIntensity;
498 lightbuf->intensity = node->intensity;
499 lightbuf->spotBeamWidth = node->beamWidth;
500 lightbuf->spotCutoff = node->cutOffAngle;
501 lightbuf->lightRadius = node->radius;
502 lightbuf->shadows = node->shadows;
503 lightbuf->lighttype = 1; //0 point 1 spot 2 direction
504 //if shadows, update proj and view matx
505 if (lightbuf->shadows) {
506 double mtrans[16];
507 mattranspose(mtrans, lightrep->matview);
508 double2float(lightbuf->matview, mtrans,16);
509 mattranspose(mtrans, lightrep->matproj);
510 double2float(lightbuf->matproj, mtrans,16);
511 }
512 if (lightrep->ilightbuf < 0)
513 glGenBuffers(1, &lightrep->ilightbuf);
514 glBindBuffer(GL_UNIFORM_BUFFER, lightrep->ilightbuf);
515 int bufsize = sizeof(struct Lightbuf);
516 glBufferData(GL_UNIFORM_BUFFER, bufsize, lightbuf, GL_STATIC_DRAW); // allocate 152 bytes of memory
517 glBindBuffer(GL_UNIFORM_BUFFER, 0);
518
519 }
520 */
521 MARK_NODE_COMPILED;
522}
523
524
525void render_SpotLight0(struct X3D_Node *parent, struct X3D_SpotLight *node) {
526 float ft;
527
528 /* if we are doing global lighting, is this one for us? */
529 RETURN_IF_LIGHT_STATE_NOT_US
530
531 COMPILE_IF_REQUIRED;
532
533 if(node->on) {
534 //both global on VF_GlobalLight on children->render, and local on prep_sibAffectors come in here
535 usehit uhit;
536 uhit.node = X3D_NODE(node);
537 uhit.userdata = parent;
538 fw_glGetDoublev(GL_MODELVIEW_MATRIX, uhit.mvm);
539 if (node->shadows) {
540 int nuse = lightTable_node_use_count(X3D_NODE(node));
541 //shadowTable_push(uhit);
542 //render_shadowMap(X3D_NODE(node));
543 uhit.ivalue = make_or_get_depth_buffer(nuse,X3D_NODE(node));
544 generate_shadowmap_2D(uhit, 0);
545
546 }
547 lightTable_push(uhit);
548 /* NOT IMPLEMENTED as of June 2022
549 if (lightpose_buffering()) {
550 //dynamic maximum lifespan 1 frame buffering of light visit transform
551 //so (sibling or global) affected shapes can share common LightPose rather than resending on every child_shape
552 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
553 float w2l[16];
554 //following textureProjector
555 double modelviewinv[16], eye2projector[16], matfull[16], mtrans[16];
556 matinverse(modelviewinv, uhit.mvm);
557 matmultiplyAFFINE(eye2projector, modelviewinv, lightrep->matview);
558 matmultiplyFULL(matfull, eye2projector, lightrep->matproj);
559 double2float(w2l, matfull, 16);
560 struct LightPose pose;
561 mattranspose(mtrans, matfull);
562 double2float(pose.eye2frustum, mtrans, 16);
563 mattranspose(mtrans, uhit.mvm);
564 double2float(pose.modelview, mtrans, 16);
565 pose.lightbuf = lightrep->ilightbuf;
566 //push_heavy should manage a reusable list of uniform buffers
567 // - the size of maximum light visits per frame
568 // - and refresh buffer contents during a push (and ignoring old contents on pop or lightTable_clear())
569 // lightTable_push_heavy(uhit, &pose); //not yet implemented
570 }
571 */
572 }
573}
574void render_SpotLight(struct X3D_SpotLight* node) {
575 render_SpotLight0(NULL, node);
576}
577/* SpotLights are done before the rendering of geometry */
578void prep_SpotLight (struct X3D_SpotLight *node) {
579 if (!renderstate()->render_light) return;
580 render_SpotLight(node);
581}
582
583void compile_EnvironmentLight(struct X3D_EnvironmentLight * node){
584}
585void render_EnvironmentLight(struct X3D_EnvironmentLight * node){
586}
587void prep_EnvironmentLight(struct X3D_EnvironmentLight * node){
588}
589
590
591void sib_prep_Light(struct X3D_Node* parent, struct X3D_Node* sibAffector);
592void sib_fin_Light(struct X3D_Node* parent, struct X3D_Node* sibAffector);
593void sib_prep_Light(struct X3D_Node* parent, struct X3D_Node* sibAffector) {
594 struct X3D_PointLight* light = X3D_POINTLIGHT(sibAffector);
595 if (renderstate()->render_light != VF_globalLight && !renderstate()->render_depth && renderstate()->render_geom) {
596 if (light->global == FALSE && light->on == TRUE) {
597 //should lightTable_push(usehit):
598 switch (light->_nodeType) {
599 case NODE_SpotLight:
600 render_SpotLight0(parent,(struct X3D_SpotLight*)sibAffector);
601 break;
602 case NODE_PointLight:
603 render_PointLight0(parent,(struct X3D_PointLight*)sibAffector);
604 break;
605 case NODE_DirectionalLight:
606 render_DirectionalLight0(parent,(struct X3D_DirectionalLight*)sibAffector);
607 break;
608 default:
609 break;
610 }
611 }
612 }
613}
614void sib_fin_Light(struct X3D_Node* parent, struct X3D_Node* sibAffector) {
615 if (renderstate()->render_light != VF_globalLight && !renderstate()->render_depth && renderstate()->render_geom) {
616 struct X3D_PointLight* light = X3D_POINTLIGHT(sibAffector);
617 if(light->global == FALSE && light->on == TRUE)
618 lightTable_pop();
619 }
620}
621
622// BORROWED FROM GENERATEDCUBEMAPTEXTURE AND MODIFIED FOR DEPTH CUBEMAP FOR POINTLIGHT >>>
623//
624// see https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping
625// for Phase I generate shadow map and Phase II use shadow map
626//
627
628#include "Component_Shape.h"
629#include "../opengl/Textures.h"
630void pushnset_framebuffer(int ibuffer);
631void popnset_framebuffer();
632
633#ifdef GL_DEPTH_COMPONENT32
634#define FW_GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT32
635#else
636#define FW_GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT16
637#endif
638int haveFrameBufferObject();
639
640
641
642void freeASCIIString(struct Uni_String* us);
643
644
645struct X3D_Node * make_depth_buffer(int width, int height) {
646 struct X3D_PixelTexture* tex = (struct X3D_PixelTexture*)createNewX3DNode(NODE_PixelTexture);;
647 PRINT_GL_ERROR_IF_ANY("make_depth_buffer START");
648 //if (tex->__subTextures.n == 0)
649 {
650
651 int i;
652 struct textureTableIndexStruct* tti;
653
654 tti = getTableIndex(tex->__textureTableIndex);
655 tti->status = TEX_LOADED; // TEX_NEEDSBINDING; //I found I didn't need - yet
656 tti->idepthbuffer = 1;
657 tti->x = width;
658 tti->y = height;
659 //tti->z = 6;
660// loadTextureNode(X3D_NODE(tex), NULL);
661 if (tti->ifbobuffer == 0 && haveFrameBufferObject()) {
662 int j;
663 tti->x = width; //by storing and retrieving initial size from here
664 tti->y = height;
665 tti->z = 1;
666 // https://www.opengl.org/wiki/Framebuffer_Object
667
668 glGenFramebuffers(1, &tti->ifbobuffer);
669 pushnset_framebuffer(tti->ifbobuffer); //binds framebuffer. we push here, in case higher up we are already rendering the whole scene to an fbo
670
671 glGenTextures(1, &tti->OpenGLTexture);
672 glBindTexture(GL_TEXTURE_2D, tti->OpenGLTexture);
673 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
674 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
675 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
676 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
677 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
678
679 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tti->OpenGLTexture, 0);
680 glDrawBuffer(GL_NONE);
681 glReadBuffer(GL_NONE);
682 popnset_framebuffer(); //tti->ifbobuffer);
683 }
684
685 }
686 PRINT_GL_ERROR_IF_ANY("make_depth_buffer END");
687
688
689 return X3D_NODE(tex);
690
691}
692
693
694struct X3D_Node* make_depth_buffer_cube(int width, int height) {
695 //called once per program run for each PointLight scenegraph USE / visit
696 //its a 'dynamic cubemap' like GeneratedCubeMapTexture
697 // that means we'll be re-rendering and re-submitting each of the 6 textures once per frame
698 // so need access.
699 // cubemap Stage I
700 struct X3D_GeneratedCubeMapTexture *cubetex = createNewX3DNode(NODE_GeneratedCubeMapTexture);
701 PRINT_GL_ERROR_IF_ANY("make_depth_buffer_cube START");
702 //if (cubetex->__subTextures.n == 0)
703 {
704
705 int i;
706 struct textureTableIndexStruct* tti;
707 tti = getTableIndex(cubetex->__textureTableIndex);
708 tti->status = TEX_LOADED; // I found I didn't need - yet TEX_NEEDSBINDING; //
709 tti->x = width;
710 tti->y = height;
711 tti->z = 1;
712 tti->idepthbuffer = 1;
713 GLuint status;
714 if (tti->ifbobuffer == 0 && haveFrameBufferObject()) {
715 int j;
716 // https://www.opengl.org/wiki/Framebuffer_Object
717 // https://learnopengl.com/Advanced-OpenGL/Cubemaps - doesn't show 'dynamic' cubemaps, but create with images, not renderbuffers, so can sample in shader
718 // https://learnopengl.com/Advanced-OpenGL/Framebuffers
719
720 {
721 glGenTextures(1, &tti->OpenGLTexture);
722 glBindTexture(GL_TEXTURE_CUBE_MAP, tti->OpenGLTexture);
723 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
724 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
725 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
726 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
727 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
728 // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml
729 for (size_t i = 0; i < 6; ++i) {
730 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
731 }
732 glGenFramebuffers(1, &tti->ifbobuffer);
733 pushnset_framebuffer(tti->ifbobuffer); //binds framebuffer. we push here, in case higher up we are already rendering the whole scene to an fbo
734 PRINT_GL_ERROR_IF_ANY("make_depth_buffer_cube 1");
735 glDrawBuffer(GL_NONE);
736 glViewport(0, 0, width, height);
737
738 //bind one tex now for fun, and to check FBO completeness, but will bind in iteration loop during depth rendering generate_shadowmap_cube
739 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + 0, tti->OpenGLTexture, 0);
740 status = glCheckNamedFramebufferStatus(tti->ifbobuffer, GL_FRAMEBUFFER);
741
742 popnset_framebuffer(); //tti->ifbobuffer);
743 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
744
745
746 }
747
748 // https://www.khronos.org/opengl/wiki/Framebuffer_Object#Framebuffer_Completeness
749 // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCheckFramebufferStatus.xhtml
750 status = glCheckNamedFramebufferStatus(tti->ifbobuffer, GL_FRAMEBUFFER);
751 if (status != GL_FRAMEBUFFER_COMPLETE) {
752 printf("make_depth_buffer_cube: framebuffer not complete\n");
753 switch (status) {
754 case GL_FRAMEBUFFER_UNDEFINED:
755 printf("GL_FRAMEBUFFER_UNDEFINED\n"); break;
756 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
757 printf("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\n"); break;
758 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
759 printf("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\n"); break;
760 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
761 printf("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER\n"); break;
762 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
763 printf("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER\n"); break;
764 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
765 printf("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE\n"); break;
766 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
767 printf("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS\n"); break;
768 case GL_FRAMEBUFFER_UNSUPPORTED:
769 printf("GL_FRAMEBUFFER_UNSUPPORTED\n"); break;
770 default:
771 printf("unknown GL error %u\n", (unsigned int)status); break;
772 }
773 }
774 }
775 }
776 PRINT_GL_ERROR_IF_ANY("make_depth_buffer_cube END");
777
778 // tell the whole system to re-create the data for these sub-children
779 //node->__regenSubTextures = TRUE;
780
781 return X3D_NODE(cubetex);
782
783}
784int make_or_get_depth_buffer(int index, struct X3D_Node* node) {
785 node->_intern = set_LightRep(node->_intern);
786 struct X3D_LightRep* lightrep = (struct X3D_LightRep* )node->_intern;
787 if (!lightrep->depth_buffer_stack) {
788 lightrep->depth_buffer_stack = newStack(struct X3D_Node*);
789 }
790 if (index > -1 && index < vectorSize(lightrep->depth_buffer_stack)) return index;
791 struct X3D_Node* depth_buffer_texture = NULL;
792 if(node->_nodeType == NODE_PointLight)
793 depth_buffer_texture = make_depth_buffer_cube(lightrep->size, lightrep->size);
794 else
795 depth_buffer_texture = make_depth_buffer(lightrep->size, lightrep->size);
796 stack_push(struct X3D_Node*, lightrep->depth_buffer_stack, X3D_NODE(depth_buffer_texture));
797 return vectorSize(lightrep->depth_buffer_stack) - 1;
798}
799
800
801
802
803// https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping
804// shows rendering of shadow maps, and debug quad rendering
805// renderQuad() renders a 1x1 XY quad in NDC
806// -----------------------------------------
807static unsigned int quadVAO = 0;
808static unsigned int quadVBO;
809void renderQuad()
810{
811 if (quadVAO == 0)
812 {
813 float quadVertices[] = {
814 // positions // texture Coords
815 -.8f, .8f, 0.0f, 0.0f, 1.0f,
816 -.8f, -.8f, 0.0f, 0.0f, 0.0f,
817 .8f, .8f, 0.0f, 1.0f, 1.0f,
818 .8f, -.8f, 0.0f, 1.0f, 0.0f,
819 };
820 // setup plane VAO
821 glGenVertexArrays(1, &quadVAO);
822 glGenBuffers(1, &quadVBO);
823 glBindVertexArray(quadVAO);
824 glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
825 glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
826 glEnableVertexAttribArray(0);
827 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
828 glEnableVertexAttribArray(1);
829 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
830 }
831 glBindVertexArray(quadVAO);
832 PRINT_GL_ERROR_IF_ANY("render_quad before glDrawArrays");
833 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
834 PRINT_GL_ERROR_IF_ANY("render_quad after glDrawArrays");
835 glBindVertexArray(0);
836}
837static struct debug_quad {
838 int textureID; // opengl texture, -1 for no texture
839 int which_debug_shader;
840 float near_plane, far_plane;
841} debug_quad = { -1,0,1.0f,15.0f };
842void set_debug_quad(int which_debug_shader, int textureID) {
843 // which_debug_shader - flag to indicate which quad shader
844 // 0=turn off debug quad
845 // 1-normal texture
846 // 2=ortho depth
847 // 3=perspective depth
848 // 4=cubemap color
849 // 5=cubemap depth latitude, longitude method
850 // 6=cubemap depth Tee method
851 // textureID - opengl texture number
852 debug_quad.textureID = textureID;
853 debug_quad.which_debug_shader = which_debug_shader;
854}
855void set_debug_quad_near_farplane(float nearplane, float farplane) {
856 debug_quad.near_plane = nearplane;
857 debug_quad.far_plane = farplane;
858}
859void render_debug_quad() {
860 //call this routinely at the end of main scene render() before swapBuffers
861 // if no debug_request just returns, else renders a quad over any rendered scene
862 int ia;
863 if (debug_quad.textureID < 0)return;
864 PRINT_GL_ERROR_IF_ANY("render_debug_quad START");
865 s_shader_capabilities_t* scap;
866 shaderflagsstruct shader_requirements;
867 memset(&shader_requirements, 0, sizeof(shaderflagsstruct));
868 shader_requirements.debug = debug_quad.which_debug_shader;
869 scap = getMyShaders(shader_requirements);
870 enableGlobalShader(scap);
871 if (debug_quad.which_debug_shader > 2 && debug_quad.which_debug_shader < 5 || debug_quad.which_debug_shader == 6) {
872 ia = glGetUniformLocation(scap->myShaderProgram, "near_plane");
873 glUniform1f(ia, debug_quad.near_plane);
874 ia = glGetUniformLocation(scap->myShaderProgram, "far_plane");
875 glUniform1f(ia, debug_quad.far_plane);
876 }
877 ia = glGetUniformLocation(scap->myShaderProgram, "textureUnit");
878 glUniform1i(ia, 0);
879 PRINT_GL_ERROR_IF_ANY("render_debug_quad before ActiveTexture");
880
881 glActiveTexture(GL_TEXTURE0);
882 PRINT_GL_ERROR_IF_ANY("render_debug_quad before enable CUBE_MAP");
883
884 if (debug_quad.which_debug_shader > 3 && debug_quad.which_debug_shader < 7) {
885 PRINT_GL_ERROR_IF_ANY("render_debug_quad before bind CUBE_MAP");
886 glBindTexture(GL_TEXTURE_CUBE_MAP, debug_quad.textureID);
887 }
888 else
889 glBindTexture(GL_TEXTURE_2D, debug_quad.textureID);
890 PRINT_GL_ERROR_IF_ANY("render_debug_quad before renderQuad");
891
892 renderQuad();
893 PRINT_GL_ERROR_IF_ANY("render_debug_quad after renderQuad");
894
895 if (debug_quad.which_debug_shader > 3 && debug_quad.which_debug_shader < 7) {
896 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
897 }
898 PRINT_GL_ERROR_IF_ANY("render_debug_quad END");
899}
900//we'll do a different matrix rotation for each face, using sideangle struct:
901static struct {
902 double angle;
903 double x;
904 double y;
905 double z;
906} sideangle[6] = {
907//{ 90.0,0.0,1.0,0.0}, //+x
908//{-90.0,0.0,1.0,0.0}, //-x
909//{-90.0,1.0,0.0,0.0}, //+y weird but works
910//{ 90.0,1.0,0.0,0.0}, //-y "
911//{ 0.0,0.0,1.0,0.0}, //+z (lhs)
912//{180.0,0.0,1.0,0.0}, //-z
913
914{ 90.0,0.0,1.0,0.0}, //+x
915{-90.0,0.0,1.0,0.0}, //-x
916{ 90.0,1.0,0.0,0.0}, //+y weird but works
917{-90.0,1.0,0.0,0.0}, //-y "
918{ 0.0,0.0,1.0,0.0}, //+z (lhs)
919{180.0,0.0,1.0,0.0}, //-z
920};
921#define RADIANS_PER_DEGREE (double)0.0174532925199432957692
922#define DEGREES_PER_RADIAN (double)57.2957795130823208768
923
924void saveImage_web3dit(struct textureTableIndexStruct* tti, char* fname);
925void fw_gluPerspective_2(double xcenter, double fovy, double aspect, double zNear, double zFar);
926void pushnset_viewport(float* vpFraction);
927void popnset_viewport();
928void render_bound_background();
929
930// called from MainLoop.c
931#include "../x3d_parser/Bindable.h"
932void generate_shadowmap_cube(usehit uhit, int index) {
933 //call from render_PointLight or render_TextureProjectorPoint once per frame (its a so-called 'dynamic cubemap', like GeneratedCubeMapTexture)
934 // usehit - same as for lightTable / shared with lightTable which holds one usehit per scenegraph node visit
935 // -- so DEF/USE of light node will have 2+ entries in lightTable and 2+ shadowmaps
936 // -- currently fbo textures are persisted in Light node > LightRep > depth_buffer_stack
937 // -- index is index in that stack,
938 // and corresponds to scenegraph sequential visit number to light node on render pass
939 //assumes depth fbo buffer / texture already exists / created elsewhere / persistent storage
940 // set viewpoint pose at light node
941 // render parent>children sub-scene to fbo texture via render_hier2(parent,VF_Geom | VF_Depth)
942 // cubemap Stage II
943 double savebackmat[16];
944
945 int isize;
946 double modelviewmatrix[16];
947 textureTableIndexStruct_s* tti;
948 float vp[4] = { 0.0f,1.0f,0.0f,1.0f }; //arbitrary
949 struct X3D_PointLight* node;
950 struct X3D_LightRep* lightrep;
951 bindablestack* bstack;
952 ttglobal tg = gglobal();
953 bstack = getActiveBindableStacks(tg);
954
955 //set_viewmatrix();
956 //this function tampers with the normal background matrix, which has already been prepped for the mainloop rendering
957 //so save it, and restore after gencubemap loop of 6
958 memcpy(savebackmat, bstack->backgroundmatrix, 16 * sizeof(double));
959
960 node = (struct X3D_PointLight*)uhit.node;
961 float radius = 10.0f;
962 if (node->_nodeType == NODE_PointLight)
963 radius = ((struct X3D_PointLight*)node)->radius;
964 else if (node->_nodeType == NODE_TextureProjectorPoint)
965 radius = ((struct X3D_TextureProjectorPoint*)node)->farDistance;
966 lightrep = (struct X3D_LightRep*)node->_intern;
967 struct X3D_GeneratedCubeMapTexture* cubetex = (struct X3D_GeneratedCubeMapTexture*)vector_get(struct X3D_Node*, lightrep->depth_buffer_stack, uhit.ivalue);;
968 memcpy(modelviewmatrix, uhit.mvm, 16 * sizeof(double));
969
970 //compile_generatedcubemap - creates framebufferobject fbo
971 tti = getTableIndex(cubetex->__textureTableIndex);
972 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps before cube 6 loop");
973
974 pushnset_framebuffer(tti->ifbobuffer); //binds framebuffer. we push here, in case higher up we are already rendering the whole scene to an fbo
975 glDrawBuffer(GL_NONE);
976 glReadBuffer(GL_NONE);
977
978 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps after drawBuffers");
979
980 pushnset_viewport(vp); //something to push so we can pop-and-set below, so any mainloop GL_BACK viewport is restored
981 glViewport(0, 0, tti->x, tti->y); //viewport we want
982 FW_GL_MATRIX_MODE(GL_PROJECTION);
983 FW_GL_PUSH_MATRIX();
984 FW_GL_LOAD_IDENTITY();
985 FW_GL_MATRIX_MODE(GL_MODELVIEW);
986 FW_GL_PUSH_MATRIX();
987 FW_GL_LOAD_IDENTITY();
988
989 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps before loop 2");
990 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
991 printf("generate_shadowmap_cube: framebuffer not complete\n");
992 //create fbo or fbo tiles collection for generatedcubemap
993 //method: we draw each face to a single framebuffer texture,
994 // and readpixels back into 6 PixelTexture tti->texdata, so its a bit like ImageCubeMap except
995 // we skip the steps of creating and reading back PixelTexture->image.p into texdata
996 glEnable(GL_TEXTURE_GEN_S);
997 glEnable(GL_TEXTURE_GEN_T);
998 glEnable(GL_TEXTURE_GEN_R);
999
1000 glBindTexture(GL_TEXTURE_CUBE_MAP, tti->OpenGLTexture);
1001
1002 for (int j = 0; j < 6; j++) { //should be 6 cubetex->__subTextures.n
1003 textureTableIndexStruct_s* ttip;
1004 struct X3D_PixelTexture* nodep;
1005 GLuint pixelType;
1006 int bytesPerPixel;
1007 PRINT_GL_ERROR_IF_ANY("generate_cube shadow before glFramebufferTexture2D");
1008 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, tti->OpenGLTexture, 0);
1009 // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glFramebufferTexture.xhtml
1010 PRINT_GL_ERROR_IF_ANY("generate_cube shadow after glFramebufferTexture2D");
1011 FW_GL_CLEAR(GL_DEPTH_BUFFER_BIT);
1012 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps GL calls 1");
1013
1014 //set viewpoint matrix for side
1015 {
1016 double world2light[16], world2lightview[16], mvm[16];
1017 double savePosOri[16], saveView[16], viewmatrix[16], mvmInverse[16], matrotside[16], world2lightviewside[16];
1018 double matproj[16];
1019 get_view_matrix(savePosOri, saveView);
1020 matmultiplyAFFINE(viewmatrix, saveView, savePosOri);
1021 //printmatrix2(viewmatrix, "vp view matrix");
1022
1023 matcopy(mvm, uhit.mvm);
1024 matinverseAFFINE(mvmInverse, mvm);
1025 //printmatrix2(uhit.mvm, "uhit.mvm");
1026
1027 matmultiplyAFFINE(world2light, viewmatrix, mvmInverse); // = world2light[16]
1028 //printmatrix2(world2light, "world2light = viewmatrix x mvmInverse");
1029
1030 matmultiplyAFFINE(world2lightview, world2light, lightrep->matview);
1031 //printmatrix2(lightrep->matview, "lighrep.matview");
1032 matrotate(matrotside,RADIANS_PER_DEGREE * sideangle[j].angle, sideangle[j].x, sideangle[j].y, sideangle[j].z);
1033 matmultiplyAFFINE(world2lightviewside, world2lightview, matrotside);
1034 //printmatrix2(world2lightview, "world2lightview = lighrep.matview x world2light");
1035
1036 //fw_glSetDoublev(GL_PROJECTION_MATRIX, lightrep->matproj); //identity
1037 projPerspective(90.0, 1.0, .1, radius, matproj);
1038 fw_glSetDoublev(GL_PROJECTION_MATRIX, matproj);
1039 //printmatrix2(lightrep->matproj, "matproj");
1040
1041 fw_glSetDoublev(GL_MODELVIEW_MATRIX, world2lightviewside);
1042 }
1043 /* 4. Nodes (not the blended ones)*/
1044 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps before render_hier");
1045
1046 profile_start("hier_geom");
1047 render_hier2(rootNode(), VF_Geom | VF_Depth);
1048 profile_end("hier_geom");
1049 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps after render_hier");
1050
1051 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps after GL calls");
1052 if (j == 5) {
1053 if (0) {
1054 //console printf of last rendered side depth map
1055 static char* texdata = NULL;
1056 if (!texdata) {
1057 texdata = malloc(tti->x * tti->y * sizeof(float));
1058 memset(texdata, 0, tti->x * tti->y * sizeof(float));
1059 }
1060 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps cube in quad prep 0");
1061 glReadPixels(0, 0, tti->x, tti->y, GL_DEPTH_COMPONENT, GL_FLOAT, texdata);
1062 float* ftex = (float*)texdata;
1063 for (int ik = 0; ik < tti->y; ik += 128) {
1064 for (int jk = 0; jk < tti->x; jk += 128)
1065 printf("%4f ", ftex[ik * tti->x + jk]);
1066 printf("\n");
1067 }
1068 }
1069 if (0) {
1070 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps cube in quad prep 2");
1071 set_debug_quad_near_farplane(.1f, radius);
1072
1073 set_debug_quad(6, tti->OpenGLTexture);
1074 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps cube in quad prep 3");
1075
1076 }
1077 }
1078 }
1079 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
1080
1081 FW_GL_MATRIX_MODE(GL_PROJECTION);
1082 FW_GL_POP_MATRIX();
1083 FW_GL_MATRIX_MODE(GL_MODELVIEW);
1084 FW_GL_POP_MATRIX();
1085
1086 popnset_viewport();
1087 popnset_framebuffer();
1088
1089 memcpy(bstack->backgroundmatrix, savebackmat, 16 * sizeof(double));
1090}
1091
1092void PRINT_GL_ERROR(GLenum _global_gl_err) {
1093 if (_global_gl_err == GL_INVALID_ENUM) {printf ("GL_INVALID_ENUM"); }
1094 else if (_global_gl_err == GL_INVALID_VALUE) {printf ("GL_INVALID_VALUE"); }
1095 else if (_global_gl_err == GL_INVALID_OPERATION) {printf ("GL_INVALID_OPERATION"); }
1096 else if (_global_gl_err == GL_STACK_OVERFLOW) {printf ("GL_STACK_UNDERFLOW"); }
1097 else if (_global_gl_err == GL_STACK_UNDERFLOW) {printf ("GL_STACK_UNDERFLOW"); }
1098 else if (_global_gl_err == GL_OUT_OF_MEMORY) {printf ("GL_OUT_OF_MEMORY"); }
1099 else if (_global_gl_err == GL_INVALID_FRAMEBUFFER_OPERATION) {printf ("GL_INVALID_FRAMEBUFFER_OPERATION"); }
1100 else if (_global_gl_err == GL_CONTEXT_LOST) {printf ("GL_CONTEXT_LOST"); }
1101 else if (_global_gl_err == GL_TABLE_TOO_LARGE) {printf ("GL_TABLE_TOO_LARGE"); }
1102 else printf ("unknown error %d ",_global_gl_err);
1103}
1104void generate_shadowmap_2D(usehit uhit, int index) {
1105 //call from render_xxxLight or render_TextureProjectorxxx once per frame
1106 // usehit - same as for lightTable/projectorTable / shared with lightTable which holds one usehit per scenegraph node visit
1107 // -- so DEF/USE of light node will have 2+ entries in lightTable and 2+ shadowmaps
1108 // -- currently fbo textures are persisted in Light node > LightRep > depth_buffer_stack
1109 // -- index is index in that stack,
1110 // and corresponds to scenegraph sequential visit number to light node on render pass
1111 //assumes depth fbo buffer / texture already exists / created elsewhere / persistent storage
1112 // set viewpoint pose at light node
1113 // render parent>children sub-scene to fbo texture via render_hier2(parent,VF_Geom | VF_Depth)
1114 double savebackmat[16];
1115
1116 int isize;
1117 double modelviewmatrix[16];
1118 textureTableIndexStruct_s* tti;
1119 float vp[4] = { 0.0f,1.0f,0.0f,1.0f }; //arbitrary
1120 struct X3D_Node* node;
1121 struct X3D_LightRep* lightrep;
1122 bindablestack* bstack;
1123 ttglobal tg = gglobal();
1124 bstack = getActiveBindableStacks(tg);
1125
1126 //this function tampers with the normal background matrix, which has already been prepped for the mainloop rendering
1127 //so save it, and restore after gencubemap loop of 6
1128 memcpy(savebackmat, bstack->backgroundmatrix, 16 * sizeof(double));
1129
1130 node = (struct X3D_Node*)uhit.node;
1131 lightrep = (struct X3D_LightRep*)node->_intern; //X3D_LightRep and X3D_ProjectorRep are same order for the depth fields
1132 struct X3D_PixelTexture* tex = (struct X3D_PixelTexture*)vector_get(struct X3D_Node*, lightrep->depth_buffer_stack,uhit.ivalue);
1133 memcpy(modelviewmatrix, uhit.mvm, 16 * sizeof(double));
1134
1135 //compile_generatedcubemap - creates framebufferobject fbo
1136 tti = getTableIndex(tex->__textureTableIndex);
1137 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps_2D before");
1138
1139 //isize = lightrep->size; //set in compile_
1140 pushnset_framebuffer(tti->ifbobuffer); //binds framebuffer. we push here, in case higher up we are already rendering the whole scene to an fbo
1141 pushnset_viewport(vp); //something to push so we can pop-and-set below, so any mainloop GL_BACK viewport is restored
1142 glViewport(0, 0, tti->x, tti->y); //viewport we want
1143 FW_GL_MATRIX_MODE(GL_PROJECTION);
1144 FW_GL_PUSH_MATRIX();
1145 FW_GL_LOAD_IDENTITY();
1146 FW_GL_MATRIX_MODE(GL_MODELVIEW);
1147 FW_GL_PUSH_MATRIX();
1148 FW_GL_LOAD_IDENTITY();
1149
1150 {
1151 textureTableIndexStruct_s* ttip;
1152 struct X3D_PixelTexture* nodep;
1153 GLuint pixelType;
1154 int bytesPerPixel;
1155 int j = 0;
1156 nodep = tex;
1157 ttip = tti;
1158
1159 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps before GL calls");
1160
1161 FW_GL_CLEAR(GL_DEPTH_BUFFER_BIT);
1162 //GLenum _global_gl_err = glGetError();
1163 //while (_global_gl_err != GL_NONE) {
1164 // PRINT_GL_ERROR(_global_gl_err);
1165 // printf(" here: %s (%s:%d)\n", "generate_shadowMaps clear depth buffer", __FILE__, __LINE__);
1166 // _global_gl_err = glGetError();
1167 //}
1168 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps GL calls 1");
1169
1170 //set viewpoint matrix
1171 {
1172 double world2light[16], world2lightview[16], mvm[16];
1173 double savePosOri[16], saveView[16], viewmatrix[16], mvmInverse[16];
1174 get_view_matrix(savePosOri, saveView);
1175 matmultiplyAFFINE(viewmatrix, saveView, savePosOri);
1176 //printmatrix2(viewmatrix, "vp view matrix");
1177
1178 if (uhit.node->_nodeType == NODE_DirectionalLight)
1179 matmultiplyAFFINE(mvm, uhit.extra, uhit.mvm);
1180 else
1181 matcopy(mvm, uhit.mvm);
1182 matinverseAFFINE(mvmInverse, mvm);
1183 //printmatrix2(uhit.mvm, "uhit.mvm");
1184
1185 matmultiplyAFFINE(world2light, viewmatrix, mvmInverse); // = world2light[16]
1186 //printmatrix2(world2light, "world2light = viewmatrix x mvmInverse");
1187
1188 matmultiplyAFFINE(world2lightview, world2light, lightrep->matview);
1189 //printmatrix2(lightrep->matview, "lighrep.matview");
1190
1191 //printmatrix2(world2lightview, "world2lightview = lighrep.matview x world2light");
1192
1193 fw_glSetDoublev(GL_PROJECTION_MATRIX, lightrep->matproj);
1194 //printmatrix2(lightrep->matproj, "matproj");
1195
1196 fw_glSetDoublev(GL_MODELVIEW_MATRIX, world2lightview);
1197 }
1198 /* 4. Nodes (not the blended ones)*/
1199 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps before render_hier");
1200
1201 profile_start("hier_geom");
1202 struct X3D_Node* root = uhit.userdata ? uhit.userdata : rootNode();
1203 render_hier2(root, VF_Geom | VF_Depth);
1204 profile_end("hier_geom");
1205 PRINT_GL_ERROR_IF_ANY("generate_shadowMaps after render_hier");
1206
1207 }
1208 //set index to 0 to debug (or 1 or which of the light visit shadow maps you want to see at end of frame)
1209 if (index == -1) set_debug_quad(2, tti->OpenGLTexture); // lightrep->idepthtexture);
1210 FW_GL_MATRIX_MODE(GL_PROJECTION);
1211 FW_GL_POP_MATRIX();
1212 FW_GL_MATRIX_MODE(GL_MODELVIEW);
1213 FW_GL_POP_MATRIX();
1214
1215 popnset_viewport();
1216 popnset_framebuffer();
1217 memcpy(bstack->backgroundmatrix, savebackmat, 16 * sizeof(double));
1218}
1219
1220
1221
1222
1223
1224void transformPositionToEye0(double *modelMatrix, float* pos)
1225{
1226 float aux[4];
1227 // assumes pos[3] = 0.0; only use first 3 of these numbers
1228 transformf(aux, pos, modelMatrix);
1229 veccopy3f(pos, aux);
1230}
1231
1232void transformDirectionToEye0(double *modelMatrix, float* dir)
1233{
1234 float* a;
1235 double *b;
1236 float aux[4];
1237 b = modelMatrix;
1238 a = dir;
1239 //should this be an inverse transpose? Is it? I have no idea.
1240 aux[0] = (float)(b[0] * a[0] + b[4] * a[1] + b[8] * a[2]);
1241 aux[1] = (float)(b[1] * a[0] + b[5] * a[1] + b[9] * a[2]);
1242 aux[2] = (float)(b[2] * a[0] + b[6] * a[1] + b[10] * a[2]);
1243 veccopy3f(dir, aux);
1244
1245}
1246
1247
1248GLint tunit(int index);
1249void sendLightInfo2(s_shader_capabilities_t* me) {
1250 // in case we are trying to render a node that has just been killed...
1251 if (me == NULL) return;
1252 /*
1253 if (light_buffering()) {
1254 sendLightInfo3(me);
1255 return;
1256 }
1257 */
1258 PRINT_GL_ERROR_IF_ANY("BEGIN sendLightInfo2");
1259 int lightcount = lightTable_count();
1260
1261 for (int j = 0; j < lightcount; j++) {
1262 usehit* uhit = lightTable_item(j);
1263 struct X3D_Node* node = uhit->node;
1264 struct X3D_PointLight* plight = X3D_POINTLIGHT(node);
1265 struct X3D_SpotLight* slight = X3D_SPOTLIGHT(node);
1266 struct X3D_DirectionalLight* dlight = X3D_DIRECTIONALLIGHT(node);
1267 struct X3D_LightRep* lightrep = (struct X3D_LightRep*)node->_intern;
1268 int lightType = 0;
1269 //0 - pointlight
1270 //1 - spotlight
1271 //2 - directionlight
1272 switch (plight->_nodeType) {
1273 case NODE_PointLight: lightType = 0; break;
1274 case NODE_SpotLight: lightType = 1; break;
1275 case NODE_DirectionalLight: lightType = 2; break;
1276 default: break;
1277 }
1278 //save a bit of bandwidth by not sending unused parameters for a light type
1279 if (lightType < 2) { //not directional
1280 GLUNIFORM3FV(me->lightAtten[j], 1, plight->attenuation.c); //.light_Attenuations);
1281 GLUNIFORM1F(me->lightRadius[j], plight->radius);
1282 float location[4];
1283 veccopy3f(location, plight->location.c);
1284 transformPositionToEye0(uhit->mvm, location);
1285 GLUNIFORM3FV(me->lightLocation[j], 1, location);
1286 }
1287 if (lightType == 1) { //spot
1288 GLUNIFORM1F(me->lightSpotCutoffAngle[j], slight->cutOffAngle);
1289 GLUNIFORM1F(me->lightSpotBeamWidth[j], slight->beamWidth);
1290 float direction[4];
1291 veccopy3f(direction, slight->direction.c);
1292 transformDirectionToEye0(uhit->mvm, direction);
1293 GLUNIFORM3FV(me->lightDirection[j], 1, direction);
1294 }
1295 if (lightType == 2) { //directional
1296 float direction[4];
1297 veccopy3f(direction, dlight->direction.c);
1298 transformDirectionToEye0(uhit->mvm, direction);
1299 GLUNIFORM3FV(me->lightDirection[j], 1, direction);
1300 }
1301 GLUNIFORM1F(me->lightAmbientIntensity[j], plight->ambientIntensity);
1302 GLUNIFORM3FV(me->lightColor[j], 1, plight->color.c);
1303 GLUNIFORM1F(me->lightIntensity[j], plight->intensity);
1304 GLUNIFORM1I(me->lightType[j], lightType);
1305 GLUNIFORM1I(me->lightshadows[j], plight->shadows);
1306 GLUNIFORM1F(me->lightshadowIntensity[j], plight->shadowIntensity);
1307 if (plight->shadows) {
1308 //lookup a textureUnit[index] index to use on this pass
1309 //process the uhit->mvm matrix for shadows
1310 struct X3D_Node* texnode = (struct X3D_Node*)vector_get(struct X3D_Node*, lightrep->depth_buffer_stack, uhit->ivalue);
1311 int itexunit, iunit;
1312 textureTableIndexStruct_s* tti;
1313 if (texnode->_nodeType == NODE_GeneratedCubeMapTexture && !gcm_method()) {
1314 struct X3D_GeneratedCubeMapTexture* tex = (struct X3D_GeneratedCubeMapTexture*)texnode;
1315 tti = getTableIndex(tex->__textureTableIndex);
1316 PRINT_GL_ERROR_IF_ANY("sendLightInfo before bind_or_share");
1317 // glEnable(GL_TEXTURE_CUBE_MAP);
1318 if (0) {
1319 GLuint target;
1320 glGetTextureParameteriv(tti->OpenGLTexture, GL_TEXTURE_TARGET, (GLint*)&target);
1321 switch (target) {
1322 case GL_TEXTURE_CUBE_MAP: printf("CUBE MAP \n"); break;
1323 case GL_TEXTURE_2D: printf("texture2D\n"); break;
1324 case GL_TEXTURE_3D: printf("texture3D\n"); break;
1325 case GL_TEXTURE_2D_ARRAY: printf("GL_TEXTURE_2D_ARRAY\n");
1326 default: printf("unknown %d \n", target); break;
1327 }
1328
1329 }
1330 itexunit = share_or_next_material_sampler_index_Cube(tti->OpenGLTexture); // returns i as in GL_TEXTUREi, next available
1331 iunit = tunitCube(itexunit); //returns index into shader samplerCube textureUnitCube[iunit]
1332 PRINT_GL_ERROR_IF_ANY("sendLightInfo after bind_or_share");
1333 glUniform1i(me->textureUnitCube[iunit], itexunit); // iunit);
1334 }
1335 else {
1336 struct X3D_PixelTexture* tex = (struct X3D_PixelTexture*)texnode;
1337 tti = getTableIndex(tex->__textureTableIndex);
1338 PRINT_GL_ERROR_IF_ANY("sendLightInfo before bind_or_share");
1339 itexunit = share_or_next_material_sampler_index_2D(tti->OpenGLTexture); // returns i as in GL_TEXTUREi, next available
1340 iunit = tunit2D(itexunit); //returns index into shader sampler2D textureUnit[iunit]
1341 PRINT_GL_ERROR_IF_ANY("sendLightInfo after bind_or_share");
1342 glUniform1i(me->textureUnit[iunit], itexunit);
1343 }
1344 GLUNIFORM1I(me->lightdepthmap[j], iunit);
1345 float w2l[16];
1346 {
1347 //following textureProjector
1348 double modelviewinv[16], eye2projector[16], matfull[16], mvm[16];
1349 if (uhit->node->_nodeType == NODE_DirectionalLight)
1350 matmultiplyAFFINE(mvm, uhit->extra, uhit->mvm);
1351 else
1352 matcopy(mvm, uhit->mvm);
1353
1354 matinverse(modelviewinv, mvm);
1355 matmultiplyAFFINE(eye2projector, modelviewinv, lightrep->matview);
1356 matmultiplyFULL(matfull, eye2projector, lightrep->matproj);
1357 double2float(w2l, matfull, 16);
1358 }
1359 //printf("w2l\n");
1360 //for (int ii = 0; ii < 4; ii++){
1361 // for (int jj = 0; jj < 4; jj++) printf("%f ", w2l[ii * 4 + jj]);
1362 // printf("\n");
1363 //}
1364 GLUNIFORMMATRIX4FV(me->lightMat[j], 1, GL_FALSE, w2l);
1365 }
1366 }
1367 GLUNIFORM1I(me->lightcount, lightcount);
1368
1369 PRINT_GL_ERROR_IF_ANY("END sendLightInfo");
1370}
1371