FreeWRL / FreeX3D 4.3.0
Component_Navigation.c
1/*
2
3
4X3D Navigation Component
5
6*/
7
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28
29
30#include <config.h>
31#include <system.h>
32#include <display.h>
33#include <internal.h>
34
35#include <libFreeWRL.h>
36
37#include "../vrml_parser/Structs.h"
38#include "../main/headers.h"
39
40#include "../x3d_parser/Bindable.h"
41#include "LinearAlgebra.h"
42#include "Collision.h"
43#include "quaternion.h"
44#include "Viewer.h"
45#include "../opengl/Frustum.h"
46#include "Children.h"
47#include "../opengl/OpenGL_Utils.h"
48#include "../scenegraph/RenderFuncs.h"
49
50
51struct X3D_Node *getActiveLayerBoundViewpoint();
52void prep_Viewpoint (struct X3D_Viewpoint *node) {
53 double a1;
54 GLint viewPort[10];
56 if (!renderstate()->render_vp) return;
57 viewer = Viewer();
58
59 /* we will never get here unless we are told that we are active by the scene graph; actually
60 doing this test can screw us up, so DO NOT do this test!
61 if(!node->isBound) return;
62 */
63 if((struct X3D_Node*)node == getActiveLayerBoundViewpoint() && !node->_donethispass){
64 node->_donethispass = 1; //if the vp id DEF/USED multiple places in the scengraph,
65 // this test takes the first one (and helps exit render_node early around virt->children)
66
67
68 {
69 //dug9slerp this fix works with a test file VP_set_orientation.x3d
70 Quaternion q3;
71 vrmlrot_to_quaternion(&q3,node->orientation.c[0],node->orientation.c[1],node->orientation.c[2],-node->orientation.c[3]);
72 quaternion_togl(&q3);
73 }
74 FW_GL_TRANSLATE_D(-node->position.c[0],-node->position.c[1],-node->position.c[2]);
75
76 /* now, lets work on the Viewpoint fieldOfView */
77 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
78 if(viewPort[2] > viewPort[3]) {
79 a1=0;
80 viewer->fieldofview = node->fieldOfView/3.1415926536*180;
81 } else {
82 a1 = node->fieldOfView;
83 a1 = atan2(sin(a1),viewPort[2]/((float)viewPort[3]) * cos(a1));
84 viewer->fieldofview = a1/3.1415926536*180;
85 }
86 }
87 // printf ("render_Viewpoint, bound to %d, fieldOfView %f \n",node,node->fieldOfView);
88}
89
90
91void prep_OrthoViewpoint (struct X3D_OrthoViewpoint *node) {
92 int ind;
93
94 if (!renderstate()->render_vp) return;
95
96 /* we will never get here unless we are told that we are active by the scene graph; actually
97 doing this test can screw us up, so DO NOT do this test!
98 if(!node->isBound) return;
99 */
100
101 if((struct X3D_Node*)node == getActiveLayerBoundViewpoint() && !node->_donethispass){
102 node->_donethispass = 1; //if the vp id DEF/USED multiple places in the scengraph,
103
104
105 /* perform OrthoViewpoint translations */
106 FW_GL_ROTATE_RADIANS(-node->orientation.c[3],node->orientation.c[0],node->orientation.c[1],
107 node->orientation.c[2]);
108 FW_GL_TRANSLATE_D(-node->position.c[0],-node->position.c[1],-node->position.c[2]);
109
110 /* now, lets work on the OrthoViewpoint fieldOfView */
111 if (node->fieldOfView.n == 4) {
112 for (ind=0; ind<4; ind++) {
113 Viewer()->orthoField[ind] = (double) node->fieldOfView.p[ind];
114 }
115 }
116 }
117}
118
119/******************************************************************************************/
120
121void proximity_Billboard (struct X3D_Billboard *node) {
122 /* printf ("prox_billboard, do nothing\n"); */
123}
124
125void prep_Billboard (struct X3D_Billboard *node) {
126 if(1){
127 //Mar 14 2018 this works with geoViewpoint (GeoTouchSensorExampleB.x3d) and viewpoint (47.x3d)
128 double mod[16], modi[16], axis[3];
129 int align;
130
131 RECORD_DISTANCE
132
133 FW_GL_PUSH_MATRIX();
134 //to align with viewepoint, cancel/undo any rotations in modelview matrix
135 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
136 float2double(axis,node->axisOfRotation.c,3);
137 align = (APPROX(veclengthd(axis),0.0f));
138 if(align){
139 //axisOfRotation== (0,0,0) as per specs means full alignment with vp
140 // cancel/undo rotations of modelview matrix:
141 double modb[16], modbi[16];
142 matrixAFFINE2RotationMatrix(modb,mod);
143 matinverseAFFINE(modbi,modb);
144 FW_GL_TRANSFORM_D(modbi);
145 }else{
146 // normal axisOfRotation
147 //we calculate an additional swing matrix around the axisOfRotation
148 //1. get the position of the vp in billboard-local-coords = vpos
149 //2. cross axisOfRotation with vpos to get a perpendicular to both
150 //3. cross axisOfRotation with zvec to get a perpendicular to both
151 //4. get a rotation difference matrix between those 2 perrp vectors
152 //5. modify modelview by subtracting off the difference rotation
153 double vpos[3], zvec[3], perpa[3], perpb[3], matr[16];
154 //calculate position of viewpoint vp in billboard-local coords: vpos
155 vecsetd(vpos,0.0,0.0,0.0);
156 matinverseAFFINE(modi,mod);
157 transformAFFINEd(vpos,vpos,modi);
158 vecnormald(vpos,vpos);
159 //z axis in billboard-local system zvec
160 vecsetd(zvec,0.0,0.0,1.0);
161 //2 cross products
162 veccrossd(perpa,axis,vpos);
163 veccrossd(perpb,axis,zvec);
164 //swing matrix around axisOfRotation
165 matrotate2vd(matr,perpa,perpb);
166 FW_GL_TRANSFORM_D(matr);
167 }
168 }else{
169 // not sure why the old way looked at viewer Quat in case of axisOfRotation 0 0 0
170 // x didn't work with geoViewpoint
171 struct point_XYZ vpos, ax, cp, cp2, arcp;
172 static const struct point_XYZ orig = {0.0, 0.0, 0.0};
173 static const struct point_XYZ zvec = {0.0, 0.0, 1.0};
174 struct orient_XYZA viewer_orient;
175 GLDOUBLE mod[16];
176 GLDOUBLE proj[16];
177 int align;
178 double len, len2, angle;
179 int sign;
180
181 RECORD_DISTANCE
182
183 ax.x = node->axisOfRotation.c[0];
184 ax.y = node->axisOfRotation.c[1];
185 ax.z = node->axisOfRotation.c[2];
186 align = (APPROX(VECSQ(ax),0));
187
188 viewer_fetch_LCS(Viewer());
189 quaternion_to_vrmlrot(&(Viewer()->Quat),
190 &(viewer_orient.x), &(viewer_orient.y),
191 &(viewer_orient.z), &(viewer_orient.a));
192
193 FW_GL_PUSH_MATRIX();
194
195 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
196 if(0){
197 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
198 FW_GLU_UNPROJECT(orig.x, orig.y, orig.z, mod, proj, viewport, &vpos.x, &vpos.y, &vpos.z);
199 }
200 if(1){
201 //feature-AFFINE_GLU_UNPROJECT
202 double modi[16];
203 matinverseAFFINE(modi,mod);
204 transform(&vpos,&orig,modi);
205 }
206 len = VECSQ(vpos);
207 if (APPROX(len, 0)) { return; }
208 VECSCALE(vpos, 1/sqrt(len));
209
210 if (align) {
211 ax.x = viewer_orient.x;
212 ax.y = viewer_orient.y;
213 ax.z = viewer_orient.z;
214 }
215
216 VECCP(ax, zvec, arcp);
217 len = VECSQ(arcp);
218 if (APPROX(len, 0)) { return; }
219
220 len = VECSQ(ax);
221 if (APPROX(len, 0)) { return; }
222 VECSCALE(ax, 1/sqrt(len));
223
224 VECCP(vpos, ax, cp); /* cp is now 90deg to both vector and axis */
225 len = sqrt(VECSQ(cp));
226 if (APPROX(len, 0)) {
227 FW_GL_ROTATE_RADIANS(-viewer_orient.a, ax.x, ax.y, ax.z);
228 return;
229 }
230 VECSCALE(cp, 1/len);
231
232 /* Now, find out angle between this and z axis */
233 VECCP(cp, zvec, cp2);
234
235 len2 = VECPT(cp, zvec); /* cos(angle) */
236 len = sqrt(VECSQ(cp2)); /* this is abs(sin(angle)) */
237
238 /* Now we need to find the sign first */
239 if (VECPT(cp, arcp) > 0)
240 {
241 sign = -1;
242 } else {
243 sign = 1;
244 }
245 angle = atan2(len2, sign*len);
246
247 FW_GL_ROTATE_RADIANS(angle, ax.x, ax.y, ax.z);
248 }
249}
250
251void fin_Billboard (struct X3D_Billboard *node) {
252 UNUSED(node);
253 FW_GL_POP_MATRIX();
254}
255
256
257void child_Billboard (struct X3D_Billboard *node) {
258 int nc = node->children.n;
259 //LOCAL_LIGHT_SAVE
260
261
262 /* any children at all? */
263 if (nc==0) return;
264
265 #ifdef CHILDVERBOSE
266 printf("RENDER BILLBOARD START %d (%d)\n",node, nc);
267 #endif
268
269 /* do we have a local light for a child? */
270 //LOCAL_LIGHT_CHILDREN(node->children);
271 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
272
273 /* now, just render the non-directionalLight children */
274 normalChildren(node->children);
275
276 if (renderstate()->render_geom && (!renderstate()->render_blend)) {
277 EXTENTTOBBOX
278 }
279
280 #ifdef CHILDVERBOSE
281 printf("RENDER BILLBOARD END %d\n",node);
282 #endif
283 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
284
285 //LOCAL_LIGHT_OFF
286}
287
288
289/******************************************************************************************/
290
291//
292//void render_NavigationInfo (struct X3D_NavigationInfo *node) {
293// /* check the set_bind eventin to see if it is TRUE or FALSE */
294// ttglobal tg = gglobal();
295// if (node->set_bind < 100) {
296// if (node->set_bind == 1) set_naviinfo(node);
297// bind_node (X3D_NODE(node), getActiveBindableStacks(tg)->navigation);
298// }
299// if(!node->isBound) return;
300//}
301
302
303/*
304Nov 28, 2016 we aren't doing collision->proxy
305COLLISION_PROXY
306http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/navigation.html#Collision
307"""
308The collision proxy, defined in the proxy field, is any legal children node as described in 10.2.1 Grouping and children node types that is used as a substitute for the Collision node's children during collision detection. The proxy is used strictly for collision detection; it is not drawn.
309"""
310http://www.web3d.org/x3d/content/examples/Basic/development/ProxyShapeExampleIndex.html
311- example showing you can re-order / change the order / length of children field and proxy should still be proxy
312*/
313
314void child_Collision (struct X3D_Collision *node) {
315 int nc = node->children.n;
316 int i;
317 struct X3D_Node *tmpN;
318
319 if(renderstate()->render_collision) {
320 /* test against the collide field (vrml) enabled (x3d) and that we actually have a proxy field */
321 if((node->collide) && (node->enabled) && !(node->proxy)) {
322 struct sCollisionInfo OldCollisionInfo;
323 struct sCollisionInfo * ci = CollisionInfo();
324 OldCollisionInfo = *ci;
325 for(i=0; i<nc; i++) {
326 void *p = ((node->children).p[i]);
327 #ifdef CHILDVERBOSE
328 printf("RENDER COLLISION %d CHILD %d\n",node, p);
329 #endif
330 render_node(p);
331 }
332 if((!APPROX(ci->Offset.x,
333 OldCollisionInfo.Offset.x)) ||
334 (!APPROX(ci->Offset.y,
335 OldCollisionInfo.Offset.y)) ||
336 (!APPROX(ci->Offset.z,
337 OldCollisionInfo.Offset.z))) {
338 /* old code was:
339 if(CollisionInfo.Offset.x != OldCollisionInfo.Offset.x ||
340 CollisionInfo.Offset.y != OldCollisionInfo.Offset.y ||
341 CollisionInfo.Offset.z != OldCollisionInfo.Offset.z) { */
342 /*collision occured
343 * bit 0 gives collision, bit 1 gives change */
344 node->__hit = (node->__hit & 1) ? 1 : 3;
345 } else
346 node->__hit = (node->__hit & 1) ? 2 : 0;
347
348 }
349 if(node->proxy) {
350 POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->proxy,tmpN)
351 render_node(tmpN);
352 }
353
354 } else { /*standard group behaviour*/
355 //LOCAL_LIGHT_SAVE
356
357 #ifdef CHILDVERBOSE
358 printf("RENDER COLLISIONCHILD START %d (%d)\n",node, nc);
359 #endif
360
361 /* do we have a local light for a child? */
362 //LOCAL_LIGHT_CHILDREN(node->children);
363 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
364
365 /* now, just render the non-directionalLight children */
366 normalChildren(node->children);
367
368 #ifdef CHILDVERBOSE
369 printf("RENDER COLLISIONCHILD END %d\n",node);
370 #endif
371 //LOCAL_LIGHT_OFF
372 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
373
374 }
375}
376
377/* LOD changes between X3D and VRML - level and children fields are "equivalent" */
378void child_LOD (struct X3D_LOD *node) {
379
380/*
381if (node->_selected != NULL) {
382struct X3D_Node *selno = X3D_NODE(node->_selected);
383printf ("childLOD %p (root %p), flags %x ",selno,rootNode,selno->_renderFlags);
384if ((selno->_renderFlags & VF_Viewpoint) == VF_Viewpoint) printf ("VF_Viewpoint ");
385if ((selno->_renderFlags & VF_Geom) == VF_Geom) printf ("VF_Geom ");
386if ((selno->_renderFlags & VF_localLight) == VF_localLight) printf ("VF_localLight ");
387if ((selno->_renderFlags & VF_Sensitive) == VF_Sensitive) printf ("VF_Sensitive ");
388if ((selno->_renderFlags & VF_Blend) == VF_Blend) printf ("VF_Blend ");
389if ((selno->_renderFlags & VF_Proximity) == VF_Proximity) printf ("VF_Proximity ");
390if ((selno->_renderFlags & VF_Collision) == VF_Collision) printf ("VF_Collision ");
391if ((selno->_renderFlags & VF_globalLight) == VF_globalLight) printf ("VF_globalLight ");
392if ((selno->_renderFlags & VF_hasVisibleChildren) == VF_hasVisibleChildren) printf ("VF_hasVisibleChildren ");
393if ((selno->_renderFlags & VF_shouldSortChildren) == VF_shouldSortChildren) printf ("VF_shouldSortChildren ");
394printf ("\n");
395}
396*/
397 render_node(node->_selected);
398}
399
400
401/* calculate the LOD distance */
402void proximity_LOD (struct X3D_LOD *node) {
403 GLDOUBLE mod[16];
404 GLDOUBLE proj[16];
405 struct point_XYZ vec;
406 double dist;
407 int nran = (node->range).n;
408 int nnod = (node->level).n;
409 int xnod = (node->children).n;
410
411 int i;
412
413 /* no range, display the first node, if it exists */
414 if(!nran) {
415 if (node->__isX3D) {
416 if (nnod > 0) node->_selected = (node->children).p[0];
417 else node->_selected = NULL;
418 } else {
419 if (xnod > 0) node->_selected = (node->level).p[0];
420 else node->_selected = NULL;
421 }
422 return;
423 }
424
425 /* calculate which one to display */
426 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
427 if(0){
428 //this is centered on the front face of the frustum, about .1 away from avatar center (approximately correct)
429 /* printf ("LOD, mat %f %f %f\n",mod[12],mod[13],mod[14]); */
430 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
431 FW_GLU_UNPROJECT(0,0,0,mod,proj,viewport, &vec.x,&vec.y,&vec.z);
432 //printf("old vec= %f %f %f\n", vec.x,vec.y,vec.z);
433 }
434 if(1){
435 //feature-AFFINE_GLU_UNPROJECT
436 //this is centered on the avatar (correct)
437 double modi[16];
438 struct point_XYZ orig = {0.0,0.0,0.0};
439 matinverseAFFINE(modi,mod);
440 transform(&vec,&orig,modi);
441 //printf("new vec= %f %f %f\n", vec.x,vec.y,vec.z);
442 //printf("\n");
443 }
444 vec.x -= (node->center).c[0];
445 vec.y -= (node->center).c[1];
446 vec.z -= (node->center).c[2];
447
448 dist = sqrt(VECSQ(vec));
449 i = 0;
450
451 while (i<nran) {
452 if(dist < ((node->range).p[i])) { break; }
453 i++;
454 }
455
456 /* is this VRML or X3D? */
457 if (node->__isX3D) {
458 if (xnod > 0) {
459 /* X3D "children" field */
460 if(i >= xnod) i = xnod-1;
461 node->_selected = (node->children).p[i];
462 /* printf ("selecting X3D nod %d \n",i); */
463 } else node->_selected = NULL;
464
465 } else {
466 if (nnod > 0) {
467 /* VRML "range" field */
468 if(i >= nnod) i = nnod-1;
469 node->_selected = (node->level).p[i];
470 /* printf ("selecting vrml nod\n"); */
471 } else { node->_selected = NULL; }
472 }
473 if(i != node->level_changed){
474 node->level_changed = i;
475 MARK_EVENT(X3D_NODE(node),offsetof(struct X3D_LOD,level_changed));
476 }
477}
478
479
480
481/************************************************************************
482 *
483 * ViewpointGroup Node
484 *
485 ************************************************************************/
486 void add_node_to_broto_context(struct X3D_Proto *currentContext,struct X3D_Node *node);
487
488void compile_ViewpointGroup (struct X3D_ViewpointGroup *node) {
489 struct X3D_ProximitySensor *pn;
490
491 /* check if we need to create the proximity node */
492 if (node->__proxNode == NULL) {
493 /* create proximity */
494 pn = (struct X3D_ProximitySensor *) createNewX3DNode(NODE_ProximitySensor);
495 if(node->_executionContext)
496 add_node_to_broto_context(X3D_PROTO(node->_executionContext),X3D_NODE(pn));
497
498 /* any changes needed here?? */
499 node->__proxNode = (void *)pn;
500
501 /* link this in so the VF_Proximity flag will propagate */
502 ADD_PARENT(X3D_NODE(pn),X3D_NODE(node));
503 }
504
505 /* get the Proximity Node */
506 pn = X3D_PROXIMITYSENSOR(node->__proxNode);
507
508 /* copy size, center over */
509 memcpy (&pn->center, &node->center, sizeof (float)*3);
510 memcpy (&pn->size, &node->size, sizeof (float)*3);
511
512 /* enable it */
513 pn->enabled=TRUE;
514
515 /* tell the proximity that it has changed */
516 pn->_change++;
517
518 MARK_NODE_COMPILED
519}
520
521
522void child_ViewpointGroup (struct X3D_ViewpointGroup *node) {
523 int i;
524
525 /* do we have an attached proximity node? If so, we'll be flagged to do
526 the sensitive pass */
527
528 /* printf ("child_ViewpointGroup, this %u rf %x \n",node,node->_renderFlags);
529 printf (" ..., render_hier vp %d geom %d light %d sens %d blend %d prox %d col %d\n",
530 render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */
531
532 if (renderstate()->render_proximity) {
533 if (node->__proxNode != NULL) {
534 /* printf ("have prox, rendering it\n"); */
535 render_node(X3D_NODE(node->__proxNode));
536
537 /* printf ("prox active %d\n",X3D_PROXIMITYSENSOR(node->__proxNode)->isActive); */
538 }
539
540 }
541
542 if (!renderstate()->render_vp) return;
543
544 /* render the viewpoints - one of these will be active */
545 for(i=0; i<node->children.n; i++) {
546 struct X3D_Node *p = X3D_NODE(node->children.p[i]);
547 if (p != NULL) {
548 render_node(p);
549 }
550 }
551
552}
553