FreeWRL / FreeX3D 4.3.0
Component_Layering.c
1/*
2
3
4X3D Layering Component
5
6*/
7/****************************************************************************
8 This file is part of the FreeWRL/FreeX3D Distribution.
9
10 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
11
12 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Lesser Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
24****************************************************************************/
25
26#include <config.h>
27#include <system.h>
28#include <display.h>
29#include <internal.h>
30
31#include <libFreeWRL.h>
32
33#include "../vrml_parser/Structs.h"
34#include "../main/headers.h"
35
36#include "../x3d_parser/Bindable.h"
37#include "Children.h"
38#include "LinearAlgebra.h"
39#include "../opengl/OpenGL_Utils.h"
40#include "../scenegraph/RenderFuncs.h"
41#include "Viewer.h"
42
43
44/*
45 http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/layering.html
46layer - called from layerset on its render and rendray
47layerset -kindof group, but layers not children: render and rendray
48viewport -usage 1: info node for Layer/LayoutLayer
49 -usage 2: (standalone Group-like) prep (push&set clip), fin(pop), ChildC
50
51status:
52oct 22, 2015: pseudo-code
53Jan 2016: version 1 attempt, with:
54 - off-spec:
55 - Layer, LayoutLayer DEF namespace/executionContext shared with main scene - no EXPORT semantics (off spec, but handy)
56 - bindables from all layers end up in ProdCon bindables lists,
57 so ViewpointList (for NextViewpoint, PrevViewpoint) will (wrongly) show/allow viewpoints from all Layers
58 (should be just from activeLayer)
59 - have an extra field in all bindables for node->_layerId if that helps
60 - on-spec:
61 - Layer, LayoutLayer pushing and popping its own binding stacks in hyperstack as per specs
62 - navigation, menubar work on activeLayer
63 - per-layer viewer = Viewer()
64 */
65
66
67typedef struct pComponent_Layering{
68 int layerId, saveActive, binding_stack_set;
69 struct X3D_Node *layersetnode;
70}* ppComponent_Layering;
71void *Component_Layering_constructor(){
72 void *v = MALLOCV(sizeof(struct pComponent_Layering));
73 memset(v,0,sizeof(struct pComponent_Layering));
74 return v;
75}
76void Component_Layering_init(struct tComponent_Layering *t){
77 //public
78 //private
79 t->prv = Component_Layering_constructor();
80 {
81 ppComponent_Layering p = (ppComponent_Layering)t->prv;
82 p->layersetnode = NULL;
83 p->layerId = 0;
84 p->saveActive = 0;
85 p->binding_stack_set = 0;
86 }
87}
88void Component_Layering_clear(struct tComponent_Text *t){
89 //public
90 //private
91 {
92 // ppComponent_Layering p = (ppComponent_Layering)t->prv;
93 //FREE_IF_NZ(p->xxx);
94 }
95}
96
97void getPickrayXY(int *x, int *y);
98int pointinsideviewport(ivec4 vp, ivec2 pt);
99ivec4 childViewport(ivec4 parentViewport, float *clipBoundary){
100 ivec4 vport;
101 vport.W = (int)((clipBoundary[1] - clipBoundary[0]) *parentViewport.W);
102 vport.X = (int)(parentViewport.X + (clipBoundary[0] * parentViewport.W));
103 vport.H = (int)((clipBoundary[3] - clipBoundary[2]) *parentViewport.H);
104 vport.Y = (int)(parentViewport.Y + (clipBoundary[2] * parentViewport.H));
105 return vport;
106}
107void prep_Viewport(struct X3D_Node * node);
108void fin_Viewport(struct X3D_Node * node);
109static float defaultClipBoundary [] = {0.0f, 1.0f, 0.0f, 1.0f}; // left/right/bottom/top 0,1,0,1
110//Layer has 3 virtual functions prep, children, fin
111//- LayerSet should be the only caller for these 3 normally, according to specs
112//- if no LayerSet but a Layer then Layer doesn't do any per-layer stacks or viewer
113void prep_Layer(struct X3D_Node * _node){
114 ttrenderstate rs;
115 struct X3D_Layer *node = (struct X3D_Layer*)_node;
116
117
118 rs = renderstate();
119
120 //There's no concept of window or viewpoint on the
121 // fwl_rendersceneupdatescene(dtime) > render_pre() > render_hier VF_Viewpoint or VF_Collision
122 // pass which is just for updating the world and avatar position within the world
123 if(!rs->render_vp && !rs->render_collision){
124 //push layer.viewport onto viewport stack, setting it as the current window
125 if(node->viewport) prep_Viewport(node->viewport);
126 }
127
128}
129void child_Layer(struct X3D_Node * _node){
130 int ivpvis;
131 Stack *vportstack;
132 ttglobal tg;
133 struct X3D_Layer *node;
134 ttrenderstate rs;
135
136 rs = renderstate();
137 ivpvis = TRUE;
138 node = (struct X3D_Layer*)_node;
139 if(!rs->render_vp && !rs->render_collision){
140
141 tg = gglobal();
142 vportstack = (Stack *)tg->Mainloop._vportstack;
143 ivpvis = currentviewportvisible(vportstack);
144 if(ivpvis)
145 setcurrentviewport(vportstack);
146 }
147 if(ivpvis){
148 if (rs->render_geom == VF_Geom)
149 glClear(GL_DEPTH_BUFFER_BIT); //if another layer has already drawn, don't clear it, just its depth fingerprint
150 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
151 normalChildren(node->children);
152 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
153 }
154}
155void fin_Layer(struct X3D_Node * _node){
156 ttrenderstate rs;
157 struct X3D_Layer *node = (struct X3D_Layer*)_node;
158
159 rs = renderstate();
160
161 if(!rs->render_vp && !rs->render_collision){
162 if(node->viewport) fin_Viewport(node->viewport);
163 }
164
165}
166
167
168struct X3D_Node* getRayHit(void);
169/*
170 How bindables -viewpoint, navigationInfo, background ...- get bound
171 in the correct layer's binding stacks:
172 1) during parsing we figure out their layerId, and assign it to
173 the bindable's new _layerId field
174 2) during set_bind we look at the bindable nodes's _layerId
175 and put it in the binding stacks for that layer
176 How we do #1 figure out layerId during parsing:
177 a) if no layerSet, everything goes into default layerId 0 (main scene layer)
178 b) when we hit a LayerSet during parsing we push_binding_stack_set()
179 with starts a list from 0
180 c) when we hit a Layer / LayoutLayer during parsing, we add it to the pushed list
181 via push_next_layerId_fro_binding_stack_set(), and it sets tg->Bindable.activeLayer
182 d) when we parse a bindable, during creation we set its _layerId = tg->bindable.activeLayer
183*/
184//static int layerId, saveActive, binding_stack_set;
185//in theory the 3 parsing temps could be stored in the LayerSet node
186void push_binding_stack_set(struct X3D_Node* layersetnode){
187 //used during parsing to control layerId for controlling binding stack use
188 ttglobal tg = gglobal();
189 ppComponent_Layering p = (ppComponent_Layering)tg->Component_Layering.prv;
190 p->binding_stack_set++;
191 p->saveActive = tg->Bindable.activeLayer;
192 p->layerId = 0;
193 p->layersetnode = layersetnode; //specs: max one of them per scenefile
194}
195void push_next_layerId_from_binding_stack_set(struct X3D_Node *layer){
196 bindablestack* bstack;
197 ttglobal tg = gglobal();
198 ppComponent_Layering p = (ppComponent_Layering)tg->Component_Layering.prv;
199
200 //only change binding stacks if there was a LayerSet node otherwise accorindg to specs everything is in one binding stack set (default layerId = 0)
201 if(p->binding_stack_set > 0){
202 p->layerId ++;
203 bstack = getBindableStacksByLayer(tg, p->layerId );
204 if(bstack == NULL){
205 bstack = MALLOCV(sizeof(bindablestack));
206 init_bindablestack(bstack, p->layerId, layer->_nodeType);
207 addBindableStack(tg,bstack);
208 }
209 //push_bindingstacks(node);
210 tg->Bindable.activeLayer = p->layerId;
211 }
212}
213
214void pop_binding_stack_set(){
215 ttglobal tg = gglobal();
216 ppComponent_Layering p = (ppComponent_Layering)tg->Component_Layering.prv;
217
218 p->binding_stack_set--;
219 tg->Bindable.activeLayer = p->saveActive;
220}
221void post_parse_set_activeLayer(){
222 ttglobal tg = gglobal();
223 ppComponent_Layering p = (ppComponent_Layering)tg->Component_Layering.prv;
224
225 if(p->layersetnode && p->layersetnode->_nodeType == NODE_LayerSet){
226 struct X3D_LayerSet *ls = (struct X3D_LayerSet*)p->layersetnode;
227 tg->Bindable.activeLayer = ls->activeLayer;
228 }
229}
230
231
232
233void prep_LayoutLayer(struct X3D_Node * _node);
234void child_LayoutLayer(struct X3D_Node * _node);
235void fin_LayoutLayer(struct X3D_Node * _node);
236
237void setup_viewpoint_part1();
238void setup_viewpoint_part3();
239void set_viewmatrix();
240void setup_projection();
241void setup_projection_tinkering();
242void upd_ray();
243void push_ray();
244void pop_ray();
245void setup_pickray0();
246void child_LayerSet(struct X3D_Node * node){
247 // has similar responsibilities to render_heir except just for Layer, LayoutLayer children
248 // child is the only virtual function for LayerSet
249 // Bindables in Core:
250 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/core.html#BindableChildrenNodes
251 // "If there is no LayerSet node defined, there shall be only one set of binding stacks"
252 // -that means its up to LayerSet to switch binding stacks, and manage per-layer modelview matrix stack
253 // Picking reverses the order of layers so top layer can 'swallow the mouse'
254
255 if(node && node->_nodeType == NODE_LayerSet){
256 int ii,i,layerId;
257
258 // UNUSED OLDCODE activeLayer
259
260 ttglobal tg;
261 struct X3D_LayerSet * layerset;
262 ttrenderstate rs;
263
264 rs = renderstate();
265 layerset = (struct X3D_LayerSet *)node;
266 tg = gglobal();
267 // UNUSED OLDCODE activeLayer = layerset->activeLayer;
268
269 for(i=0;i<layerset->order.n;i++){
270
271 int i0, saveActive, isActive;
272 struct X3D_Node *rayhit;
273 struct X3D_Layer * layer = NULL;
274 // UNUSED OLDCODE X3D_Viewer *viewer;
275 bindablestack* bstack;
276
277 ii = i;
278 //if(0) //uncomment to pick in same layer order as render, for diagnostic testing
279 if(rs->render_sensitive == VF_Sensitive){
280 ii = layerset->order.n - ii -1; //reverse order compared to rendering
281 }
282
283 layerId = layerset->order.p[ii];
284 //check if its a valid ordinal
285 i0 = max(0,layerId -1);
286 if(i0 < 0 || i0 > layerset->layers.n -1)
287 continue; //skip if we don't have an ordinal to match
288
289 isActive = layerId == tg->Bindable.activeLayer;
290 layer = (struct X3D_Layer*)layerset->layers.p[i0];
291
292 if(rs->render_sensitive == VF_Sensitive){
293 if(!layer->isPickable) continue; //skip unpickable layers on sensitive pass
294 }
295 if(rs->render_collision == VF_Collision && !isActive)
296 continue; //skip non-navigation layers on collision pass
297
298
299 //push/set binding stacks (some of this done in x3d parsing now, so bstack shouldn't be null)
300 bstack = getBindableStacksByLayer(tg, layerId );
301 if(bstack == NULL){
302 bstack = MALLOCV(sizeof(bindablestack));
303 init_bindablestack(bstack, layerId, layer->_nodeType);
304 addBindableStack(tg,bstack);
305 }
306 saveActive = tg->Bindable.activeLayer;
307 // UNUSED OLDCODE viewer = (X3D_Viewer*)bstack->viewer;
308 // UNUSED OLDCODE if(viewer) printf("layerid=%d ortho=%d ofield=%f %f %f %f\n",layerId,viewer->ortho,viewer->orthoField[0],viewer->orthoField[1],viewer->orthoField[2],viewer->orthoField[3]);
309
310 tg->Bindable.activeLayer = layerId;
311
312 //per-layer modelview matrix is handled here in LayerSet because according
313 //to the specs if there is no LayerSet, then there's only one
314 //set of binding stacks (one modelview matrix)
315
316 //push modelview matrix
317 if(!isActive){
318 FW_GL_MATRIX_MODE(GL_PROJECTION);
319 FW_GL_PUSH_MATRIX();
320 FW_GL_MATRIX_MODE(GL_MODELVIEW);
321 FW_GL_PUSH_MATRIX();
322 if(rs->render_vp == VF_Viewpoint){
323 setup_viewpoint_part1();
324 }else{
325 set_viewmatrix();
326 }
327 }
328 if(!rs->render_vp && !rs->render_collision )
329 setup_projection();
330 if(rs->render_sensitive == VF_Sensitive){
331 push_ray();
332 if(!isActive) upd_ray();
333 }
334
335
336 //both layer and layoutlayer can be in here
337 if(layer->_nodeType == NODE_Layer){
338 prep_Layer((struct X3D_Node*)layer);
339 child_Layer((struct X3D_Node*)layer);
340 fin_Layer((struct X3D_Node*)layer);
341 }
342 else if(layer->_nodeType == NODE_LayoutLayer){
343 prep_LayoutLayer((struct X3D_Node*)layer);
344 child_LayoutLayer((struct X3D_Node*)layer);
345 fin_LayoutLayer((struct X3D_Node*)layer);
346 }
347 rayhit = NULL;
348 if(rs->render_sensitive == VF_Sensitive){
349 rayhit = getRayHit(); //if there's a clear pick of something on a higher layer, no need to check lower layers
350 pop_ray();
351 }
352
353
354 //pop modelview matrix
355 if(!isActive){
356 if(rs->render_vp == VF_Viewpoint){
357 setup_viewpoint_part3();
358 }
359 FW_GL_MATRIX_MODE(GL_PROJECTION);
360 FW_GL_POP_MATRIX();
361 FW_GL_MATRIX_MODE(GL_MODELVIEW);
362 FW_GL_POP_MATRIX();
363 }
364
365 //pop binding stacks
366 tg->Bindable.activeLayer = saveActive;
367 //setup_projection();
368 if(rayhit) break;
369 }
370 tg->Bindable.activeLayer = layerset->activeLayer;
371 }
372}
373
374//not sure what I need for viewport.
375//Situation #1 standalone viewport:
376//Maybe there should be a push and pop from a viewport stack, if rendering its children
377// ie pre: push vport
378// render: render children via normalChildren
379// fin/post: pop vport
380//Situation #2 viewport in SFNode viewport field of another layer / layout node
381// the host node would do
382// pre: push vport
383// render: render itself
384// post/fin: pop vport
385// Apr 2020 - decided to put in some isotropic scale correction - like STRETCH for the LayoutLayer
386// just looks at the current and parent viewports to see any change of aspect, and corrects for it
387void pushaspect(Stack *vportstack, ivec4 vport){
388 ivec4 lastp = stack_top(ivec4,vportstack); //parent context viewport
389 float aspect_last = (float)lastp.W/(float)lastp.H;
390 float aspect = (float)vport.W/(float)vport.H;
391 double change = aspect/aspect_last;
392 double mat[16], mat2[16];
393 if(1){
394 //apply aspect-change-stretch-scale to projection matrix
395 fw_glGetDoublev(GL_PROJECTION_MATRIX, mat);
396 FW_GL_MATRIX_MODE(GL_PROJECTION);
397 FW_GL_PUSH_MATRIX();
398 loadIdentityMatrix(mat2);
399 if(change > 0.0)
400 mat2[0] = 1.0/change;
401 matmultiplyFULL(mat,mat,mat2);
402 fw_glSetDoublev(GL_PROJECTION_MATRIX, mat);
403 FW_GL_MATRIX_MODE(GL_MODELVIEW);
404
405 }else{
406 //or apply to modelview matrix
407 FW_GL_MATRIX_MODE(GL_MODELVIEW);
408 fw_glGetDoublev(GL_MODELVIEW_MATRIX, mat);
409 FW_GL_PUSH_MATRIX();
410 loadIdentityMatrix(mat2);
411 if(change > 0.0)
412 mat2[0] = 1.0/change;
413 //matscale(mat2,1.0,1.0,1.0);
414 matmultiplyFULL(mat,mat,mat2);
415 fw_glSetDoublev(GL_MODELVIEW_MATRIX, mat);
416
417 FW_GL_MATRIX_MODE(GL_MODELVIEW);
418 }
419}
420void popaspect(){
421 if(1){
422 //apply aspect-change-stretch-scale to projection matrix
423 FW_GL_MATRIX_MODE(GL_PROJECTION);
424 FW_GL_POP_MATRIX();
425 FW_GL_MATRIX_MODE(GL_MODELVIEW);
426 }else{
427 //or apply to modelview matrix
428 FW_GL_MATRIX_MODE(GL_MODELVIEW);
429 FW_GL_POP_MATRIX();
430 FW_GL_MATRIX_MODE(GL_MODELVIEW);
431 }
432}
433void prep_Viewport(struct X3D_Node * node){
434 if(node && node->_nodeType == NODE_Viewport){
435 Stack *vportstack;
436 ivec4 pvport,vport;
437 float *clipBoundary; // left/right/bottom/top 0,1,0,1
438 ttrenderstate rs;
439 ttglobal tg;
440 struct X3D_Viewport * viewport = (struct X3D_Viewport *)node;
441 tg = gglobal();
442
443 rs = renderstate();
444 //There's no concept of window or viewpoint on the
445 // fwl_rendersceneupdatescene(dtime) > render_pre() > render_hier VF_Viewpoint or VF_Collision
446 // pass which is just for updating the world and avatar position within the world
447 if(!rs->render_vp && !rs->render_collision){
448
449 //push viewport onto viewport stack, setting it as the current window
450 vportstack = (Stack *)tg->Mainloop._vportstack;
451 pvport = stack_top(ivec4,vportstack); //parent context viewport
452 clipBoundary = defaultClipBoundary;
453 if(viewport->clipBoundary.p && viewport->clipBoundary.n > 3)
454 clipBoundary = viewport->clipBoundary.p;
455
456 vport = childViewport(pvport,clipBoundary);
457 if(rs->render_sensitive){
458 int mouseX, mouseY, inside;
459 ivec2 pt;
460 getPickrayXY(&mouseX, &mouseY);
461 pt.X = mouseX;
462 pt.Y = mouseY;
463 inside = pointinsideviewport(vport,pt);
464 if(!inside){
465 vport.W = 0;
466 vport.H = 0;
467 }
468 }
469 pushaspect(vportstack,vport);
470 pushviewport(vportstack, vport);
471 if(currentviewportvisible(vportstack)){
472 setcurrentviewport(vportstack);
473 upd_ray();
474 }
475 }
476 }
477
478}
479
480void child_Viewport(struct X3D_Node * nodein){
481 if(nodein && nodein->_nodeType == NODE_Viewport){
482 Stack *vportstack;
483 struct X3D_Viewport * viewport, *node;
484 ttglobal tg;
485 tg = gglobal();
486
487 viewport = node = (struct X3D_Viewport *)nodein;
488
489 vportstack = (Stack *)tg->Mainloop._vportstack;
490
491 if(currentviewportvisible(vportstack)){
492 prep_sibAffectors((struct X3D_Node*)node,&viewport->__sibAffectors);
493 prep_BBox((struct BBoxFields*)&node->bboxCenter);
494 normalChildren(viewport->children);
495 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
496 fin_sibAffectors((struct X3D_Node*)node,&viewport->__sibAffectors);
497 }
498 }
499}
500void fin_Viewport(struct X3D_Node * node){
501 if(node && node->_nodeType == NODE_Viewport){
502 Stack *vportstack;
503 ttrenderstate rs;
504 ttglobal tg;
505 // OLDCODE UNUSED struct X3D_Viewport * viewport = (struct X3D_Viewport *)node;
506
507 // compiler warning mitigation
508 UNUSED(rs);
509
510 tg = gglobal();
511
512 rs = renderstate();
513
514 if(!rs->render_vp && !rs->render_collision){
515
516 vportstack = (Stack *)tg->Mainloop._vportstack;
517
518 //pop viewport
519 popaspect();
520 popviewport(vportstack);
521 setcurrentviewport(vportstack);
522 upd_ray();
523 }
524 }
525}