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