FreeWRL/FreeX3D  3.0.0
Component_EnvironSensor.c
1 /*
2 
3 
4 X3D Environmental Sensors 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 "../vrml_parser/CRoutes.h"
39 #include "../main/headers.h"
40 
41 #include "LinearAlgebra.h"
42 #include "Component_Geospatial.h"
43 #include "../opengl/Frustum.h"
44 #include "../opengl/OpenGL_Utils.h"
45 #include "../scenegraph/Component_Shape.h"
46 #include "../scenegraph/RenderFuncs.h"
47 
48 
50 //int candoVisibility = TRUE;
51 typedef struct pComponent_EnvironSensor{
52  /* can we do a VisibiltySensor? Only if we have OpenGL support for OcclusionCulling */
53  int candoVisibility;// = TRUE;
54 
56 
57 static void *Component_EnvironSensor_constructor(){
58  void *v = MALLOCV(sizeof(struct pComponent_EnvironSensor));
59  memset(v,0,sizeof(struct pComponent_EnvironSensor));
60  return v;
61 }
62 
63 // iglobal.c calls this one.
64 void Component_EnvironSensor_init(struct tComponent_EnvironSensor *t){
65  //public
66  //private
67  t->prv = Component_EnvironSensor_constructor();
68  {
70  /* can we do a VisibiltySensor? Only if we have OpenGL support for OcclusionCulling */
71  p->candoVisibility = TRUE;
72 
73  }
74 }
75 
76 #ifdef VISIBILITYOCCLUSION
77 
78 static void rendVisibilityBox (struct X3D_VisibilitySensor *node);
79 #endif
80 
81 //PROXIMITYSENSOR(ProximitySensor,center,,);
82 
83 /* ProximitySensor and GeoProximitySensor are same "code" at this stage of the game */
84 //#define PROXIMITYSENSOR(type,center,initializer1,initializer2)
85 void proximity_ProximitySensor (struct X3D_ProximitySensor *node) {
86  /* Viewer pos = t_r2 */
87  double cx,cy,cz;
88  double len;
89  struct point_XYZ dr1r2;
90  struct point_XYZ dr2r3;
91  struct point_XYZ nor1,nor2;
92  struct point_XYZ ins;
93  static const struct point_XYZ yvec = {0,0.05,0};
94  static const struct point_XYZ zvec = {0,0,-0.05};
95  static const struct point_XYZ zpvec = {0,0,0.05};
96  static const struct point_XYZ orig = {0,0,0};
97  struct point_XYZ t_zvec, t_yvec, t_orig, t_center;
98  GLDOUBLE modelMatrix[16];
99  GLDOUBLE projMatrix[16];
100  GLDOUBLE view2prox[16];
101 
102  if(!((node->enabled))) return;
103  //initializer1
104  //initializer2
105 
106  /* printf (" vp %d geom %d light %d sens %d blend %d prox %d col %d\n",*/
107  /* render_vp,render_geom,render_light,render_sensitive,render_blend,render_proximity,render_collision);*/
108 
109  /* transforms viewers coordinate space into sensors coordinate space.
110  * this gives the orientation of the viewer relative to the sensor.
111  */
112  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
113  FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, projMatrix);
114  FW_GLU_UNPROJECT(orig.x,orig.y,orig.z,modelMatrix,projMatrix,viewport,
115  &t_orig.x,&t_orig.y,&t_orig.z);
116  FW_GLU_UNPROJECT(zvec.x,zvec.y,zvec.z,modelMatrix,projMatrix,viewport,
117  &t_zvec.x,&t_zvec.y,&t_zvec.z);
118  FW_GLU_UNPROJECT(yvec.x,yvec.y,yvec.z,modelMatrix,projMatrix,viewport,
119  &t_yvec.x,&t_yvec.y,&t_yvec.z);
120  matinverse(view2prox,modelMatrix);
121  transform(&t_center,&orig, view2prox);
122 
123 
124  /*printf ("\n");
125  printf ("unprojected, t_orig (0,0,0) %lf %lf %lf\n",t_orig.x, t_orig.y, t_orig.z);
126  printf ("unprojected, t_yvec (0,0.05,0) %lf %lf %lf\n",t_yvec.x, t_yvec.y, t_yvec.z);
127  printf ("unprojected, t_zvec (0,0,-0.05) %lf %lf %lf\n",t_zvec.x, t_zvec.y, t_zvec.z);
128  */
129  cx = t_center.x - ((node->center ).c[0]);
130  cy = t_center.y - ((node->center ).c[1]);
131  cz = t_center.z - ((node->center ).c[2]);
132 
133  if(((node->size).c[0]) == 0 || ((node->size).c[1]) == 0 || ((node->size).c[2]) == 0) return;
134 
135  if(fabs(cx) > ((node->size).c[0])/2 ||
136  fabs(cy) > ((node->size).c[1])/2 ||
137  fabs(cz) > ((node->size).c[2])/2) {
138  //printf ("outside (Geo)ProximitySensor\n");
139  return;
140  }
141  //printf ("within (Geo)ProximitySensor\n");
142 
143  /* Ok, we now have to compute... */
144  (node->__hit) = 1;
145 
146  /* Position */
147  ((node->__t1).c[0]) = (float)t_center.x;
148  ((node->__t1).c[1]) = (float)t_center.y;
149  ((node->__t1).c[2]) = (float)t_center.z;
150 
151  VECDIFF(t_zvec,t_orig,dr1r2); /* Z axis */
152  VECDIFF(t_yvec,t_orig,dr2r3); /* Y axis */
153 
154  /* printf (" dr1r2 %lf %lf %lf\n",dr1r2.x, dr1r2.y, dr1r2.z);
155  printf (" dr2r3 %lf %lf %lf\n",dr2r3.x, dr2r3.y, dr2r3.z);
156  */
157 
158  len = sqrt(VECSQ(dr1r2)); VECSCALE(dr1r2,1/len);
159  len = sqrt(VECSQ(dr2r3)); VECSCALE(dr2r3,1/len);
160 
161  /* printf ("scaled dr1r2 %lf %lf %lf\n",dr1r2.x, dr1r2.y, dr1r2.z);
162  printf ("scaled dr2r3 %lf %lf %lf\n",dr2r3.x, dr2r3.y, dr2r3.z);
163  */
164 
165  /*
166  printf("PROX_INT: (%f %f %f) (%f %f %f) (%f %f %f)\n (%f %f %f) (%f %f %f)\n",
167  t_orig.x, t_orig.y, t_orig.z,
168  t_zvec.x, t_zvec.y, t_zvec.z,
169  t_yvec.x, t_yvec.y, t_yvec.z,
170  dr1r2.x, dr1r2.y, dr1r2.z,
171  dr2r3.x, dr2r3.y, dr2r3.z
172  );
173  */
174 
175  if(fabs(VECPT(dr1r2, dr2r3)) > 0.001) {
176  printf ("Sorry, can't handle unevenly scaled ProximitySensors yet :("
177  "dp: %f v: (%f %f %f) (%f %f %f)\n", VECPT(dr1r2, dr2r3),
178  dr1r2.x,dr1r2.y,dr1r2.z,
179  dr2r3.x,dr2r3.y,dr2r3.z
180  );
181  return;
182  }
183 
184 
185  if(APPROX(dr1r2.z,1.0)) {
186  /* rotation */
187  ((node->__t2).c[0]) = (float) 0;
188  ((node->__t2).c[1]) = (float) 0;
189  ((node->__t2).c[2]) = (float) 1;
190  ((node->__t2).c[3]) = (float) atan2(-dr2r3.x,dr2r3.y);
191  } else if(APPROX(dr2r3.y,1.0)) {
192  /* rotation */
193  ((node->__t2).c[0]) = (float) 0;
194  ((node->__t2).c[1]) = (float) 1;
195  ((node->__t2).c[2]) = (float) 0;
196  ((node->__t2).c[3]) = (float) atan2(dr1r2.x,dr1r2.z);
197  } else {
198  /* Get the normal vectors of the possible rotation planes */
199  nor1 = dr1r2;
200  nor1.z -= 1.0;
201  nor2 = dr2r3;
202  nor2.y -= 1.0;
203 
204  /* Now, the intersection of the planes, obviously cp */
205  VECCP(nor1,nor2,ins);
206 
207  len = sqrt(VECSQ(ins)); VECSCALE(ins,1/len);
208 
209  /* the angle */
210  VECCP(dr1r2,ins, nor1);
211  VECCP(zpvec, ins, nor2);
212  len = sqrt(VECSQ(nor1)); VECSCALE(nor1,1/len);
213  len = sqrt(VECSQ(nor2)); VECSCALE(nor2,1/len);
214  VECCP(nor1,nor2,ins);
215 
216  ((node->__t2).c[3]) = (float) -atan2(sqrt(VECSQ(ins)), VECPT(nor1,nor2));
217 
218  /* rotation - should normalize sometime... */
219  ((node->__t2).c[0]) = (float) ins.x;
220  ((node->__t2).c[1]) = (float) ins.y;
221  ((node->__t2).c[2]) = (float) ins.z;
222  }
223  /*
224  printf("NORS: (%f %f %f) (%f %f %f) (%f %f %f)\n",
225  nor1.x, nor1.y, nor1.z,
226  nor2.x, nor2.y, nor2.z,
227  ins.x, ins.y, ins.z
228  );
229  */
230 }
231 
232 
233 
234 
235 /* ProximitySensor code for ClockTick */
236 void do_ProximitySensorTick( void *ptr) {
237  struct X3D_ProximitySensor *node = (struct X3D_ProximitySensor *)ptr;
238 
239  /* if not enabled, do nothing */
240  if (!node) return;
241  if (node->__oldEnabled != node->enabled) {
242  node->__oldEnabled = node->enabled;
243  MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_ProximitySensor, enabled));
244  }
245  if (!node->enabled) return;
246 
247  /* did we get a signal? */
248  if (node->__hit) {
249  //avatar inside proximity sensor
250  if (!node->isActive) {
251  #ifdef SEVERBOSE
252  printf ("PROX - initial defaults\n");
253  #endif
254 
255  node->isActive = TRUE;
256  node->enterTime = TickTime();
257  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, isActive));
258  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, enterTime));
259  }
260 
261  /* now, has anything changed? */
262  if (memcmp ((void *) &node->position_changed,(void *) &node->__t1,sizeof(struct SFColor))) {
263  #ifdef SEVERBOSE
264  printf ("PROX - position changed!!! \n");
265  #endif
266 
267  memcpy ((void *) &node->position_changed,
268  (void *) &node->__t1,sizeof(struct SFColor));
269  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, position_changed));
270  }
271  if (memcmp ((void *) &node->orientation_changed, (void *) &node->__t2,sizeof(struct SFRotation))) {
272  #ifdef SEVERBOSE
273  printf ("PROX - orientation changed!!!\n ");
274  #endif
275 
276  memcpy ((void *) &node->orientation_changed,
277  (void *) &node->__t2,sizeof(struct SFRotation));
278  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, orientation_changed));
279  }
280  } else {
281  //avatar outside proximity sensor or not enabled
282  if (node->isActive) {
283  #ifdef SEVERBOSE
284  printf ("PROX - stopping\n");
285  #endif
286 
287  node->isActive = FALSE;
288  node->exitTime = TickTime();
289  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, isActive));
290 
291  MARK_EVENT (ptr, offsetof(struct X3D_ProximitySensor, exitTime));
292  }
293  }
294  node->__hit=FALSE;
295 }
296 
297 
298 
299 void transformMBB(GLDOUBLE *rMBBmin, GLDOUBLE *rMBBmax, GLDOUBLE *matTransform, GLDOUBLE* inMBBmin, GLDOUBLE* inMBBmax);
300 int transformMBB4d(GLDOUBLE *rMBBmin, GLDOUBLE *rMBBmax, GLDOUBLE *matTransform, GLDOUBLE* inMBBmin, GLDOUBLE* inMBBmax, int isAffine);
301 int __gluInvertMatrixd(const GLDOUBLE m[16], GLDOUBLE invOut[16]);
302 void __gluMultMatrixVecd(const GLDOUBLE matrix[16], const GLDOUBLE in[4], GLDOUBLE out[4]);
303 
304 
305 static void twoPoints2RayMatrix(double *ptnear, double* ptfar, double* rayMatrix){
306  double R1[16], R2[16], R3[16], T[16], rayMatrixInverse[16];
307  double *A, *B, C[3];
308  double yaw, pitch;
309 
310  A = ptnear;
311  B = ptfar;
312  //nearside point
313  mattranslate(T,A[0],A[1],A[2]);
314  vecdifd(C,B,A);
315  vecnormald(C,C);
316  if(0) printf("Cdif %f %f %f\n",C[0],C[1],C[2]);
317  yaw = atan2(C[0],-C[2]);
318  matrixFromAxisAngle4d(R1, -yaw, 0.0, 1.0, 0.0);
319  transformAFFINEd(C,C,R1);
320  if(0) printf("Yawed Cdif %f %f %f\n",C[0],C[1],C[2]);
321  pitch = atan2(C[1],-C[2]);
322  if(0) printf("atan2 yaw=%f pitch=%f\n",yaw,pitch);
323  pitch = -pitch;
324  if(0) printf("[yaw=%f pitch=%f\n",yaw,pitch);
325 
326  matrixFromAxisAngle4d(R1, pitch, 1.0, 0.0, 0.0);
327  if(0) printmatrix2(R1,"pure R1");
328  matrixFromAxisAngle4d(R2, yaw, 0.0, 1.0, 0.0);
329  if(0) printmatrix2(R2,"pure R2");
330  matmultiplyAFFINE(R3,R1,R2);
331  if(0) printmatrix2(R3,"R3=R1*R2");
332  matmultiplyAFFINE(rayMatrixInverse,R3, T);
333  matinverseAFFINE(rayMatrix,rayMatrixInverse);
334 
335 }
336 
337 static int frustumHitsMBB(float *extent){
338  //goal say if an extent is maybe inside (err toward inside) of the view frustum
339  //http://cgvr.informatik.uni-bremen.de/teaching/cg_literatur/lighthouse3d_view_frustum_culling/index.html
340  // overlap tests: https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_collision_detection
341  // cuboid space: frustum is a -1 to 1 cube
342  GLDOUBLE modelMatrix[16], projectionMatrix[16];
343  int i,isIn;
344  GLDOUBLE smin[3], smax[3], shapeMBBmin[3], shapeMBBmax[3];
345 
346  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
347  FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, projectionMatrix);
348 
349  /* generate mins and maxes for avatar cylinder in avatar space to represent the avatar collision volume */
350  for(i=0;i<3;i++)
351  {
352  shapeMBBmin[i] = extent[i*2 + 1];
353  shapeMBBmax[i] = extent[i*2];
354  }
355  //check view space is it behind the frontplane / viewpoint
356  transformMBB(smin,smax,modelMatrix,shapeMBBmin,shapeMBBmax); //transform shape's MBB into view space
357  isIn = TRUE;
358  isIn = smin[2] < 0.0; //-z is in front of viewpoint
359  if(isIn){
360  //plane check: rotate so lower left corner of screen is in center and do xy comparison
361  // repeat for upper-right
362  /*
363  frustum-plane aligned method
364  - in view space (apply modelview, but not projection)
365  - plus a translation and rotation so looking straight down a side of frustum
366  - then can do simple X or Y test if something is out
367  - to reduce flops, can rotate so looking down corner-edge (2 planes intersect) and do X,Y
368  - then only need 2 runs to check 4 planes
369  How: - (similar to pickray-style matrix method - see mainloop.c setup_pickray0() about line 5409 july 2016)
370  1. generate near+far frustum corner points:
371  - unproject a couple of cuboid points along a corner of the frustom: x-1,y-1, near (z=1) and far (z=-1 or vice versa)
372  2. from the 2 points compute a yaw, and a pitch, using atan2 etc
373  3. make a rotation matrix from yaw and pitch
374  4. from the near point make a Translation
375  5. multiply the rotation and translation
376  6. maybe inverse, depending on which way you're going.
377  7. then you could concatonate that matrix with your modelview
378  8. transform your geom node extent using this concatonated matrix
379  9. check transformed shape MBB/AABB against x=0 and y=0 planes: if to the left, then out, if above then out.
380  10. repeat for diagonal edge/corner of frustum x=1,y=1 in cuboid space
381  11. check near and far planes if you want to, although near is done by cone planes except a little near-cone, and far: who cares.
382  */
383  int k;
384  double rayMatrix[16], modelMatrixPlus[16], projInverse[16], nearplane, farplane;
385  double A[4], B[4], a[4], b[4];
386 
387  //we are working in something close to view space, so
388  //-transform frustum cuboid to view via projection inverse, to set up 'plus'
389  //-transform node extent to view via modelview matrix, 'plus' a bit to align with frustum
390 
391  // OLDCODE iret = __gluInvertMatrixd( projectionMatrix, projInverse);
392 
393  //k: from lower-left corner of frustum to upper-right corner...
394  for(k=-1;k<=1;k+=2)
395  {
396  double xy;
397  xy = 1.0*k; //use -1,-1 for lower-left, 1,1 for upper-right
398  a[0] = a[1] = b[0] = b[1] = xy;
399  a[3] = b[3] = 1.0; //homogenous .w for full 4x4
400 
401  //nearside point
402  a[2] = -1.0;
403  __gluMultMatrixVecd(projInverse, a, A);
404  vecscaled(A,A,1.0/A[3]); //divide by homogenous .w
405  nearplane = A[2]; //near and far set elsewhere: ie .1 - 21000, here we recover nearplane, could use for testing
406 
407  //farside point
408  b[2] = 1.0;
409  __gluMultMatrixVecd(projInverse, b, B);
410  vecscaled(B,B,1.0/B[3]);
411  farplane = B[2];
412 
413  twoPoints2RayMatrix(A,B,rayMatrix); //compute 'plus' part
414 
415  matmultiplyAFFINE(modelMatrixPlus,modelMatrix,rayMatrix);
416 
417  //transform shape's MBB into frustum-plane (frustum corner) aligned space
418  transformMBB(smin,smax,modelMatrixPlus,shapeMBBmin,shapeMBBmax);
419  //now fustum corner is at xy 0,0, so its a simple xy test against 0
420  for(i=0;i<2;i++){
421  if(k==-1)
422  isIn = isIn && smax[i] > 0.0;
423  if(k==1)
424  isIn = isIn && smin[i] < 0.0;
425  }
426  }
427  if(isIn){
428  //check near and far planes
429  transformMBB(smin,smax,modelMatrix,shapeMBBmin,shapeMBBmax);
430  isIn = isIn && smin[2] < nearplane; //these are -ve numbers - z is positive backwards
431  isIn = isIn && smax[2] > farplane;
432  }
433  }
434 
435  return isIn;
436 }
437 
438 
439 void other_VisibilitySensor (struct X3D_VisibilitySensor *node) {
440  // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/envsensor.html#VisibilitySensor
441  //ttrenderstate rs;
442  ttglobal tg = gglobal();
443  /* if not enabled, do nothing */
444  if (!node) return;
445  if (!node->enabled)
446  return;
447  {
448  ppComponent_EnvironSensor p = (ppComponent_EnvironSensor)tg->Component_EnvironSensor.prv;
449  if (!p->candoVisibility) return;
450  //aabb method
451  /* first time through, if we have a visibility sensor, but do not have the OpenGL ability to
452  use it, we print up a console message */
453  //1. transform local axix-aligned bounding box AABB from local to cuboid
454  // cuboid_aabb = projection * modelview * local_aabb
455  //2. intersect cuboid_aabb with cuboid which is -1 to 1 in 3 dimensions
456  //3. if they don't intersect, not visible, else maybe visible
457  {
458  //update extent in case size or center changed
459  int i, ihit;
460  float emin[3], emax[3];
461  vecadd3f(emax, node->center.c, node->size.c);
462  vecdif3f(emin, node->center.c, node->size.c);
463  for(i=0;i<3;i++)
464  {
465  node->_extent[i*2 + 1] = emin[i];
466  node->_extent[i*2] = emax[i];
467  }
468  ihit = frustumHitsMBB(node->_extent);
469  if(ihit){
470  node->__Samples++; //initialized to 0 once each frame elsewhere
471  //can come in here multiple times per frame: once per each viewport (stereo 2, quad 4) x once per each DEF/USE
472  //if any DEF/USE or viewport visible, then __visible = TRUE:
473  //if visibility was different on last frame, then send events
474 
475  }
476  }
477 
478 #ifdef VISIBILITYOCCLUSION
479  //occlusion method
480  if (tg->Frustum.OccFailed) {
481  p->candoVisibility = FALSE;
482  ConsoleMessage("VisibilitySensor: OpenGL on this machine does not support GL_ARB_occlusion_query");
483  return;
484  }
485 
486  rs = renderstate();
487  RECORD_DISTANCE
488 
489  if (rs->render_blend) {
490 
491  //BEGINOCCLUSIONQUERY
492  beginOcclusionQuery(node,renderstate()->render_geom);
493  LIGHTING_OFF
494  DISABLE_CULL_FACE
495 
496  rendVisibilityBox(node);
497 
498  ENABLE_CULL_FACE
499  LIGHTING_ON
500 
501  //ENDOCCLUSIONQUERY
502  endOcclusionQuery(node,renderstate()->render_geom);
503  }
504 #endif
505  }
506 }
507 
508 
509 #ifdef VISIBILITYOCCLUSION
510 
511 static void rendVisibilityBox (struct X3D_VisibilitySensor *node) {
512 #ifdef HAVE_TO_REIMPLEMENT
513  extern GLfloat boxnorms[]; /* in CFuncs/statics.c*/
514  float *pt;
515  float x = ((node->size).c[0])/2;
516  float y = ((node->size).c[1])/2;
517  float z = ((node->size).c[2])/2;
518  float cx = node->center.c[0];
519  float cy = node->center.c[1];
520  float cz = node->center.c[2];
521 
522  /* test for <0 of sides */
523  if ((x < 0) || (y < 0) || (z < 0)) return;
524 
525  /* for BoundingBox calculations */
526  setExtent(cx+x, cx-x, cx+y, cx-y, cx+z, cx-z,X3D_NODE(node));
527 
528 
529  /* printf ("VISIBILITY BOXc 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 NODE_NEEDS_COMPILING {
533  /* have to regen the shape*/
534  MARK_NODE_COMPILED
535 
536  /* MALLOC memory (if possible)*/
537  /* do not worry about the __points.n field; we know the size by default */
538  if (!node->__points.p) node->__points.p = MALLOC (struct SFVec3f*, sizeof(struct SFVec3f)*(36));
539 
540 
541  /* now, create points; 4 points per face.*/
542  pt = (float *) node->__points.p;
543 
544  #define PTF0 *pt++ = cx+x; *pt++ = cy+y; *pt++ = cz+z;
545  #define PTF1 *pt++ = cx-x; *pt++ = cy+y; *pt++ = cz+z;
546  #define PTF2 *pt++ = cx-x; *pt++ = cy-y; *pt++ = cz+z;
547  #define PTF3 *pt++ = cx+x; *pt++ = cy-y; *pt++ = cz+z;
548  #define PTR0 *pt++ = cx+x; *pt++ = cy+y; *pt++ = cz-z;
549  #define PTR1 *pt++ = cx-x; *pt++ = cy+y; *pt++ = cz-z;
550  #define PTR2 *pt++ = cx-x; *pt++ = cy-y; *pt++ = cz-z;
551  #define PTR3 *pt++ = cx+x; *pt++ = cy-y; *pt++ = cz-z;
552 
553 
554  PTF0 PTF1 PTF2 PTF0 PTF2 PTF3 /* front */
555  PTR2 PTR1 PTR0 PTR3 PTR2 PTR0 /* back */
556  PTF0 PTR0 PTR1 PTF0 PTR1 PTF1 /* top */
557  PTF3 PTF2 PTR2 PTF3 PTR2 PTR3 /* bottom */
558  PTF0 PTF3 PTR3 PTF0 PTR3 PTR0 /* right */
559  PTF1 PTR1 PTR2 PTF1 PTR2 PTF2 /* left */
560 
561  /* finished, and have good data */
562  }
563 
564  FW_GL_DEPTHMASK(FALSE);
565  /* note the ALPHA of zero - totally transparent */
566 
567  //OLDCODE FW_GL_COLOR4F(0.0f, 1.0f, 0.0f, 0.0f);
568 
569  /* Draw it; assume VERTEX and NORMALS already defined.*/
570  FW_GL_VERTEX_POINTER(3,GL_FLOAT,0,(GLfloat *)node->__points.p);
571  FW_GL_NORMAL_POINTER(GL_FLOAT,0,boxnorms);
572 
573  /* do the array drawing; sides are simple 0-1-2-3, 4-5-6-7, etc quads */
574  sendArraysToGPU (GL_TRIANGLES, 0, 36);
575  FW_GL_DEPTHMASK(TRUE);
576 #endif// HAVE_TO_REIMPLEMENT
577 }
578 #endif // VISIBILITYOCCLUSION
579 
580 
581 void do_VisibilitySensorTick (void *ptr) {
582  struct X3D_VisibilitySensor *node = (struct X3D_VisibilitySensor *) ptr;
583 
584  /* if not enabled, do nothing */
585  if (!node) return;
586  if (node->__oldEnabled != node->enabled) {
587  node->__oldEnabled = node->enabled;
588  MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_VisibilitySensor, enabled));
589  }
590  if (!node->enabled) return;
591  /* are we enabled? */
592 
593  #ifdef SEVERBOSE
594  printf ("do_VisibilitySensorTick, samples %d\n",node->__samples);
595  #endif
596 
597  if (node->__Samples > 0) {
598  if (!node->isActive) {
599  #ifdef SEVERBOSE
600  printf ("visibilitysensor - now active\n");
601  #endif
602 
603  node->isActive = 1;
604  node->enterTime = TickTime();
605  MARK_EVENT (ptr, offsetof(struct X3D_VisibilitySensor, isActive));
606  MARK_EVENT (ptr, offsetof(struct X3D_VisibilitySensor, enterTime));
607  }
608  } else {
609  if (node->isActive) {
610  #ifdef SEVERBOSE
611  printf ("visibilitysensor - going inactive\n");
612  #endif
613 
614  node->isActive = 0;
615  node->exitTime = TickTime();
616  MARK_EVENT (ptr, offsetof(struct X3D_VisibilitySensor, isActive));
617  MARK_EVENT (ptr, offsetof(struct X3D_VisibilitySensor, exitTime));
618  }
619  }
620  node->__Samples = 0; //clear for next frame count
621 }
622 /* don't need - all done in do_TransformSensor
623 void other_TransformSensor (struct X3D_TransformSensor *node) {
624  // if not enabled, do nothing
625  if (!node) return;
626  if (!node->enabled)
627  return;
628 
629  {
630  //aabb method
631  //1. transform local axix-aligned bounding box AABB from local to cuboid
632  // cuboid_aabb = projection * modelview * local_aabb
633  //2. intersect cuboid_aabb with cuboid which is -1 to 1 in 3 dimensions
634  //3. if they don't intersect, not visible, else maybe visible
635  {
636  //update extent in case size or center changed
637  int i, ihit;
638  float emin[3], emax[3];
639  vecadd3f(emax, node->center.c, node->size.c);
640  vecdif3f(emin, node->center.c, node->size.c);
641  for(i=0;i<3;i++)
642  {
643  node->_extent[i*2 + 1] = emin[i];
644  node->_extent[i*2] = emax[i];
645  }
647  //if(ihit){
648  //}
649  }
650  }
651 }
652 */
653  int overlapMBBs(GLDOUBLE *MBBmin1, GLDOUBLE *MBBmax1, GLDOUBLE *MBBmin2, GLDOUBLE* MBBmax2);
654 
655 /*
656 TransformSensor
657  http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/envsensor.html#TransformSensor
658  - added in specs v3.2
659  - a targetObject (Node) can be USEd in multiple places, each place with a different modelview matrix state
660  - so how implement on the node end? This is similar/analogous to the problem with the picksensor component:
661  freewrl is great at viewer / node interactions, but not node/node. we seem to be missing something. But what?
662  - a more general node/node interaction table for picksensors, transformsensors and any other node-node interaction
663  General USE-USE system
664  how:
665  1) renderfuncs struct USEHIT { node *node; modelviewMatrix mvm; }
666  functions: usehit_add(node*,mvm,flag), USEHIT = usehit_next(node*); usehit_clear();
667  2) (sensornode*,do_sensorfunc) tuple registered, sensornode knows target node and
668  a) flags target and self on one pass with VF_USE,
669  b) then searches on next pass for self and other in double loop and does all USE_USE combinations
670  c) end of do_first() calls usehit_clear() for fresh transforms on next pass
671 */
672 
673 
674 // ticks are a good place to summarize activites from various places around the scenegraph
675 // do_tick is called once per frame, from outside of render_hier, so there's no modelview matrix on stack
676 // http://www.web3d.org/documents/specifications/19775-1/V3.3/Part01/components/envsensor.html#TransformSensor
677 // "Instanced (DEF/USE) TransformSensor nodes use the union of all the boxes to check for enter and exit."
678 void do_TransformSensorTick (void *ptr) {
679  int ishit,i;
680  usehit *mehit, *uhit;
681  struct X3D_Node *unode,*menode;
682  struct X3D_TransformSensor *node = (struct X3D_TransformSensor *) ptr;
683 
684  // if not enabled, do nothing
685  if (!node) return;
686  if (node->__oldEnabled != node->enabled) {
687  node->__oldEnabled = node->enabled;
688  MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_TransformSensor, enabled));
689  }
690  // are we enabled?
691  if (!node->enabled) return;
692  //if the sensor has zero size its equivalent to not-enabled - can't send events accroding to specs
693  if(((node->size).c[0]) <= 0.0f || ((node->size).c[1]) <= 0.0f || ((node->size).c[2]) <= 0.0f) return;
694 
695  #ifdef SEVERBOSE
696  printf ("do_TransformSensorTick enabled\n");
697  #endif
698 
699  //temp clear hit flag
700  ishit = 0;
701  mehit = NULL;
702  unode = node->targetObject;
703  menode = (struct X3D_Node*)node; //upcaste
704  if(unode){
705  //check all USE-USE combinations of this node and targetObject
706  //find next this
707  while((mehit = usehit_next(menode,mehit))){
708  //int iret;
709  double meinv[16],memin[3],memax[3];
710  float emin[3], emax[3], halfsize[3];
711 
712  matinverseAFFINE(meinv,mehit->mvm);
713  //iret = __gluInvertMatrixd( mehit->mvm, meinv);
714 
715  if(0){
716  //check inverse
717  double ident[16];
718  int j;
719  matmultiplyAFFINE(ident,meinv,mehit->mvm);
720 
721  printf("inverse check do_TransformSensor\n");
722  for(i=0;i<4;i++){
723  for(j=0;j<4;j++) printf("%lf ",ident[i*3+j]);
724  printf("\n");
725  }
726  printf("\n");
727  }
728  //update extent on me, in case center or size has changed
729  vecscale3f(halfsize,node->size.c,.5f);
730  vecadd3f(emax, node->center.c, halfsize);
731  vecdif3f(emin, node->center.c, halfsize);
732  for(i=0;i<3;i++)
733  {
734  node->_extent[i*2 + 1] = emin[i];
735  node->_extent[i*2] = emax[i];
736  }
737  for(i=0;i<3;i++)
738  {
739  memin[i] = node->_extent[i*2 + 1];
740  memax[i] = node->_extent[i*2];
741  }
742 
743  //find next target
744  uhit = NULL;
745  while((uhit = usehit_next(unode,uhit))){
746  //see if they intersect, if so do something about it
747  //-prepare matrixTarget2this
748  double u2me[16], umin[3],umax[3],uumin[3],uumax[3];
749  matmultiplyAFFINE(u2me,uhit->mvm,meinv);
750  //-transform target AABB/MBB from target space to this space
751  for(i=0;i<3;i++)
752  {
753  umin[i] = unode->_extent[i*2 + 1];
754  umax[i] = unode->_extent[i*2];
755  }
756  transformMBB(uumin,uumax,u2me,umin,umax);
757  //-see if AABB intersect
758  if( overlapMBBs(memin, memax, uumin, uumax) ){
759  //-if so take action
760  static const struct point_XYZ yvec = {0,0.05,0};
761  static const struct point_XYZ zvec = {0,0,-0.05};
762  static const struct point_XYZ zpvec = {0,0,0.05};
763  static const struct point_XYZ orig = {0,0,0};
764  struct point_XYZ t_zvec, t_yvec, t_orig; //, t_center;
765  struct point_XYZ nor1,nor2;
766  struct point_XYZ ins;
767  double len;
768  struct point_XYZ dr1r2;
769  struct point_XYZ dr2r3;
770  double t1u[3], t1me[3];
771 
772  ishit++;
773  if (!node->isActive) {
774  #ifdef SEVERBOSE
775  printf ("transformensor - now active\n");
776  #endif
777 
778  node->isActive = 1;
779  node->enterTime = TickTime();
780  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, isActive));
781  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, enterTime));
782  }
783  // has target position or orientation changed wrt transform sensor?
784  // if so send position_changed / orientation_changed events
785  //transform position
786  for(i=0;i<3;i++) t1u[i] = (umin[i] + umax[i])*.5;
787  transformAFFINEd(t1me,t1u,u2me);
788  for(i=0;i<3;i++) node->__t1.c[i] = (float)t1me[i] - node->center.c[i];
789  if (memcmp ((void *) &node->position_changed,(void *) &node->__t1,sizeof(struct SFColor))) {
790  #ifdef SEVERBOSE
791  printf ("PROX - position changed!!! \n");
792  #endif
793 
794  memcpy ((void *) &node->position_changed,
795  (void *) &node->__t1,sizeof(struct SFColor));
796  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, position_changed));
797  }
798  //transform orientation
799  transformAFFINE(&t_yvec,&yvec,u2me);
800  transformAFFINE(&t_zvec,&zvec,u2me);
801  transformAFFINE(&t_orig,&orig,u2me);
802  VECDIFF(t_zvec,t_orig,dr1r2); /* Z axis */
803  VECDIFF(t_yvec,t_orig,dr2r3); /* Y axis */
804 
805  /* printf (" dr1r2 %lf %lf %lf\n",dr1r2.x, dr1r2.y, dr1r2.z);
806  printf (" dr2r3 %lf %lf %lf\n",dr2r3.x, dr2r3.y, dr2r3.z);
807  */
808 
809  len = sqrt(VECSQ(dr1r2)); VECSCALE(dr1r2,1/len);
810  len = sqrt(VECSQ(dr2r3)); VECSCALE(dr2r3,1/len);
811 
812  /* printf ("scaled dr1r2 %lf %lf %lf\n",dr1r2.x, dr1r2.y, dr1r2.z);
813  printf ("scaled dr2r3 %lf %lf %lf\n",dr2r3.x, dr2r3.y, dr2r3.z);
814  */
815 
816  /*
817  printf("PROX_INT: (%f %f %f) (%f %f %f) (%f %f %f)\n (%f %f %f) (%f %f %f)\n",
818  t_orig.x, t_orig.y, t_orig.z,
819  t_zvec.x, t_zvec.y, t_zvec.z,
820  t_yvec.x, t_yvec.y, t_yvec.z,
821  dr1r2.x, dr1r2.y, dr1r2.z,
822  dr2r3.x, dr2r3.y, dr2r3.z
823  );
824  */
825 
826  if(fabs(VECPT(dr1r2, dr2r3)) > 0.001) {
827  printf ("Sorry, can't handle unevenly scaled ProximitySensors yet :("
828  "dp: %f v: (%f %f %f) (%f %f %f)\n", VECPT(dr1r2, dr2r3),
829  dr1r2.x,dr1r2.y,dr1r2.z,
830  dr2r3.x,dr2r3.y,dr2r3.z
831  );
832  return;
833  }
834 
835 
836  if(APPROX(dr1r2.z,1.0)) {
837  /* rotation */
838  ((node->__t2).c[0]) = (float) 0;
839  ((node->__t2).c[1]) = (float) 0;
840  ((node->__t2).c[2]) = (float) 1;
841  ((node->__t2).c[3]) = (float) atan2(-dr2r3.x,dr2r3.y);
842  } else if(APPROX(dr2r3.y,1.0)) {
843  /* rotation */
844  ((node->__t2).c[0]) = (float) 0;
845  ((node->__t2).c[1]) = (float) 1;
846  ((node->__t2).c[2]) = (float) 0;
847  ((node->__t2).c[3]) = (float) atan2(dr1r2.x,dr1r2.z);
848  } else {
849  /* Get the normal vectors of the possible rotation planes */
850  nor1 = dr1r2;
851  nor1.z -= 1.0;
852  nor2 = dr2r3;
853  nor2.y -= 1.0;
854 
855  /* Now, the intersection of the planes, obviously cp */
856  VECCP(nor1,nor2,ins);
857 
858  len = sqrt(VECSQ(ins)); VECSCALE(ins,1/len);
859 
860  /* the angle */
861  VECCP(dr1r2,ins, nor1);
862  VECCP(zpvec, ins, nor2);
863  len = sqrt(VECSQ(nor1)); VECSCALE(nor1,1/len);
864  len = sqrt(VECSQ(nor2)); VECSCALE(nor2,1/len);
865  VECCP(nor1,nor2,ins);
866 
867  ((node->__t2).c[3]) = (float) -atan2(sqrt(VECSQ(ins)), VECPT(nor1,nor2));
868 
869  /* rotation - should normalize sometime... */
870  ((node->__t2).c[0]) = (float) ins.x;
871  ((node->__t2).c[1]) = (float) ins.y;
872  ((node->__t2).c[2]) = (float) ins.z;
873  }
874 
875  if (memcmp ((void *) &node->orientation_changed, (void *) &node->__t2,sizeof(struct SFRotation))) {
876  #ifdef SEVERBOSE
877  printf ("PROX - orientation changed!!!\n ");
878  #endif
879 
880  memcpy ((void *) &node->orientation_changed,
881  (void *) &node->__t2,sizeof(struct SFRotation));
882  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, orientation_changed));
883  }
884  }
885  }
886  }
887 
888  if(!ishit){
889  if (node->isActive) {
890  #ifdef SEVERBOSE
891  printf ("transformsensor - going inactive\n");
892  #endif
893 
894  node->isActive = 0;
895  node->exitTime = TickTime();
896  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, isActive));
897  MARK_EVENT (ptr, offsetof(struct X3D_TransformSensor, exitTime));
898  }
899  }
900 
901  //ask this node and target node to both save their modelviewmatrix for each USE, when visited, on the upcoming frame (do_ called from do_first())
902  node->targetObject->_renderFlags |= VF_USE;
903  }
904  node->_renderFlags |= VF_USE;
905 
906 }