FreeWRL/FreeX3D  3.0.0
Viewer.c
1 /*
2 
3 
4 CProto ???
5 
6 */
7 
8 /****************************************************************************
9  This file is part of the FreeWRL/FreeX3D Distribution.
10 
11  Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12 
13  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14  it under the terms of the GNU Lesser Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25 ****************************************************************************/
26 
27 
28 
29 #include <config.h>
30 #include <system.h>
31 #include <display.h>
32 #include <internal.h>
33 
34 #include <libFreeWRL.h>
35 
36 #include "../vrml_parser/Structs.h"
37 #include "../opengl/OpenGL_Utils.h"
38 #include "../main/headers.h"
39 
40 #include "LinearAlgebra.h"
41 #include "quaternion.h"
42 #include "Viewer.h"
43 #include "../x3d_parser/Bindable.h"
44 #include "ui/common.h" // for ppcommon
45 
46 //moved to libfreewrl.h
47 //enum {
48 // CHORD_YAWZ,
49 // CHORD_YAWPITCH,
50 // CHORD_ROLL,
51 // CHORD_XY
52 //} input_chords;
53 
54 static void init_stereodefaults(X3D_Viewer *Viewer)
55 {
56  /* must call this before getting values from command line in options.c */
57  Viewer->shutterGlasses = 0;
58  Viewer->anaglyph = 0;
59  Viewer->sidebyside = 0;
60  Viewer->updown = 0;
61  Viewer->isStereo = 0;
62  Viewer->eyedist = 0.065;
63  //For sidebyside: average human eyebase 2.4inches/65mm.
64  // We want it narrower, 57mm or 2.25 inches
65  // for 6" wide viewport: 2.25/6 = .375
66  // for shutter and anaglyph, .5 gets both eyes looking at the
67  // same point on the screen (for nominal distance object)
68  Viewer->screendist = 0.375; //was .8
69  Viewer->stereoParameter = 0.01; //was .4 or toe-in. Toe-in can force your eyes wall-eyed esp. in side-by-side, so set near zero.
70  Viewer->dominantEye = 1; /*0=Left 1=Right used for picking*/
71  Viewer->eitherDominantEye = 1; //1=can pick with either eye depending on which stereoside mouse is in, see setup_pickside()
72  Viewer->iprog[0] = 0; /* left red */
73  Viewer->iprog[1] = 1; /* right green */
74  Viewer->haveQuadbuffer = 0;
75 }
76 
77 
78 typedef struct pViewer{
79  int examineCounter;// = 5;
80 
81  int viewer_initialized;
86 
87  FILE *exfly_in_file;
88  struct point_XYZ viewer_lastP;
89  int exflyMethod; //0 or 1; /* could be a user settable option, which kind of exfly to do */
90  int StereoInitializedOnce;//. = 0;
91  GLboolean acMask[3][3]; //anaglyphChannelMask
92  //X3D_Viewer Viewer; /* moved to Bindables.h > bindablestacks */
93  /* viewpoint slerping */
94  double viewpoint2rootnode[16];
95  double viewpointnew2rootnode[16];
96  int vp2rnSaved;
97  double old2new[16];
98  double identity[16];
99  double tickFrac;
100  Quaternion sq;
101  double sp[3];
102  int keychord;
103  int dragchord;
104 
105 }* ppViewer;
106 void *Viewer_constructor(){
107  void *v = MALLOCV(sizeof(struct pViewer));
108  memset(v,0,sizeof(struct pViewer));
109  return v;
110 }
111 void Viewer_init(struct tViewer *t){
112  //public
113  t->stereotype = 0;
114  //private
115  t->prv = Viewer_constructor();
116  {
117  ppViewer p = (ppViewer)t->prv;
118 
119  p->examineCounter = 5;
120 
121  p->viewer_initialized = FALSE;
122  #ifdef _MSC_VER
123  p->exflyMethod = 1; /* could be a user settable option, which kind of exfly to do */
124  #else
125  p->exflyMethod = 0;
126  #endif
127  p->StereoInitializedOnce = 0;
128  p->acMask[0][0] = (GLboolean)1; // R = 1, 0, 0
129 
130  p->acMask[1][1] = (GLboolean)1; // C = 0, 1, 1
131  p->acMask[1][2] = (GLboolean)1;
132 
133  /* viewpoint slerping */
134  loadIdentityMatrix(p->viewpoint2rootnode);
135  p->vp2rnSaved = FALSE; //on startup it binds before saving
136  loadIdentityMatrix(p->old2new);
137  loadIdentityMatrix(p->identity);
138  p->tickFrac = 0.0; //for debugging slowly
139 //init_stereodefaults(viewer);
140  p->StereoInitializedOnce = 1;
141  p->keychord = CHORD_XY; // default on startup
142  p->dragchord = CHORD_YAWZ;
143  }
144 }
145 
146 
147 
148 //static void handle_tick_walk(void);
149 //static void handle_tick_walk2(double dtime);
150 static void handle_tick_fly(void);
151 static void handle_tick_exfly(void);
152 static void handle_tick_fly2(double dtime);
153 
154 /* used for EAI calls to get the current speed. Not used for general calcs */
155 /* we DO NOT return as a float, as some gccs have trouble with this causing segfaults */
156 void getCurrentSpeed() {
158  // OLDCODE UNUSED ppViewer p;
159  ttglobal tg = gglobal();
160  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
161  viewer = Viewer();
162  tg->Mainloop.BrowserSpeed = tg->Mainloop.BrowserFPS * (fabs(viewer->VPvelocity.x) + fabs(viewer->VPvelocity.y) + fabs(viewer->VPvelocity.z));
163 }
164 
165 void viewer_default0(X3D_Viewer *viewer, int vpnodetype) {
166  Quaternion q_i;
167  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
168 
169  viewer->fieldofview = 45.0;
170  viewer->fovZoom = 1.0;
171 
172  viewer->VPvelocity.x = 0.0; viewer->VPvelocity.y = 0.0; viewer->VPvelocity.z = 0.0;
173  viewer->Pos.x = 0; viewer->Pos.y = 0; viewer->Pos.z = 10;
174  viewer->currentPosInModel.x = 0; viewer->currentPosInModel.y = 0; viewer->currentPosInModel.z = 10;
175  viewer->AntiPos.x = 0; viewer->AntiPos.y = 0; viewer->AntiPos.z = 0;
176 
177  vrmlrot_to_quaternion (&viewer->Quat,1.0,0.0,0.0,0.0);
178  vrmlrot_to_quaternion (&viewer->bindTimeQuat,1.0,0.0,0.0,0.0);
179  vrmlrot_to_quaternion (&viewer->prepVPQuat,0.0,1.0,0.0,3.14);
180  vrmlrot_to_quaternion (&q_i,1.0,0.0,0.0,0.0);
181  quaternion_inverse(&(viewer->AntiQuat),&q_i);
182 
183  viewer->headlight = TRUE;
184  /* tell the menu buttons of the state of this headlight */
185  //setMenuButton_headlight(viewer->headlight);
186  viewer->speed = 1.0;
187  viewer->Dist = 10.0;
188  memcpy (&viewer->walk, &p->viewer_walk,sizeof (X3D_Viewer_Walk));
189  memcpy (&viewer->examine, &p->viewer_examine, sizeof (X3D_Viewer_Examine));
190  memcpy (&viewer->fly, &p->viewer_fly, sizeof (X3D_Viewer_Fly));
191  memcpy (&viewer->ypz,&p->viewer_ypz, sizeof (X3D_Viewer_Spherical));
192 
193  if(vpnodetype == NODE_OrthoViewpoint){
194  //for LayoutLayer default NONE, Ortho
195  fwl_set_viewer_type0(viewer,VIEWER_NONE);
196  viewer->orthoField[0] = -1.0;
197  viewer->orthoField[1] = -1.0;
198  viewer->orthoField[2] = 1.0;
199  viewer->orthoField[3] = 1.0;
200  viewer->nearPlane = 0.1;
201  viewer->farPlane = 210000.0;
202  viewer->ortho = TRUE;
203  }else{
204  //all other viewpoint types - Viewpoint, GeoViewpoint ...
205  fwl_set_viewer_type0(viewer,VIEWER_EXAMINE);
206  }
207  viewer->LookatMode = 0;
208  //set_eyehalf( Viewer.eyedist/2.0,
209  // atan2(Viewer.eyedist/2.0,Viewer.screendist)*360.0/(2.0*3.1415926));
210 
211  /* assume we are not bound to a GeoViewpoint */
212  viewer->GeoSpatialNode = NULL;
213 
214 }
215 //ppViewer p = (ppViewer)gglobal()->Viewer.prv;
216 //X3D_Viewer _Viewer; /* has to be defined somewhere, so it found itself stuck here */
217 X3D_Viewer *ViewerByLayerId(int layerid)
218 {
219  X3D_Viewer *viewer;
220  bindablestack *bstack;
221  ttglobal tg;
222  // OLDCODE UNUSED ppViewer p;
223  tg = gglobal();
224  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
225  //per-layer viewer
226  bstack = getBindableStacksByLayer(tg,layerid);
227  if(!bstack->viewer){
228  int vpnodetype;
229  viewer = MALLOCV(sizeof(X3D_Viewer));
230  memset(viewer,0,sizeof(X3D_Viewer));
231  vpnodetype = bstack->nodetype == NODE_LayoutLayer ? NODE_OrthoViewpoint : NODE_Viewpoint;
232  viewer_default0(viewer,vpnodetype);
233  init_stereodefaults(viewer);
234  bstack->viewer = viewer;
235  }
236  return bstack->viewer;
237 }
238 X3D_Viewer *Viewer()
239 {
240  ttglobal tg;
241  tg = gglobal();
242  return ViewerByLayerId(tg->Bindable.activeLayer);
243 }
244 
245 void viewer_default() {
246  X3D_Viewer *viewer;
247  viewer = Viewer();
248  viewer_default0(viewer,NODE_Viewpoint);
249 }
250 
251 
252 void resolve_pos20(X3D_Viewer *viewer);
253 void viewer_init (X3D_Viewer *viewer, int type) {
254  Quaternion q_i;
255  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
256 
257  /* if we are brand new, set up our defaults */
258  if (!p->viewer_initialized) {
259  p->viewer_initialized = TRUE;
260 
261  /* what are we - EXAMINE, FLY, etc... */
262  viewer->type = type;
263 
264  viewer->Pos.x = 0; viewer->Pos.y = 0; viewer->Pos.z = 10;
265  viewer->currentPosInModel.x = 0; viewer->currentPosInModel.y = 0; viewer->currentPosInModel.z = 10;
266  viewer->AntiPos.x = 0; viewer->AntiPos.y = 0; viewer->AntiPos.z = 0;
267 
268 
269  vrmlrot_to_quaternion (&viewer->Quat,1.0,0.0,0.0,0.0);
270  vrmlrot_to_quaternion (&viewer->bindTimeQuat,1.0,0.0,0.0,0.0);
271  vrmlrot_to_quaternion (&viewer->prepVPQuat,1.0,0.0,0.0,0.0);
272  vrmlrot_to_quaternion (&q_i,1.0,0.0,0.0,0.0);
273  quaternion_inverse(&(viewer->AntiQuat),&q_i);
274 
275  viewer->headlight = TRUE;
276  viewer->collision = FALSE;
277  /* tell the menu buttons of the state of this headlight */
278  //setMenuButton_headlight(viewer->headlight);
279  viewer->speed = 1.0;
280  viewer->Dist = 10.0;
281  //viewer->exploreDist = 10.0;
282  memcpy (&viewer->walk, &p->viewer_walk,sizeof (X3D_Viewer_Walk));
283  memcpy (&viewer->examine, &p->viewer_examine, sizeof (X3D_Viewer_Examine));
284  memcpy (&viewer->fly, &p->viewer_fly, sizeof (X3D_Viewer_Fly));
285  memcpy (&viewer->ypz,&p->viewer_ypz, sizeof (X3D_Viewer_Spherical));
286 
287 
288  /* SLERP code for moving between viewpoints */
289  viewer->SLERPing = FALSE;
290  viewer->startSLERPtime = 0.0;
291  viewer->transitionType = 1; /* assume LINEAR */
292  viewer->transitionTime = 1.0; /* assume 1 second */
293 
294  /* Orthographic projections */
295  viewer->ortho = FALSE;
296 
297  viewer->doExamineModeDistanceCalculations = FALSE;
298 
299  /* orientation - 0 is normal */
300  viewer->screenOrientation = 0;
301 
302  viewer->nearPlane=DEFAULT_NEARPLANE; /* near Clip plane - MAKE SURE that statusbar is not in front of this!! */
303  viewer->farPlane=DEFAULT_FARPLANE; /* a good default value */
304  viewer->backgroundPlane = DEFAULT_BACKGROUNDPLANE; /* where Background and TextureBackground nodes go */
305  viewer->fieldofview=45.0;
306  viewer->fovZoom = 1.0;
307 
308  viewer->wasBound = FALSE;
309  }
310 
311  resolve_pos20(viewer);
312 
313 }
314 
315 
316 int getCRouteCount();
317 void printStatsRoutes()
318 {
319  ConsoleMessage("%25s %d\n","Routes count", getCRouteCount());
320 }
321 
322 void printStatsBindingStacks();
323 void printStatsResources();
324 void printStatsEvents();
325 void printStatsNodes();
326 void printStats()
327 {
328  printMaxStackUsed();
329  printStatsResources();
330  printStatsEvents();
331  printStatsNodes();
332  printStatsRoutes();
333  printStatsBindingStacks();
334 }
335 
336 void
337 print_viewer()
338 {
339  X3D_Viewer *viewer;
340 
341  struct orient_XYZA ori;
342  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
343  viewer = Viewer();
344 
345  quaternion_to_vrmlrot(&(viewer->Quat), &(ori.x),&(ori.y),&(ori.z), &(ori.a));
346  ConsoleMessage("Viewpoint local{\n");
347  ConsoleMessage("\tPosition[%.4f, %.4f, %.4f]\n", (viewer->Pos).x, (viewer->Pos).y, (viewer->Pos).z);
348  ConsoleMessage("\tQuaternion[%.4f, %.4f, %.4f, %.4f]\n", (viewer->Quat).w, (viewer->Quat).x, (viewer->Quat).y, (viewer->Quat).z);
349  ConsoleMessage("\tOrientation[%.4f, %.4f, %.4f, %.4f]\n", ori.x, ori.y, ori.z, ori.a);
350  ConsoleMessage("}\n");
351  getCurrentPosInModel(FALSE);
352  ConsoleMessage("World Coordinates of Avatar [%.4f, %.4f %.4f]\n",viewer->currentPosInModel.x,viewer->currentPosInModel.y,viewer->currentPosInModel.z);
353  printStats();
354 }
355 
356 int fwl_get_headlight() {
357  return(Viewer()->headlight);
358 }
359 
360 void fwl_toggle_headlight() {
361  X3D_Viewer *viewer;
362  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
363  viewer = Viewer();
364 
365  if (viewer->headlight == TRUE) {
366  viewer->headlight = FALSE;
367  } else {
368  viewer->headlight = TRUE;
369  }
370  /* tell the menu buttons of the state of this headlight */
371  //setMenuButton_headlight(viewer->headlight);
372 
373 }
374 /* July 7, 2012 I moved .collision from params to x3d_viewer struct,
375  so its like headlight and navmode */
376 void setNoCollision() {
377  X3D_Viewer *viewer;
378  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
379  viewer = Viewer();
380  viewer->collision = 0;
381  //fwl_setp_collision(0);
382  //setMenuButton_collision(viewer->collision); //fwl_getp_collision());
383 }
384 int get_collision() {
385  return fwl_getCollision(); //fwl_getp_collision();
386 }
387 void toggle_collision() {
388  X3D_Viewer *viewer;
389  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
390  viewer = Viewer();
391  viewer->collision = 1 - viewer->collision;
392 
393  //fwl_setp_collision(!fwl_getp_collision());
394  //setMenuButton_collision(viewer->collision); //fwl_getp_collision());
395 }
396 
397 int fwl_getCollision(){
398  X3D_Viewer *viewer;
399  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
400  viewer = Viewer();
401  return viewer->collision;
402 }
403 void fwl_setCollision(int state) {
404  X3D_Viewer *viewer;
405  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
406  viewer = Viewer();
407  viewer->collision = state;
408 }
409 
410 void fwl_init_StereoDefaults()
411 {
412  X3D_Viewer *viewer;
413  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
414  viewer = Viewer();
415  if(!p->StereoInitializedOnce)
416  init_stereodefaults(viewer);
417  p->StereoInitializedOnce = 1;
418 }
419 
420 
421 void set_eyehalf(const double eyehalf, const double eyehalfangle) {
422  X3D_Viewer *viewer;
423  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
424  viewer = Viewer();
425 
426  viewer->eyehalf = eyehalf;
427  viewer->eyehalfangle = eyehalfangle;
428  //viewer->isStereo = 1;
429 }
430 void resolve_pos2();
431 void fwl_set_viewer_type0(X3D_Viewer *viewer, const int type) {
432  ttglobal tg = gglobal();
433  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
434 
435  if(viewer->type != type){
436  tg->Mainloop.CTRL = FALSE; //turn off any leftover 3-state toggle
437  switch(viewer->type){
438  case VIEWER_LOOKAT:
439  case VIEWER_EXPLORE:
440  viewer->LookatMode = 0; //turn off leftover lookatMode
441  break;
442  default:
443  break;
444  }
445  }
446 
447  switch(type) {
448  case VIEWER_EXAMINE:
449  resolve_pos20(viewer);
450  case VIEWER_NONE:
451  case VIEWER_WALK:
452  case VIEWER_EXFLY:
453  case VIEWER_TPLANE:
454  case VIEWER_RPLANE:
455  case VIEWER_TILT:
456  case VIEWER_FLY2:
457  case VIEWER_TURNTABLE:
458  case VIEWER_DIST:
459  case VIEWER_FLY:
460  viewer->type = type;
461  break;
462  case VIEWER_SPHERICAL:
463  //3 state toggle stack
464  if(viewer->type == type){
465  //this is a request to toggle on/off FOV (field-of-view) adjustment for SPHERICAL mode
466  if(tg->Mainloop.CTRL){
467  tg->Mainloop.CTRL = FALSE;
468  }else{
469  tg->Mainloop.CTRL = TRUE; //in handle_spherical, check if button==3 (RMB) or CTRL + button==1
470  }
471  }else{
472  //request to toggle on EXPLORE mode
473  viewer->type = type;
474  }
475  break;
476 
477  case VIEWER_EXPLORE:
478  //3 state toggle stack
479  if(viewer->type == type){
480  //this is a request to toggle on/off CTRL for EXPLORE mode
481  if(tg->Mainloop.CTRL){
482  tg->Mainloop.CTRL = FALSE;
483  viewer->LookatMode = 0;
484  }else{
485  tg->Mainloop.CTRL = TRUE;
486  viewer->LookatMode = 1; //tells mainloop to turn off sensitive
487  }
488  }else{
489  //request to toggle on EXPLORE mode
490  viewer->type = type;
491  }
492  break;
493  case VIEWER_LOOKAT:
494  //2 state toggle
495  if(viewer->type == type){
496  //this is a request to toggle off LOOKAT mode
497  viewer->type = viewer->lastType;
498  viewer->LookatMode = 0;
499  }else{
500  //request to toggle on LOOKAT mode
501  viewer->lastType = viewer->type;
502  viewer->LookatMode = 1; //tells mainloop to turn off sensitive
503  viewer->type = type;
504  }
505  break;
506  default:
507  ConsoleMessage ("Viewer type %d is not supported. See Viewer.h.\n", type);
508  viewer->type = VIEWER_NONE;
509  break;
510  }
511 
512  /* set velocity array to zero again - used only for EAI */
513  viewer->VPvelocity.x=0.0; viewer->VPvelocity.y=0.0; viewer->VPvelocity.z=0.0;
514 
515  /* can the currently bound viewer type handle this */
516  /* if there is no bound viewer, just ignore (happens on initialization) */
517  if (vectorSize(getActiveBindableStacks(tg)->navigation) >0)
518  if (viewer->oktypes[type]==FALSE) {
519  //setMenuButton_navModes(viewer->type);
520  return;
521  }
522 
523  if(1) viewer_init(viewer,type); //feature-EXPLORE
524 
525  /* tell the window menu what we are */
526  //setMenuButton_navModes(viewer->type);
527 
528 }
529 void fwl_set_viewer_type(const int type) {
530  X3D_Viewer *viewer;
531  viewer = Viewer();
532 
533  fwl_set_viewer_type0(viewer, type);
534 }
535 
536 
537 
538 #ifdef VERBOSE
539 #define VIEWER_STRING(type) ( \
540  type == VIEWER_NONE ? "NONE" : ( \
541  type == VIEWER_EXAMINE ? "EXAMINE" : ( \
542  type == VIEWER_WALK ? "WALK" : ( \
543  type == VIEWER_EXFLY ? "EXFLY" : ( \
544  type == VIEWER_SPHERICAL ? "SPHERICAL" : (\
545  type == VIEWER_TURNTABLE ? "TURNTABLE" : (\
546  type == VIEWER_FLY ? "FLY" : "UNKNOWN"))))))
547 #endif //VERBOSE
548 
549 #ifdef _MSC_VER
550 #define strcasecmp _stricmp
551 #endif
552 
553 struct navmode {
554  char *key;
555  int type;
556 } navmodes [] = {
557  {"NONE",VIEWER_NONE},
558  {"WALK",VIEWER_WALK},
559  {"FLY",VIEWER_FLY},
560  {"EXAMINE",VIEWER_EXAMINE},
561  {"SPHERICAL",VIEWER_SPHERICAL},
562  {"TURNTABLE",VIEWER_TURNTABLE},
563  {"EXPLORE",VIEWER_EXPLORE},
564  {"LOOKAT",VIEWER_LOOKAT},
565  {"YAWZ",VIEWER_YAWZ},
566  {"XY",VIEWER_XY},
567  {"YAWPITCH",VIEWER_YAWPITCH},
568  {"ROLL",VIEWER_ROLL},
569  {"DIST",VIEWER_DIST},
570  {NULL,0},
571 };
572 char * lookup_navmodestring(int navmode){
573  int i;
574  char *retval;
575  struct navmode *nm;
576  i = 0;
577  retval = NULL;
578  do{
579  nm = &navmodes[i];
580  if(nm->type == navmode){
581  retval = nm->key;
582  break;
583  }
584  i++;
585  }while(navmodes[i].key);
586  if(!retval) retval = "NONE";
587  return retval;
588 }
589 int lookup_navmode(char *cmode){
590  int i;
591  int retval;
592  struct navmode *nm;
593  i = 0;
594  retval = 0;
595  do{
596  nm = &navmodes[i];
597  if(!strcasecmp(nm->key,cmode)){
598  retval = nm->type;
599  break;
600  }
601  i++;
602  }while(navmodes[i].key);
603  return retval;
604 }
605 char* fwl_getNavModeStr()
606 {
607  X3D_Viewer *viewer;
608  // OLDCODE UNUSED ttglobal tg = gglobal();
609  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
610  viewer = Viewer();
611  return lookup_navmodestring(viewer->type);
612  //switch(viewer->type) {
613  //case VIEWER_NONE:
614  // return "NONE";
615  //case VIEWER_EXAMINE:
616  // return "EXAMINE";
617  //case VIEWER_WALK:
618  // return "WALK";
619  //case VIEWER_EXFLY:
620  // return "EXFLY";
621  //case VIEWER_TPLANE:
622  // return "TPLANE";
623  //case VIEWER_RPLANE:
624  // return "RPLANE";
625  //case VIEWER_TILT:
626  // return "TILT";
627  //case VIEWER_FLY2:
628  // return "FLY2";
629  //case VIEWER_SPHERICAL:
630  // return "SPHERICAL";
631  //case VIEWER_TURNTABLE:
632  // return "TURNTABLE";
633  //case VIEWER_FLY:
634  // return "FLY";
635  //case VIEWER_LOOKAT:
636  // return "LOOKAT";
637  //case VIEWER_EXPLORE:
638  // return "EXPLORE";
639  //default:
640  // return "NONE";
641  //}
642  //return "NONE";
643 }
644 int fwl_getNavMode()
645 {
646  X3D_Viewer *viewer;
647  // OLDCODE UNUSED ttglobal tg = gglobal();
648  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
649  viewer = Viewer();
650  return viewer->type;
651 }
652 int fwl_setNavMode(char *mode){
653  int imode = lookup_navmode(mode);
654  fwl_set_viewer_type(imode);
655  return 0;
656 }
657 
658 //int use_keys() {
659 // ppViewer p = (ppViewer)gglobal()->Viewer.prv;
660 //
661 // if (viewer->type == VIEWER_FLY) {
662 // return TRUE;
663 // }
664 // return TRUE; //FALSE; //Navigation-key_and_drag
665 //}
666 
667 void resolve_pos(){}
668 void resolve_pos20(X3D_Viewer *viewer) {
669  /* my($this) = @_; */
670  struct point_XYZ rot, z_axis = { 0, 0, 1 };
671  Quaternion q_inv;
672  //double dist = 0;
673  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
674 
675  X3D_Viewer_Examine *examine = &viewer->examine;
676 
677 
678  //if (viewer->type == VIEWER_EXAMINE || (viewer->type == VIEWER_LOOKAT && viewer->lastType == VIEWER_EXAMINE) ) {
679  /* my $z = $this->{Quat}->invert->rotate([0,0,1]); */
680  quaternion_inverse(&q_inv, &(viewer->Quat));
681  quaternion_rotation(&rot, &q_inv, &z_axis);
682 
683  /* my $d = 0; for(0..2) {$d += $this->{Pos}[$_] * $z->[$_]} */
684  //dist = VECPT(viewer->Pos, rot);
685 
686  /* $this->{Origin} = [ map {$this->{Pos}[$_] - $d * $z->[$_]} 0..2 ]; */
687 /*
688 printf ("RP, before orig calc %4.3f %4.3f %4.3f\n",examine->Origin.x, examine->Origin.y,examine->Origin.z);
689 */
690  (examine->Origin).x = (viewer->Pos).x - viewer->Dist * rot.x;
691  (examine->Origin).y = (viewer->Pos).y - viewer->Dist * rot.y;
692  (examine->Origin).z = (viewer->Pos).z - viewer->Dist * rot.z;
693 /*
694 printf ("RP, aft orig calc %4.3f %4.3f %4.3f\n",examine->Origin.x, examine->Origin.y,examine->Origin.z);
695 */
696  //}
697 }
698 void resolve_pos2() {
699  X3D_Viewer *viewer;
700  viewer = Viewer();
701  resolve_pos20(viewer);
702 }
703 double vecangle2(struct point_XYZ* V1, struct point_XYZ* V2, struct point_XYZ* rotaxis) {
704  /* similar full circle angle computation as:
705  double matrotate2v()
706  */
707 
708  double cosine, sine, ulen, vlen, scale, dot, angle;
709  struct point_XYZ cross;
710  /* use dot product to get cosine: cosTheta = (U dot V)/(||u||||v||) */
711  dot = vecdot(V1,V2);
712  ulen = sqrt(vecdot(V1,V1));
713  vlen = sqrt(vecdot(V2,V2));
714  scale = ulen*vlen;
715  if( APPROX(scale, 0.0) )
716  {
717  rotaxis->y = rotaxis->z = 0.0;
718  rotaxis->x = 1.0; //arbitrary axis
719  return 0.0;
720  }
721  cosine = dot/scale;
722  /* use cross product to get sine: ||u X v|| = ||u||||v||sin(theta) or sinTheta = ||uXv||/(||u||||v||)*/
723  veccross(&cross,*V1,*V2);
724  sine = sqrt(vecdot(&cross,&cross))/scale;
725  /* get full circle unambiguous angle using both cosine and sine */
726  angle = atan2(sine,cosine);
727  vecnormal(rotaxis,&cross);
728  return angle;
729 }
730 void avatar2BoundViewpointVerticalAvatar(GLDOUBLE *matA2BVVA, GLDOUBLE *matBVVA2A)
731 {
732  /* goal: make 2 transform matrices to go back and forth from Avatar A to
733  Bound-Viewpoint-Vertical aligned Avatar-centric (no translations or scales - just 2 tilts) coordinates
734  */
735  X3D_Viewer *viewer;
736  struct point_XYZ tilted;
737  struct point_XYZ downvec = {0.0,-1.0,0.0};
738  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
739  viewer = Viewer();
740 
741  //downvec is in bound viewpoint space
742  quaternion_rotation(&tilted, &viewer->Quat, &downvec);
743  //tilted is in avatar space.
744  matrotate2v(matA2BVVA,downvec,tilted);
745  matrotate2v(matBVVA2A,tilted,downvec);
746  //printmatrix2(matA2BVVA,"A2BVVA" );
747  //printmatrix2(matBVVA2A,"BVVA2A");
748  return;
749 }
750 
751 void viewer_level_to_bound()
752 {
753 /*
754 Goal: Gravity as per specs
755 Gravity:
756  From specs > abstract > architecture > 23.3.4 NavigationInfo:
757  "The speed, avatarSize and visibilityLimit values are all scaled by the transformation being applied
758  to the currently bound Viewpoint node.
759  If there is no currently bound Viewpoint node, the values are interpreted in the world coordinate system. "
760 
761  "For purposes of terrain following, the browser maintains a notion of the down direction (down vector), since gravity
762  is applied in the direction of the down vector. This down vector shall be along the negative Y-axis in the
763  local coordinate system of the currently bound Viewpoint node (i.e., the accumulation of the Viewpoint node's
764  ancestors' transformations, not including the Viewpoint node's orientation field)."
765 
766  From specs > abstract > architecture > 23.3.5 Viewpoint
767  "When a Viewpoint node is at the top of the stack, the user's view is
768  conceptually re-parented as a child of the Viewpoint node."
769 
770  "Navigation types (see 23.3.4 NavigationInfo) that require a definition of a down vector (e.g., terrain following)
771  shall use the negative Y-axis of the coordinate system of the currently bound Viewpoint node.
772  Likewise, navigation types that require a definition of an up vector shall use the positive Y-axis of the
773  coordinate system of the currently bound Viewpoint node. The orientation field of the Viewpoint node does
774  not affect the definition of the down or up vectors. This allows the author to separate the viewing direction
775  from the gravity direction."
776 
777  Implication: if your entire scene is tilted (ie. Z up), along with your viewpoint, you shouldn't notice.
778  Even when terrain following, stepping, colliding.
779 
780 Transforms:
781 World > [TransformStack] > Bound-Viewpoint > [Quat + Pos] > viewer/avatar > [AntiQuat + AntiPos?] > Bound-Viewpoint > Inverse[TransformStack] > World
782 Viewer.Quat, Viewer.Pos - local pose of avatar wrt its currently bound viewpoint parent.
783  Includes/contains the viewpoint node's position and orientation field info.
784 
785 ViewerUpVector: - looks like a global tilt of the avatar - I don't use it here or in collision
786 ViewerUpVector computation - see RenderFuncs.c L595
787 */
788 
789  /*
790  first attempts at leveling avatar to bound viewpoint:
791  1. Transform a bound-viewpoint-coordinates down vector {0,-1,0} to avatar coords using Quat
792  2. compute tilts of that down vector in avatar space
793  3. apply inverse tilts to end of transform chain ie Quat = Quat*inverse(tilts)
794  */
795  struct point_XYZ rotaxis, tilted;
796  Quaternion q, Quat; //, AntiQuat;
797  double angle;
798  X3D_Viewer *viewer;
799  struct point_XYZ downvec = {0.0,-1.0,0.0};
800  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
801 
802  viewer = Viewer();
803  Quat = viewer->Quat;
804  //AntiQuat = Viewer.AntiQuat;
805  quaternion_rotation(&tilted, &Quat, &downvec);
806  //tilted is in avatar space.
807  angle = vecangle2(&downvec,&tilted,&rotaxis);
808  if( APPROX(angle,0.0) ) return; //we're level already
809  vrmlrot_to_quaternion(&q, rotaxis.x, rotaxis.y, rotaxis.z, -angle );
810  quaternion_normalize(&q);
811  quaternion_multiply(&(viewer->Quat), &q, &Quat);
812  quaternion_normalize(&(viewer->Quat));
813 
814  /* make sure Viewer.Dist is configured properly for Examine mode */
815  //CALCULATE_EXAMINE_DISTANCE
816 }
817 
818 void viewer_togl(double fieldofview)
819 {
820  /* goal: take the curring Viewer pose (.Pos, .Quat) and set openGL transforms
821  to prepare for a separate call to move the viewpoint -
822  (currently done in Mainloop.c setup_viewpoint())
823  Explanation of AntiPos, AntiQuat:
824  If there's a viewpoint vp, We want to
825  a) navigate away from the initial bind_viewpoint transform + (.position,.orientation) pose
826  b) start navigation from where vp.position, vp.orientation tell us.
827  c) remain responsive if there are changes to .position or .orientation. during run
828  - either javascript or routing may change vp.position, .orientation
829  To accomodate all this we:
830  a) create variables Viewer.Pos, .Quat to hold the navigation
831  b) initially set Viewer.Pos, .Quat to vp.position, .orientation
832  - see INITIALIZE_POSE_ANTIPOSE and its use in bind_viewpoint
833  c) subtract off initially bound .position, .orientation and add on current .position, .orientation
834  - below, AntiPos and AntiQuat hold the original .orientation, .position values set during bind_viewpoint
835  - prep_viewpoint in module Component_Navigation then adds back on
836  the current (javascript/routing changed) .position, .orientation
837  - if no change to .position, .orientation after binding, then these 2
838  (AntiPos,AntiQuat) and (vp.position,vp.orientation) are equal and cancel
839  leaving the .Pos, .Quat -initially with .position, .orientation- in the modelview matrix stack
840  */
841  X3D_Viewer *viewer;
842  // OLDCODE UNUSED ttglobal tg;
843  // OLDCODE UNUSED ppViewer p;
844  // OLDCODE UNUSED tg = gglobal();
845  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
846 
847  viewer = Viewer();
848  if (viewer->isStereo) /* buffer != GL_BACK) */
849  set_stereo_offset0(); /*Viewer.iside, Viewer.eyehalf, Viewer.eyehalfangle);*/
850 
851  if (viewer->SLERPing) {
852  double tickFrac;
853  Quaternion slerpedDiff;
854  struct point_XYZ pos, antipos;
855 
856 /*
857 printf ("SLERPing...\n");
858 printf ("\t startSlerpPos %lf %lf %lf\n",Viewer.startSLERPPos.x,Viewer.startSLERPPos.y,Viewer.startSLERPPos.z);
859 printf ("\t Pos %lf %lf %lf\n",Viewer.Pos.x,Viewer.Pos.y,Viewer.Pos.z);
860 printf ("\t startSlerpAntiPos %lf %lf %lf\n",Viewer.startSLERPAntiPos.x,Viewer.startSLERPAntiPos.y,Viewer.startSLERPAntiPos.z);
861 printf ("\t AntiPos %lf %lf %lf\n",Viewer.AntiPos.x,Viewer.AntiPos.y,Viewer.AntiPos.z);
862 */
863 
864  /* printf ("slerping in togl, type %s\n", VIEWER_STRING(Viewer.type)); */
865  tickFrac = (TickTime() - viewer->startSLERPtime)/viewer->transitionTime;
866  //tickFrac = tickFrac/4.0;
867  //printf ("tick frac %lf\n",tickFrac);
868 
869  pos.x = viewer->Pos.x * tickFrac + (viewer->startSLERPPos.x * (1.0 - tickFrac));
870  pos.y = viewer->Pos.y * tickFrac + (viewer->startSLERPPos.y * (1.0 - tickFrac));
871  pos.z = viewer->Pos.z * tickFrac + (viewer->startSLERPPos.z * (1.0 - tickFrac));
872  /* printf("ticfrac= %lf pos.xyz= %lf %lf %lf\n",tickFrac,pos.x,pos.y,pos.z); */
873  antipos.x = viewer->AntiPos.x * tickFrac + (viewer->startSLERPAntiPos.x * (1.0 - tickFrac));
874  antipos.y = viewer->AntiPos.y * tickFrac + (viewer->startSLERPAntiPos.y * (1.0 - tickFrac));
875  antipos.z = viewer->AntiPos.z * tickFrac + (viewer->startSLERPAntiPos.z * (1.0 - tickFrac));
876 
877  quaternion_slerp (&slerpedDiff,&viewer->startSLERPQuat,&viewer->Quat,tickFrac);
878 
879  quaternion_togl(&slerpedDiff);
880  FW_GL_TRANSLATE_D(-pos.x, -pos.y, -pos.z);
881  FW_GL_TRANSLATE_D(antipos.x, antipos.y, antipos.z);
882  quaternion_slerp (&slerpedDiff,&viewer->startSLERPAntiQuat,&viewer->AntiQuat,tickFrac);
883  quaternion_togl(&slerpedDiff);
884 
885 
886  if (tickFrac >= 1.0) viewer->SLERPing = FALSE;
887  } else {
888  quaternion_togl(&viewer->Quat);
889  FW_GL_TRANSLATE_D(-(viewer->Pos).x, -(viewer->Pos).y, -(viewer->Pos).z);
890  FW_GL_TRANSLATE_D((viewer->AntiPos).x, (viewer->AntiPos).y, (viewer->AntiPos).z);
891  quaternion_togl(&viewer->AntiQuat);
892 
893  }
894 
895  getCurrentPosInModel(TRUE);
896 }
897 
898 /* go through the modelMatrix and see where we are. Notes:
899  - this should ideally be done in prep_Viewpoint, but if there is NO viewpoint... at least
900  here, it gets called. (that is why the antipos is added in here)
901 
902  - for X3D Viewpoints, this one adds in the AntiPos; for GeoViewpoints, we do a get after
903  doing Geo transform and rotation that are integral with the GeoViewpoint node.
904 */
905 
906 
907 void getCurrentPosInModel (int addInAntiPos) {
908  X3D_Viewer *viewer;
909  struct point_XYZ rp;
910  struct point_XYZ tmppt;
911 
912  GLDOUBLE modelMatrix[16];
913  GLDOUBLE inverseMatrix[16];
914  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
915  viewer = Viewer();
916 
917  /* "Matrix Quaternion FAQ: 8.050
918  Given the current ModelView matrix, how can I determine the object-space location of the camera?
919 
920  The "camera" or viewpoint is at (0., 0., 0.) in eye space. When you
921  turn this into a vector [0 0 0 1] and multiply it by the inverse of
922  the ModelView matrix, the resulting vector is the object-space
923  location of the camera.
924 
925  OpenGL doesn't let you inquire (through a glGet* routine) the
926  inverse of the ModelView matrix. You'll need to compute the inverse
927  with your own code." */
928 
929 
930  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
931 
932 /* printf ("togl, before inverse, %lf %lf %lf\n",modelMatrix[12],modelMatrix[13],modelMatrix[14]);
933  printf ("Viewer end _togl modelview Matrix: \n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n",
934  modelMatrix[0], modelMatrix[4], modelMatrix[ 8], modelMatrix[12],
935  modelMatrix[1], modelMatrix[5], modelMatrix[ 9], modelMatrix[13],
936  modelMatrix[2], modelMatrix[6], modelMatrix[10], modelMatrix[14],
937  modelMatrix[3], modelMatrix[7], modelMatrix[11], modelMatrix[15]);
938 */
939 
940 
941  matinverseAFFINE(inverseMatrix,modelMatrix);
942 
943 /*
944 printf ("togl, after inverse, %lf %lf %lf\n",inverseMatrix[12],inverseMatrix[13],inverseMatrix[14]);
945  printf ("inverted modelview Matrix: \n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n\t%5.2f %5.2f %5.2f %5.2f\n",
946  inverseMatrix[0], inverseMatrix[4], inverseMatrix[ 8], inverseMatrix[12],
947  inverseMatrix[1], inverseMatrix[5], inverseMatrix[ 9], inverseMatrix[13],
948  inverseMatrix[2], inverseMatrix[6], inverseMatrix[10], inverseMatrix[14],
949  inverseMatrix[3], inverseMatrix[7], inverseMatrix[11], inverseMatrix[15]);
950 */
951 
952 
953  tmppt.x = inverseMatrix[12];
954  tmppt.y = inverseMatrix[13];
955  tmppt.z = inverseMatrix[14];
956 
957 
958 
959  if (addInAntiPos) {
960  /* printf ("going to do rotation on %f %f %f\n",tmppt.x, tmppt.y, tmppt.z); */
961  quaternion_rotation(&rp, &viewer->bindTimeQuat, &tmppt);
962  /* printf ("new inverseMatrix after rotation %4.2f %4.2f %4.2f\n",rp.x, rp.y, rp.z); */
963 
964  viewer->currentPosInModel.x = viewer->AntiPos.x + rp.x;
965  viewer->currentPosInModel.y = viewer->AntiPos.y + rp.y;
966  viewer->currentPosInModel.z = viewer->AntiPos.z + rp.z;
967  } else {
968  //at scene root level, after setup_viewpoint(), modelview matrix is the view matrix, and has all transforms
969  // including .orientation, .position anti-pos, antiquat applied
970  if(0) quaternion_rotation(&rp, &viewer->bindTimeQuat, &tmppt);
971  if(1) {rp.x = tmppt.x; rp.y = tmppt.y; rp.z = tmppt.z;}
972  viewer->currentPosInModel.x = rp.x;
973  viewer->currentPosInModel.y = rp.y;
974  viewer->currentPosInModel.z = rp.z;
975  }
976 
977 
978 /* printf ("getCurrentPosInModel, so, our place in object-land is %4.2f %4.2f %4.2f\n",
979  Viewer.currentPosInModel.x, Viewer.currentPosInModel.y, Viewer.currentPosInModel.z);
980 */
981 }
982 
983 
984 
985 
986 double quadratic(double x,double a,double b,double c)
987 {
988  /* y = a*x*x + b*x + c; */
989  return x*x*a + x*b + c;
990 }
991 double xsign_quadratic(double x,double a,double b,double c)
992 {
993  /* y = sign(x)*(a*abs(x)*abs(x) + b*abs(x) + c); */
994  double xSign;
995  //xSign = _copysign(1.0,x); _MSC_VER
996  if(x < 0.0) xSign = -1.0; else xSign = 1.0;
997  x = fabs(x);
998  return xSign*quadratic(x,a,b,c);
999 }
1000 static void handle_walk(const int mev, const unsigned int button, const float x, const float y) {
1001 /*
1002  * walk.xd,zd are in a plane parallel to the scene/global horizon.
1003  * walk.yd is vertical in the global/scene
1004  * walk.rd is an angle in the global/scene horizontal plane (around vertical axis)
1005 */
1006  ttglobal tg;
1007  // OLDCODE UNUSED ppViewer p;
1008  X3D_Viewer *viewer;
1009 
1010  X3D_Viewer_Walk *walk;
1011  double frameRateAdjustment = 1.0;
1012  tg = gglobal();
1013  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1014  viewer = Viewer();
1015  walk = &viewer->walk;
1016 
1017  if( tg->Mainloop.BrowserFPS > 0)
1018  frameRateAdjustment = 20.0 / tg->Mainloop.BrowserFPS; /* lets say 20FPS is our speed benchmark for developing tuning parameters */
1019  else
1020  frameRateAdjustment = 1.0;
1021 
1022 
1023  if (mev == ButtonPress ) {
1024  walk->SY = y;
1025  walk->SX = x;
1026  } else if (mev == MotionNotify) {
1027  if (button == 1) {
1028  /* July31,2010 new quadratic speed: allows you slow speed with small mouse motions, or
1029  fast speeds with large mouse motions. The .05, 5.0 etc are tuning parameters - I tinkered / experimented
1030  using the townsite scene http://dug9.users.sourceforge.net/web3d/townsite_2014/townsite.x3d
1031  which has the default navigationInfo speed (1.0) and is to geographic scale in meters.
1032  If the tuning params don't work for you please fix/iterate/re-tune/change back/put a switch
1033  I find them amply speedy, maybe yaw a bit too fast
1034  dug9: button 1 ZD: .05 5.0 0.0 RD: .1 .5 0.0
1035  button 3 XD: 5.0 10.0 0.0 YD: 5.0 10.0 0.0
1036  */
1037  walk->ZD = -xsign_quadratic(y - walk->SY,.05,5.0,0.0)*viewer->speed * frameRateAdjustment;
1038  walk->RD = xsign_quadratic(x - walk->SX,0.1,0.5,0.0)*frameRateAdjustment;
1039  //walk->ZD = (y - walk->SY) * Viewer.speed;
1040  //walk->RD = (x - walk->SX) * 0.1;
1041  } else if (button == 3) {
1042  walk->XD = xsign_quadratic(x - walk->SX,5.0,10.0,0.0)*viewer->speed * frameRateAdjustment;
1043  walk->YD = xsign_quadratic(y - walk->SY,5.0,10.0,0.0)*viewer->speed * frameRateAdjustment;
1044  //walk->XD = (x - walk->SX) * Viewer.speed;
1045  //walk->YD = -(y - walk->SY) * Viewer.speed;
1046  }
1047  } else if (mev == ButtonRelease) {
1048  if (button == 1) {
1049  walk->ZD = 0;
1050  walk->RD = 0;
1051  } else if (button == 3) {
1052  walk->XD = 0;
1053  walk->YD = 0;
1054  }
1055  }
1056 }
1057 
1058 
1059 static double
1060  norm(const Quaternion *quat)
1061  {
1062  return(sqrt(
1063  quat->w * quat->w +
1064  quat->x * quat->x +
1065  quat->y * quat->y +
1066  quat->z * quat->z
1067  ));
1068  }
1069 
1070 
1071 void handle_examine(const int mev, const unsigned int button, float x, float y) {
1072  Quaternion q, q_i, arc;
1073  struct point_XYZ pp = { 0, 0, 0};
1074  double squat_norm;
1075  // OLDCODE UNUSED ppViewer p;
1076  X3D_Viewer *viewer;
1077  X3D_Viewer_Examine *examine;
1078  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1079  viewer = Viewer();
1080  examine = &viewer->examine;
1081  pp.z=viewer->Dist;
1082 
1083  if (mev == ButtonPress) {
1084  if (button == 1) {
1085  resolve_pos2();
1086 /*
1087  printf ("\n");
1088  printf ("bp, before SQ %4.3f %4.3f %4.3f %4.3f\n",examine->SQuat.x, examine->SQuat.y, examine->SQuat.z, examine->SQuat.w);
1089  printf ("bp, before OQ %4.3f %4.3f %4.3f %4.3f\n",examine->OQuat.x, examine->OQuat.y, examine->OQuat.z, examine->OQuat.w);
1090  printf ("bp, before Q %4.3f %4.3f %4.3f %4.3f\n",Viewer.Quat.x, Viewer.Quat.y, Viewer.Quat.z, Viewer.Quat.w);
1091  printf ("bp, before, pos %4.3f %4.3f %4.3f\n",Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z);
1092  printf ("bp, before, aps %4.3f %4.3f %4.3f\n",Viewer.AntiPos.x, Viewer.AntiPos.y, Viewer.AntiPos.z);
1093 */
1094  xy2qua(&(examine->SQuat), x, y);
1095  quaternion_set(&(examine->OQuat), &(viewer->Quat));
1096 /*
1097  printf ("bp, after SQ %4.3f %4.3f %4.3f %4.3f\n",examine->SQuat.x, examine->SQuat.y, examine->SQuat.z, examine->SQuat.w);
1098  printf ("bp, after OQ %4.3f %4.3f %4.3f %4.3f\n",examine->OQuat.x, examine->OQuat.y, examine->OQuat.z, examine->OQuat.w);
1099  printf ("bp, after Q %4.3f %4.3f %4.3f %4.3f\n",Viewer.Quat.x, Viewer.Quat.y, Viewer.Quat.z, Viewer.Quat.w);
1100  printf ("bp, after, pos %4.3f %4.3f %4.3f\n",Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z);
1101  printf ("bp, after, aps %4.3f %4.3f %4.3f\n",Viewer.AntiPos.x, Viewer.AntiPos.y, Viewer.AntiPos.z);
1102 */
1103 
1104  } else if (button == 3) {
1105  examine->SY = y;
1106  examine->ODist = max(0.1,viewer->Dist);
1107  }
1108  } else if (mev == MotionNotify) {
1109  if (button == 1) {
1110  squat_norm = norm(&(examine->SQuat));
1111  /* we have missed the press */
1112  if (APPROX(squat_norm, 0)) {
1113  fprintf(stderr, "Viewer handle_examine: mouse event DRAG - missed press\n");
1114  /* $this->{SQuat} = $this->xy2qua($mx,$my); */
1115  xy2qua(&(examine->SQuat), x, y);
1116  /* $this->{OQuat} = $this->{Quat}; */
1117  quaternion_set(&(examine->OQuat), &(viewer->Quat));
1118  } else {
1119  /* my $q = $this->xy2qua($mx,$my); */
1120  xy2qua(&q, x, y);
1121  /* my $arc = $q->multiply($this->{SQuat}->invert()); */
1122  quaternion_inverse(&q_i, &(examine->SQuat));
1123  quaternion_multiply(&arc, &q, &q_i);
1124 
1125 
1126  /* $this->{Quat} = $arc->multiply($this->{OQuat}); */
1127  quaternion_multiply(&(viewer->Quat), &arc, &(examine->OQuat));
1128  }
1129  } else if (button == 3) {
1130  #ifndef DISABLER
1131  viewer->Dist = examine->ODist * exp(examine->SY - y);
1132  #else
1133  viewer->Dist = (0 != y) ? examine->ODist * examine->SY / y : 0;
1134  #endif
1135  }
1136  }
1137 
1138  quaternion_inverse(&q_i, &(viewer->Quat));
1139  quaternion_rotation(&(viewer->Pos), &q_i, &pp);
1140 /*
1141  printf ("bp, after quat rotation, pos %4.3f %4.3f %4.3f\n",Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z);
1142 */
1143  viewer->Pos.x += (examine->Origin).x;
1144  viewer->Pos.y += (examine->Origin).y;
1145  viewer->Pos.z += (examine->Origin).z;
1146 /*
1147 printf ("examine->origin %4.3f %4.3f %4.3f\n",examine->Origin.x, examine->Origin.y, examine->Origin.z);
1148 */
1149 }
1150 
1151 double get_viewer_dist(){
1152  return Viewer()->Dist;
1153 }
1154 
1155 void handle_dist(const int mev, const unsigned int button, float x, float y) {
1156  /* different than z, this adjusts the viewer->Dist value for examine, turntable, explore, lookat
1157  - all without using RMB (right mouse button), so mobile friendly
1158  */
1159  //examine variant - doesn't move the vp/.pos
1160  Quaternion q_i;
1161  struct point_XYZ pp = { 0, 0, 0};
1162  double yy;
1163  X3D_Viewer *viewer;
1164  // OLDCODE UNUSED ppViewer p;
1165  X3D_Viewer_Examine *examine;
1166  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1167  viewer = Viewer();
1168  examine = &viewer->examine;
1169  pp.z=viewer->Dist;
1170 
1171  //ConsoleMessage("handle_dist but %d mev %d\n", button, mev);
1172  //yy = 1.0 - y;
1173  yy = y;
1174  if (mev == ButtonPress) {
1175  if (button == 1) {
1176  resolve_pos2();
1177  examine->SY = yy;
1178  examine->ODist = max(0.1,viewer->Dist);
1179  }
1180  } else if (mev == MotionNotify) {
1181  if (button == 1) {
1182  #ifndef DISABLER
1183  viewer->Dist = examine->ODist * exp(2.0 * (examine->SY - yy));
1184  #else
1185  viewer->Dist = (0 != yy) ? examine->ODist * examine->SY / yy : 0;
1186  #endif
1187  //printf("v.dist=%lf\n",viewer->Dist);
1188  }
1189  }
1190  quaternion_inverse(&q_i, &(viewer->Quat));
1191  quaternion_rotation(&(viewer->Pos), &q_i, &pp);
1192 /*
1193  printf ("bp, after quat rotation, pos %4.3f %4.3f %4.3f\n",Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z);
1194 */
1195  viewer->Pos.x += (examine->Origin).x;
1196  viewer->Pos.y += (examine->Origin).y;
1197  viewer->Pos.z += (examine->Origin).z;
1198 
1199 }
1200 
1201 double display_screenRatio();
1202 void handle_turntable(const int mev, const unsigned int button, float x, float y) {
1203  /*
1204  Like handle_spherical, except:
1205  move the viewer->Pos in the opposite direction from where we are looking
1206  */
1207  double frameRateAdjustment;
1208  X3D_Viewer_Spherical *ypz;
1209  X3D_Viewer *viewer;
1210  // OLDCODE UNUSED ppViewer p;
1211  ttglobal tg = gglobal();
1212  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1213  viewer = Viewer();
1214  ypz = &viewer->ypz; //just a place to store last mouse xy during drag
1215 
1216  if(APPROX(viewer->Dist,0.0)){
1217  //no pivot point yet
1218  viewer->Dist = 10.0;
1219  }
1220 
1221  if( tg->Mainloop.BrowserFPS > 0)
1222  frameRateAdjustment = 20.0 / tg->Mainloop.BrowserFPS; /* lets say 20FPS is our speed benchmark for developing tuning parameters */
1223  else
1224  frameRateAdjustment = 1.0;
1225 
1226 
1227  if (mev == ButtonPress) {
1228  if (button == 1 || button == 3) {
1229  ypz->x = x;
1230  ypz->y = y;
1231  }
1232  }
1233  else if (mev == MotionNotify)
1234  {
1235  Quaternion qyaw, qpitch;
1236  double dyaw, dpitch;
1237  struct point_XYZ pp, yaxis;
1238  double yaw, pitch; //dist,
1239  Quaternion quat;
1240 
1241  yaw = pitch = 0.0;
1242  if (button == 1 || button == 3){
1243  struct point_XYZ dd,ddr;
1244  yaxis.x = yaxis.z = 0.0;
1245  yaxis.y = 1.0;
1246  //pp = viewer->Pos;
1247  //if(0) resolve_pos2();
1248  //if(1) {
1249  //(examine->Origin).x = (viewer->Pos).x - viewer->Dist * rot.x;
1250  dd.x = dd.y = 0.0; dd.z = viewer->Dist; //exploreDist;
1251  quat = viewer->Quat;
1252  quaternion_inverse(&quat,&quat);
1253  quaternion_rotation(&ddr, &quat, &dd);
1254  vecdiff(&viewer->examine.Origin,&viewer->Pos,&ddr);
1255  //}
1256 
1257  //if(0) vecdiff(&pp,&viewer->examine.Origin,&viewer->Pos);
1258  //if(1) vecdiff(&pp,&viewer->Pos,&viewer->examine.Origin);
1259  pp = ddr;
1260  //if(0) printf("D=%f O=%f %f %f P=%f %f %f pp=%f %f %f\n", viewer->Dist,
1261  //viewer->examine.Origin.x,viewer->examine.Origin.y,viewer->examine.Origin.z,
1262  //viewer->Pos.x,viewer->Pos.y,viewer->Pos.z,
1263  //pp.x,pp.y,pp.z
1264  //);
1265  //dist = veclength(pp);
1266  vecnormal(&pp, &pp);
1267  yaw = -atan2(pp.x, pp.z);
1268  pitch = -(acos(vecdot(&pp, &yaxis)) - PI*.5);
1269  }
1270  if (button == 1) {
1271  dyaw = -(ypz->x - x) * viewer->fieldofview*PI / 180.0*viewer->fovZoom * display_screenRatio(); //tg->display.screenRatio;
1272  dpitch = (ypz->y - y) * viewer->fieldofview*PI / 180.0*viewer->fovZoom;
1273  //if(0){
1274  // dyaw = -dyaw;
1275  // dpitch = -dpitch;
1276  //}
1277  yaw += dyaw;
1278  pitch += dpitch;
1279  }else if (button == 3) {
1280  //distance drag
1281  if(0){
1282  //peddling
1283  double d, fac;
1284  d = (y - ypz->y)*.5; // .25;
1285  if (d > 0.0)
1286  fac = ((d * 2.0) + (1.0 - d) * 1.0);
1287  else
1288  {
1289  d = fabs(d);
1290  fac = ((d * .5) + (1.0 - d) * 1.0);
1291  }
1292  //dist *= fac;
1293  viewer->Dist *= fac;
1294  }
1295  if(1) {
1296  //handle_tick_explore quadratic
1297  //double quadratic = -xsign_quadratic(y - ypz->y,5.0,10.0,0.0);
1298  ypz->ypz[1] = -xsign_quadratic(y - ypz->y,100.0,10.0,0.0)*viewer->speed * frameRateAdjustment *.15;
1299  //printf("quad=%f y-y %f s=%f fra=%f\n",quadratic,y-ypz->y,viewer->speed,frameRateAdjustment);
1300  }
1301  }
1302  if (button == 1 || button == 3)
1303  {
1304  vrmlrot_to_quaternion(&qyaw, 0.0, 1.0, 0.0, yaw);
1305  vrmlrot_to_quaternion(&qpitch, 1.0, 0.0, 0.0, pitch);
1306  quaternion_multiply(&quat, &qpitch, &qyaw);
1307  quaternion_normalize(&quat);
1308 
1309  quaternion_set(&(viewer->Quat), &quat);
1310  //move the viewer->pos in the opposite direction that we are looking
1311  quaternion_inverse(&quat, &quat);
1312  pp.x = 0.0;
1313  pp.y = 0.0;
1314  pp.z = viewer->Dist; //dist;
1315  quaternion_rotation(&(viewer->Pos), &quat, &pp);
1316  //remember the last drag coords for next motion
1317  vecadd(&viewer->Pos,&viewer->examine.Origin,&viewer->Pos);
1318  }
1319  if( button == 1){
1320  ypz->x = x;
1321  ypz->y = y;
1322  }
1323  }else if(mev == ButtonRelease) {
1324  if (button == 3) {
1325  ypz->ypz[1] = 0.0;
1326  }
1327  }
1328 }
1329 
1330 
1331 void handle_spherical(const int mev, const unsigned int button, float x, float y) {
1332  /* handle_examine almost works except we don't want roll-tilt, and we want to zoom */
1333  int ibutton;
1334  Quaternion qyaw, qpitch;
1335  double dyaw,dpitch;
1336  /* unused double dzoom; */
1337  X3D_Viewer *viewer;
1338  X3D_Viewer_Spherical *ypz;
1339  // OLDCODE UNUSED ppViewer p;
1340  ttglobal tg = gglobal();
1341  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1342  viewer = Viewer();
1343  ypz = &viewer->ypz;
1344  ibutton = button;
1345  if(ibutton == 1 && tg->Mainloop.CTRL) ibutton = 3; //RMB method for mobile/touch
1346 
1347  if (mev == ButtonPress) {
1348  if (ibutton == 1 || ibutton == 3) {
1349  ypz->x = x;
1350  ypz->y = y;
1351  }
1352  } else if (mev == MotionNotify) {
1353  if (ibutton == 1) {
1354  double yaw, pitch;
1355  Quaternion quat;
1356  struct point_XYZ dd, ddr, yaxis;
1357 
1358  //step 1 convert Viewer.Quat to yaw, pitch (discard any roll)
1359  yaxis.x = yaxis.z = 0.0;
1360  yaxis.y = 1.0;
1361 
1362  dd.x = dd.y = 0.0; dd.z = 1.0;
1363  quat = viewer->Quat;
1364  quaternion_inverse(&quat,&quat);
1365  quaternion_rotation(&ddr, &quat, &dd);
1366  yaw = -atan2(ddr.x,ddr.z);
1367  pitch = -(acos(vecdot(&ddr, &yaxis)) - PI*.5);
1368 
1369  //step 2 add on any mouse motion as yaw,pitch chord
1370  dyaw = (ypz->x - x) * viewer->fieldofview*PI/180.0*viewer->fovZoom * display_screenRatio(); //tg->display.screenRatio;
1371  dpitch = -(ypz->y - y) * viewer->fieldofview*PI/180.0*viewer->fovZoom;
1372  yaw += dyaw;
1373  pitch += dpitch;
1374 
1375  //step 3 convert yaw, pitch back to Viewer.Quat
1376  vrmlrot_to_quaternion(&qyaw, 0.0, 1.0, 0.0, yaw);
1377  vrmlrot_to_quaternion(&qpitch, 1.0, 0.0, 0.0, pitch);
1378  quaternion_multiply(&quat, &qpitch, &qyaw);
1379  quaternion_normalize(&quat);
1380 
1381  quaternion_set(&(viewer->Quat), &quat);
1382 
1383  } else if (ibutton == 3) {
1384  double d, fac;
1385  d = -(y - ypz->y)*.5;
1386  fac = pow(10.0,d);
1387  viewer->fovZoom = viewer->fovZoom * fac;
1388  //viewer->fovZoom = DOUBLE_MIN(2.0,DOUBLE_MAX(.125,viewer->fovZoom));
1389  }
1390  if(ibutton == 1 || ibutton == 3){
1391  ypz->x = x;
1392  ypz->y = y;
1393  }
1394  }
1395 }
1396 
1397 
1398 
1399 /* fly2, tilt, tplane, rplane form a set that replaces keyboard fly for
1400  touch devices. Collision / gravity only differentiates WALK, and treats all
1401  other modes the same as fly.
1402  When FLY mode is set from the scene, the front end (or statusbarHud)
1403  switches to FLY2 which navigates similar to walk mode except with
1404  (default) no gravity and a (default) spherical collision volume.
1405 */
1406 void viewer_lastQ_set(Quaternion *lastQ);
1407 void handle_fly2(const int mev, const unsigned int button, float x, float y) {
1408  /* there's a handle_tick_fly2() so handle_fly2() must turn on/off the
1409  tick action based on mev (mouse up/down/move)
1410  */
1411  X3D_Viewer *viewer;
1412  // OLDCODE UNUSED ttglobal tg;
1413  // OLDCODE UNUSED ppViewer p;
1414  X3D_Viewer_InPlane *inplane;
1415  // OLDCODE UNUSED tg = gglobal();
1416  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1417  viewer = Viewer();
1418  inplane = &viewer->inplane;
1419 
1420  if (mev == ButtonPress) {
1421  inplane->x = x;
1422  inplane->y = y;
1423  inplane->xx = x;
1424  inplane->yy = y;
1425  inplane->on = 1;
1426  } else if (mev == MotionNotify) {
1427  inplane->xx = x;
1428  inplane->yy = y;
1429  } else if (mev == ButtonRelease ) {
1430  inplane->on = 0;
1431  }
1432 
1433 }
1434 
1435 
1436 
1437 void handle_tick_fly2(double dtime) {
1438  ttglobal tg;
1439  // OLDCODE UNUSED ppViewer p;
1440  X3D_Viewer_InPlane *inplane;
1441  double frameRateAdjustment, xx, yy, zz, rot;
1442  struct point_XYZ xyz;
1443  Quaternion q, nq;
1444  X3D_Viewer *viewer;
1445  tg = gglobal();
1446  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1447  viewer = Viewer();
1448  inplane = &viewer->inplane;
1449 
1450  if( tg->Mainloop.BrowserFPS > 0)
1451  frameRateAdjustment = 20.0 / tg->Mainloop.BrowserFPS;
1452  else
1453  frameRateAdjustment = 1.0;
1454 
1455  if (inplane->on) {
1456  xx = inplane->xx - inplane->x;
1457  yy = inplane->yy - inplane->y;
1458  zz = -xsign_quadratic(yy,.05,5.0,0.0)*viewer->speed * frameRateAdjustment;
1459  zz *= 0.15;
1460 
1461  xyz.x = 0.0;
1462  xyz.y = 0.0;
1463  xyz.z = zz;
1464 
1465  rot = xsign_quadratic(xx,0.1,0.5,0.0)*frameRateAdjustment;
1466  //printf("rot=%lf zz=%lf\n",rot,zz);
1467  memcpy(&q,&viewer->Quat,sizeof(Quaternion));
1468  vrmlrot_to_quaternion (&nq,0.0,1.0,0.0,0.4*rot);
1469  viewer_lastQ_set(&nq); //wall penetration - last avatar pose is stored before updating
1470  quaternion_multiply(&(viewer->Quat), &nq, &q); //Quat = walk->RD * Quat
1471  //does the Z gets transformed by the quat?
1472  increment_pos(&xyz);
1473  //inplane->x = x;
1474  //inplane->y = y;
1475  //CALCULATE_EXAMINE_DISTANCE
1476  }
1477 
1478 }
1479 
1480 void handle_lookat(const int mev, const unsigned int button, float x, float y) {
1481  /* do nothing on mouse down or mouse move
1482  on mouse up, trigger node picking action in mainloop
1483  */
1484  X3D_Viewer *viewer;
1485  // OLDCODE UNUSED ttglobal tg;
1486  // OLDCODE UNUSED ppViewer p;
1487  // OLDCODE UNUSED tg = gglobal();
1488  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1489  viewer = Viewer();
1490 
1491  switch(mev){
1492  case ButtonPress:
1493  //trigger a node pick in mainloop, followed by viewpoint transition
1494  viewer->LookatMode = 2;
1495  //printf("lookat press\n");
1496  break;
1497  case MotionNotify:
1498  //do nothing
1499  //printf("lookat motion\n");
1500  break;
1501  case ButtonRelease:
1502  //printf("looat release\n");
1503  //viewer->lookatmode should == 3 coming in here
1504  if(viewer->type == VIEWER_LOOKAT)
1505  fwl_set_viewer_type(VIEWER_LOOKAT); //toggle off LOOKAT
1506  if(viewer->type == VIEWER_EXPLORE)
1507  fwl_set_viewer_type(VIEWER_EXPLORE); //toggle off LOOKAT
1508  viewer->LookatMode = 0; //VIEWER_EXPLORE
1509 
1510  break;
1511  }
1512 
1513 }
1514 void handle_tick_lookat() {
1515  X3D_Viewer *viewer;
1516  // OLDCODE UNUSED ttglobal tg;
1517  // OLDCODE UNUSED ppViewer p;
1518  // OLDCODE UNUSED tg = gglobal();
1519  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1520  //stub in case we need the viewer or viewpoint transition here
1521  viewer = Viewer();
1522  switch(viewer->LookatMode){
1523  case 0: //not in use
1524  case 1: //someone set viewer to lookat mode: mainloop shuts off sensitive, turns on lookat cursor
1525  case 2: //mouseup tells mainloop to pick a node at current mousexy, turn off lookatcursor
1526  case 3: //mainloop picked a node, now transition
1527  case 4: //transition complete, restore previous nav type
1528  break;
1529  }
1530 }
1531 
1532 void handle_explore(const int mev, const unsigned int button, float x, float y) {
1533  /*
1534  Like handle_spherical, except:
1535  move the viewer->Pos in the opposite direction from where we are looking
1536  */
1537  int ctrl;
1538  // OLDCODE UNUSED X3D_Viewer_Spherical *ypz;
1539  X3D_Viewer *viewer;
1540  // OLDCODE UNUSED ppViewer p;
1541  ttglobal tg = gglobal();
1542  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1543  viewer = Viewer();
1544  // OLDCODE UNUSED ypz = &viewer->ypz; //just a place to store last mouse xy during drag
1545  ctrl = tg->Mainloop.CTRL;
1546 
1547 
1548  if(ctrl) {
1549  //we're in pick mode - we'll re-use some lookat code
1550  handle_lookat(mev,button,x,y);
1551  return;
1552  }
1553  if(APPROX(viewer->Dist,0.0)){
1554  //no pivot point yet
1555  handle_spherical(mev,button,x,y);
1556  return;
1557  }
1558  handle_turntable(mev, button, x, y);
1559 }
1560 
1561 void handle_tplane(const int mev, const unsigned int button, float x, float y) {
1562  /* handle_walk with 3button mouse, RMB, can do X,Y in plane, but not rotation
1563  for touch screen with one finger, we want a nav mode called InPlane to
1564  do the X,Y shifts and rotation in the plane of the camera screen
1565  (about camera-axis/Z)
1566  */
1567  X3D_Viewer *viewer;
1568  X3D_Viewer_InPlane *inplane;
1569  //double frameRateAdjustment;//,xx,yy;
1570  //struct point_XYZ xyz;
1571  // OLDCODE UNUSED ppViewer p;
1572  //ttglobal tg = gglobal();
1573  // OLDCODE UNUSED p = (ppViewer)gglobal()->Viewer.prv;
1574  viewer = Viewer();
1575  inplane = &viewer->inplane;
1576 
1577  //if( tg->Mainloop.BrowserFPS > 0)
1578  // frameRateAdjustment = 20.0 / tg->Mainloop.BrowserFPS; /* lets say 20FPS is our speed benchmark for developing tuning parameters */
1579  //else
1580  // frameRateAdjustment = 1.0;
1581 
1582  if (mev == ButtonPress) {
1583  inplane->x = x; //x;
1584  inplane->y = y; //y;
1585  inplane->on = 1;
1586  } else if (mev == MotionNotify) {
1587  inplane->xx = x; //.15 * xsign_quadratic(x - inplane->x,5.0,10.0,0.0)*viewer->speed * frameRateAdjustment;
1588  inplane->yy = y; //-.15f * xsign_quadratic(y - inplane->y,5.0,10.0,0.0)*viewer->speed * frameRateAdjustment;
1589  } else if(mev == ButtonRelease){
1590  inplane->xx = 0.0f;
1591  inplane->yy = 0.0f;
1592  inplane->on = 0;
1593  }
1594 }
1595 void handle_tick_tplane(double dtime){
1596  X3D_Viewer *viewer;
1597  X3D_Viewer_InPlane *inplane;
1598  //Quaternion quatr, quatt, quat;
1599  struct point_XYZ pp;
1600  // OLDCODE UNUSED ttglobal tg;
1601  // OLDCODE UNUSED ppViewer p;
1602  // OLDCODE UNUSED tg = gglobal();
1603  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1604  viewer = Viewer();
1605 
1606  inplane = &viewer->inplane;
1607  if(inplane->on){
1608  if(0){
1609  pp.x = xsign_quadratic(inplane->xx - inplane->x,300.0,100.0,0.0) *dtime;
1610  pp.y = xsign_quadratic(inplane->yy - inplane->y,300.0,100.0,0.0) *dtime;
1611  }else{
1612  pp.x = xsign_quadratic(inplane->xx - inplane->x,3.0,1.0,0.0)*max(1.0,viewer->Dist) * dtime;
1613  pp.y = xsign_quadratic(inplane->yy - inplane->y,3.0,1.0,0.0)*max(1.0,viewer->Dist) * dtime;
1614  }
1615  pp.z = 0.0;
1616  //vecadd(&viewer->Pos,&viewer->Pos,&pp);
1617  increment_pos(&pp);
1618  }
1619 }
1620 
1621 void handle_rtplane(const int mev, const unsigned int button, float x, float y) {
1622  /* handle_walk with 3button mouse, RMB, can do X,Y in plane, but not rotation
1623  for touch screen with one finger, we want a nav mode called InPlane to
1624  do the X,Y shifts and rotation in the plane of the camera screen
1625  (about camera-axis/Z)
1626  */
1627  X3D_Viewer *viewer;
1628  X3D_Viewer_InPlane *inplane;
1629  Quaternion nq, q_v;
1630  double xx,yy, frameRateAdjustment;
1631  // OLDCODE UNUSED ppViewer p;
1632  ttglobal tg = gglobal();
1633  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1634  viewer = Viewer();
1635  inplane = &viewer->inplane;
1636 
1637  if( tg->Mainloop.BrowserFPS > 0)
1638  frameRateAdjustment = 20.0 / tg->Mainloop.BrowserFPS; /* lets say 20FPS is our speed benchmark for developing tuning parameters */
1639  else
1640  frameRateAdjustment = 1.0;
1641 
1642  if (mev == ButtonPress) {
1643  inplane->x = x;
1644  inplane->y = y;
1645  } else if (mev == MotionNotify) {
1646  if(0){
1647  //static drag
1648  double drot = atan2(yy,xx) - atan2(inplane->y,inplane->x);
1649  //printf("y=%lf x=%lf inplane-y=%lf inplanex=%lf\n",yy,xx,inplane->y,inplane->x);
1650  quaternion_set(&q_v, &(viewer->Quat));
1651  vrmlrot_to_quaternion(&nq, 0.0, 0.0, 1.0, drot);
1652  quaternion_multiply(&(viewer->Quat), &nq, &q_v);
1653  inplane->x = xx;
1654  inplane->y = yy;
1655  //CALCULATE_EXAMINE_DISTANCE
1656  }
1657  if(1){
1658  //handle_tick quadratic drag
1659  inplane->xx = xsign_quadratic(x - inplane->x,0.1,0.5,0.0)*frameRateAdjustment;
1660  inplane->yy = xsign_quadratic(y - inplane->y,0.1,0.5,0.0)*frameRateAdjustment;
1661  }
1662 
1663  } else if (mev == ButtonRelease) {
1664  if (button == 1) {
1665  inplane->xx = 0.0f;
1666  inplane->yy = 0.0f;
1667  }
1668  }
1669 }
1670 
1671 void handle_tick_rplane(double dtime){
1672  X3D_Viewer *viewer;
1673  X3D_Viewer_InPlane *inplane;
1674  Quaternion quatr;
1675  //struct point_XYZ pp;
1676  double roll;
1677  // OLDCODE UNUSED ttglobal tg;
1678  // OLDCODE UNUSED ppViewer p;
1679  // OLDCODE UNUSED tg = gglobal();
1680  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1681  viewer = Viewer();
1682 
1683  inplane = &viewer->inplane;
1684  if(inplane->on){
1685  roll = xsign_quadratic(inplane->xx - inplane->x,2.0,2.0,0.0)*dtime;
1686  vrmlrot_to_quaternion (&quatr,0.0,0.0,1.0,roll); //roll about z axis
1687  quaternion_multiply(&(viewer->Quat), &quatr, &(viewer->Quat));
1688  quaternion_normalize(&(viewer->Quat));
1689  }
1690 
1691 }
1692 void handle_tick_tilt(double dtime) {
1693  X3D_Viewer *viewer;
1694  X3D_Viewer_InPlane *inplane;
1695  Quaternion quatt;
1696  //struct point_XYZ pp;
1697  double yaw, pitch;
1698  // OLDCODE UNUSED ttglobal tg;
1699  // OLDCODE UNUSED ppViewer p;
1700  // OLDCODE UNUSED tg = gglobal();
1701  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
1702  viewer = Viewer();
1703 
1704  inplane = &viewer->inplane;
1705  if(inplane->on){
1706  yaw = xsign_quadratic(inplane->xx - inplane->x,2.0,2.0,0.0)*dtime;
1707  vrmlrot_to_quaternion (&quatt,0.0,1.0,0.0,yaw); //tilt about x axis
1708  quaternion_multiply(&(viewer->Quat), &quatt, &(viewer->Quat));
1709  pitch = -xsign_quadratic(inplane->yy - inplane->y,2.0,2.0,0.0)*dtime;
1710  vrmlrot_to_quaternion (&quatt,1.0,0.0,0.0,pitch); //tilt about x axis
1711  quaternion_multiply(&(viewer->Quat), &quatt, &(viewer->Quat));
1712  quaternion_normalize(&(viewer->Quat));
1713  }
1714 }
1715 
1716 /************************************************************************************/
1717 
1718 
1719 void handle0(const int mev, const unsigned int button, const float x, const float yup)
1720 {
1721  X3D_Viewer *viewer;
1722  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1723  viewer = Viewer();
1724  /* ConsoleMessage("Viewer handle: viewer_type %s, mouse event %d, button %u, x %f, y %f\n",
1725  lookup_navmodestring(viewer->type), mev, button, x, yup); */
1726 
1727  if (button == 2) {
1728  return;
1729  }
1730  switch(viewer->type) {
1731  case VIEWER_NONE:
1732  break;
1733  case VIEWER_EXAMINE:
1734  handle_examine(mev, button, ((float) x), ((float) yup));
1735  break;
1736  case VIEWER_WALK:
1737  handle_walk(mev, button, ((float) x), ((float) yup));
1738  break;
1739  case VIEWER_EXFLY:
1740  break;
1741  case VIEWER_FLY:
1742  handle_fly2(mev, button, ((float) x), ((float) yup)); //feature-Navigation_key_and_drag
1743  break;
1744  case VIEWER_FLY2:
1745  handle_fly2(mev,button,((float) x),((float)yup));
1746  break;
1747  case VIEWER_TILT:
1748  case VIEWER_RPLANE:
1749  handle_rtplane(mev,button,((float) x),((float)yup)); //roll, tilt: one uses x, one uses y - separate handle_ticks though
1750  break;
1751  case VIEWER_TPLANE:
1752  handle_tplane(mev,button,((float) x),((float)yup)); //translation in the viewer plane
1753  break;
1754  case VIEWER_SPHERICAL:
1755  handle_spherical(mev,button,((float) x),((float)yup)); //spherical panorama
1756  break;
1757  case VIEWER_TURNTABLE:
1758  handle_turntable(mev, button, ((float)x), ((float)yup)); //examine without roll around world 0,0,0 origin - like a 3D editor with authoring plane
1759  break;
1760  case VIEWER_LOOKAT:
1761  handle_lookat(mev, button, ((float)x), ((float)yup)); //as per navigationInfo specs, you toggle on, then click an object and it flys you there
1762  break;
1763  case VIEWER_EXPLORE:
1764  handle_explore(mev, button, ((float)x), ((float)yup)); //as per specs, like turntable around any point you pick with CTRL click
1765  break;
1766  case VIEWER_DIST:
1767  handle_dist(mev,button,(float)x,(float)yup);
1768  default:
1769  break;
1770  }
1771 }
1772 
1773 #define FLYREMAP {{'a',NUM0},{'z',NUMDEC},{'j',LEFT_KEY},{'l',RIGHT_KEY},{'p',UP_KEY},{';',DOWN_KEY},{'8',NUM8},{'k',NUM2},{'u',NUM4},{'o',NUM6 },{'7',NUM7},{'9',NUM9}}
1774 
1775 //BEGIN dug9 Feb2015 >>>
1776 //GOAL for this refactoring: CHORD mappings of arrow keys for keyboard navigation and mouse xy drags for FLY navigation
1777 // and keeping in mind mobile devices may not want a keyboard on the screen, but they may have 4 arrow keys
1778 // - and -if no change to UI menus/no use of chords by user- the default behaviour is what we do now
1779 Key FLYREMAP2 [] = {{'a',NUM0},{'z',NUMDEC},{'j',LEFT_KEY},{'l',RIGHT_KEY},{'p',UP_KEY},{';',DOWN_KEY},{'8',NUM8},{'k',NUM2},{'u',NUM4},{'o',NUM6 },{'7',NUM7},{'9',NUM9}};
1780 int FLYREMAP2SIZE = 12;
1781 Key FLYCHORDREMAP [] = {
1782 {'j',LEFT_KEY},{'l',RIGHT_KEY},{'p',UP_KEY},{';',DOWN_KEY}
1783 };
1784 int arrowkeys [] = {LEFT_KEY,RIGHT_KEY,UP_KEY,DOWN_KEY};
1785 //int isArrowkey(int key){
1786 // int iret, i;
1787 // iret = 0;
1788 // for(i=0;i<4;i++)
1789 // if(key == arrowkeys[i]) iret = 1;
1790 // return iret;
1791 //}
1792 int indexArrowkey(int key){
1793  int iret, i;
1794  iret = -1;
1795  for(i=0;i<4;i++)
1796  if(key == arrowkeys[i]) iret = i;
1797  return iret;
1798 }
1799 
1800 //movements of the camera (with respect to the scene)
1801 enum {
1802  FLY_X_LEFT,
1803  FLY_X_RIGHT,
1804  FLY_Y_DOWN,
1805  FLY_Y_UP,
1806  FLY_Z_FORWARD,
1807  FLY_Z_REVERSE,
1808  FLY_PITCH_UP,
1809  FLY_PITCH_DOWN,
1810  FLY_YAW_LEFT,
1811  FLY_YAW_RIGHT,
1812  FLY_ROLL_COUNTERCLOCKWISE,
1813  FLY_ROLL_CLOCKWISE,
1814 } fly_key_command;
1815 Key fly_normalkeys [] = {
1816  {'j',FLY_X_LEFT},
1817  {'l',FLY_X_RIGHT},
1818  {';',FLY_Y_DOWN},
1819  {'p',FLY_Y_UP},
1820  {'a',FLY_Z_FORWARD},
1821  {'z',FLY_Z_REVERSE},
1822  {'k',FLY_PITCH_UP},
1823  {'8',FLY_PITCH_DOWN},
1824  {'u',FLY_YAW_LEFT},
1825  {'o',FLY_YAW_RIGHT},
1826  {'7',FLY_ROLL_COUNTERCLOCKWISE},
1827  {'9',FLY_ROLL_CLOCKWISE},
1828 };
1829 
1830 //enum {
1831 // CHORD_YAWZ,
1832 // CHORD_YAWPITCH,
1833 // CHORD_ROLL,
1834 // CHORD_XY
1835 //} input_chords;
1836 char *chordnames [] = {"YAWZ","YAWPITCH","ROLL","XY"};
1837 //the flychord table is bloated with redundancies, but explicit. FLYCHORDMAP2 int[4][4] would be briefer, but harder to trace.
1838 typedef struct flychord {
1839  int chord;
1840  Key arrows[4];
1841 } flychord;
1842 flychord FLYCHORDREMAP2 [] = {
1843  {CHORD_YAWZ, {{FLY_YAW_LEFT,LEFT_KEY},{FLY_YAW_RIGHT,RIGHT_KEY},{FLY_Z_FORWARD,UP_KEY},{FLY_Z_REVERSE,DOWN_KEY}}},
1844  {CHORD_YAWPITCH,{{FLY_YAW_LEFT,LEFT_KEY},{FLY_YAW_RIGHT,RIGHT_KEY},{FLY_PITCH_UP,UP_KEY},{FLY_PITCH_DOWN,DOWN_KEY}}},
1845  {CHORD_ROLL, {{FLY_ROLL_COUNTERCLOCKWISE,LEFT_KEY},{FLY_ROLL_CLOCKWISE,RIGHT_KEY},{FLY_ROLL_COUNTERCLOCKWISE,UP_KEY},{FLY_ROLL_CLOCKWISE,DOWN_KEY}}},
1846  {CHORD_XY, {{FLY_X_LEFT,LEFT_KEY},{FLY_X_RIGHT,RIGHT_KEY},{FLY_Y_UP,UP_KEY},{FLY_Y_DOWN,DOWN_KEY}}},
1847 };
1848 
1849 int viewer_getKeyChord(){
1850  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1851  return p->keychord;
1852 }
1853 void viewer_setKeyChord(int chord){
1854  int chord1;
1855  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1856  chord1 = chord;
1857  if(chord1 > 3) chord1 = 0;
1858  if(chord1 < 0) chord1 = 3;
1859  p->keychord = chord1;
1860 }
1861 char *fwl_getKeyChord(){
1862  return chordnames[viewer_getKeyChord()];
1863 }
1864 
1865 int fwl_setKeyChord(char *chordname){
1866  int i, ok;
1867  ok = FALSE;
1868  for(i=0;i<4;i++){
1869  if(!strcasecmp(chordname,chordnames[i])){
1870  viewer_setKeyChord(i); //or should I expand from i to CHORD_YAWZ etc
1871  ok = TRUE;
1872  break;
1873  }
1874  }
1875  return ok;
1876 }
1877 int viewer_getDragChord(){
1878  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1879  return p->dragchord;
1880 }
1881 void viewer_setDragChord(int chord){
1882  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1883  p->dragchord = chord;
1884 }
1885 void viewer_setNextDragChord(){
1886  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1887  p->dragchord = p->dragchord == CHORD_XY ? CHORD_YAWZ : p->dragchord + 1;
1888 }
1889 char *fwl_getDragChord(){
1890  return chordnames[viewer_getDragChord()];
1891 }
1892 int fwl_setDragChord(char *chordname){
1893  int i, ok;
1894  ok = FALSE;
1895  for(i=0;i<4;i++){
1896  if(!strcasecmp(chordname,chordnames[i])){
1897  viewer_setDragChord(i); //or should I expand from i to CHORD_YAWZ etc
1898  ok = TRUE;
1899  break;
1900  }
1901  }
1902  return ok;
1903 }
1904 
1905 //next: in lookup_fly_key we would check if its an arrow key, and if so, use the current keychord to lookup the keyfly command.
1906 // from that we would look up the normal key
1907 int lookup_fly_arrow(int key){
1908  //check if this is an arrow key. If so lookup in the current chord to get the motion command
1909  //and from motion command lookup the 'normal' equivalent key
1910  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1911  int idxarrow, idxnormal;
1912  int iret = 0;
1913  idxarrow = indexArrowkey(key);
1914  if(idxarrow > -1){
1915  //rather than 2 nested loops, comparing, we will trust the ordering and index in
1916  idxnormal = FLYCHORDREMAP2[p->keychord].arrows[idxarrow].key;
1917  //same here - we'll trust the order and index in
1918  iret = fly_normalkeys[idxnormal].key;
1919  }
1920  return iret;
1921 }
1922 //<<< END dug9 Feb2015
1923 char lookup_fly_extended(int key){
1924  int i;
1925  char kp = 0;
1926  Key ps[KEYS_HANDLED] = FLYREMAP;
1927  for(i=0;i<KEYS_HANDLED;i++){
1928  if(key==ps[i].hit){
1929  kp = ps[i].key;
1930  break;
1931  }
1932  }
1933  return kp;
1934 }
1935 char lookup_fly_key(int key){
1936  //check for special/extended characters related to fly mode, such as numpad and arrow keys
1937  char kp = 0;
1938  kp = lookup_fly_arrow(key); //check arrow keys first
1939  if(!kp)
1940  kp = lookup_fly_extended(key); //else other extended characters
1941  return kp;
1942 }
1943 static struct flykey_lookup_type {
1944  char key;
1945  int motion; //translation 0, rotation 1
1946  int axis; //0=x,1=y,2=z
1947  int sign; //-1 left 1 right
1948  int command;
1949 } flykey_lookup [] = {
1950  {'j', 0, 0, -1, FLY_X_LEFT},
1951  {'l', 0, 0, 1, FLY_X_RIGHT},
1952  {';', 0, 1, -1, FLY_Y_DOWN},
1953  {'p', 0, 1, 1, FLY_Y_UP,},
1954  {'a', 0, 2, -1, FLY_Z_FORWARD},
1955  {'z', 0, 2, 1, FLY_Z_REVERSE},
1956 
1957  {'k', 1, 0, -1, FLY_YAW_LEFT},
1958  {'8', 1, 0, 1, FLY_YAW_RIGHT},
1959  {'u', 1, 1, -1, FLY_PITCH_UP},
1960  {'o', 1, 1, 1, FLY_PITCH_DOWN},
1961  {'7', 1, 2, -1, FLY_ROLL_COUNTERCLOCKWISE},
1962  {'9', 1, 2, 1, FLY_ROLL_CLOCKWISE}
1963 };
1964 
1965 
1966 struct flykey_lookup_type *getFlyIndex(char key){
1967  struct flykey_lookup_type *flykey;
1968  int index = -1;
1969  flykey = NULL;
1970  for(index=0;index<KEYS_HANDLED;index++){
1971  if(key == flykey_lookup[index].key ) break;
1972  }
1973  if(index > -1)
1974  flykey = &flykey_lookup[index];
1975  return flykey;
1976 }
1977 int isFlyKey(char key){
1978  int i, index = -1;
1979  index = indexArrowkey(key);
1980  if(index == -1)
1981  for(i=0;i<KEYS_HANDLED;i++)
1982  if(key == flykey_lookup[i].key ){
1983  index = i;
1984  break;
1985  }
1986  return index > -1 ? 1 : 0;
1987 }
1988 void handle_key(const char key, double keytime)
1989 {
1990  char _key;
1991  //int i;
1992  X3D_Viewer *viewer;
1993  X3D_Viewer_Fly *fly;
1994  struct flykey_lookup_type *flykey;
1995  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
1996  viewer = Viewer();
1997 
1998  fly = &viewer->fly;
1999  //printf("%c",key);
2000  //if (viewer->type == VIEWER_FLY) { //Navigation-key_and_drag
2001  /* $key = lc $key; */
2002  _key = (char) tolower((int) key);
2003  if(!isFlyKey(_key)){
2004  //printf("not fly key\n");
2005  return;
2006  }
2007  //printf("is flykey\n");
2008  flykey = getFlyIndex(_key);
2009  if(flykey){
2010  if(flykey->motion > -1 && flykey->motion < 2 && flykey->axis > -1 && flykey->axis < 3){
2011  fly->down[flykey->motion][flykey->axis].direction = flykey->sign;
2012  fly->down[flykey->motion][flykey->axis].epoch = keytime; //initial keydown
2013  fly->down[flykey->motion][flykey->axis].era = keytime; //will decrement as we apply velocity in fly
2014  fly->down[flykey->motion][flykey->axis].once = 1;
2015  }
2016  }
2017  //} //Navigation-key_and_drag
2018 }
2019 
2020 
2021 void handle_keyrelease(const char key, double keytime)
2022 {
2023  char _key;
2024  //int i;
2025  X3D_Viewer *viewer;
2026  X3D_Viewer_Fly *fly;
2027  struct flykey_lookup_type *flykey;
2028  // OLD UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2029  viewer = Viewer();
2030  /* my($this,$time,$key) = @_; */
2031 
2032  fly = &viewer->fly;
2033 
2034  //if (viewer->type == VIEWER_FLY) { //Navigation-key_and_drag
2035  /* $key = lc $key; */
2036  _key = (char) tolower((int) key);
2037  if(!isFlyKey(_key)) return;
2038  flykey = getFlyIndex(_key);
2039  if(flykey){
2040  if(flykey->motion > -1 && flykey->motion < 2 && flykey->axis > -1 && flykey->axis < 3){
2041  int *ndown = &fly->ndown[flykey->motion][flykey->axis];
2042  if((*ndown) < 10){
2043  //up to 20 key chirps per axis are stored, with their elapsed time down measured in the keyboard's thread
2044  fly->wasDown[flykey->motion][flykey->axis][*ndown].direction = fly->down[flykey->motion][flykey->axis].direction;
2045  fly->wasDown[flykey->motion][flykey->axis][*ndown].epoch = keytime - fly->down[flykey->motion][flykey->axis].epoch; //total pressedTime
2046  fly->wasDown[flykey->motion][flykey->axis][*ndown].era = keytime - fly->down[flykey->motion][flykey->axis].era; //unused keydown time
2047  fly->wasDown[flykey->motion][flykey->axis][*ndown].once = fly->down[flykey->motion][flykey->axis].once; //a flag for the handle_tick to play with
2048  (*ndown)++;
2049  }
2050  fly->down[flykey->motion][flykey->axis].direction = 0;
2051  }
2052  }
2053  //} //Navigation-key_and_drag
2054 }
2055 
2056 /* wall penetration detection variables
2057  lastP - last avatar position, relative to current avatar position at 0,0,0 in avatar space
2058  - is a sum of walk_tick and collision displacement increment_pos()
2059  lastQ - quaternion increment from walk_tick which applies to previous lastP:
2060  if current frame number is i, and lastP is from i-1, then lastQ applies to i-1 lastP
2061 */
2062 //struct point_XYZ viewer_lastP;
2063 void viewer_lastP_clear()
2064 {
2065  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2066 
2067  p->viewer_lastP.x = p->viewer_lastP.y = p->viewer_lastP.z = 0.0;
2068 }
2069 void viewer_lastQ_set(Quaternion *lastQ)
2070 {
2071  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2072  quaternion_rotation(&p->viewer_lastP,lastQ,&p->viewer_lastP);
2073 }
2074 void viewer_lastP_add(struct point_XYZ *vec)
2075 {
2076  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2077  if(get_collision()) /* fw_params.collision use if(1) to test with toggling_collision */
2078  {
2079  VECADD(p->viewer_lastP,*vec);
2080  }
2081  else
2082  viewer_lastP_clear();
2083 }
2084 
2085 struct point_XYZ viewer_get_lastP()
2086 {
2087  /* returns a vector from avatar to the last avatar location ie on the last loop, in avatar space */
2088  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2089 
2090  struct point_XYZ nv = p->viewer_lastP;
2091  vecscale(&nv,&nv,-1.0);
2092  return nv;
2093 }
2094 
2095 
2096 
2097 /*
2098  * handle_tick_walk: called once per frame.
2099  *
2100  * Sets viewer to next expected position.
2101  * This should be called before position sensor calculations
2102  * (and event triggering) take place.
2103  * Position dictated by this routine is NOT final, and is likely to
2104  * change if the viewer is left in a state of collision. (ncoder)
2105  * according to web3d specs, the gravity vector is determined by
2106  * the currently bound viewpoint vertical CBVV
2107  * walk.xd,zd are in a plane parallel to the CBV horizon.
2108  * walk.yd is vertical in the CBVV direction
2109  * walk.rd is an angle in the CBVV horizontal plane (around vertical axis parallel to the CBVV)
2110  */
2111 
2112 static void handle_tick_walk()
2113 {
2114  X3D_Viewer *viewer;
2115  X3D_Viewer_Walk *walk;
2116  Quaternion q, nq;
2117  struct point_XYZ pp;
2118  // OLD UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2119  viewer = Viewer();
2120  walk = &viewer->walk;
2121 
2122  //for normal walking with left button down, only walk->ZD and walk->RD are non-zero
2123  pp.x = 0.15 * walk->XD;
2124  pp.y = 0.15 * walk->YD;
2125  pp.z = 0.15 * walk->ZD;
2127 
2128  /* walk mode transforms: (dug9 July 15, 2011)
2129  0.World Coordinates
2130  -- transform stack
2131  ---- 1.viewpoint node - currently bound viewpoint (CBV) gravity direction vector determined here
2132  ------ .position/(.Pos during navigation)
2133  -------- 2.#avatar body proposed - collisions, gravity and wall penetration can be computed here and += to .Pos
2134  ---------- .orientation/(.Quat during navigation) horizontal/pan part (this *= walk.RD)
2135  ------------ 3.(walk->ZD in these coords, and must be transformed by inverse(.Quat) into .Pos delta)
2136  ------------ 3.#avatar body current (BVVA) - collisions, gravity and wall penetration computed here and += to .Pos
2137  -------------- .orientation/.Quat tilts part (up/down and camera z axis tilts)^
2138  ---------------- 4.avatar camera
2139  ^There's no way for the user to tilt in walk mode. To tilt:
2140  a) switch to Fly, tilt with keyboard commands, then switch back to walk,
2141  b) script against viewpoint.orientation, or
2142  c) put non-zero viewpoint orientation in the scene file
2143  # since 2009 the walk avatar collisions,gravity,wall-pen have been done in what has been
2144  called avatar space or BVVA bound viewpoint vertical avatar - same as avatar camera
2145  with tilts removed, but pan applied, so same space as walk->ZD is applied above
2146  However because the avatar collision volume is symmetric around the vertical axis,
2147  it doesn't have to pan-rotate with the avatar to do its job, so it could be done
2148  in .position space, with a few code touch ups. This would also still work in Fly mode
2149  which has a spherically symmetric collision volume.
2150 
2151  fly mode transforms:
2152  - simpler - you point, and fly in the direction you pointed, spherical collision volume:
2153  0.World
2154  1. viewpoint
2155  2. avatar position .Pos
2156  3. avatar orientation .Quat
2157  (collisions currently done here), input device XY mapped to XYZ motion here
2158  Notice the order of transforms is the same for Fly mode:
2159  .Pos += inverse(.Quat)*inputXYZ - see increment_pos()
2160 
2161  (dug9 May 2015) in a bit more detail:
2162  Shape
2163  Model part of ModelView transform
2164  (world coordinates)
2165  View part of ModelView transform
2166  viewpoint
2167  viewpoint.position
2168  viewpoint.rotation
2169  opengl camera
2170 
2171  */
2172 
2173  q.w = (viewer->Quat).w;
2174  q.x = (viewer->Quat).x;
2175  q.y = (viewer->Quat).y;
2176  q.z = (viewer->Quat).z;
2177  vrmlrot_to_quaternion (&nq,0.0,1.0,0.0,0.4*walk->RD);
2178  //quaternion_to_vrmlrot(&nq,&ff[0],&ff[1],&ff[2],&ff[3]);
2179  //if(walk->RD != 0.0)
2180  // printf("\n");
2181  viewer_lastQ_set(&nq); //wall penetration - last avatar pose is stored before updating
2182  //split .Quat into horizontal pan and 2 tilts, then:
2183  // .Quat = .Quat * walk->RD (if I reverse the order, the tilts don't rotate with the avatar)
2184  // .Pos += inverse(planar_part(.Quat)) * walk->ZD
2185  //this should rotate the tilts with the avatar
2186  quaternion_multiply(&(viewer->Quat), &q, &nq); //Quat = walk->RD * Quat
2187  quaternion_normalize(&(viewer->Quat));
2188  {
2189  double angle;
2190  struct point_XYZ tilted;
2191  struct point_XYZ rotaxis = {0.0, 1.0, 0.0};
2192  Quaternion qlevel,qplanar;
2193  struct point_XYZ down = {0.0, -1.0, 0.0};
2194 
2195  //split .Quat into horizontal pan and 2 vertical tilts
2196  quaternion_rotation(&tilted,&q,&down);
2197  angle = vecangle2(&down,&tilted, &rotaxis);
2198  vrmlrot_to_quaternion (&qlevel,rotaxis.x,rotaxis.y,rotaxis.z,-angle);
2199 
2200  quaternion_multiply(&qplanar,&qlevel,&q);
2201  //quaternion_to_vrmlrot(&qplanar,&aa[0],&aa[1],&aa[2],&aa[3]);
2202 
2203  //use resulting horizontal pan quat to transform walk->Z
2204  {
2205  //from increment_pos()
2206  struct point_XYZ nv;
2207  struct point_XYZ vec;
2208  Quaternion q_i;
2209  //ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2210  vec.x = pp.x;
2211  vec.y = pp.y;
2212  vec.z = pp.z;
2213  viewer_lastP_add(&vec); //wall penetration - last avatar pose is stored before updating
2214 
2215  /* bound-viewpoint-space > Viewer.Pos,Viewer.Quat > avatar-space */
2216  //quaternion_inverse(&q_i, &(viewer->Quat)); //<<increment_pos(vec)
2217  quaternion_inverse(&q_i, &qplanar); //<< I need this in increment_pos
2218  quaternion_rotation(&nv, &q_i, &vec);
2219 
2220  /* save velocity calculations for this mode; used for EAI calls only */
2221  viewer->VPvelocity.x = nv.x; viewer->VPvelocity.y = nv.y; viewer->VPvelocity.z = nv.z;
2222  /* and, act on this change of location. */
2223  viewer->Pos.x += nv.x; /* Viewer.Pos must be in bound-viewpoint space */
2224  viewer->Pos.y += nv.y;
2225  viewer->Pos.z += nv.z;
2226 
2227 
2228  /* printf ("increment_pos; oldpos %4.2f %4.2f %4.2f, anti %4.2f %4.2f %4.2f nv %4.2f %4.2f %4.2f \n",
2229  Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z,
2230  Viewer.AntiPos.x, Viewer.AntiPos.y, Viewer.AntiPos.z,
2231  nv.x, nv.y, nv.z); */
2232  }
2233 
2234  }
2235 
2236  /* make sure Viewer.Dist is configured properly for Examine mode */
2237  //CALCULATE_EXAMINE_DISTANCE
2238 }
2239 
2240 //an external program or app may want to set or get the viewer pose, with no slerping
2241 //SSR - these set/getpose are called from _DisplayThread
2242 static int negate_pos = TRUE;
2243 void viewer_setpose( double *quat4, double *vec3){
2244  /* sign change on pos, but not quat, because freewrl conventions are different
2245  +Quat goes in direction world2vp
2246  -Pos goes in direction world2vp
2247  */
2248  X3D_Viewer *viewer;
2249  double vec[3];
2250  // OLDCODE UNUSED ttglobal tg = (ttglobal) gglobal();
2251  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
2252  viewer = Viewer();
2253  veccopyd(vec,vec3);
2254  if(negate_pos) vecnegated(vec,vec);
2255  double2pointxyz(&viewer->Pos,vec);
2256  double2quat(&viewer->Quat,quat4);
2257 }
2258 void viewer_getpose( double *quat4, double *vec3){
2259  /* Freewrl initializes .Quat, .Pos from viewpoint.position, viewpoint.orientation during viewpoint binding
2260  (or gives a default if no bound viewpoint)
2261  Viewer.Quat = inverse(vp.orientation) //changes sense from x3d vp2world, to opengl sense world2vp
2262  Viewer.Pos = vp.position //remains in x3d sense vp2world
2263  */
2264  X3D_Viewer *viewer;
2265  // OLDCODE UNUSED ttglobal tg = (ttglobal) gglobal();
2266  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
2267  viewer = Viewer();
2268  pointxyz2double(vec3,&viewer->Pos);
2269  if(negate_pos)
2270  vecnegated(vec3,vec3);
2271  quat2double(quat4,&viewer->Quat);
2272 }
2273 void viewer_getbindpose( double *quat4, double *vec3){
2274 /* The bind-time-equivalent viewpoint pose can be got
2275  from the Anti variables intialized by INITIATE_POSITION_ANTIPOSITION macro
2276  which copies the .position, .orientation values from the viewpoint node fields
2277  (if a viewpoint is bound, otherwise defaults are set during startup)
2278 */
2279  X3D_Viewer *viewer;
2280  Quaternion q_i;
2281  // OLDCODE UNUSED ttglobal tg = (ttglobal) gglobal();
2282  //OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
2283  viewer = Viewer();
2284  pointxyz2double(vec3,&viewer->AntiPos); //.Pos
2285  if(negate_pos)
2286  vecnegated(vec3,vec3);
2287  quaternion_inverse(&q_i,&viewer->AntiQuat);
2288  quat2double(quat4,&q_i);
2289 }
2290 void viewer_getview( double *viewMatrix){
2291  /* world - View - Viewpoint - .position - .orientation */
2292  //view matrix includes Transform(s) * viewpoint.position * viewpoint.orientation
2293  //we need to separate the Transforms from the .position and .orientation
2294  //double vec3[3], quat4[4];
2295  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, viewMatrix);
2296  //viewer_getpose(quat4,vec3);
2297  //viewMatrix *= inv_quat4
2298  //viewMatrix *= inv_vec3
2299 }
2300 void viewer_setview( double *viewMatrix){
2301  FW_GL_SETDOUBLEV(GL_MODELVIEW_MATRIX, viewMatrix);
2302 }
2303 
2304 /* formerly package VRML::Viewer::ExFly
2305  * entered via the "f" key.
2306  *
2307  * External input for x,y,z and quat. Reads in file
2308  * /tmp/inpdev (macro IN_FILE), which is a single line file that is
2309  * updated by some external program.
2310  *
2311  * eg:
2312  * 9.67 -1.89 -1.00 0.99923 -0.00219 0.01459 0.03640
2313  *
2314  * Do nothing for the mouse.
2315  */
2316 
2317 /* my $in_file = "/tmp/inpdev"; */
2318 /* #JAS my $in_file_date = stat($in_file)->mtime; */
2319 /* my $string = ""; */
2320 /* my $inc = 0; */
2321 /* my $inf = 0; */
2322 //#ifdef _MSC_VER
2323 //static int exflyMethod = 1; /* could be a user settable option, which kind of exfly to do */
2324 //#else
2325 //static int exflyMethod = 0;
2326 //#endif
2327 static void
2328 handle_tick_exfly()
2329 {
2330  X3D_Viewer *viewer;
2331  size_t len = 0;
2332  char string[STRING_SIZE];
2333  float px,py,pz,q1,q2,q3,q4;
2334  size_t rv; /* unused, but here for compile warnings */
2335  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2336  viewer = Viewer();
2337 
2338  UNUSED(rv); // mitigate compiler warnings
2339 
2340  memset(string, 0, STRING_SIZE * sizeof(char));
2341 
2342  /*
2343  * my $chk_file_date = stat($in_file)->mtime;
2344  * following uncommented as time on file only change
2345  * once per second - should change this...
2346  *
2347  * $in_file_date = $chk_file_date;
2348  */
2349 
2350 /* sysopen ($inf, $in_file, O_RDONLY) or */
2351 /* die "Error reading external sensor input file $in_file\n"; */
2352 /* $inc = sysread ($inf, $string, 100); */
2353 /* close $inf; */
2354  if ((p->exfly_in_file = fopen(IN_FILE, "r")) == NULL) {
2355  fprintf(stderr,
2356  "Viewer handle_tick_exfly: could not open %s for read, returning to EXAMINE mode.\nSee the FreeWRL man page for further details on the usage of Fly - External Sensor input mode.\n",
2357  IN_FILE);
2358 
2359  /* allow the user to continue in default Viewer mode */
2360  viewer->type = VIEWER_EXAMINE;
2361  //setMenuButton_navModes(viewer->type);
2362  return;
2363  }
2364  rv = fread(string, sizeof(char), IN_FILE_BYTES, p->exfly_in_file);
2365  if (ferror(p->exfly_in_file)) {
2366  fprintf(stderr,
2367  "Viewer handle_tick_exfly: error reading from file %s.",
2368  IN_FILE);
2369  fclose(p->exfly_in_file);
2370  return;
2371  }
2372  fclose(p->exfly_in_file);
2373 
2374 /* if (length($string)>0) */
2375  if ((len = strlen(string)) > 0) {
2376  if(p->exflyMethod == 0)
2377  {
2378  //MUFTI input data
2379  len = sscanf (string, "%f %f %f %f %f %f %f",&px,&py,&pz,
2380  &q1,&q2,&q3,&q4);
2381 
2382  /* read error? */
2383  if (len != 7) return;
2384 
2385  (viewer->Pos).x = px;
2386  (viewer->Pos).y = py;
2387  (viewer->Pos).z = pz;
2388 
2389  (viewer->Quat).w = q1;
2390  (viewer->Quat).x = q2;
2391  (viewer->Quat).y = q3;
2392  (viewer->Quat).z = q4;
2393  }else if(p->exflyMethod == 1){
2394  //dug9 WiiMote data written from a C# program
2395  static int lastbut = 0;
2396  int mev, but;
2397  len = sscanf (string, "%d %f %f ",&but,&px,&py);
2398  if (len != 3) return;
2399  mev = ButtonRelease;
2400  if(but) mev = MotionNotify;
2401  if(but != lastbut)
2402  {
2403  mev = (but==1 || but==4)? ButtonPress : ButtonRelease;
2404  }
2405  // change raw wii values from ( -1 to 1 ) to (0 - 1.0)
2406  //px = (px + 1.0)*.5; //done in wiimote code
2407  //py = 1.0 - (py + 1.0)*.5; //done in wiimote code
2408  handle_walk(mev,but,px,py);
2409  handle_tick_walk();
2410  lastbut = but;
2411  }
2412  }
2413 }
2414 
2415 
2416 
2417 /* FLY mode change Aug 29, 2014 dug9:
2418  I was having trouble adjusting speeds on a fast computer -
2419  - multiple keystrokes didn't change linear speed
2420  - angluar speed was too fast.
2421  New design:
2422  Goal: slow and fast frame rates both work
2423  Linear: if the user presses a key again/mulitple times before the related speed decay finishes,
2424  those keystrokes are interpreted as a desire to increase speed.
2425  Angular: brief taps do small angle adjustments 1/64 of full circle, holding key down does full rotation in 6 seconds
2426  In both cases, the keydown elapsed time is measured in the keybaord thread, not the display/rendering thread
2427 */
2428 
2429 static void handle_tick_fly()
2430 {
2431  X3D_Viewer *viewer;
2432  X3D_Viewer_Fly *fly;
2433  Quaternion q_v, nq = { 1, 0, 0, 0 };
2434  struct point_XYZ v;
2435  double changed = 0.0, time_diff = -1.0;
2436  int i;
2437  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2438  viewer = Viewer();
2439 
2440  fly = &viewer->fly;
2441 
2442  //sleep(400); //slow frame rate to test frame-rate-dependent actions
2443  if (fly->lasttime < 0) {
2444  fly->lasttime = TickTime();
2445  return;
2446  } else {
2447  double dtime = TickTime();
2448  time_diff = dtime - fly->lasttime;
2449  if (APPROX(time_diff, 0)) {
2450  return;
2451  }
2452  fly->lasttime = dtime;
2453  if(time_diff < 0.0) return; //skip a frame if the clock wraps around
2454  }
2455 
2456 
2457  /* has anything changed? if so, then re-render */
2458 
2459  /* linear movement */
2460  for (i = 0; i < 3; i++) {
2461  //fade old velocity, using something like exponential decay Ni = N(i-1)*e**(k*t) where k < 0
2462  if(!fly->down[0][i].direction){
2463  double dtime = fly->lasttime - fly->down[0][i].epoch; //fly->ttransition[i][0];
2464  if(dtime > .25) //delay decay, waiting for more speed-indicating keystrokes
2465  fly->Velocity[0][i] *= pow(0.04, time_diff);
2466  }
2467  //if its almost 0, clamp to zero
2468  if(fabs(fly->Velocity[0][i]) < .001){
2469  fly->Velocity[0][i] = 0.0;
2470  }
2471  //if key action, add new velocity
2472  if(fly->down[0][i].direction){
2473  //key is currently down
2474  fly->Velocity[0][i] += (fly->ndown[0][i]+fly->down[0][i].once)*fly->down[0][i].direction * viewer->speed * .1 * max(viewer->Dist,1.0);
2475  fly->down[0][i].once = 0;
2476  fly->ndown[0][i] = 0; //p->translaten[i] = 0;
2477  //fly->ttransition[i][0] = fly->lasttime; //save time of last [i] translate, for delaying decay
2478  }
2479  changed += fly->Velocity[0][i];
2480  }
2481 
2482  /* if we do NOT have a GeoViewpoint node, constrain all 3 axis */
2483  if (viewer->GeoSpatialNode == NULL)
2484  if(0) for (i = 0; i < 3; i++) {
2485  if (fabs(fly->Velocity[0][i]) >9.0)
2486  fly->Velocity[0][i] /= (fabs(fly->Velocity[0][i]) /9.0);
2487  }
2488 
2489  /* angular movement
2490  key chirp - a quck press and release on a key
2491  Velocity - (not velocity) amount of angle in radians we want to turn on this tick
2492  era - elapsed time between key down and keyup, as measured in the keyboard thread
2493  - used to ramp up angular speed based on how long you hold the key down
2494  - quick chirps on the key will give you smaller 'touch-up' angles
2495  goal: so it works with both fast frame rate/FPS and slow
2496  fast: chirps and instant visual feedback on angle turned with key held down
2497  slow: count your chirps, 64 chirps per full circle/2PI, or hold key down and count seconds 6 seconds = 2PI
2498  */
2499  for (i = 0; i < 3; i++) {
2500  static double radians_per_second = .6; //seems to turn 2x faster than this
2501  fly->Velocity[1][i] = 0.0;
2502  if(!fly->down[1][i].direction){
2503  fly->Velocity[1][i] *= pow(0.04, time_diff);
2504  }else{
2505  //the key is currently being held down, use a bit of it here
2506  double rps = radians_per_second;
2507  //double pressedEra = fly->lasttime - fly->down[1][i].epoch; //rEra[i];
2508  //normally not a chirp, but could be - a chirp here will hardly show, so no harm in double doing chirps here and below
2509  double era = fly->lasttime - fly->down[1][i].era; //- .25; //save a chirp worth because it gets chirped below when the key comes up
2510  fly->Velocity[1][i] += era * fly->down[1][i].direction * rps; // * 0.025;
2511  fly->down[1][i].era += era; //subtract what we just used
2512  //printf("*");
2513  }
2514  if(fly->ndown[1][i]){
2515  //there were some keydowns that happened between ticktimes, add their effects here
2516  int k;
2517  double rps = radians_per_second * .33;
2518  for(k=0; k<fly->ndown[1][i]; k++){
2519  double era = fly->wasDown[1][i][k].era; //unused keydown time
2520  double pressedEra = fly->wasDown[1][i][k].epoch; //total pressedTime
2521  //printf("+%f %f \n",era,pressedTime);
2522  if(pressedEra <= .1)
2523  era = .25; //a key chirp. Which can be too fast to measure in keyboard thread, so we give it a consistent down time (era)
2524  //printf("%d ",fly->wasDown[k][i][1].direction);
2525  fly->Velocity[1][i] += era * fly->wasDown[1][i][k].direction * rps; // * 0.025;
2526  }
2527  fly->ndown[1][i] = 0;
2528  }
2529  if (fabs(fly->Velocity[1][i]) > 0.8) {
2530  fly->Velocity[1][i] /= (fabs(fly->Velocity[1][i]) / 0.8);
2531  }
2532  changed += fly->Velocity[1][i];
2533  /* printf ("avel %d %f\n",i,fly->AVelocity[i]); */
2534  }
2535 
2536  /* have we done anything here? */
2537  if (APPROX(changed,0.0)) return;
2538  v.x = fly->Velocity[0][0] * time_diff;
2539  v.y = fly->Velocity[0][1] * time_diff;
2540  v.z = fly->Velocity[0][2] * time_diff;
2541  increment_pos(&v);
2542 
2543  nq.x = fly->Velocity[1][0];// * time_diff;
2544  nq.y = fly->Velocity[1][1]; // * time_diff;
2545  nq.z = fly->Velocity[1][2]; // * time_diff;
2546  quaternion_normalize(&nq);
2547 
2548  quaternion_set(&q_v, &(viewer->Quat));
2549  quaternion_multiply(&(viewer->Quat), &nq, &q_v);
2550  quaternion_normalize(&(viewer->Quat));
2551 
2552  /* make sure Viewer.Dist is configured properly for Examine mode */
2553  //CALCULATE_EXAMINE_DISTANCE
2554 
2555 }
2556 
2557 void
2558 handle_tick()
2559 {
2560  X3D_Viewer *viewer;
2561  double lasttime, dtime, time_diff;
2562  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2563  viewer = Viewer();
2564  lasttime = viewer->lasttime;
2565 
2566  time_diff = 0.0;
2567  //sleep(400); //slow frame rate to test frame-rate-dependent actions
2568  if (lasttime < 0) {
2569  viewer->lasttime = TickTime();
2570  return;
2571  } else {
2572  dtime = TickTime();
2573  time_diff = dtime - viewer->lasttime; //TickTime is computed once per frame, and handle_tick() is called once per frame
2574  if (APPROX(time_diff, 0)) {
2575  return;
2576  }
2577  viewer->lasttime = dtime;
2578  if(time_diff < 0.0) return; //skip a frame if the clock wraps around
2579  }
2580 
2581  switch(viewer->type) {
2582  case VIEWER_NONE:
2583  break;
2584  case VIEWER_EXAMINE:
2585  break;
2586  case VIEWER_WALK:
2587  handle_tick_walk();
2588  break;
2589  case VIEWER_EXFLY:
2590  handle_tick_exfly();
2591  break;
2592  case VIEWER_FLY:
2593  switch(p->dragchord){
2594  case CHORD_YAWPITCH:
2595  handle_tick_tilt(time_diff);
2596  break;
2597  case CHORD_ROLL:
2598  handle_tick_rplane(time_diff);
2599  break;
2600  case CHORD_XY:
2601  handle_tick_tplane(time_diff);
2602  break;
2603  case CHORD_YAWZ:
2604  default:
2605  handle_tick_fly2(time_diff); //fly2 like (WALK - G) except no RMB PAN, drags aligned to Viewer (vs walk aligned to bound Viewpoint vertical)
2606  break;
2607  }
2608  break;
2609  case VIEWER_FLY2:
2610  handle_tick_fly2(time_diff); //yawz
2611  break;
2612  case VIEWER_LOOKAT:
2613  handle_tick_lookat();
2614  break;
2615  case VIEWER_TPLANE:
2616  handle_tick_tplane(dtime);
2617  break;
2618  case VIEWER_RPLANE:
2619  handle_tick_rplane(dtime);
2620  break;
2621  case VIEWER_TILT:
2622  handle_tick_tilt(dtime);
2623  break;
2624  case VIEWER_EXPLORE:
2625  break;
2626  case VIEWER_SPHERICAL:
2627  //do nothing special on tick
2628  break;
2629  case VIEWER_TURNTABLE:
2630  break;
2631  case VIEWER_DIST:
2632  break;
2633  default:
2634  break;
2635  }
2636  if(viewer->type != VIEWER_NONE){
2637  handle_tick_fly(); //Navigation-key_and_drag
2638  }
2639  if (viewer->doExamineModeDistanceCalculations) {
2640  /*
2641  printf ("handle_tick - doing calculations\n");
2642  */
2643  CALCULATE_EXAMINE_DISTANCE
2644  resolve_pos();
2645  p->examineCounter --;
2646 
2647  if (p->examineCounter < 0) {
2648  viewer->doExamineModeDistanceCalculations = FALSE;
2649  p->examineCounter = 5;
2650  }
2651  }
2652 }
2653 
2654 
2655 
2656 /*
2657  * Semantics: given a viewpoint and orientation,
2658  * we take the center to revolve around to be the closest point to origin
2659  * on the z axis.
2660  * Changed Feb27 2003 JAS - by fixing $d to 10.0, we make the rotation
2661  * point to be 10 metres in front of the user.
2662  */
2663 
2664 /* ArcCone from TriD */
2665 void
2666 xy2qua(Quaternion *ret, const double x, const double y)
2667 {
2668  double _x = x - 0.5, _y = y - 0.5, _z, dist;
2669  _x *= 2;
2670  _y *= 2;
2671 
2672  dist = sqrt((_x * _x) + (_y * _y));
2673 
2674  if (dist > 1.0) {
2675  _x /= dist;
2676  _y /= dist;
2677  dist = 1.0;
2678  }
2679  _z = 1 - dist;
2680 
2681  ret->w = 0;
2682  ret->x = _x;
2683  ret->y = _y;
2684  ret->z = _z;
2685  quaternion_normalize(ret);
2686 }
2687 
2688 
2689 
2690 //static GLboolean acMask[2][3]; //anaglyphChannelMask
2691 void setmask(GLboolean *mask,int r, int g, int b)
2692 {
2693  mask[0] = (GLboolean)r;
2694  mask[1] = (GLboolean)g;
2695  mask[2] = (GLboolean)b;
2696 }
2697 void Viewer_anaglyph_setSide(int iside)
2698 {
2699  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2700  /* draw in gray */
2701  /* and use channel masks */
2702  GLboolean t = 1;
2703  glColorMask(p->acMask[iside][0],p->acMask[iside][1],p->acMask[iside][2],t);
2704 }
2705 void Viewer_anaglyph_clearSides()
2706 {
2707  glColorMask(1,1,1,1);
2708 }
2709 //true static:
2710 static char * RGBACM = "RGBACM";
2711 static int indexRGBACM(int a)
2712 {
2713  return (int) (strchr(RGBACM,a)-RGBACM);
2714 }
2715 int getAnaglyphPrimarySide(int primary, int iside){
2716  //primary red=0, green=1, blue=2
2717  //iside left=0, right=1, neither=2
2718  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2719  return (int)p->acMask[iside][primary];
2720 }
2721 
2722 void setAnaglyphPrimarySide(int primary, int iside){
2723  //primary red=0, green=1, blue=2
2724  //iside left=0, right=1, neither=2
2725  //it assumes you are setting it to true,
2726  //and turns other sides off the primary automatically
2727  //the user interface should look like this:
2728  //R G B
2729  //* Left
2730  // * Right
2731  // * Neither
2732  //the neither is side=2, and allows the user to turn a primary off all sides
2733  int i;
2734  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2735  for(i=0;i<3;i++)
2736  if(iside == i)
2737  p->acMask[i][primary] = (GLboolean)1;
2738  else
2739  p->acMask[i][primary] = (GLboolean)0;
2740 }
2741 void setAnaglyphSideColor(char val, int iside)
2742 {
2743  X3D_Viewer *viewer;
2744  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2745  viewer = Viewer();
2746  viewer->iprog[iside] = indexRGBACM(val);
2747  if(viewer->iprog[iside] == -1 )
2748  {
2749  printf ("warning, command line anaglyph parameter incorrect - was %c need something like RG\n",val);
2750  viewer->iprog[iside] = iside;
2751  }
2752  /* used for anaglyphMethod==2 */
2753  switch (viewer->iprog[iside]) {
2754  case 0: //'R':
2755  setmask(p->acMask[iside],1,0,0);
2756  break;
2757  case 1: //'G':
2758  setmask(p->acMask[iside],0,1,0);
2759  break;
2760  case 2: //'B':
2761  setmask(p->acMask[iside],0,0,1);
2762  break;
2763  case 3: //'A':
2764  setmask(p->acMask[iside],1,1,0);
2765  break;
2766  case 4: //'C':
2767  setmask(p->acMask[iside],0,1,1);
2768  break;
2769  case 5://'M':
2770  setmask(p->acMask[iside],1,0,1);
2771  break;
2772  }
2773 }
2774 void fwl_set_AnaglyphParameter(const char *optArg) {
2775 /*
2776  NOTE: "const char" means that you wont modify it in the function :)
2777  */
2778  X3D_Viewer *viewer;
2779  const char* glasses;
2780  int len;
2781  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2782  viewer = Viewer();
2783 
2784  glasses = optArg;
2785  len = (int) strlen(optArg);
2786  if(len !=2 && len != 3)
2787  {
2788  printf ("warning, command line anaglyph parameter incorrect - was %s need something like RC or LRN\n",optArg);
2789  glasses ="RC"; len = 2;
2790  }
2791  if(len == 2){
2792  setAnaglyphSideColor(glasses[0],0);
2793  setAnaglyphSideColor(glasses[1],1);
2794  }else if(len == 3){
2795  int i, iside;
2796  for(i=0;i<3;i++){
2797  switch(optArg[i]){
2798  case 'L': iside = 0;break;
2799  case 'R': iside = 1;break;
2800  case 'N': iside = 2;break;
2801  default:
2802  iside = 2;
2803  }
2804  setAnaglyphPrimarySide(i,iside);
2805  }
2806  }
2807  //Viewer.iprog[0] = indexRGBACM(glasses[0]);
2808  //Viewer.iprog[1] = indexRGBACM(glasses[1]);
2809  //if(Viewer.iprog[0] == -1 || Viewer.iprog[1] == -1)
2810  //{
2811  // printf ("warning, command line anaglyph parameter incorrect - was %s need something like RG\n",optArg);
2812  // Viewer.iprog[0] = 0;
2813  // Viewer.iprog[1] = 1;
2814  //}
2815  viewer->anaglyph = 1; /*0=none 1=active */
2816  viewer->shutterGlasses = 0;
2817  viewer->sidebyside = 0;
2818  viewer->updown = 0;
2819  viewer->isStereo = 1;
2820  setStereoBufferStyle(1);
2821 }
2822 /* shutter glasses, stereo view from Mufti@rus */
2823 /* handle setting shutter from parameters */
2824 void fwl_init_Shutter (void)
2825 {
2826  /* if you put --shutter on the command line, you'll come in here twice:
2827  first: from options.c but haveQuadbuffer will == 0 because we haven't init gl yet, so don't know
2828  second: post_gl_init - we'll know haveQuadbuffer which might = 1 (if not it goes into flutter mode)
2829  */
2830  X3D_Viewer *viewer;
2831  // OLDCODE UNUSED ppViewer p;
2832  ttglobal tg = gglobal();
2833  // OLDCODE UNUSED p= (ppViewer)tg->Viewer.prv;
2834  viewer = Viewer();
2835 
2836  tg->display.shutterGlasses = 2;
2837  viewer->shutterGlasses = 2;
2838  setStereoBufferStyle(1);
2839  if(viewer->haveQuadbuffer)
2840  {
2841  tg->display.shutterGlasses = 1; /* platform specific pixelformat/window initialization code should hint PRF_STEREO */
2842  viewer->shutterGlasses = 1;
2843  setStereoBufferStyle(0);
2844  }
2845  viewer->isStereo = 1;
2846 
2847 }
2848 
2849 void fwl_init_SideBySide()
2850 {
2851  X3D_Viewer *viewer;
2852  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2853  viewer = Viewer();
2854 
2855  setStereoBufferStyle(1);
2856  viewer->isStereo = 1;
2857  viewer->sidebyside = 1;
2858  viewer->screendist = min(viewer->screendist,.375);
2859  viewer->stereoParameter = min(viewer->stereoParameter,.01);
2860 }
2861 void fwl_init_UpDown()
2862 {
2863  X3D_Viewer *viewer;
2864  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2865  viewer = Viewer();
2866 
2867  setStereoBufferStyle(1);
2868  viewer->isStereo = 1;
2869  viewer->updown = 1;
2870  viewer->screendist = min(viewer->screendist,.375);
2871  viewer->stereoParameter = min(viewer->stereoParameter,.01);
2872 }
2873 
2874 void clear_shader_table();
2875 void setAnaglyph()
2876 {
2877  X3D_Viewer *viewer;
2878  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2879  viewer = Viewer();
2880 
2881  /* called from post_gl_init and hud/options (option.c calls fwl_set_AnaglyphParameter above) */
2882  viewer->anaglyph = 1;
2883  viewer->isStereo = 1;
2884  clear_shader_table();
2885  setStereoBufferStyle(1);
2886 }
2887 void setMono()
2888 {
2889  X3D_Viewer *viewer;
2890  // OLDCODE UNUSED ppViewer p;
2891  ttglobal tg = gglobal();
2892  // OLDCODE UNUSED p = (ppViewer)tg->Viewer.prv;
2893  viewer = Viewer();
2894 
2895  viewer->isStereo = 0;
2896  if(viewer->anaglyph)
2897  {
2898  glColorMask(1,1,1,1);
2899  clear_shader_table();
2900  }
2901  viewer->anaglyph = 0;
2902  viewer->sidebyside = 0;
2903  viewer->updown = 0;
2904  viewer->shutterGlasses = 0;
2905  tg->display.shutterGlasses = 0;
2906 
2907 }
2908 
2909 /*
2910 #define VIEWER_STEREO_OFF 0
2911 #define VIEWER_STEREO_SHUTTERGLASSES 1
2912 #define VIEWER_STEREO_SIDEBYSIDE 2
2913 #define VIEWER_STEREO_ANAGLYPH 3
2914 #define VIEWER_STEREO_UPDOWN 4
2915 */
2916 
2917 static void setStereo(int type)
2918 {
2919  /* type: 0 off 1 shutterglasses 2 sidebyside 3 analgyph */
2920  /* can only be called after opengl is initialized */
2921  //initStereoDefaults();
2922  gglobal()->Viewer.stereotype = type;
2923  setMono();
2924  switch(type)
2925  {
2926  case VIEWER_STEREO_OFF: {/*setMono()*/;break;}
2927  case VIEWER_STEREO_SHUTTERGLASSES: {fwl_init_Shutter(); break;}
2928  case VIEWER_STEREO_SIDEBYSIDE: {fwl_init_SideBySide(); break;}
2929  case VIEWER_STEREO_ANAGLYPH: {setAnaglyph(); break;}
2930  case VIEWER_STEREO_UPDOWN: {fwl_init_UpDown(); break;}
2931  default: break;
2932  }
2933 }
2934 void toggleOrSetStereo(int type)
2935 {
2936  /* if user clicks the active stereovision type on a HUD, then it should turn it off - back to mono
2937  if it's not active, then it should be set active*/
2938  X3D_Viewer *viewer;
2939  int curtype, shut;
2940  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2941  viewer = Viewer();
2942 
2943  shut = viewer->shutterGlasses ? 1 : 0;
2944  curtype = viewer->isStereo*( (shut)*1 + viewer->sidebyside*2 + viewer->anaglyph*3 + viewer->updown*4);
2945  if(type != curtype) {
2946  setStereo(type);
2947  } else {
2948  setMono();
2949  gglobal()->Viewer.stereotype = 0;
2950  }
2951 
2952 }
2953 void fwl_setPickraySide(int ipreferredSide, int either){
2954  X3D_Viewer *viewer;
2955  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2956  viewer = Viewer();
2957  viewer->dominantEye = ipreferredSide;
2958  viewer->eitherDominantEye = either;
2959 
2960 }
2961 void fwl_getPickraySide(int *ipreferredSide, int *either){
2962  X3D_Viewer *viewer;
2963  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2964  viewer = Viewer();
2965  *ipreferredSide = viewer->dominantEye ;
2966  *either = viewer->eitherDominantEye;
2967 }
2968 void updateEyehalf()
2969 {
2970  X3D_Viewer *viewer;
2971  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
2972  viewer = Viewer();
2973  if( viewer->screendist != 0.0)
2974  {
2975  //old semantics (variable meanings)
2976  //eyedist - object space distance between left and right viewpoints
2977  //screendist - distance to toe-in target
2978  //stereoParameter - distance from infinity line to toe-in target
2979  //set_eyehalf( viewer->eyedist/2.0,atan2(viewer->eyedist/2.0,viewer->screendist)*360.0/(2.0*3.1415926));
2980 
2981  //new semantics as of March 12, 2012
2982  //eyedist - object space distance between left and right viewpoints
2983  //stereoParameter - tan(toe in angle per side)
2984  // 0=looking at infinity
2985  // 1= 45 degree toe-in per side (90 degree converengence)
2986  // .4 = 22 degree toe-in angle per side
2987  //screendist - distance from viewpoint center to 'central' viewport edge
2988  // - measured in fraction-of side-viewport
2989  // - central viewport edge:
2990  // left edge of right stereo viewport
2991  // right edge of left stereo viewport
2992  // = .5 - for shutterglasses and anaglyph, both sides are centered on the screen
2993  // - for sidebyside, both sides are centered on their respective left and right viewports
2994  // average human eyebase 65mm or 2.5" - we prefer 2.25" or 57mm. For a 6" screen 2.25/6 = .375
2995  set_eyehalf( viewer->eyedist/2.0,atan(viewer->stereoParameter)*180.0/3.1415926);
2996  }
2997 }
2998 
2999 void viewer_postGLinit_init(void)
3000 {
3001 
3002 //#if defined(FREEWRL_SHUTTER_GLASSES) || defined(FREEWRL_STEREO_RENDERING)
3003  X3D_Viewer *viewer;
3004  int type;
3005  s_renderer_capabilities_t *rdr_caps;
3006  ttglobal tg = gglobal();
3007  // OLDCODE UNUSED ppViewer p = (ppViewer)tg->Viewer.prv;
3008  viewer = Viewer();
3009  rdr_caps = tg->display.rdr_caps;
3010 
3011  // see if we can use quad buffer here or not.
3012  viewer->haveQuadbuffer = (rdr_caps->quadBuffer== GL_TRUE);
3013 
3014  //if (viewer->haveQuadbuffer) ConsoleMessage ("viewer_postGLinit_init, HAVE quad buffer"); else ConsoleMessage ("viewer_postGLinit, no quad buffer");
3015 
3016  updateEyehalf();
3017 
3018  type = VIEWER_STEREO_OFF;
3019  if( viewer->shutterGlasses ) type = VIEWER_STEREO_SHUTTERGLASSES;
3020  if( viewer->sidebyside ) type = VIEWER_STEREO_SIDEBYSIDE;
3021  if( viewer->updown ) type = VIEWER_STEREO_UPDOWN;
3022  if( viewer->anaglyph ==1 ) type = VIEWER_STEREO_ANAGLYPH;
3023 
3024  if(type==VIEWER_STEREO_SHUTTERGLASSES)
3025  {
3026  // does this opengl driver/hardware support GL_STEREO? p.469, p.729 RedBook and
3027  // WhiteDune > swt.c L1306
3028  if (!viewer->haveQuadbuffer ) {
3029  ConsoleMessage("Unable to get quadbuffer stereo visual, switching to flutter mode\n");
3030  }
3031  }
3032 
3033  setStereo(type);
3034 
3035 //#else
3036 //setStereo(VIEWER_STEREO_OFF);
3037 //#endif
3038 
3039 }
3040 
3041 void fwl_set_StereoParameter (const char *optArg) {
3042 
3043  X3D_Viewer *viewer;
3044  int i;
3045 
3046  //if(Viewer.isStereo == 0)
3047  // initStereoDefaults();
3048  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3049  viewer = Viewer();
3050 
3051  i = sscanf(optArg,"%lf",&viewer->stereoParameter);
3052  if (i==0) printf ("warning, command line stereo parameter incorrect - was %s\n",optArg);
3053  else updateEyehalf();
3054 }
3055 
3056 void fwl_set_EyeDist (const char *optArg) {
3057  int i;
3058  //if(Viewer.isStereo == 0)
3059  // initStereoDefaults();
3060  X3D_Viewer *viewer;
3061  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3062  viewer = Viewer();
3063 
3064  i= sscanf(optArg,"%lf",&viewer->eyedist);
3065  if (i==0) printf ("warning, command line eyedist parameter incorrect - was %s\n",optArg);
3066  else updateEyehalf();
3067 }
3068 
3069 void fwl_set_ScreenDist (const char *optArg) {
3070  int i;
3071  //if(Viewer.isStereo == 0)
3072  // initStereoDefaults();
3073  X3D_Viewer *viewer;
3074  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3075  viewer = Viewer();
3076 
3077  i= sscanf(optArg,"%lf",&viewer->screendist);
3078  if (i==0) printf ("warning, command line screendist parameter incorrect - was %s\n",optArg);
3079  else updateEyehalf();
3080 }
3081 /* end of Shutter glasses, stereo mode configure */
3082 
3083 void set_stereo_offset0() /*int iside, double eyehalf, double eyehalfangle)*/
3084 {
3085  double x = 0.0, angle = 0.0;
3086  X3D_Viewer *viewer;
3087  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3088  viewer = Viewer();
3089 
3090  if (viewer->iside == 0) {
3091  /* left */
3092  x = viewer->eyehalf;
3093  angle = viewer->eyehalfangle; //old semantics: * viewer->stereoParameter; /*stereoparamter: 0-1 1=toe in to cross-over at Screendist 0=look at infinity, eyes parallel*/
3094  } else if (viewer->iside == 1) {
3095  /* right */
3096  x = -viewer->eyehalf;
3097  angle = -viewer->eyehalfangle; //old semantics: * viewer->stereoParameter;
3098  }
3099  FW_GL_TRANSLATE_D(x, 0.0, 0.0);
3100  FW_GL_ROTATE_D(angle, 0.0, 1.0, 0.0);
3101 }
3102 
3103 /* used to move, in WALK, FLY modes. */
3104 void increment_pos(struct point_XYZ *vec) {
3105  struct point_XYZ nv;
3106  Quaternion q_i;
3107  X3D_Viewer *viewer;
3108  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3109  viewer = Viewer();
3110 
3111  viewer_lastP_add(vec);
3112 
3113  /* bound-viewpoint-space > Viewer.Pos,Viewer.Quat > avatar-space */
3114  quaternion_inverse(&q_i, &(viewer->Quat));
3115  quaternion_rotation(&nv, &q_i, vec);
3116 
3117  /* save velocity calculations for this mode; used for EAI calls only */
3118  viewer->VPvelocity.x = nv.x; viewer->VPvelocity.y = nv.y; viewer->VPvelocity.z = nv.z;
3119  /* and, act on this change of location. */
3120  viewer->Pos.x += nv.x; /* Viewer.Pos must be in bound-viewpoint space */
3121  viewer->Pos.y += nv.y;
3122  viewer->Pos.z += nv.z;
3123 
3124 
3125  /* printf ("increment_pos; oldpos %4.2f %4.2f %4.2f, anti %4.2f %4.2f %4.2f nv %4.2f %4.2f %4.2f \n",
3126  Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z,
3127  Viewer.AntiPos.x, Viewer.AntiPos.y, Viewer.AntiPos.z,
3128  nv.x, nv.y, nv.z); */
3129 
3130 }
3131 
3132 /* We have a OrthoViewpoint node being bound. (not a GeoViewpoint node) */
3133 void bind_OrthoViewpoint (struct X3D_OrthoViewpoint *vp) {
3134  Quaternion q_i;
3135  float xd, yd,zd;
3136  X3D_Viewer *viewer;
3137  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3138  viewer = ViewerByLayerId(vp->_layerId);
3139 
3140 
3141  /* did bind_node tell us we could bind this guy? */
3142  if (!(vp->isBound)) return;
3143 
3144  /* SLERPing */
3145  /* record position BEFORE calculating new Viewpoint position */
3146  INITIATE_SLERP
3147 
3148  /* calculate distance between the node position and defined centerOfRotation */
3149  INITIATE_POSITION
3150 
3151  /* assume Perspective, unless Otrho set */
3152  viewer->ortho=TRUE;
3153  if (vp->fieldOfView.n == 4) {
3154  /* Ortho mapping - glOrtho order left/right/bottom/top
3155  assume X3D says left bottom right top */
3156  viewer->orthoField[0] = (double) vp->fieldOfView.p[0];
3157  viewer->orthoField[1] = (double) vp->fieldOfView.p[1];
3158  viewer->orthoField[2] = (double) vp->fieldOfView.p[2];
3159  viewer->orthoField[3] = (double) vp->fieldOfView.p[3];
3160  } else {
3161  ERROR_MSG("OrthoViewpoint - fieldOfView must have 4 parameters");
3162  viewer->orthoField[0] = -1.0;
3163  viewer->orthoField[1] = -1.0;
3164  viewer->orthoField[2] = 1.0;
3165  viewer->orthoField[3] = 1.0;
3166  }
3167 
3168  /* printf ("orthoviewpoint binding distance %f\n",Viewer.Dist); */
3169 
3170  /* since this is not a bind to a GeoViewpoint node... */
3171  viewer->GeoSpatialNode = NULL;
3172 
3173  /* set the examine mode rotation origin */
3174  INITIATE_ROTATION_ORIGIN
3175 
3176  /* printf ("BVP, origin %4.3f %4.3f %4.3f\n",Viewer.examine->Origin.x, Viewer.examine->Origin.y, Viewer.examine->Origin.z); */
3177 
3178  /* set Viewer position and orientation */
3179 
3180  /*
3181  printf ("bind_OrthoViewpoint, setting Viewer to %f %f %f orient %f %f %f %f\n",vp->position.c[0],vp->position.c[1],
3182  vp->position.c[2],vp->orientation.c[0],vp->orientation.c[1],vp->orientation.c[2], vp->orientation.c[3]);
3183  printf (" node %d fieldOfView %f\n",vp,vp->fieldOfView);
3184  printf (" center of rotation %f %f %f\n",vp->centerOfRotation.c[0], vp->centerOfRotation.c[1],vp->centerOfRotation.c[2]);
3185  */
3186 
3187  /*
3188 
3189  From specs > abstract > architecture > 23.3.5 Viewpoint
3190  "When a Viewpoint node is at the top of the stack, the user's view is
3191  conceptually re-parented as a child of the Viewpoint node. All subsequent changes to the Viewpoint node's
3192  coordinate system change the user's view (e.g., changes to any ancestor transformation nodes or to
3193  the Viewpoint node's position or orientation fields)."
3194 
3195  "Navigation types (see 23.3.4 NavigationInfo) that require a definition of a down vector (e.g., terrain following)
3196  shall use the negative Y-axis of the coordinate system of the currently bound Viewpoint node.
3197  Likewise, navigation types that require a definition of an up vector shall use the positive Y-axis of the
3198  coordinate system of the currently bound Viewpoint node. The orientation field of the Viewpoint node does
3199  not affect the definition of the down or up vectors. This allows the author to separate the viewing direction
3200  from the gravity direction."
3201 
3202  concept of transformations Jan3,2010:
3203 world coords > [Transform stack] > bound Viewpoint > [Viewer.Pos,.Quat] > avatar
3204 " < inverse[Transformstack] < " < [AntiPos,AntiQuat] < avatar
3205  gravity (according to specs): Y-down in the (bound Viewpoint local coords).
3206  The viewpoint node orientation field doesn't count toward specs-gravity.
3207  If you want global-gravity, then put your viewpoint node at the scene level, or compute a
3208  per-frame gravity for spherical worlds - see mainloop.c render_collisions.
3209 
3210  Implication: the user button LEVEL should level out/cancel/zero the bound-viewpoint's orientation field value.
3211 
3212  */
3213 
3214  INITIATE_POSITION_ANTIPOSITION
3215  /* printf ("bind_OrthoViewpoint, pos %f %f %f antipos %f %f %f\n",Viewer.Pos.x, Viewer.Pos.y, Viewer.Pos.z, Viewer.AntiPos.x, Viewer.AntiPos.y, Viewer.AntiPos.z);
3216  */
3217 
3218  viewer_lastP_clear();
3219  resolve_pos();
3220  setMenuStatusVP (vp->description->strptr);
3221 
3222 }
3223 
3224 /* called from main, after the new viewpoint is setup */
3225 int slerp_viewpoint(int itype)
3226 {
3227  int iret;
3228  X3D_Viewer *viewer;
3229  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3230  viewer = Viewer();
3231 
3232  iret = 0;
3233  if(viewer->SLERPing3 && itype==3){
3234  //navigation 'm' LOOKAT and 'g' EXPLORE non-vp-bind slerping comes through here
3235  // slerps: viewer->pos, .quat, .dist
3236  double tickFrac;
3237  tickFrac = (TickTime() - viewer->startSLERPtime)/viewer->transitionTime;
3238  tickFrac = min(1.0,tickFrac); //clamp to max 1.0 otherwise a slow frame rate will overshoot
3239  quaternion_slerp(&viewer->Quat,&viewer->startSLERPQuat,&viewer->endSLERPQuat,tickFrac);
3240  point_XYZ_slerp(&viewer->Pos,&viewer->startSLERPPos,&viewer->endSLERPPos,tickFrac);
3241  general_slerp(&viewer->Dist,&viewer->startSLERPDist,&viewer->endSLERPDist,1,tickFrac);
3242  if(tickFrac >= 1.0) {
3243  viewer->SLERPing3 = 0;
3244  resolve_pos2(); //may not need this if examine etc do it
3245  }
3246  iret = 1;
3247  //now we let normal rendering use the viewer quat, pos, dist during rendering
3248  }else if(viewer->SLERPing2 && p->vp2rnSaved && itype==2) {
3249  //viewpoint slerp-on-bind comes through here
3250  if(viewer->SLERPing2justStarted)
3251  {
3252  //rn rootnode space, vpo/vpn old and new viewpoint space
3253  double vpo2rn[16];
3254  //double rn2vpo[16];
3255  double vpn2rn[16],rn2vpn[16];
3256  //double rn2rn[16];
3257  double diffrn[16];
3258  memcpy(vpo2rn,p->viewpoint2rootnode,sizeof(double)*16);
3259  if(viewer->LookatMode==3){
3260  memcpy(vpn2rn,p->viewpointnew2rootnode,sizeof(double)*16);
3261  }else{
3262  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, p->viewpoint2rootnode);
3263  memcpy(vpn2rn,p->viewpoint2rootnode,sizeof(double)*16);
3264  }
3265  //matinverse(rn2vpo,vpo2rn);
3266  matinverseAFFINE(rn2vpn,vpn2rn);
3267  //this works a bit:
3268  // diff_RN[rn x rn] = vpo2rn[rn x vpo] * rn2vpn[vpn x rn]
3269  //printmatrix2(vpo2rn,"vpo2rn");
3270  //printmatrix2(rn2vpn,"rn2vpn");
3271  matmultiplyAFFINE(diffrn,vpo2rn,rn2vpn);
3272  //printmatrix2(diffrn,"AFFINE diffrn");
3273  //matmultiplyFULL(diffrn,vpo2rn,rn2vpn);
3274  //printmatrix2(diffrn,"FULL diffrn");
3275 
3276  //slerping quat and point_XYZ
3277  matrix_to_quaternion(&p->sq,diffrn);
3278  quaternion_normalize(&p->sq);
3279  p->sp[0] = diffrn[12];
3280  p->sp[1] = diffrn[13];
3281  p->sp[2] = diffrn[14];
3282 
3283  viewer->SLERPing2justStarted = FALSE;
3284  //p->tickFrac = 0.0;
3285  //printf("in slerping2juststarted ");
3286  }
3287  //back transform by slerped amount
3288  {
3289  double tickFrac;
3290  Quaternion qdif,qzero;
3291  double vzero[3], vshift[3];
3292 
3293  tickFrac = (TickTime() - viewer->startSLERPtime)/viewer->transitionTime;
3294  /*
3295  if(0){ //debugging slowly
3296  p->tickFrac += .1;
3297  tickFrac = min(tickFrac,p->tickFrac);
3298  }*/
3299  tickFrac = DOUBLE_MIN(tickFrac,1.0);
3300  tickFrac = DOUBLE_MAX(tickFrac,0.0);
3301  //printf(" %4.1lf",tickFrac);
3302  //slerping quat and point
3303  vzero[0] = vzero[1] = vzero[2] = 0.0;
3304  vrmlrot_to_quaternion(&qzero, 0.0,1.0,0.0,0.0); //zero it
3305  quaternion_slerp(&qdif,&p->sq,&qzero,tickFrac);
3306  general_slerp(vshift,p->sp,vzero,3,tickFrac);
3307  if(1){
3308  FW_GL_TRANSLATE_D(vshift[0],vshift[1],vshift[2]);
3309  quaternion_togl(&qdif);
3310  }
3311  if(tickFrac > .99)
3312  {
3313  viewer->SLERPing2 = FALSE;
3314  //printf(" done\n");
3315  }
3316  }
3317  iret = 1;
3318  }
3319  return iret;
3320 }
3321 void setup_viewpoint_slerp(double* center, double pivot_radius, double vp_radius){
3322  /* when you don't have a new viewpoint to bind to, but know where you want the viewer to go
3323  with a transform relative to the viewer, instead of bind_viewpoint call
3324  setup_viewpoint_slerp(pointInEyespace, radiusOfShapeInEyespace)
3325 
3326  */
3327  //GLDOUBLE matTargeti[16]; //, matTarget[16], mv[16];
3328  //double dradius; //distance,
3329  double yaw, pitch; //, R1[16], R2[16], R3[16], R3i[16]; //, T[16], matQuat[16]; //, matAntiQuat[16];
3330  double C[3];
3331  //Quaternion sq;
3332  Quaternion q_i;
3333  Quaternion qyaw, qpitch, qtmp;
3334  struct point_XYZ PC;
3335 
3336  X3D_Viewer *viewer;
3337  // OLDCODE UNUSED ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3338 
3339  double pos[3] = {0.0,0.0,0.0}; //rpos[3],
3340  struct point_XYZ pp,qq;
3341  viewer = Viewer();
3342 
3343  veccopyd(pos,center);
3344 
3345  //dradius = max(viewer->Dist, radius + 5.0);
3346  //distance = veclengthd(pos);
3347  //distance = (distance - dradius)/distance;
3348  vecnormald(pos,pos);
3349  vecscaled(pos,pos,vp_radius); //distance);
3350  //dradius = veclengthd(pos);
3351 
3352  viewer->SLERPing3 = 1;
3353 
3354  //Dec 2014 another attempt at non-bind viewpoint slerping
3355  //method:
3356  // 1. snapshot the current viewer->quat, viewer->pos, viewer->dist as startSLERP
3357  // 2. compute ending pos, quat, dist and set as endSLERP .Pos, .Quat .Dist
3358  // 3. in viewpoint_slerp(), slerp from starting to ending
3359 
3360  // 1. snapshot current viewer quat,pos,dist as startSLERP
3361  viewer->startSLERPPos = viewer->Pos;
3362  viewer->startSLERPQuat = viewer->Quat;
3363  viewer->startSLERPDist = viewer->Dist;
3364  viewer->startSLERPtime = TickTime();
3365 
3366  // 2. compute end pos,quat,dist as endSLERP
3367  // generally we have a vector center, and a pitch,roll
3368  // end = start + center,pitch,roll
3369  // except we need to do proper transform concatonation:
3370  /*
3371  0.World
3372  1. viewpoint
3373  2. avatar position .Pos
3374  3. avatar orientation .Quat
3375  our center, pitch, yaw are observed here
3376  Order of transforms:
3377  .Pos += inverse(.Quat)*center
3378  */
3379  viewer->endSLERPDist = vp_radius;
3380 
3381  // end = start + ...
3382  // endPos = startPos + inverse(startQuat)*center
3383  quaternion_normalize(&viewer->startSLERPQuat);
3384  quaternion_inverse( &q_i,&viewer->startSLERPQuat);
3385  vecdifd(pos,center,pos);
3386  double2pointxyz(&pp,pos);
3387  quaternion_rotation(&qq, &q_i, &pp);
3388  vecadd(&viewer->endSLERPPos,&viewer->startSLERPPos,&qq);
3389 
3390  // endQuat = startQuat*toQuat(yaw)*toQuat(pitch)
3391  //when you pick, your pickray and shape object isn't usually dead center in the viewport. In that case,
3392  //besides translating the viewer, you also want to turn the camera to look at the
3393  //center of the shape (turning somewhat toward the pickray direction, but more precisely to the shape object ccenter)
3394  //compute yaw from our pickray
3395  veccopyd(C,pos);
3396  if( APPROX( vecnormald(C,C), 0.0) )
3397  C[2] = 1.0;
3398  //if we are too close, we don't want to turn 180 to move away, we just want to back up
3399  if(C[2] < 0.0)
3400  vecscaled(C,C,-1.0);
3401  yaw = -atan2(C[0],C[2]);
3402  //apply the yaw to the pickray, so that all that's left of the pickray is the pitch part
3403  vrmlrot_to_quaternion(&qyaw, 0.0,1.0,0.0,yaw);
3404  double2pointxyz(&PC,C);
3405  quaternion_rotation(&PC,&qyaw,&PC);
3406  //compute pitch from yaw-transformed pickray
3407  pitch = atan2(PC.y,PC.z);
3408  vrmlrot_to_quaternion(&qpitch,1.0,0.0,0.0,pitch);
3409  //quatEnd = quatPitch*quatYaw*quatStart
3410  quaternion_multiply(&qtmp,&qyaw,&qpitch);
3411  quaternion_multiply(&viewer->endSLERPQuat,&qtmp,&viewer->startSLERPQuat);
3412  //if(0) if(viewer->LookatMode == 3){ moved to handle_lookat
3413  // if(viewer->type == VIEWER_LOOKAT)
3414  // fwl_set_viewer_type(VIEWER_LOOKAT); //toggle off LOOKAT
3415  // if(viewer->type == VIEWER_EXPLORE)
3416  // fwl_set_viewer_type(VIEWER_EXPLORE); //toggle off LOOKAT
3417  // viewer->LookatMode = 0; //VIEWER_EXPLORE
3418  //}
3419  //viewer_lastP_clear(); //not sure I need this - its for wall penetration
3420 }
3421 
3422 
3423 
3424 /* We have a Viewpoint node being bound. (not a GeoViewpoint node) */
3425 void bind_Viewpoint (struct X3D_Viewpoint *vp) {
3426  Quaternion q_i;
3427  float xd, yd,zd;
3428  X3D_Viewer *viewer;
3429  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3430 
3431  /* did bind_node tell us we could bind this guy? */
3432  if (!(vp->isBound)) return;
3433 
3434  /* SLERPing */
3435  /* record position BEFORE calculating new Viewpoint position */
3436  /*
3437  dug9 - viewpoint slerping: what I see as of July 12, 2011:
3438  in the scene file if there's non-zero position
3439  and orientation values in the fields of the first bindable viewpoint these values are
3440  modified by slerping, during the initial bind. After the initial slerp, slerping code runs,
3441  but accomplishes no effective slerping.
3442  If the fields (.position, .orientation) are zero when the viewpoint first binds,
3443  slerp code runs but no effective slerping.
3444 
3445  dug9 - my concept of how a viewpoint slerp should work, as of July 12, 2011:
3446  A smooth transition between current world pose and newly bound viewpoint world pose.
3447  definition of 'pose': 6 parameters consisting of 3 translations and 3 rotations in 3D space
3448  representing the position and direction of an assymetric object
3449  viewpoint pose: transform stack + (.Pos, .Quat)
3450  - on initial binding without viewpoint slerping
3451  (.Pos,.Quat) = (.position,.orientation)
3452  - on initial bind with viewpoint slerping
3453  (.Pos,.Quat) = (.position,.orientation) - pose_difference
3454  pose_difference = (new viewpoint pose) - (last viewpoint pose)
3455  more detail...
3456  1. during a viewpoint bind the 'pose_difference' between the old and new viewpoint poses
3457  is computed from their transform stacks
3458  pose_difference = new_viewpoint_world_pose - old_viewpoint_world_pose
3459  2. the new viewpoint is bound -as normally done without slerping- so it's at the new pose
3460  3. the new viewpoint's pose is multiplied by inverse(pose_difference) effectively
3461  putting the camera part of the new viewpoint back to the old viewpoints camera pose.
3462  This could be done by multiplying the position and orientation fields
3463  4. slerping is started to reduce pose_difference to zero at which point slerping stops
3464  and the camera is at it's viewpoint's final pose
3465  there needs to be variables for the following:
3466  a) pose_difference - a translation and rotation
3467  b) original position and orientation fields
3468  c) modified position and orientation fields
3469  modified_viewpoint_pose = inverse(pose_difference) * bound_viewpoint_pose
3470  the easy part is getting the position and orientation fields, which are simple properties
3471  of viewpoints.
3472  pose_difference:
3473  The hard part: getting the pose_difference which is found by traversing
3474  the scenegraph to both viewpoints, at some point in time in the frame cycle. But when?
3475  Options:
3476  A. as needed during a viewpoint bind, and with slerping on, call a function to
3477  traverse the scenegraph especially for getting the 2 viewpoint global transforms
3478  B. every time a viewpoint is visited on a scenegraph traversal, store its global transform
3479  with it, so it's refreshed often, and becomes a property of the viewpoint which
3480  can be accessed immediately when slerping begins. And hope that's good enough, which
3481  during a very busy event cascade, it might not be.
3482  C. stagger the start of slerping to cover 2 frames
3483  - on the bind frame, we can call a function to invert the current modelview matrix, for
3484  the old viewpoint.
3485  - on the next frame, ditto for the new viewpoint, then start slerping
3486  - problem: there's one frame where the camera jitters to the new pose, then on the
3487  next frame back to the old pose where it starts slerping.
3488  solution: to avoid this, on the second
3489  frame, before starting to draw, perhaps in prep_Viewpoint(), when we have the
3490  current modelview matrix for the new viewpoint, this is the point when we would
3491  compute the pose_difference and the initial pose parameters.
3492  Current process: ==============================================
3493  prep_Viewpoint() in Component_Navigation.c L.80
3494  - does the per-frame slerp increment
3495  - does the viewpoint field values of .orientation,.position
3496  bind_Viewpoint() in Viewer.c L.1925 (here) - sets up the slerping values and flags
3497  viewer_togl() in Viewer.c L.515 - computes slerp increment,
3498  - does part not done by prep_Viewpoint
3499  - so net of that prep_Viewpoint + viewer_togl() combined
3500  pose_difference = (new_world_pose) - (old_world_pose)
3501  pose_increment = slerpIncrement(pose_difference)
3502  - turns off slerping flag when done.
3503 
3504  Call stack:
3505  mainloop L.503 (before render geometry)
3506  startOfLoopNodeUpdates() in OpenGL_Utils L.3406
3507  bind_Viewpoint() (here)
3508  mainloop L.630 (before render geometry)
3509  render_pre()
3510  setup_viewpoint()
3511  viewer_togl()
3512  render_hier(rootnode,VF_Viewpoint)
3513  prep_Viewpoint() - only called on the current bound viewpoint
3514  mainloop L.647 (for render_hier(,VF_sensitive) after render geometry)
3515  setup_viewpoint()
3516  ditto
3517  mainloop L.764
3518  SEND_BIND_IF_REQUIRED(tg->ProdCon.setViewpointBindInRender)
3519  prodcon L.604 send_bind_to(X3D_NODE(t->viewpointnodes[i]), 0);
3520  send_bind_to() in Bindables.c L.267
3521  bind_viewpoint()
3522  generally setup_viewpoint() is called when needed before any
3523  non-VF_Viewpoint render_hier() call to update the current pose
3524  -and modelview matrix- of the camera
3525  Variables and what they mean:
3526  Viewer.
3527  .position -viewpoint field, only changes through scripting
3528  .orientation -viewpoint field, only changes through scripting
3529  - when you re-bind to a viewpoint later, these will be the originals or script modified
3530  - transform useage:
3531  ShapeCoordinates
3532  Transform stack shape2world (model part of modelview)
3533  WorldCoordinates (at scene root)
3534  Transform stack to CBV (view part of modelview)
3535  Currently Bound Viewpoint (CBV)
3536  .Pos (== .position after bind, then navigation changes it)
3537  viewpoint avatar
3538  .Quat (== .orientation after bind, then navigation changes it)
3539  viewpoint camera (so called eye coords)
3540  .Pos: on binding, it gets a fresh copy of the .position field of the CBV
3541  - and navigation changes it
3542  .Quat: on binding, it gets a fresh copy of the .orientation field of the CBV
3543  - and navigation changes it
3544  - LEVEL/viewer_level_to_bound() changes it
3545  .bindTimeQuat: == .orientation (of CBV) through lifecycle
3546  - used to un-rotate modelviewmatrix (which includes .Pos,.Quat)
3547  to getcurrentPosInModel() (Q. should it be the whole .Quat?)
3548 
3549  .AntiPos
3550  .AntiQuat: == inverse(.orientation)
3551  [.prepVPQuat == .orientation, used in prep_Viewpoint, which is wrong because it's not updated from scripting against .orientation]
3552  .currentPosInModel - used to calculate examine distance, GeoLOD range, debugging
3553  - starts as .position, updated in getCurrentPosInModel()
3554  No-slerping use of variables in prep_Viewpoint():
3555  rotate(prepVPQuat)
3556  translate(viewer->position)
3557  New process: =======================================================
3558  Goal: get both the old and new modelview matrices together, so pose_difference can be
3559  computed and applied to new viewpoint orientation/position before render() on the
3560  2nd loop.
3561  Proposed process:
3562  in bind_viewpoint, set a flag for prep_viewpoint saying its a newly bound viewpoint
3563  - save the current modelview matrix
3564  after prep_viewpoint in mainloop, call a new function:
3565  slerp_viewpoint():
3566  a) the first time in on a newly bound viewpoint
3567  - retrieve the last modelview stored by bind_viewpoint
3568  - get the modelview matrix for the new viewpoint
3569  - compute pose_difference between the old and new viewpoints
3570  pose_difference = last_modelviewmatrix*inverse(newModelViewMatrix)
3571  - modify the position and orientation fields of the new one
3572  with pose_difference ie .Pos, .Orient += slerp(pose_difference)
3573  - setup the slerping numbers
3574  b) subsequent visits on the bound viewpoint
3575  - do slerping increment to reduce pose_difference gradually to zero
3576  - apply to .Pos,.Quat
3577  - shut off slerping when done
3578  */
3579  //INITIATE_SLERP
3580  //if(false){
3581  viewer = ViewerByLayerId(vp->_layerId);
3582  if (viewer->transitionType != VIEWER_TRANSITION_TELEPORT && viewer->wasBound) {
3583  viewer->SLERPing = FALSE; //TRUE;
3584  viewer->startSLERPtime = TickTime();
3585  memcpy (&viewer->startSLERPPos, &viewer->Pos, sizeof (struct point_XYZ));
3586  memcpy (&viewer->startSLERPAntiPos, &viewer->AntiPos, sizeof (struct point_XYZ));
3587  memcpy (&viewer->startSLERPQuat, &viewer->Quat, sizeof (Quaternion));
3588  memcpy (&viewer->startSLERPAntiQuat, &viewer->AntiQuat, sizeof (Quaternion));
3589  memcpy (&viewer->startSLERPbindTimeQuat, &viewer->bindTimeQuat, sizeof (Quaternion));
3590  memcpy (&viewer->startSLERPprepVPQuat, &viewer->prepVPQuat, sizeof (Quaternion));
3591 
3592  /* slerp Mark II */
3593  viewer->SLERPing2 = TRUE;
3594  viewer->SLERPing2justStarted = TRUE;
3595  //printf("binding\n");
3596  //save for future slerps
3597  p->vp2rnSaved = TRUE; //I probably don't need this flag, I always bind before prep_viewpoint()
3598  //printf("S");
3599  FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, p->viewpoint2rootnode); //we bind from the root, so this would be just pos, rot of viewpoint in matrix form?
3600  //printf("S");
3601 
3602  } else {
3603  viewer->SLERPing = FALSE;
3604  viewer->SLERPing2 = FALSE;
3605  }
3606 
3607  viewer->wasBound = TRUE;
3608  /* calculate distance between the node position and defined centerOfRotation */
3609  INITIATE_POSITION
3610 
3611  /* assume Perspective, unless Otrho set */
3612  viewer->ortho=FALSE;
3613 
3614  /* printf ("viewpoint binding distance %f\n",Viewer.Dist); */
3615 
3616  /* since this is not a bind to a GeoViewpoint node... */
3617  viewer->GeoSpatialNode = NULL;
3618 
3619  /* set the examine mode rotation origin */
3620  INITIATE_ROTATION_ORIGIN
3621 
3622  /* set Viewer position and orientation */
3623  /*
3624 
3625  From specs > abstract > architecture > 23.3.5 Viewpoint
3626  "When a Viewpoint node is at the top of the stack, the user's view is
3627  conceptually re-parented as a child of the Viewpoint node. All subsequent changes to the Viewpoint node's
3628  coordinate system change the user's view (e.g., changes to any ancestor transformation nodes or to
3629  the Viewpoint node's position or orientation fields)."
3630 
3631  "Navigation types (see 23.3.4 NavigationInfo) that require a definition of a down vector (e.g., terrain following)
3632  shall use the negative Y-axis of the coordinate system of the currently bound Viewpoint node.
3633  Likewise, navigation types that require a definition of an up vector shall use the positive Y-axis of the
3634  coordinate system of the currently bound Viewpoint node. The orientation field of the Viewpoint node does
3635  not affect the definition of the down or up vectors. This allows the author to separate the viewing direction
3636  from the gravity direction."
3637 
3638  concept of transformations Jan3,2010:
3639 world coords > [Transform stack] > bound Viewpoint > [Viewer.Pos,.Quat] > avatar
3640 " < inverse[Transformstack] < " < [AntiPos,AntiQuat] < avatar
3641  gravity (according to specs): Y-down in the (bound Viewpoint local coords).
3642  The viewpoint node orientation field doesn't count toward specs-gravity.
3643  If you want global-gravity, then put your viewpoint node at the scene level, or compute a
3644  per-frame gravity for spherical worlds - see mainloop.c render_collisions.
3645 
3646  Implication: the user button LEVEL should level out/cancel/zero the bound-viewpoint's orientation field value.
3647 
3648  */
3649 
3650  INITIATE_POSITION_ANTIPOSITION
3651 
3652  viewer_lastP_clear();
3653  resolve_pos();
3654  setMenuStatusVP (vp->description->strptr);
3655 }
3656 
3657 int fwl_getAnaglyphSide(int whichSide) {
3658  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3659  //glColorMask(p->acMask[iside][0],p->acMask[iside][1],p->acMask[iside][2],t);
3660 
3661  if ((whichSide<0) || (whichSide>1)) {
3662  return 0;
3663  }
3664 
3665  /* should return
3666  000 0 Black
3667  001 1 Blue
3668  010 2 Green
3669  011 3 Cyan
3670  100 4 Red
3671  101 5 Magenta
3672  110 6 Yellow
3673  111 7 White
3674  */
3675 
3676  return (p->acMask[whichSide][0] << 2) | (p->acMask[whichSide][1] << 1) | (p->acMask[whichSide][2]);
3677 }
3678 
3679 
3680 
3681 
3682 // Android - we are loading in a new file while keeping the system sane.
3683 void Android_reset_viewer_to_defaults() {
3684  //ConsoleMessage("********** Android_reset_viewer_to_defaults");
3685  // reset the viewer to initial mode.
3686  X3D_Viewer *viewer;
3687  ppViewer p = (ppViewer)gglobal()->Viewer.prv;
3688  viewer = Viewer();
3689  p->viewer_initialized = FALSE;
3690 
3691  viewer_default();
3692  viewer->SLERPing2 = FALSE;
3693  viewer->SLERPing = FALSE;
3694 }
3695 
3696 int viewer_iside(){
3697  return Viewer()->iside;
3698 }
Definition: Viewer.c:78
Definition: Viewer.h:196
Definition: Viewer.h:174