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 "../opengl/Frustum.h"
49#include "../scenegraph/RenderFuncs.h"
50#include "../ui/common.h"
51
52
53struct X3D_Node *getActiveLayerBoundViewpoint();
54struct X3D_Node* getSelectedViewpoint();
55void prep_Viewpoint (struct X3D_Viewpoint *node) {
56 double a1;
57 GLint viewPort[10];
58 X3D_Viewer *viewer;
59 if (!renderstate()->render_vp) return;
60 viewer = Viewer();
61
62 /* we will never get here unless we are told that we are active by the scene graph; actually
63 doing this test can screw us up, so DO NOT do this test!
64 if(!node->isBound) return;
65 */
66 node->_reachablethispass = TRUE;
67 //if((struct X3D_Node*)node == getActiveLayerBoundViewpoint() && !node->_donethispass){
68 if ((struct X3D_Node*)node == getSelectedViewpoint() && !node->_donethispass) {
69 node->_donethispass = 1; //if the vp id DEF/USED multiple places in the scengraph,
70 // this test takes the first one (and helps exit render_node early around virt->children)
71
72
73 {
74 //dug9slerp this fix works with a test file VP_set_orientation.x3d
75 Quaternion q3;
76 vrmlrot_to_quaternion(&q3,node->orientation.c[0],node->orientation.c[1],node->orientation.c[2],-node->orientation.c[3]);
77 quaternion_togl(&q3);
78 }
79 FW_GL_TRANSLATE_D(-node->position.c[0],-node->position.c[1],-node->position.c[2]);
80
81 /* now, lets work on the Viewpoint fieldOfView */
82 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
83 if(viewPort[2] > viewPort[3]) {
84 a1=0;
85 viewer->fieldofview = node->fieldOfView/3.1415926536*180;
86 } else {
87 a1 = node->fieldOfView;
88 a1 = atan2(sin(a1),viewPort[2]/((float)viewPort[3]) * cos(a1));
89 viewer->fieldofview = a1/3.1415926536*180;
90 }
91 }
92 // printf ("render_Viewpoint, bound to %d, fieldOfView %f \n",node,node->fieldOfView);
93}
94void draw_viewpoint(int type, float *fov, float aspect);
95void render_Viewpoint (struct X3D_Viewpoint *node) {
96 float center[3],size[3];
97 if(node->_show_pin_point || fwl_getShowViewpoints())
98 draw_bbox(double2float(center,node->_pin_point.c,3),vecset3f(size,.4f,.4f,.4f));
99 if(fwl_getShowViewpoints()){
100 FW_GL_PUSH_MATRIX();
101 FW_GL_TRANSLATE_D(node->_position.c[0],node->_position.c[1],node->_position.c[2]);
102 FW_GL_ROTATE_RADIANS( node->_orientation.c[3],node->_orientation.c[0],node->_orientation.c[1],
103 node->_orientation.c[2]);
104 //draw_bbox(vecset3f(center,0.0,0.0,0.0),vecset3f(size,.4f,.4f,1.2f));
105 draw_viewpoint(node->_nodeType,&node->fieldOfView,node->aspectRatio);
106 FW_GL_POP_MATRIX();
107 }
108}
109void render_OrthoViewpoint (struct X3D_OrthoViewpoint *node) {
110 float center[3],size[3];
111 if(node->_show_pin_point || fwl_getShowViewpoints())
112 draw_bbox(double2float(center,node->_pin_point.c,3),vecset3f(size,.4f,.4f,.4f));
113 if(fwl_getShowViewpoints()){
114 FW_GL_PUSH_MATRIX();
115 FW_GL_TRANSLATE_D(node->_position.c[0],node->_position.c[1],node->_position.c[2]);
116 FW_GL_ROTATE_RADIANS( node->_orientation.c[3],node->_orientation.c[0],node->_orientation.c[1],
117 node->_orientation.c[2]);
118 //draw_bbox(vecset3f(center,0.0,0.0,0.0),vecset3f(size,.4f,.4f,1.2f));
119 draw_viewpoint(node->_nodeType,node->fieldOfView.p,1.0f);
120
121 FW_GL_POP_MATRIX();
122 }
123}
124void prep_OrthoViewpoint (struct X3D_OrthoViewpoint *node) {
125 int ind;
126
127 if (!renderstate()->render_vp) return;
128
129 /* we will never get here unless we are told that we are active by the scene graph; actually
130 doing this test can screw us up, so DO NOT do this test!
131 if(!node->isBound) return;
132 */
133 node->_reachablethispass = TRUE;
134 //if((struct X3D_Node*)node == getActiveLayerBoundViewpoint() && !node->_donethispass){
135 if((struct X3D_Node*)node == getSelectedViewpoint() && !node->_donethispass){
136 node->_donethispass = 1; //if the vp id DEF/USED multiple places in the scengraph,
137
138
139 /* perform OrthoViewpoint translations */
140 FW_GL_ROTATE_RADIANS(-node->orientation.c[3],node->orientation.c[0],node->orientation.c[1],
141 node->orientation.c[2]);
142 FW_GL_TRANSLATE_D(-node->position.c[0],-node->position.c[1],-node->position.c[2]);
143
144 /* now, lets work on the OrthoViewpoint fieldOfView */
145 if (node->fieldOfView.n == 4) {
146 for (ind=0; ind<4; ind++) {
147 Viewer()->orthoField[ind] = (double) node->fieldOfView.p[ind];
148 }
149 }
150 }
151}
152
153/******************************************************************************************/
154
155void proximity_Billboard (struct X3D_Billboard *node) {
156 /* printf ("prox_billboard, do nothing\n"); */
157}
158
159void prep_Billboard (struct X3D_Billboard *node) {
160 if(1){
161 //Mar 14 2018 this works with geoViewpoint (GeoTouchSensorExampleB.x3d) and viewpoint (47.x3d)
162 double mod[16], modi[16], matr[16], axis[3];
163 int align;
164
165 push_transform_local_identity();
166
167 FW_GL_PUSH_MATRIX();
168
169 //to align with viewepoint, cancel/undo any rotations in modelview matrix
170 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
171 float2double(axis,node->axisOfRotation.c,3);
172 align = (APPROX(veclengthd(axis),0.0f));
173 if(align){
174 //axisOfRotation== (0,0,0) as per specs means full alignment with vp
175 // cancel/undo rotations of modelview matrix:
176 double modb[16], modbi[16];
177 matrixAFFINE2RotationMatrix(modb,mod);
178 matinverseAFFINE(matr,modb);
179 FW_GL_TRANSFORM_D(matr);
180 }else{
181 // normal axisOfRotation
182 //we calculate an additional swing matrix around the axisOfRotation
183 //1. get the position of the vp in billboard-local-coords = vpos
184 //2. cross axisOfRotation with vpos to get a perpendicular to both
185 //3. cross axisOfRotation with zvec to get a perpendicular to both
186 //4. get a rotation difference matrix between those 2 perrp vectors
187 //5. modify modelview by subtracting off the difference rotation
188 double vpos[3], zvec[3], perpa[3], perpb[3];
189 //calculate position of viewpoint vp in billboard-local coords: vpos
190 vecsetd(vpos,0.0,0.0,0.0);
191 matinverseAFFINE(modi,mod);
192 transformAFFINEd(vpos,vpos,modi);
193 vecnormald(vpos,vpos);
194 //z axis in billboard-local system zvec
195 vecsetd(zvec,0.0,0.0,1.0);
196 //2 cross products
197 veccrossd(perpa,axis,vpos);
198 veccrossd(perpb,axis,zvec);
199 //swing matrix around axisOfRotation
200 matrotate2vd(matr,perpa,perpb);
201 FW_GL_TRANSFORM_D(matr);
202 }
203 reset_transform_local(matr);
204
205 }else{
206 // not sure why the old way looked at viewer Quat in case of axisOfRotation 0 0 0
207 // x didn't work with geoViewpoint
208 struct point_XYZ vpos, ax, cp, cp2, arcp;
209 static const struct point_XYZ orig = {.x=0.0, .y=0.0, .z=0.0};
210 static const struct point_XYZ zvec = {.x=0.0, .y=0.0, .z=1.0};
211 struct orient_XYZA viewer_orient;
212 GLDOUBLE mod[16];
213 GLDOUBLE proj[16];
214 int align;
215 double len, len2, angle;
216 int sign;
217
218 ax.x = node->axisOfRotation.c[0];
219 ax.y = node->axisOfRotation.c[1];
220 ax.z = node->axisOfRotation.c[2];
221 align = (APPROX(VECSQ(ax),0));
222
223 viewer_fetch_LCS(Viewer());
224 quaternion_to_vrmlrot(&(Viewer()->Quat),
225 &(viewer_orient.x), &(viewer_orient.y),
226 &(viewer_orient.z), &(viewer_orient.a));
227
228 FW_GL_PUSH_MATRIX();
229
230 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
231 if(0){
232 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
233 FW_GLU_UNPROJECT(orig.x, orig.y, orig.z, mod, proj, viewport, &vpos.x, &vpos.y, &vpos.z);
234 }
235 if(1){
236 //feature-AFFINE_GLU_UNPROJECT
237 double modi[16];
238 matinverseAFFINE(modi,mod);
239 transform(&vpos,&orig,modi);
240 }
241 len = VECSQ(vpos);
242 if (APPROX(len, 0)) { return; }
243 VECSCALE(vpos, 1/sqrt(len));
244
245 if (align) {
246 ax.x = viewer_orient.x;
247 ax.y = viewer_orient.y;
248 ax.z = viewer_orient.z;
249 }
250
251 VECCP(ax, zvec, arcp);
252 len = VECSQ(arcp);
253 if (APPROX(len, 0)) { return; }
254
255 len = VECSQ(ax);
256 if (APPROX(len, 0)) { return; }
257 VECSCALE(ax, 1/sqrt(len));
258
259 VECCP(vpos, ax, cp); /* cp is now 90deg to both vector and axis */
260 len = sqrt(VECSQ(cp));
261 if (APPROX(len, 0)) {
262 FW_GL_ROTATE_RADIANS(-viewer_orient.a, ax.x, ax.y, ax.z);
263 return;
264 }
265 VECSCALE(cp, 1/len);
266
267 /* Now, find out angle between this and z axis */
268 VECCP(cp, zvec, cp2);
269
270 len2 = VECPT(cp, zvec); /* cos(angle) */
271 len = sqrt(VECSQ(cp2)); /* this is abs(sin(angle)) */
272
273 /* Now we need to find the sign first */
274 if (VECPT(cp, arcp) > 0)
275 {
276 sign = -1;
277 } else {
278 sign = 1;
279 }
280 angle = atan2(len2, sign*len);
281
282 FW_GL_ROTATE_RADIANS(angle, ax.x, ax.y, ax.z);
283 }
284}
285
286void fin_Billboard (struct X3D_Billboard *node) {
287 UNUSED(node);
288 pop_transform_local();
289
290 FW_GL_POP_MATRIX();
291}
292
293
294void child_Billboard (struct X3D_Billboard *node) {
295 int nc = node->children.n;
296
297 /* any children at all? */
298 if (nc==0) return;
299
300 /* do we have a local light for a child? */
301 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
302 /* now, just render the non-directionalLight children */
303 prep_BBox((struct BBoxFields*)&node->bboxCenter);
304 normalChildren(node->children);
305 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,TRUE);
306 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
307}
308
309
310/******************************************************************************************/
311
312//
313//void render_NavigationInfo (struct X3D_NavigationInfo *node) {
314// /* check the set_bind eventin to see if it is TRUE or FALSE */
315// ttglobal tg = gglobal();
316// if (node->set_bind < 100) {
317// if (node->set_bind == 1) set_naviinfo(node);
318// bind_node (X3D_NODE(node), getActiveBindableStacks(tg)->navigation);
319// }
320// if(!node->isBound) return;
321//}
322
323
324/*
325Nov 28, 2016 we aren't doing collision->proxy
326COLLISION_PROXY
327http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/navigation.html#Collision
328"""
329The 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.
330"""
331http://www.web3d.org/x3d/content/examples/Basic/development/ProxyShapeExampleIndex.html
332- example showing you can re-order / change the order / length of children field and proxy should still be proxy
333*/
334
335void child_Collision (struct X3D_Collision *node) {
336 int nc = node->children.n;
337 int i;
338 struct X3D_Node *tmpN;
339
340 if(renderstate()->render_collision) {
341 /* test against the collide field (vrml) enabled (x3d) and that we actually have a proxy field */
342 if((node->collide) && (node->enabled) && !(node->proxy)) {
343 struct sCollisionInfo OldCollisionInfo;
344 struct sCollisionInfo * ci = CollisionInfo();
345 OldCollisionInfo = *ci;
346 for(i=0; i<nc; i++) {
347 void *p = ((node->children).p[i]);
348 #ifdef CHILDVERBOSE
349 printf("RENDER COLLISION %d CHILD %d\n",node, p);
350 #endif
351 render_node(p);
352 }
353 if((!APPROX(ci->Offset.x,
354 OldCollisionInfo.Offset.x)) ||
355 (!APPROX(ci->Offset.y,
356 OldCollisionInfo.Offset.y)) ||
357 (!APPROX(ci->Offset.z,
358 OldCollisionInfo.Offset.z))) {
359 /* old code was:
360 if(CollisionInfo.Offset.x != OldCollisionInfo.Offset.x ||
361 CollisionInfo.Offset.y != OldCollisionInfo.Offset.y ||
362 CollisionInfo.Offset.z != OldCollisionInfo.Offset.z) { */
363 /*collision occured
364 * bit 0 gives collision, bit 1 gives change */
365 node->__hit = (node->__hit & 1) ? 1 : 3;
366 } else
367 node->__hit = (node->__hit & 1) ? 2 : 0;
368
369 }
370 if(node->proxy) {
371 POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->proxy,tmpN)
372 render_node(tmpN);
373 }
374
375 } else { /*standard group behaviour*/
376
377 /* do we have a local light for a child? */
378 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
379 /* now, just render the non-directionalLight children */
380 prep_BBox((struct BBoxFields*)&node->bboxCenter);
381 normalChildren(node->children);
382 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
383
384 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
385 }
386}
387
388/* LOD changes between X3D and VRML - level and children fields are "equivalent" */
389void child_LOD (struct X3D_LOD *node) {
390
391/*
392if (node->_selected != NULL) {
393struct X3D_Node *selno = X3D_NODE(node->_selected);
394printf ("childLOD %p (root %p), flags %x ",selno,rootNode,selno->_renderFlags);
395if ((selno->_renderFlags & VF_Viewpoint) == VF_Viewpoint) printf ("VF_Viewpoint ");
396if ((selno->_renderFlags & VF_Geom) == VF_Geom) printf ("VF_Geom ");
397if ((selno->_renderFlags & VF_localLight) == VF_localLight) printf ("VF_localLight ");
398if ((selno->_renderFlags & VF_Sensitive) == VF_Sensitive) printf ("VF_Sensitive ");
399if ((selno->_renderFlags & VF_Blend) == VF_Blend) printf ("VF_Blend ");
400if ((selno->_renderFlags & VF_Proximity) == VF_Proximity) printf ("VF_Proximity ");
401if ((selno->_renderFlags & VF_Collision) == VF_Collision) printf ("VF_Collision ");
402if ((selno->_renderFlags & VF_globalLight) == VF_globalLight) printf ("VF_globalLight ");
403if ((selno->_renderFlags & VF_hasVisibleChildren) == VF_hasVisibleChildren) printf ("VF_hasVisibleChildren ");
404if ((selno->_renderFlags & VF_shouldSortChildren) == VF_shouldSortChildren) printf ("VF_shouldSortChildren ");
405printf ("\n");
406}
407*/
408
409 prep_BBox((struct BBoxFields*)&node->bboxCenter);
410 render_node(node->_selected);
411 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
412
413}
414
415
416/* calculate the LOD distance */
417void proximity_LOD (struct X3D_LOD *node) {
418 GLDOUBLE mod[16];
419 GLDOUBLE proj[16];
420 struct point_XYZ vec;
421 double dist;
422 struct X3D_Node** pp;
423 int n;
424
425 int nran = (node->range).n;
426
427 int i;
428 n = 0;
429 pp = NULL;
430 int spec = X3D_PROTO(node->_executionContext)->__specversion;
431 if (spec < 300 || node->level.n) {
432 pp = node->level.p;
433 n = node->level.n;
434 }
435 else {
436 pp = node->children.p;
437 n = node->children.n;
438 }
439
440 /* no range, display the first node, if it exists */
441 if(!nran) {
442 if (n > 0)
443 node->_selected = pp[0];
444 else
445 node->_selected = NULL;
446 return;
447 }
448
449 /* calculate which one to display */
450 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
451 if(0){
452 //this is centered on the front face of the frustum, about .1 away from avatar center (approximately correct)
453 /* printf ("LOD, mat %f %f %f\n",mod[12],mod[13],mod[14]); */
454 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
455 FW_GLU_UNPROJECT(0,0,0,mod,proj,viewport, &vec.x,&vec.y,&vec.z);
456 //printf("old vec= %f %f %f\n", vec.x,vec.y,vec.z);
457 }
458 if(1){
459 //feature-AFFINE_GLU_UNPROJECT
460 //this is centered on the avatar (correct)
461 double modi[16];
462 struct point_XYZ orig = {.x=0.0,.y=0.0,.z=0.0};
463 matinverseAFFINE(modi,mod);
464 transform(&vec,&orig,modi);
465 //printf("new vec= %f %f %f\n", vec.x,vec.y,vec.z);
466 //printf("\n");
467 }
468 vec.x -= (node->center).c[0];
469 vec.y -= (node->center).c[1];
470 vec.z -= (node->center).c[2];
471
472 dist = sqrt(VECSQ(vec));
473 i = 0;
474
475 while (i<nran) {
476 if(dist < ((node->range).p[i])) { break; }
477 i++;
478 }
479
480
481 if (n > 0) {
482 if (i >= n) i = n - 1;
483 if (node->_lastMethod > 0) {
484 if (Viewer()->SLERPing || Viewer()->SLERPing2 || Viewer()->SLERPing3)
485 node->_lastMethod = 1;
486 else
487 node->_lastMethod = 0; //last time it was user selecting VP. skip one frame to get new distance established
488 }
489 else {
490 node->_selected = pp[i];
491 }
492 }
493 else { node->_selected = NULL; }
494
495 if(i != node->level_changed){
496 node->level_changed = i;
497 MARK_EVENT(X3D_NODE(node),offsetof(struct X3D_LOD,level_changed));
498 }
499}
500
501
502
503/************************************************************************
504 *
505 * ViewpointGroup Node
506 *
507 ************************************************************************/
508 void add_node_to_broto_context(struct X3D_Proto *currentContext,struct X3D_Node *node);
509
510void compile_ViewpointGroup (struct X3D_ViewpointGroup *node) {
511 struct X3D_ProximitySensor *pn;
512
513 /* check if we need to create the proximity node */
514 if (node->__proxNode == NULL) {
515 /* create proximity */
516 pn = (struct X3D_ProximitySensor *) createNewX3DNode(NODE_ProximitySensor);
517 if(node->_executionContext)
518 add_node_to_broto_context(X3D_PROTO(node->_executionContext),X3D_NODE(pn));
519
520 /* any changes needed here?? */
521 node->__proxNode = (void *)pn;
522
523 /* link this in so the VF_Proximity flag will propagate */
524 ADD_PARENT(X3D_NODE(pn),X3D_NODE(node));
525 }
526
527 /* get the Proximity Node */
528 pn = X3D_PROXIMITYSENSOR(node->__proxNode);
529
530 /* copy size, center over */
531 memcpy (&pn->center, &node->center, sizeof (float)*3);
532 memcpy (&pn->size, &node->size, sizeof (float)*3);
533
534 /* enable it */
535 pn->enabled=TRUE;
536
537 /* tell the proximity that it has changed */
538 pn->_change++;
539
540 MARK_NODE_COMPILED
541}
542
543
544void child_ViewpointGroup (struct X3D_ViewpointGroup *node) {
545 int i;
546
547 /* do we have an attached proximity node? If so, we'll be flagged to do
548 the sensitive pass */
549
550 /* printf ("child_ViewpointGroup, this %u rf %x \n",node,node->_renderFlags);
551 printf (" ..., render_hier vp %d geom %d light %d sens %d blend %d prox %d col %d\n",
552 render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision); */
553
554 if (renderstate()->render_proximity) {
555 if (node->__proxNode != NULL) {
556 /* printf ("have prox, rendering it\n"); */
557 render_node(X3D_NODE(node->__proxNode));
558
559 /* printf ("prox active %d\n",X3D_PROXIMITYSENSOR(node->__proxNode)->isActive); */
560 }
561
562 }
563
564 if (!renderstate()->render_vp) return;
565
566 /* render the viewpoints - one of these will be active */
567 for(i=0; i<node->children.n; i++) {
568 struct X3D_Node *p = X3D_NODE(node->children.p[i]);
569 if (p != NULL) {
570 render_node(p);
571 }
572 }
573
574}
575#ifdef _MSC_VER
576#define strcasecmp stricmp
577#endif //_MSC_VER
578void draw_frustum(float *corners);
579static double screespace_allowed_error = 5.0; //pixles?
580void compile_Tile(struct X3D_Tile *node){
581
582}
583void prep_Tile(struct X3D_Tile *node){
584}
585static int tile_view_frozen = FALSE;
586int getTileViewFrozen(){
587 return tile_view_frozen;
588}
589void toggleTileViewFrozen(){
590 //July 2020 currently hooked to '=' key
591 tile_view_frozen = 1 - tile_view_frozen;
592 if(tile_view_frozen) printf("FREEZING tile view\n");
593 else printf("UN-FREEZING tile view\n");
594}
595enum {
596 TILE_REFINE_DEFAULT = 0,
597 TILE_REFINE_REPLACE = 1,
598 TILE_REFINE_ADD = 2,
599};
600enum {
601 BOUNDING_VOLUME_NONE = 0, //can't have NONE because we need a range for SSE calc, and get it from BBOX etc
602 BOUNDING_VOLUME_BBOX = 1,
603 BOUNDING_VOLUME_SPHERE = 2,
604 BOUNDING_VOLUME_REGION = 3,
605};
606void child_Tile(struct X3D_Tile *node){
607//
608// similar to Tiles3D?
609// https://github.com/CesiumGS/3d-tiles/blob/master/3d-tiles-overview.pdf
610//
611 double screenspace_error = 1.e+06;
612 static double mod[16], proj[16], mvproj[16];
613 static struct Planed frustum_planes[6];
614 static float frustum_corners[24];
615 static int have_frustum_corners;
616 static int have_mod = FALSE;
617 static int child_tile = FALSE;
618 static int once = FALSE;
619 int root_tile = FALSE;
620 if(!child_tile) root_tile = TRUE;
621
622 if(!once){
623 printf("Press '=' key to FREEZE / UNFREEZE Tile computational Viewpoint\n");
624 once = TRUE;
625 }
626
627 if( (!getTileViewFrozen() || !have_mod) && root_tile){
628 //for texting we need a way to freeze the viewpoint used for
629 // computing screenspace error and frustun
630 //FW_GL_MATRIX_MODE(GL_MODELVIEW);
631 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
632 //FW_GL_MATRIX_MODE(GL_PROJECTION);
633 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
634 //FW_GL_MATRIX_MODE(GL_MODELVIEW);
635 matmultiplyFULL(mvproj,mod,proj);
636 setFrustumPlanes(mvproj,frustum_planes);
637 have_frustum_corners = frustum_generate_corner_points(frustum_planes, frustum_corners);
638 have_mod = TRUE;
639 }
640 if(have_frustum_corners && root_tile){
641 draw_frustum(frustum_corners);
642 }
643 int refine, cbvtype, bvtype;
644 refine = TILE_REFINE_DEFAULT; //we should get it from a stack, so top one dominates.
645 if(!strcasecmp(node->refine->strptr,"REPLACE")) refine = TILE_REFINE_REPLACE;
646 else if(!strcasecmp(node->refine->strptr,"ADD")) refine = TILE_REFINE_ADD;
647
648 //for bounding volumes we want good 'lazy defaults' and that's to not do frustum culling ==NONE
649 // if no boundingVolume is specified, or no boundingVolumeType is specified.
650 cbvtype = BOUNDING_VOLUME_NONE;
651 if(!strcasecmp(node->contentVolumeType->strptr,"BBOX")) cbvtype = BOUNDING_VOLUME_BBOX;
652 else if(!strcasecmp(node->contentVolumeType->strptr,"SPHERE")) cbvtype = BOUNDING_VOLUME_SPHERE;
653 else if(!strcasecmp(node->contentVolumeType->strptr,"REGION")) cbvtype = BOUNDING_VOLUME_REGION;
654 if(node->contentVolume.n == 0) cbvtype = BOUNDING_VOLUME_NONE;
655 bvtype = BOUNDING_VOLUME_NONE;
656 if(!strcasecmp(node->boundingVolumeType->strptr,"BBOX")) bvtype = BOUNDING_VOLUME_BBOX;
657 else if(!strcasecmp(node->boundingVolumeType->strptr,"SPHERE")) bvtype = BOUNDING_VOLUME_SPHERE;
658 else if(!strcasecmp(node->boundingVolumeType->strptr,"REGION")) bvtype = BOUNDING_VOLUME_REGION;
659 if(node->boundingVolume.n == 0) bvtype = BOUNDING_VOLUME_NONE;
660
661 int inview_content, inview_tile;
662 inview_content = inview_tile = TRUE;
663 //adapted from proximit_LOD
664 {
665 double modi[16], orig[3], origb[3], vec[3],vecb[3], vec4[4], range, viewspace_error, nearplane_error;
666 int viewPort[10];
667 /* calculate which one to display */
668 // Tiles3D S.1 screen space error:
669 // sse = (geometricError * screenHeight) / (tileDistance* 2*tan(fovy/2))
670 // our method: transform 2 points from tile space to screen space
671 // - in tile space they are geometricError distance apart
672 // - in screen space they will be SSE apart
673 // - should work for orthoViewpoint as well as perspective
674 {
675 range = 100.0;
676 //1) get distance-to-tile
677 if(bvtype == BOUNDING_VOLUME_BBOX && node->boundingVolume.n == 12){
678 //for X3D could have separate OBB oriented bounding box and BBOX standard bounding box
679 float2double(orig,node->boundingVolume.p,3);
680 transformAFFINEd(vec,orig,mod);
681 range = veclengthd(vec);
682 }else if(bvtype == BOUNDING_VOLUME_SPHERE && node->boundingVolume.n == 4){
683 float2double(orig,node->boundingVolume.p,3);
684 transformAFFINEd(vec,orig,mod);
685 range = veclengthd(vec);
686 }else if(bvtype == BOUNDING_VOLUME_REGION && node->boundingVolume.n == 6){
687 }
688 //2) transform 2 points geometricError apart in X, into screenspace
689 vecsetd(vec4,node->geometricError,0.0,-range);
690 vec4[3] = 1.0;
691 transformFULL4d(vec4,vec4,proj);
692 vecscaled(orig,vec4,1.0/vec4[3]);
693
694 vecsetd(vec4,0.0,0.0,-range);
695 vec4[3] = 1.0;
696 transformFULL4d(vec4,vec4,proj);
697 vecscaled(origb,vec4,1.0/vec4[3]);
698
699 //3) get the 2 points distance apart in screen space
700 vecdifd(vec,orig,origb);
701 nearplane_error = veclengthd(vec);
702 FW_GL_GETINTEGERV(GL_VIEWPORT, viewPort);
703 screenspace_error = (nearplane_error / 2.0) * (double) viewPort[2];
704
705 }
706 //printf("screen %lf near %lf view %lf\n",screenspace_error,nearplane_error,viewspace_error);
707
708 //test bounding volume against view frustum
709 if(cbvtype == BOUNDING_VOLUME_BBOX && node->contentVolume.n == 12)
710 {
711 float p3fn24[24], extent6[6], overlap[6];
712 orientedBBox2vec3fn(p3fn24, node->contentVolume.p);
713
714 inview_content = FALSE;
715 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/
716 if(0){
717 //CLIP-SPACE / CUBOID SPACE CULL (doesn't work July 18, 2020
718 double dd[4];
719 float ff[3], cuboid[6];
720 int inside = FALSE;
721 extent6f_constructor(cuboid,-1.0f,1.0f,-1.0f,1.0f,-1.0f,1.0f);
722
723 for(int i=0;i<8;i++){
724 float2double(dd,&p3fn24[3*i],3);
725 dd[3] = 1.0;
726 transformFULL4d(dd,dd,mvproj);
727 vecscaled(dd,dd,1.0/dd[3]);
728 double2float(ff,dd,3);
729 inside = inside || extent6f_point_inside(cuboid,ff);
730 }
731 float2double(dd,node->contentVolume.p,3); //center point
732 dd[3] = 1.0;
733 transformFULL4d(dd,dd,mvproj);
734 vecscaled(dd,dd,1.0/dd[3]);
735 double2float(ff,dd,3);
736 inside = inside || extent6f_point_inside(cuboid,ff);
737
738 inview_content = inside;
739 }
740 if(0){
741 //geometric cull - simple corner point cull - in viewer space, works a bit July 22, 2020
742 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/
743 double dd[3];
744 float ftemp[3];
745 int inpoint, inside = FALSE;
746 for(int i=0;i<8;i++){
747 float2double(dd,&p3fn24[3*i],3);
748 //transformAFFINEd(dd,dd,mod);
749 inpoint = frustum_point_inside(frustum_planes,dd);
750 inside = inside || inpoint;
751 //if(inpoint && child_tile) draw_bbox(&p3fn24[3*i],vecset3f(ftemp,30.0f,30.0f,30.0f));
752 }
753 float2double(dd,node->contentVolume.p,3); //center point
754 inpoint = frustum_point_inside(frustum_planes,dd);
755 inside = inside || inpoint;
756 //if(inpoint && child_tile) draw_bbox(node->contentVolume.p,vecset3f(ftemp,10.0f,50.0f,10.0f));
757
758 inview_content = inside;
759 }
760 if(1){
761 //geometric cull - box corners vs frustum - works July 23, 2020
762 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes/
763 inview_content = frustum_box_inside(frustum_planes,p3fn24,8);
764 }
765 }
766 if(bvtype == BOUNDING_VOLUME_BBOX && node->boundingVolume.n == 12)
767 {
768 float p3fn24[24], extent6[6], overlap[6];
769 double mvproj[16];
770 matmultiplyFULL(mvproj,proj,mod);
771 orientedBBox2vec3fn(p3fn24, node->boundingVolume.p);
772 inview_tile = FALSE;
773 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/
774 if(0){
775 //CLIP-SPACE / CUBOID SPACE CULL (doesn't work July 18, 2020
776 double dd[4];
777 float ff[3], cuboid[6];
778 extent6f_constructor(cuboid,-1.0f,1.0f,-1.0f,1.0f,-1.0f,1.0f);
779 int inside = FALSE;
780 for(int i=0;i<8;i++){
781 float2double(dd,&p3fn24[3*i],3);
782 dd[3] = 1.0;
783 transformFULL4d(dd,dd,mvproj);
784 vecscaled(dd,dd,1.0/dd[3]);
785 double2float(ff,dd,3);
786 inside = inside || extent6f_point_inside(cuboid,ff);
787 }
788 float2double(dd,node->boundingVolume.p,3); //center point
789 dd[3] = 1.0;
790 transformFULL4d(dd,dd,mvproj);
791 vecscaled(dd,dd,1.0/dd[3]);
792 double2float(ff,dd,3);
793 inside = inside || extent6f_point_inside(cuboid,ff);
794
795 inview_tile = inside;
796 }
797 if(0){
798 //geometric cull - simple corner point cull - in viewer space, works a bit July 22, 2020
799 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes-ii/
800 double dd[3];
801 float ftemp[3];
802 int inpoint, inside = FALSE;
803 for(int i=0;i<8;i++){
804 float2double(dd,&p3fn24[3*i],3);
805 inpoint = frustum_point_inside(frustum_planes,dd);
806 inside = inside || inpoint;
807 //if(inpoint && child_tile) draw_bbox(&p3fn24[3*i],vecset3f(ftemp,30.0f,30.0f,30.0f));
808 //printf("bvcoord %d %f %f %f\n",i,p3fn24[i*3],p3fn24[i*3+1],p3fn24[i*3+2]);
809
810
811 }
812 float2double(dd,node->boundingVolume.p,3); //center point
813 inpoint = frustum_point_inside(frustum_planes,dd);
814 inside = inside || inpoint;
815 //if(inpoint && child_tile) draw_bbox(node->boundingVolume.p,vecset3f(ftemp,10.0f,50.0f,10.0f));
816
817 inview_tile = inside;
818 }
819 if(1){
820 //geometric cull - box corners vs frustum - works July 23, 2020
821 // http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-testing-boxes/
822 inview_tile = frustum_box_inside(frustum_planes,p3fn24,8);
823 }
824
825 }
826 }
827 if(root_tile) child_tile = TRUE;
828 int no_sse_cull = FALSE;
829 int no_bv_cull = FALSE;
830 prep_BBox((struct BBoxFields*)&node->bboxCenter);
831 if(cbvtype == BOUNDING_VOLUME_NONE || cbvtype == BOUNDING_VOLUME_BBOX && inview_content || no_bv_cull)
832 if(screenspace_error <= screespace_allowed_error || node->children.n == 0 || refine == TILE_REFINE_ADD || no_sse_cull){
833 render_node(node->content);
834 //content > Inline may need signal to load or unload
835 if(node->showContent == FALSE){
836 node->showContent = TRUE;
837 MARK_EVENT (X3D_NODE(node),offsetof (struct X3D_Tile, showContent));
838 }
839 }else{
840 if(node->showContent == TRUE){
841 node->showContent = FALSE;
842 MARK_EVENT (X3D_NODE(node),offsetof (struct X3D_Tile, showContent));
843 }
844 }
845 if(bvtype == BOUNDING_VOLUME_NONE || bvtype == BOUNDING_VOLUME_BBOX && inview_tile || no_bv_cull)
846 if(screenspace_error > screespace_allowed_error && node->children.n > 0 || no_sse_cull){
847 //adapted from child_Group:
848 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
849 //prep_BBox((struct BBoxFields*)&node->bboxCenter);
850 normalChildren(node->children);
851 //fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
852 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
853 }
854 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
855 if(root_tile) child_tile = FALSE;
856
857
858}
859void proximity_Tile(struct X3D_Tile *node){
860 //double mod[16],modi[16], orig[3], vec[3];
861
863 //FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
866 //vecsetd(orig,0,.0,0.0,0.0);
867 //matinverseAFFINE(modi,mod);
868 //transformAFFINEd(vec,orig,modi);
871
872
873}