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