FreeWRL/FreeX3D  3.0.0
MainLoop.c
1 /*
2 
3  FreeWRL support library.
4  Main loop : handle events, ...
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 #include <config.h>
29 #include <system.h>
30 #include <system_threads.h>
31 #include <display.h>
32 #include <internal.h>
33 
34 #include <libFreeWRL.h>
35 #include <list.h>
36 #include <threads.h>
37 #if HAVE_SYS_TIME_H
38 # include <sys/time.h>
39 #endif
40 #if HAVE_TIME_H
41 # include <time.h>
42 #endif
43 
44 #include <sys/stat.h> // for mkdir
45 
46 
47 #include "../vrml_parser/Structs.h"
48 #include "../vrml_parser/CRoutes.h"
49 #include "headers.h"
50 #include "../vrml_parser/CParseGeneral.h"
51 #include "../world_script/JScript.h"
52 #include "../world_script/CScripts.h"
53 #include "Snapshot.h"
54 #include "../scenegraph/LinearAlgebra.h"
55 #include "../scenegraph/Collision.h"
56 
57 #include "../scenegraph/Viewer.h"
58 #include "../input/SensInterps.h"
59 #include "../x3d_parser/Bindable.h"
60 #include "../input/EAIHeaders.h"
61 
62 #include "../scenegraph/Component_KeyDevice.h" /* resolving implicit declarations */
63 #include "../opengl/Frustum.h"
64 #include "../input/InputFunctions.h"
65 
66 #include "../opengl/LoadTextures.h"
67 #include "../opengl/OpenGL_Utils.h"
68 #include "../ui/statusbar.h"
69 #include "../ui/CursorDraw.h"
70 #include "../scenegraph/RenderFuncs.h"
71 
72 #include "../ui/common.h"
73 #include "../io_files.h"
74 
75 #include "ProdCon.h"
76 #include "../scenegraph/quaternion.h"
77 
78 ivec2 ivec2_init(int x, int y);
79 ivec4 ivec4_init(int x, int y, int w, int h);
80 
81 int getRayHitAndSetLookatTarget();
82 void transformMBB(GLDOUBLE *rMBBmin, GLDOUBLE *rMBBmax, GLDOUBLE *matTransform, GLDOUBLE* inMBBmin, GLDOUBLE* inMBBmax);
83 
84 // for getting time of day
85 #if !defined(_MSC_VER)
86 #include <sys/time.h>
87 #endif
88 
89 void (*newResetGeometry) (void) = NULL;
90 
91 #ifdef WANT_OSC
92  #define USE_OSC 1
93 #else
94  #define USE_OSC 0
95 #endif
96 
97 #ifdef OLDCODE
98 OLDCODE #if defined(_ANDROID )
99 OLDCODE void setAquaCursor(int ctype) { };
100 OLDCODE
101 OLDCODE #endif // _ANDROID
102 #endif //OLDCODE
103 
104 #include "MainLoop.h"
105 
106 static int debugging_trigger_state;
107 void toggle_debugging_trigger(){
108  //set trigger with ',' keyboard command,
109  debugging_trigger_state = 1 - debugging_trigger_state;
110 }
111 int get_debugging_trigger_once(){
112  int iret = debugging_trigger_state;
113  if(iret) debugging_trigger_state = 0;
114  return iret;
115 }
116 int get_debugging_trigger(){
117  return debugging_trigger_state;
118 }
119 
120 double TickTime()
121 {
122  return gglobal()->Mainloop.TickTime;
123 }
124 double lastTime()
125 {
126  return gglobal()->Mainloop.lastTime;
127 }
128 /* Sensor table. When clicked, we get back from getRayHit the fromnode,
129  have to look up type and data in order to properly handle it */
130 struct SensStruct {
131  struct X3D_Node *fromnode;
132  struct X3D_Node *datanode;
133  void (*interpptr)(void *, int, int, int);
134 };
135 #define LMB 1
136 #define RMB 3
137 // and course #define MMB 2
138 // but it gives a compiler warning on Linux...
139 
140 //conceptually a Touch isa Drag. A touch device will send in multiple coordinates, with the same ID,
141 // and what that means is you are updating the terminal endpoint of a Touch or Drag.
142 // If its a new touch/drag ID then of course you also are setting the start point.
143 //Drags don't inherently have a concept of isOver. Our backend needs to create that.
144 // For example the SHIFT key.
145 //Funny as of May 2016 we don't store startpoint in the touch - it's state seems to be scattered
146 enum {
147  TOUCHCLAIMANT_UNCLAIMED = 0, //means no one has looked at it yet to make a claim
148  TOUCHCLAIMANT_PEDAL = 1, // for | ORing
149  TOUCHCLAIMANT_SENSOR = 2,
150  TOUCHCLAIMANT_NAVIGATION = 4,
151  TOUCHCLAIMANT_NONE = 8, //means something like hover
152 };
153 struct Touch
154 {
155  //int buttonState[4]; /*none down=0, LMB =1, MMB=2, RMB=3*/
156  int buttonState; //0 up, 1 down. For ^ hover mode, buttonstate will be 0 even when touch down
157  int mev; /* down/press=4, move/drag=6, up/release=5 */
158  unsigned int ID; /* for multitouch: 0-20, represents one finger drag. Recycle after an up */
159  int inUse; //flag for garbage collection/recycling = 0 not in use, else in use
160  float angle; /*some multitouch -like smarttech- track the angle of the finger */
161  int x; //coordinates as registered at scene level, after transformations in the contenttype stack
162  int y; //y-up
163  float fx,fy; //normalized coordinates ie -1 to 1 or 0 to 1 for navigation
164  int dragStart; //flag set generically on mouse down, and cleared by claimant when they've applied mousedown
165  int dragEnd; //flag set generically on mouse up, and cleared by claimant after cleaning up their drag state
166  int windex; //multi_window window index 0=default for regular freewrl
167  void* stageId; //unique ID for a stage, should be same for pick and render passes, otherwise in render not-for-me
168  int rx,ry; //raw input coords at emulation level, for finding and dragging and rendering
169  int claimant; // {unprocessed,pedal,sensor,navigation,none}
170  int passed; //which claimants have seen it and passed on claiming it {PEDAL | SENSOR | NAV }
171 
172  struct X3D_Node* CursorOverSensitive;//=NULL; /* is Cursor over a Sensitive node?*/
173  struct X3D_Node* oldCOS;//=NULL; /* which node was cursor over before this node?*/
174  struct X3D_Node* lastPressedOver;// = NULL;/* the sensitive node that the mouse was last buttonpressed over.*/
175  struct X3D_Node* lastOver;// = NULL; /* the sensitive node that the mouse was last moused over.*/
176  int lastOverButtonPressed;// = FALSE; /* catch the 1 to 0 transition for button presses and isOver in TouchSensors */
177 
178  void *hypersensitive;
179  int hyperhit;
180  double justModel[16];
181  struct point_XYZ hp;
182 
183 };
184 
185 //#ifdef ANGLEPROJECT
186 //mysterious/funny: angleproject's gl2.h has GL_BACK 0x0405 like glew.h,
187 //but if I use it as a renderbuffer number angleproject blackscreens - it likes 0 for GL_BACK.
188 #define FW_GL_BACK 0
189 //#endif
190 
191 void pushviewport(Stack *vpstack, ivec4 vp){
192  stack_push(ivec4,vpstack,vp);
193 }
194 void popviewport(Stack *vpstack){
195  stack_pop(ivec4,vpstack);
196 }
197 int overlapviewports(ivec4 vp1, ivec4 vp2){
198  //0 - outside, 1 - vp1 inside vp2 -1 vp2 inside vp1 2 overlapping
199  int inside = 0;
200  inside = vp1.X >= vp2.X && (vp1.X+vp1.W) <= (vp2.X+vp2.W) ? 1 : 0;
201  if(!inside){
202  inside = vp2.X >= vp1.X && (vp2.X+vp2.W) <= (vp1.X+vp1.W) ? -1 : 0;
203  }
204  if(!inside){
205  inside = vp1.X > (vp2.X+vp2.W) || vp1.X > (vp1.X+vp1.W) || vp1.Y > (vp2.Y+vp2.H) || vp2.Y > (vp1.Y+vp1.H) ? 0 : 2;
206  }
207  return inside;
208 }
209 ivec4 intersectviewports(ivec4 vp1, ivec4 vp2){
210  ivec4 vpo;
211  vpo.X = max(vp1.X,vp2.X);
212  vpo.W = min(vp1.X+vp1.W,vp2.X+vp2.W) - vpo.X;
213  vpo.Y = max(vp1.Y,vp2.Y);
214  vpo.H = min(vp1.Y+vp1.H,vp2.Y+vp2.H) - vpo.Y;
215  //printf("olap [%d %d %d %d] ^ [%d %d %d %d] = [%d %d %d %d]\n",vp1.X,vp1.Y,vp1.W,vp1.H,vp2.X,vp2.Y,vp2.W,vp2.H,vpo.X,vpo.Y,vpo.W,vpo.H);
216  return vpo;
217 }
218 int visibleviewport(ivec4 vp){
219  int ok = vp.W > 0 && vp.H > 0;
220  return ok;
221 }
222 int pointinsideviewport(ivec4 vp, ivec2 pt){
223  int inside = TRUE;
224  inside = inside && pt.X <= (vp.X + vp.W) && (pt.X >= vp.X);
225  inside = inside && pt.Y <= (vp.Y + vp.H) && (pt.Y >= vp.Y);
226  return inside;
227 }
228 int pointinsidecurrentviewport(Stack *vpstack, ivec2 pt){
229  ivec4 vp = stack_top(ivec4,vpstack);
230  return pointinsideviewport(vp,pt);
231 }
232 void intersectandpushviewport(Stack *vpstack, ivec4 childvp){
233  ivec4 currentvp = stack_top(ivec4,vpstack);
234  ivec4 olap = intersectviewports(childvp,currentvp);
235  pushviewport(vpstack, olap); //I need to unconditionally push, because I will be unconditionally popping later
236 }
237 ivec4 currentViewport(Stack *vpstack){
238  return stack_top(ivec4,vpstack);
239 }
240 int currentviewportvisible(Stack *vpstack){
241  ivec4 currentvp = stack_top(ivec4,vpstack);
242  return visibleviewport(currentvp);
243 }
244 void setcurrentviewport(Stack *_vpstack){
245  ivec4 vp = stack_top(ivec4,_vpstack);
246  glViewport(vp.X,vp.Y,vp.W,vp.H);
247 }
248 ivec4 viewportFraction(ivec4 vp, float *fraction){
249  /*
250  x3d specs > Layering > viewport
251  MFFloat [in,out] clipBoundary 0 1 0 1 [0,1]
252  "The clipBoundary field is specified in fractions of the normal render surface in the sequence left/right/bottom/top. "
253  so my fraction calculation should be something like:
254  L = X + W*f0
255  R = X + W*f1
256  B = Y + H*f2
257  T = Y + H*f3
258 
259  W = R - L
260  H = T - B
261  X = L
262  Y = B
263  */
264  ivec4 res;
265  int L,R,B,T; //left,right,bottom,top
266 
267  L = (int)(vp.X + vp.W*fraction[0]);
268  R = (int)(vp.X + vp.W*fraction[1]);
269  B = (int)(vp.Y + vp.H*fraction[2]);
270  T = (int)(vp.Y + vp.H*fraction[3]);
271 
272  res.W = R - L;
273  res.H = T - B;
274  res.X = L;
275  res.Y = B;
276 
277  return res;
278 }
279 
280 /* eye can be computed automatically from vp (viewpoint)
281  mono == vp
282  stereo - move left and right from vp by half-eyebase
283  front, top, right - use vp position and a primary direction
284 */
285 //typedef struct eye {
286 // float *viewport; //fraction of parent viewport left, width, bottom, height
287 // void (*pick)(struct eye *e, float *ray); //pass in pickray (and tranform back to prior stage)
288 // float pickray[6]; //store transformed pickray
289 // void (*cursor)(struct eye *e, int *x, int *y); //return transformed cursor coords, in pixels
290 // //BOOL sbh; //true if render statusbarhud at this stage, eye
291 //} eye;
292 
293 /* contenttype abstracts scene, statusbarhud, and HMD (head-mounted display) textured-distortion-grid
294  - each type has a prep and a render and some data, and a way to handle a pickray
295  - general idea comes from an opengl gui project (dug9gui). When you read 'contenttype' think 'gui widget'.
296 */
297 //===========NEW=====Nov27,2015================>>>>>
298 enum {
299  CONTENT_GENERIC, //defaults, render and pick can be delegated to
300  CONTENT_SCENE, //good old fashioned vrml / x3d scene
301  CONTENT_STATUSBAR, //statusbarHud.c (SBH) menu system
302  CONTENT_SWITCH, //switch case on children, a child chooser
303  CONTENT_MULTITOUCH, //touch display emulator, turn on with SBH > options > emulate multitouch
304  CONTENT_E3DMOUSE, //emulate 3D mouse
305  CONTENT_TEXTUREGRID, //texture-from-fbo-render over a planar mesh/grid, rendered with ortho and diffuse light
306  CONTENT_ORIENTATION, //screen orientation widget for 'screenOrientation2' application of mobile device screen orientation 90, 180, 270
307  CONTENT_CAPTIONTEXT, //text, but just one line
308  CONTENT_TEXTPANEL, //ConsoleMessage panel, using dual ring buffers: one for raw text stream, other for pointers to \n in first buffer
309  CONTENT_LAYER, //children are rendered one over top of the other, with zbuffer clearing between children
310  CONTENT_SPLITTER, //not implemented, a splitter widget
311  CONTENT_QUADRANT, //semi- implemented, a quadrant panel where the scene viewpoint is altered to side, front, top for 3 panels
312  CONTENT_STAGE, //opengl buffer to render to, GL_BACK or FBO (file buffer object), does clearcolor and clear depth before rendering children or self
313  CONTENT_STEREO_SIDEBYSIDE, //like quadrant, but 2 viewports, and view matrices parallel and separated by eyebase
314  CONTENT_STEREO_ANAGLYPH,//used with colored anaglyph lenses ie Amber/yellow left, Blue right, or Red left, Cyan right etc
315  CONTENT_STEREO_UPDOWN, //like sidebyside, but with one viewport above the other (used for screen-interlace-LCD-eyewear and some HMDs)
316  CONTENT_STEREO_SHUTTER, //like sidebyside or updown, but using opengl quadbuffer stereo, and shutter glasses
317  //CONTENT_TARGETWINDOW, //(target windows aren't implemented as contenttype
318 } content_types;
319 
320 //typedef struct eye {
321 // //int iyetype;
322 // void (*render)(void *self);
323 // void (*computeVP)(void *self, void *vp); //side, top, front, vp for quadrant or splitter
324 // void (*navigate)(void *self); //like handle0 except per-eye
325 // int (*pick)(void *self); //per-eye
326 //} eye;
327 //eye *new_eye(){
328 // return MALLOCV(sizeof(eye));
329 //}
330 int haveFrameBufferObject()
331 {
332  int iret = TRUE;
333 #if defined(GLEW) || defined(GLEW_MX)
334  iret = GLEW_ARB_framebuffer_object != 0;
335 #endif
336  return iret;
337 }
338 
339 void pushnset_framebuffer(int ibuffer){
340  Stack *framebufferstack;
341  //int jbuffer;
342  framebufferstack = (Stack *)gglobal()->Mainloop._framebufferstack;
343  //jbuffer = stack_top(int,framebufferstack);
344  stack_push(int,framebufferstack,ibuffer);
345 
346  //before gl 3.1 fbos were an extension
347  if (haveFrameBufferObject() ){
348  glBindFramebuffer(GL_FRAMEBUFFER,0);
349  glBindFramebuffer(GL_FRAMEBUFFER, ibuffer);
350  //printf("pushframebuffer from %d to %d\n",jbuffer,ibuffer);
351  }
352 }
353 void popnset_framebuffer(){
354  int ibuffer;
355  //int jbuffer;
356  Stack *framebufferstack;
357  framebufferstack = (Stack *)gglobal()->Mainloop._framebufferstack;
358  //jbuffer = stack_top(int,framebufferstack);
359  stack_pop(int,framebufferstack);
360  ibuffer = stack_top(int,framebufferstack);
361  //before gl 3.1 fbos were an extension
362  if (haveFrameBufferObject()){
363  glBindFramebuffer(GL_FRAMEBUFFER,0);
364  glBindFramebuffer(GL_FRAMEBUFFER, ibuffer);
365  }
366  //printf("popframebuffer from %d to %d\n",jbuffer,ibuffer);
367 }
368 void pushnset_viewport(float *vpFraction){
369  //call this from render() function (not from pick function)
370  ivec4 ivport;
371  Stack *vportstack;
372  vportstack = (Stack *)gglobal()->Mainloop._vportstack;
373  ivport = currentViewport(vportstack);
374  ivport = viewportFraction(ivport, vpFraction);
375  pushviewport(vportstack,ivport);
376  setcurrentviewport(vportstack); //does opengl call
377 }
378 void popnset_viewport(){
379  //call this from render() function (not from pick function)
380  Stack *vportstack;
381  vportstack = (Stack *)gglobal()->Mainloop._vportstack;
382  popviewport(vportstack);
383  setcurrentviewport(vportstack); //does opengl call
384 }
385 int checknpush_viewport(float *vpfraction, int mouseX, int mouseY){
386  Stack *vportstack;
387  ivec4 ivport, ivport1;
388  ivec2 pt;
389  int iret;
390 
391  vportstack = (Stack *)gglobal()->Mainloop._vportstack;
392  ivport = currentViewport(vportstack);
393  ivport1 = viewportFraction(ivport, vpfraction);
394  pt.X = mouseX;
395  pt.Y = mouseY;
396  iret = pointinsideviewport(ivport1,pt);
397  if(iret) pushviewport(vportstack,ivport1);
398  //else {
399  // printf("in checknpush_viewport in:\n");
400  // printf("ivp %d %d %d %d fraction %f %f %f %f\n",ivport.X,ivport.W,ivport.Y,ivport.H,vpfraction[0],vpfraction[1],vpfraction[2],vpfraction[3],mouseX,mouseY);
401  // printf("ivp1 %d %d %d %d mouse %d %d\n",ivport1.X,ivport1.W,ivport1.Y,ivport1.H,mouseX,mouseY);
402  //}
403  return iret;
404 
405 }
406 void pop_viewport(){
407  Stack *vportstack;
408  vportstack = (Stack *)gglobal()->Mainloop._vportstack;
409  popviewport(vportstack);
410  //printf("%d ",vportstack->n);
411 }
412 ivec4 get_current_viewport(){
413  Stack *vportstack;
414  vportstack = (Stack *)gglobal()->Mainloop._vportstack;
415  return stack_top(ivec4,vportstack);
416 }
417 float defaultClipBoundary [] = {0.0f, 1.0f, 0.0f, 1.0f}; //left,right,bottom,top fraction of pixel window
418 
419 
420 /* abstract contenttype - like a widget, it has a render() and a pick()
421  - pick() uses the widget viewport to filter, and a contenttype can also process/play with the mouse buttons/xy
422  - pick bottoms out in a function that does navigation immediately,
423  and stores the pick xy etc in a struct touch[] for later picking
424  - for contenttype_scene the 'real' picking happens on the render() pass, therefore there's no point
425  setting fancy things on a stack to communicate between a pick function and the scene backend picking
426  because the picking stack isn't active during 'real' picking - the render stack is
427  - alternate / supplementary ideas not implemented:
428  - instead of passing xy down, it could pass a multi-touch[] array down, filtering the touches against
429  the viewports as it goes down the pick() stack
430  - combining the pick and render passes somehow
431  (but should mouse navigation occur once per frame? or per-stereo/quad-window?)
432  - render() uses the widget viewport
433 
434  specifc contenttypes and shaders
435  - the freewrl global shader system is the default. It's just when rendering
436  - statusbarHud
437  - atlas text: contenttypes > captiontext, textpanel
438  - atlas text: Text > ScreenFontStyle
439  that we exit the globalshader system momentarily, to use a simpler shader, by calling
440  finishedwithglobalshader(), and restoreglobalshader() before and after gl_useProgram section
441 */
442 
443 typedef struct contenttype contenttype;
444 void register_contenttype(void *ct);
445 void free_contenttypes();
446 typedef struct tcontenttype {
447  int itype; //enum content_types: 0 scene, 1 statusbarHud, 2 texture grid
448  // 3 layer 4 splitter 5 quadrant 6 fbo 10 stage 11 targetwindow
449  contenttype *contents; //iterate over concrete-type children using children->next, NULL at end of children list
450  contenttype *next; //helps parent iterate over its children including this
451  contenttype *pnext; //reverse list of next
452  float viewport[4]; //fraction relative to parent, L,R,B,T as per x3d specs > Layering > Viewport > clipBoundary: "fractions of (parent surface) in the sequence left/right/bottom/top default 0 1 0 1
453  //ivec4 ipixels; //offset pixels left, right, bottom, top relative to parent, +right and +up
454  void (*render)(void *self);
455  int (*pick)(void *self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex); // a generalization of mouse. HMD IMU vs mouse?
456 } tcontenttype;
457 typedef struct contenttype {
458  tcontenttype t1; //superclass in abstract derived class
459 }contenttype;
460 void content_render(void *_self){
461  //generic render for intermediate level content types (leaf/terminal content types will have their own render())
462  contenttype *c, *self;
463 
464  self = (contenttype *)_self;
465  pushnset_viewport(self->t1.viewport);
466  c = self->t1.contents;
467  //FW_GL_CLEAR_COLOR(self->t1.cc.r,self->t1.cc.g,self->t1.cc.b,self->t1.cc.a);
468  while(c){
469  c->t1.render(c);
470  c = c->t1.next;
471  }
472  popnset_viewport();
473 }
474 int content_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
475  //generic render for intermediate level content types (leaf/terminal content types will have their own render())
476  int iret;
477  contenttype *c, *self;
478 
479  self = (contenttype *)_self;
480  iret = 0;
481  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
482  c = self->t1.contents;
483  while(c){
484  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
485  if(iret > 0) break; //handled
486  c = c->t1.next;
487  }
488  pop_viewport();
489  }
490  return iret;
491 }
492 void init_tcontenttype(tcontenttype *self){
493  self->itype = CONTENT_GENERIC;
494  self->contents = NULL;
495  self->render = content_render;
496  self->pick = content_pick;
497  memcpy(self->viewport,defaultClipBoundary,4*sizeof(float));
498  //self->ipixels = ivec4_init;
499  self->next = NULL;
500  self->pnext = NULL;
501 }
502 
503 typedef struct contenttype_scene {
504  tcontenttype t1;
505  //int stereotype; // none, sxs, ud, an, quadbuf
506  //color anacolors[2];
507  //eye eyes[6]; //doesn't make sense yet to have eyes for general content type, does it?
509 static void render();
510 int setup_pickside0(int x, int y, int *iside, ivec4 *vportleft, ivec4 *vportright);
511 void scene_render(void *self){
512  render();
513 }
514 void setup_picking();
515 void fwl_handle_aqua_multiNORMAL(const int mev, const unsigned int button, int x, int y, unsigned int ID, int windex);
516 int scene_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
517  int iret;
518  contenttype *self;
519 
520  self = (contenttype *)_self;
521  iret = 0;
522  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
523  ivec4 vport[2];
524  int iside, inside;
525  //printf("scene_pick mx %d my %d ",mouseX,mouseY);
526  inside = setup_pickside0(mouseX,mouseY,&iside,&vport[0],&vport[1]);
527  if(inside){
528  Stack *vpstack = (Stack*)gglobal()->Mainloop._vportstack;
529  pushviewport(vpstack,vport[iside]);
530  fwl_handle_aqua_multiNORMAL(mev,butnum,mouseX,mouseY,ID,windex);
531  iret = 1; //inside - should we set iret here?
532  popviewport(vpstack);
533  }
534  pop_viewport();
535  }
536  return iret;
537 }
538 contenttype *new_contenttype_scene(){
539  contenttype_scene *self = MALLOCV(sizeof(contenttype_scene));
540  register_contenttype(self);
541  init_tcontenttype(&self->t1);
542  self->t1.itype = CONTENT_SCENE;
543  self->t1.render = scene_render;
544  self->t1.pick = scene_pick;
545  return (contenttype*)self;
546 }
547 int statusbar_getClipPlane();
548 typedef struct contenttype_statusbar {
549  tcontenttype t1;
550  int clipplane;
552 void render_statusbar0();
553 void statusbar_render(void *_self){
554  //make this like layer, render contents first in clipplane-limited viewport, then sbh in whole viewport
555  Stack *vportstack;
556  int pushed;
557  contenttype_statusbar *self;
558  contenttype *c;
559 
560  self = (contenttype_statusbar *)_self;
561  pushnset_viewport(self->t1.viewport);
562  self->clipplane = statusbar_getClipPlane();
563 
564  vportstack = NULL;
565  pushed = 0;
566  if(self->clipplane != 0){
567  ivec4 ivport;
568  ttglobal tg;
569  tg = gglobal();
570 
571  vportstack = (Stack*)tg->Mainloop._vportstack;
572  ivport = stack_top(ivec4,vportstack);
573  ivport.H -= self->clipplane;
574  ivport.Y += self->clipplane;
575  stack_push(ivec4,vportstack,ivport);
576  pushed = 1;
577  }
578  c = self->t1.contents;
579  //FW_GL_CLEAR_COLOR(self->t1.cc.r,self->t1.cc.g,self->t1.cc.b,self->t1.cc.a);
580  while(c){
581  c->t1.render(c);
582  c = c->t1.next;
583  }
584  if(pushed) {
585  stack_pop(ivec4,vportstack);
586  }
587  render_statusbar0(); //draw statusbarHud
588  popnset_viewport();
589 }
590 int statusbar_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
591  contenttype *c;
592  contenttype_statusbar *self;
593  int iret = 0;
594 
595  //make this like layer, checking sbh first, then if not handled try contents in clipplane-limited viewport
596 
597  self = (contenttype_statusbar *)_self;
598  iret = 0;
599  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
600  iret = statusbar_handle_mouse1(mev,butnum,mouseX,mouseY,windex);
601  if(!iret){
602  int pushed;
603  Stack *vportstack;
604  vportstack = NULL;
605  pushed = 0;
606  if(self->clipplane != 0){
607  ivec4 ivport;
608  ttglobal tg;
609  tg = gglobal();
610 
611  vportstack = (Stack*)tg->Mainloop._vportstack;
612  ivport = stack_top(ivec4,vportstack);
613  ivport.H -= self->clipplane;
614  ivport.Y += self->clipplane;
615  stack_push(ivec4,vportstack,ivport);
616  pushed = 1;
617  }
618 
619  c = self->t1.contents;
620  while(c){
621  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
622  if(iret > 0) break; //handled
623  c = c->t1.next;
624  }
625  if(pushed) {
626  stack_pop(ivec4,vportstack);
627  }
628  }
629  pop_viewport();
630  }
631  return iret;
632 }
633 contenttype *new_contenttype_statusbar(){
634  contenttype_statusbar *self = MALLOCV(sizeof(contenttype_statusbar));
635  register_contenttype(self);
636  init_tcontenttype(&self->t1);
637  self->t1.itype = CONTENT_STATUSBAR;
638  self->t1.render = statusbar_render;
639  self->t1.pick = statusbar_pick;
640  self->clipplane = 0; //16; //can be 0 if nothing pinned, or 16+32=48 if both statusbar+menubar pinned
641  return (contenttype*)self;
642 }
643 
644 
645 //SWITCH
646 typedef struct contenttype_switch {
647  tcontenttype t1;
648  int whichCase;
649  int *whichPtr;
651 void render_switch0();
652 void switch_render(void *_self){
653  //make this like layer, render contents first in clipplane-limited viewport, then sbh in whole viewport
654  int i,iwhich;
655  contenttype_switch *self;
656  contenttype *c;
657 
658  self = (contenttype_switch *)_self;
659  pushnset_viewport(self->t1.viewport);
660  c = self->t1.contents;
661  i = 0;
662  while(c){
663  iwhich = *(self->whichPtr);
664  if(i == iwhich){
665  c->t1.render(c);
666  }
667  c = c->t1.next;
668  i++;
669  }
670  popnset_viewport();
671 }
672 int switch_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
673  contenttype *c;
674  contenttype_switch *self;
675  int iret = 0;
676 
677  //make this like layer, checking sbh first, then if not handled try contents in clipplane-limited viewport
678 
679  self = (contenttype_switch *)_self;
680  iret = 0;
681  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
682  int i = 0;
683  c = self->t1.contents;
684  while(c){
685  if(i == *(self->whichPtr)){
686  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
687  if(iret > 0) break; //handled
688  }
689  c = c->t1.next;
690  i++;
691  }
692  pop_viewport();
693  }
694  return iret;
695 }
696 contenttype *new_contenttype_switch(){
697  contenttype_switch *self = MALLOCV(sizeof(contenttype_switch));
698  register_contenttype(self);
699  init_tcontenttype(&self->t1);
700  self->t1.itype = CONTENT_SWITCH;
701  self->t1.render = switch_render;
702  self->t1.pick = switch_pick;
703  self->whichCase = -1;
704  self->whichPtr = &self->whichCase;
705  return (contenttype*)self;
706 }
707 void contenttype_switch_set_which(contenttype *_self, int which){
708  contenttype_switch *self = (contenttype_switch *)_self;
709  self->whichCase = which;
710  self->whichPtr = &self->whichCase;
711 }
712 void contenttype_switch_set_which_ptr(contenttype *_self, int *whichPtr){
713  contenttype_switch *self = (contenttype_switch *)_self;
714  self->whichPtr = whichPtr;
715 }
716 
717 
718 
719 
720 
721 
722 
723 typedef struct AtlasFont AtlasFont;
724 typedef struct AtlasEntrySet AtlasEntrySet;
725 AtlasFont *searchAtlasTableOrLoad(char *facename, int EMpixels);
726 AtlasEntrySet* searchAtlasFontForSizeOrMake(AtlasFont *font,int EMpixels);
727 typedef struct vec4 {float X; float Y; float Z; float W;} vec4;
728 vec4 vec4_init(float x, float y, float z, float w);
729 int render_captiontext(AtlasFont *font, int *utf32, int len32, vec4 color);
730 typedef struct contenttype_captiontext {
731  tcontenttype t1;
732  char *caption;
733  int len;
734  int *utf32;
735  int len32;
736  int nalloc;
737  AtlasFont *font;
738  char *fontname;
739  int fontSize;
740  AtlasEntrySet *set;
741  float percentSize;
742  int EMpixels;
743  int maxadvancepx;
744  float angle;
745  vec4 color;
747 void captiontext_render(void *_self){
749 
750  self = (contenttype_captiontext *)_self;
751  pushnset_viewport(self->t1.viewport);
752 
753  render_captiontext(self->font, self->utf32, self->len32, self->color);
754  popnset_viewport();
755 }
756 int captiontext_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
757  int iret = 0;
758  return iret;
759 }
760 contenttype *new_contenttype_captiontext(char *fontname, int EMpixels, vec4 color){
761  contenttype_captiontext *self = MALLOCV(sizeof(contenttype_captiontext));
762  register_contenttype(self);
763  init_tcontenttype(&self->t1);
764  self->t1.itype = CONTENT_CAPTIONTEXT;
765  self->t1.render = captiontext_render;
766  self->t1.pick = captiontext_pick;
767  self->set = NULL;
768  self->EMpixels = EMpixels;
769  self->font = NULL;
770  self->color = color;
771  self->fontname = fontname;
772  self->caption = NULL;
773  self->utf32 = NULL;
774  self->len = 0;
775  self->len32 = 0;
776  self->nalloc = 0;
777  self->font = (AtlasFont*)searchAtlasTableOrLoad(fontname,EMpixels);
778  if(!self->font){
779  printf("dug9gui: Can't find font %s do you have the wrong name?\n",fontname);
780  }
781  //self->set = (void *)self->font->set; //searchAtlasFontForSizeOrMake(self->font,EMpixels);
782  return (contenttype*)self;
783 }
784 
785 // in ComponentTextures.c ... JAS
786 unsigned int *utf8_to_utf32(unsigned char *utf8string, unsigned int *str32, unsigned int *len32);
787 void captiontext_setString(void *_self, char *utf8string){
788  int lenstr;
790  lenstr = strlen(utf8string);
791  if(self->nalloc < lenstr){
792  self->caption = realloc(self->caption,lenstr+1);
793  //in theory utf32 should always be <= utf8 length, make same size and extra room
794  self->utf32 = realloc(self->utf32,(lenstr+1)*sizeof(int));
795  self->nalloc = lenstr;
796  }
797  strcpy(self->caption,utf8string);
798  self->len = lenstr;
799  self->utf32 = utf8_to_utf32(self->caption,self->utf32,&self->len32);
800 }
801 
802 
803 //#ifdef DUALRINGBUFFER
804 //DUAL RING BUFFER CONSOLEMESSAGE
805 //our thanks go to dug9 for adapting/contributing this dual ringbuffer method from his dug9gui project
806 #include "list.h"
807 typedef struct consoleLine {
808  char *line;
809  int len;
810  int endline;
811 } consoleLine;
812 
813 typedef struct BUTitem BUTitem;
814 typedef struct BUTitem {
815  unsigned char *B;
816  BUTitem *prev;
817  BUTitem *next;
818 }BUTitem;
819 typedef struct contenttype_textpanel {
820  tcontenttype t1;
821  AtlasEntrySet *set;
822  AtlasFont *font;
823  char *fontname;
824  int fontSize;
825  int maxadvancepx;
826  vec4 color;
827  //float percentSize;
828  //float angle;
829 
830  int maxlines;
831  int maxlen;
832  int wrap;
833 
834  //blob method
835  unsigned char *Ablob;
836  int blobsize;
837  unsigned char *S, *E; //static pointers to BLOB start and end
838  unsigned char *Z,*z; //start and end pointers of written non-stale BLOB data, move as more data written
839  BUTitem *Blist; //storage for \n ring buffer
840  BUTitem *bhead; //head of the \n ring buffer
841  int added;
842  int rowsize; //malloced size of *row
843  unsigned char *row; //buffer for combining split rows for rendering
844  int initialized; //flag for initializing whatever update calls on each loop, so on first loop it can initialize backend/model of MVC
846 void textpanel_render(void *self);
847 contenttype *new_contenttype_textpanel(char* fontname, int EMpixels, int maxlines, int maxlen, int wrap){
848  int i, iem;
849  contenttype_textpanel *self = MALLOCV(sizeof(contenttype_textpanel));
850  register_contenttype(self);
851  init_tcontenttype(&self->t1);
852  self->t1.itype = CONTENT_TEXTPANEL;
853  self->t1.render = textpanel_render;
854  // default t1-> pick self->t1.pick = textpanel_pick;
855 
856  self->color = vec4_init(1.0f,1.0f,1.0f,0.0f);
857  //self->super.super.type = GUI_TEXTPANEL;
858  self->maxlines = maxlines;
859  self->maxlen = maxlen; // max line length
860  self->wrap = wrap; //bool if true, cut lines when too long, else render on one line up to maxlen and scroll in X
861  self->set = NULL;
862 
863  //blob method
864  self->blobsize = self->maxlines * self->maxlen;
865  self->Ablob = (unsigned char*)MALLOCV(self->blobsize+1);
866  register_contenttype(self->Ablob);
867  memset(self->Ablob,0,self->blobsize+1); //the +1 is so Ablob ends in \0 and we can printf it for debuggin
868  self->Z = self->z = self->Ablob;
869  self->S = self->Ablob;
870  self->E = self->Ablob + self->blobsize;
871  self->Blist = MALLOCV(sizeof(BUTitem)*self->maxlines);
872  register_contenttype(self->Blist);
873  self->rowsize = self->maxlen;
874  self->row = MALLOCV(self->rowsize +1);
875  register_contenttype(self->row);
876 
877  for(i=0;i<self->maxlines;i++){
878  int prev, next;
879  prev = i - 1;
880  next = i + 1;
881  if(prev < 0) prev = self->maxlines -1;
882  if(next > self->maxlines -1) next = 0;
883  self->Blist[i].next = &self->Blist[next];
884  self->Blist[i].prev = &self->Blist[prev];
885  self->Blist[i].B = self->Z;
886  }
887  self->bhead = &self->Blist[0];
888  self->added = 0;
889  //ouch
890  self->fontname = fontname;
891  iem = (int)(EMpixels * fwl_getDensityFactor());
892  self->fontSize = iem;
893  self->maxadvancepx = iem/2; //use the one in atlasEntry which is more specific
894  self->initialized = FALSE;
895  //self->font =
896  self->fontname = fontname;
897  self->font = (AtlasFont*)searchAtlasTableOrLoad(fontname,iem);
898  if(!self->font){
899  printf("dug9gui: Can't find font %s do you have the wrong name?\n",fontname);
900  }
901  /*
902  AtlasFont *font = (AtlasFont*)searchGUItable(font_table,fontname);
903  if(font){
904  self->font = font;
905  if(font->atlasSizes.n){
906  for(int i=0;i<font->atlasSizes.n;i++){
907  AtlasEntrySet *aes = vector_get(AtlasEntrySet*,&font->atlasSizes,i);
908  if(aes->EMpixels == EMpixels){
909  self->set = aes;
910  }
911  }
912  }
913  }
914  */
915  return (contenttype*)self;
916 }
917 
918 
919 /*
920 BLOB (binary large object) method for accumulating consoleMessages and wrapping/splitting
921  for fixed width console rendering
922 Net benefit of the BLOB algo (vs list-of-fixed-length-strings):
923 - Rendering can wrap easily, recomputing wrap splits on each frame using pointer arithmetic, without iterating over string chars
924 - line-length limit bigger: size of BLOB instead of maxlen
925 - no per-frame mallocs/frees/strdups
926 
927 More algo details: we are using 2 circular / ring buffers:
928 - one for unsigned char* text bytes
929 - one to record \n locations in the buffer, during incoming writes (makes it fast to render)
930 http://en.wikipedia.org/wiki/Circular_buffer
931 1. for generic ring buffers you need to store the length of the buffer,
932  and/or both start and end of data, with start = end+1, so its possible to tell
933  when the buffer is exactly empty vs full
934  (for us self-z == self->Z when exactly empty, otherwise self->z == self->Z + 1 when full)
935 2. our case differs from generic circular/ring buffer algos:
936 a) we never 'get/take/remove' from either ring buffer during read/render.
937  Instead it's up to the write to do all updates to the buffer, and keep overwriting stale data.
938 b) because we do 2 circular buffers -\n struct list and char* blob- we must combine/union/min our 'limits'
939  when reading backward from the newest data to the oldest, so that we don't hit stale
940  pointers/over-written data.
941 Arbitrary design choices: for the blob / char* ring buffer: a well known hassle is wrapping when we hit
942  the end of the buffer, and some algos have fancy techniques such as pointer mirroring or
943  'bip' 2-chunk method. We deal straighforwardly with the break in the data by detecting
944  where it is, and making 2 memcpys on write and 2 on read.
945 
946 Structs:
947 - a circular blob buffer 128rows*128cols = 16k will work as an intermediary between
948  consolemessage strings and word-wrapped display
949 - Blist is a fixed-size (maxrows) circularly linked list of small structs with pointers into the blob
950 - awkward part is split as a string wraps around to start of blobA
951 -- can be handled uniformly during render with B,T,U pointers
952 
953 ABLOB RING BUFFER
954 S Zz E
955 ================================================================
956  A--------T
957 U---------------------B last \n
958  most recent char
959 *static
960 ^singleton
961 *^ S,E start and end pointers to BLOBA buffer, E = S + blobsize
962 ^ zZ moving border of wraparound buffer z=start, Z=end
963  - starts out as z=S,Z=S, Z grows till == E, thereafter z=Z+1 and keeps moving
964 B ptr to last char received from consolemessage
965 A ptr to previous \n
966 T,U wraparound pointers: normally T=U=B, except when wrapping around then T=E, U=S
967 sw screen width in chars
968 line - incoming string from ConsoleMessage which may or may not end in \n
969 chunk - data between last char written and previous \n or (if no \n in blob) z
970  if there's a wraparound split, chunk = B-U + T-A otherwise chunk B-A
971 row - screen-width (or less) slice of chunk
972 
973 Algo:
974 To compute number of screen rows in chunk:
975 n = ceil[(B-U + T-A)/sw]
976 Thats for one chunk.
977 The listB circularly linked list will hold maxline list of \n pointers into ABLOB
978 Rendering will loop starting at the last char written, and work back to compute
979 number of screen rows and split points, stopping the iteration when listB is exhausted
980 or maxlines reached/exceeded, or ABLOB pointer == z
981 
982 Updating ABLOB with a new incoming string:
983 when receiving a string with no \n, and there was no \n on last string, the last Blist item is updated.
984 If prior string had a \n, a new Blist item is set, and the Blist head pointer is set to point to the new item.
985 
986 */
987 
988 
989 void TextPanel_AddLine_blobMethodB(contenttype_textpanel *self, char *line, int len, int endline){
990  unsigned char *T, *U, *B;
991  int lenT, lenU, haveTU;
992  BUTitem *BUTI;
993 
994  T = min(self->Z + len, self->E);
995  U = T == self->E ? self->S : T;
996  lenT = T - self->Z;
997  lenU = len - lenT;
998  haveTU = lenU > 0;
999  memcpy(self->Z,line,lenT);
1000  if(haveTU)
1001  memcpy(U,&line[lenT],lenU);
1002  B = U + lenU;
1003  BUTI = endline? self->bhead->next : self->bhead;
1004  BUTI->B = B;
1005  self->bhead = BUTI;
1006  self->Z = B;
1007  self->added = min(self->added + len, self->blobsize + 1);
1008  if(self->added > self->blobsize){
1009  //buffer full, move start
1010  self->z = self->z + 1;
1011  if(self->z > self->E) self->z = self->S;
1012  }
1013 }
1014 void TextPanel_AddString(void *_self, char *string){
1015  //takes a printf string which may be long and have embedded \n, may or may not end on \n
1016  //and splits it on \n, calls AddLine for each one.
1018  int endline, endstring;
1019  char *s = string;
1020  if(s == NULL) return;
1021  endstring = (*s) == '\0';
1022  while(!endstring){
1023  char *ln = s;
1024  while( (*ln) != '\0' && (*ln) != '\n') ln++;
1025  endline = (*ln) == '\n';
1026  endstring = (*ln) == '\0';
1027  TextPanel_AddLine_blobMethodB(self,s,ln-s,endline);
1028  ln++;
1029  s = ln;
1030  }
1031 }
1032 void fwg_register_consolemessage_callbackB(void *data, void(*callback)(void*,char *));
1033 void textpanel_register_as_console(void *_self){
1034  fwg_register_consolemessage_callbackB(_self,TextPanel_AddString);
1035 }
1036 ivec2 pixel2text(int x, int y, int rowheight, int maxadvancepx){
1037  int h = rowheight;
1038  int w = maxadvancepx;
1039  ivec2 ret = ivec2_init(x/w,y/h);
1040  return ret;
1041 }
1042 ivec2 text2pixel(int x, int y, int rowheight, int maxadvancepx){
1043  int h = rowheight;
1044  int w = maxadvancepx;
1045  ivec2 ret = ivec2_init(x*w, y*h);
1046  return ret;
1047 }
1048 
1049 void atlasfont_get_rowheight_charwidth_px(AtlasFont *font, int *rowheight, int *maxadvancepx);
1050 static int show_ringtext = 0;
1051 int before_textpanel_render_rows(AtlasFont *font, vec4 color);
1052 int textpanel_render_row(AtlasFont *font, char * cText, int len, int *pen_x, int *pen_y);
1053 void after_textpanel_render_rows();
1054 void textpanel_render_blobmethod(contenttype_textpanel *_self, ivec4 ivport){
1055 /* completely re-renders the textpanel, from the ABLOB and Blist ringbuffers
1056  - call once per frame
1057  - re-splits lines on each frame
1058  - if your textpanel starts small in Y, this will auto-expand its Y dimension till maxlines * rowheight in size
1059  Benefit (vs. buffering split lines) - if you tilt your device to a new orientation ie portrait to landscape
1060  then the text will be resplit and re-scrolled for the new panel shape automatically
1061  Implementation Options:
1062  - auto-resizing of panel up to a maxheight. Benefits:
1063  a) always draw text from bottom of panel up, simplfying to one loop
1064  b) panel scrolling can automatically turn on/off as needed (you need to assign the scrolling function)
1065  - vs fixed size panel
1066  x to get top-down look to scrolling you need 2 loops, one to count lines, one to draw
1067  x you could scroll same distance even when there's nothing to see
1068 */
1069  int jline, jrow, nrows, isFull, moredata, rowheight, maxadvancepx, atlasOK;
1070 
1071  ivec2 panelsizechars;
1072  BUTitem *BUTI, *LBUTI;
1073  contenttype_textpanel *self;
1074 
1075  self = (contenttype_textpanel *)_self;
1076  //we'll assume this is a 'leaf' contenttype - no children worth rendering
1077  if(self->t1.itype != CONTENT_TEXTPANEL) return;
1078  if(!self->font ) return;
1079 
1080  atlasfont_get_rowheight_charwidth_px(self->font,&rowheight,&maxadvancepx);
1081  panelsizechars = ivec2_init(ivport.W / maxadvancepx, ivport.H / rowheight);
1082  panelsizechars.X = min(panelsizechars.X,self->maxlen);
1083  panelsizechars.Y = min(panelsizechars.Y,self->maxlines);
1084 
1085  //if(panelsizechars.X+1 > self->rowsize) {
1086  // self->rowsize = panelsizechars.X+1;
1087  // self->row = realloc(self->row,self->rowsize);
1088  //}
1089  BUTI = self->bhead;
1090 
1091  //compute lines needed
1092  jline = 0; //number of \n lines processed
1093  jrow = 0; // number of screen rows processed
1094  //work backward from bottom up the buffer, stopping at:
1095  //a) maxrows (ie we have enough \n to fill our console rows)
1096  //b) self->z (ie we hit stale data in the char* ring buffer)
1097  // whichever is less
1098  isFull = self->added > self->blobsize;
1099  moredata = min(self->added,self->blobsize);
1100  do{
1101  int i, nchars, bchars, achars, hasTU, Trow;
1102  unsigned char *B, *A, *U, *T, *P;
1103  LBUTI = BUTI->prev;
1104  B = BUTI->B;
1105  //calculate numbe of wordwrapped lines - we'll just split uncerimoniously like a console rather than looking for a space like a word processor
1106  U = B;
1107  T = B;
1108  hasTU = FALSE;
1109  A = LBUTI->B;
1110  if(B < A){
1111  U = self->S;
1112  T = self->E;
1113  hasTU = TRUE;
1114  }
1115  nchars = (B - U + T - A);
1116  achars = T - A;
1117  bchars = nchars - achars;
1118 
1119  nrows = (int)ceil((float)nchars/(float)panelsizechars.X);
1120  Trow = nrows -1 - (T - A)/panelsizechars.X; //if hasTU split, which panel row is it in?
1121  //hasTU = B != T; //is there a ABLOB buffer split (TU) in this \n delimited line?
1122  //ABLOB - some scenarios it has to work with. The hard part: handling the ABLOB TU break
1123  //S Zz E
1124  //======================================================\n=========
1125  //U111222222222222333333B A11111111T
1126  //3 textpanel rows 1,2 and 3, with 3 being a partial row, and 1 being split by circular buffer
1127  //U111111111111111111111B A11111111T
1128  //one partial row being split by circular buffer
1129  // A1111111111111TUB
1130  //normal case, one row, no split
1131  // A1111222233333TUB
1132  //normal case, 3 rows, no split
1133  //
1134  P = B;
1135  atlasOK = before_textpanel_render_rows(self->font, self->color);
1136  //printf("\rivport.Y %d ",ivport.Y);
1137 
1138  if(atlasOK)
1139  for(i=0;i<nrows;i++){
1140  char *row;
1141  int l0, l1, i0, lenrow, pen_x, pen_y;
1142  ivec2 xy;
1143 
1144  i0 = (nrows-i-1)*panelsizechars.X;
1145  lenrow = min(nchars - i0,panelsizechars.X);
1146  moredata -= lenrow;
1147  if(moredata < 0) //was <= changed to < Mar 12, 2016 because skipping first line
1148  break; //stale (overwritten) BLOB/ringbuffer data
1149  jrow++;
1150  if(jrow > panelsizechars.Y) //would be rendered off-panel
1151  break;
1152  row = &P[-nchars + i0];
1153  if(hasTU && Trow == i){
1154  l0 = T - &A[i0];
1155  l1 = lenrow - l0;
1156  row = self->row;
1157  memcpy(&row[l0],U,l1);
1158  memcpy(row,&A[i0],l0);
1159  P = &self->E[bchars];
1160  }
1161  if(0){
1162  //debugging
1163  if(!strncmp(row,"`~",2)){
1164  //last row of my synthetic data
1165  //lets see the blob ringbuffer
1166  printf("===========\n");
1167  printf("%s",self->Ablob);
1168  printf("\n===========\n");
1169 
1170  }
1171  }
1172  if(0){
1173  int k;
1174  //debugging
1175  for(k=0;k<lenrow;k++){
1176  if(row[k] != '.'){
1177  printf("T-A=%d B-U=%d Z=%d S=%d E=%d A=%d B=%d &Aio= %d\n",(int)(T-A),(int)(B-U),(int)self->Z, (int)self->S,(int)self->E,(int)A,(int)B,(int)&A[i0]);
1178  row[k] = '?';
1179  }
1180  }
1181  }
1182  //OK got row and lenrow, now render it
1183  //textchars2panelpixel
1184  xy = text2pixel(0,jrow,rowheight,maxadvancepx);
1185  //panelpixel2screenpixel?
1186  //original, from dug9gui:
1187  //pen_y = (int)me->super.proportions.botRight.Y;
1188  //pen_x = xy.X;
1189  //pen_y -= xy.Y;
1190 
1191  pen_y = ivport.Y;
1192  pen_x = xy.X;
1193  pen_y -= xy.Y;
1194 
1195  //check if this line is visible, as measured by its bounding box. skip render if not
1196  //ivec4 box = ivec4_init(pen_x,pen_y,lenrow*self->set->maxadvancepx,self->set->rowheight);
1197  //ivec4 currentvp = stack_top(ivec4,_vpstack);
1198  //if(overlapviewports(box, currentvp)) //seems not properly aligned, a little too aggressive
1199  textpanel_render_row(self->font, row, lenrow,&pen_x, &pen_y); //&xy.X,&xy.Y);
1200  if(show_ringtext){
1201  //debugging
1202  memcpy(self->row,row,lenrow);
1203  self->row[lenrow] = '\n';
1204  self->row[lenrow+1] = '\0';
1205  printf("%s",self->row);
1206  }
1207  jline++;
1208  }
1209  //moredata -= nchars;
1210  BUTI = BUTI->prev;
1211  }while(jrow < panelsizechars.Y && moredata > 0); //nrows > 0); //jline < panelsizechars.Y
1212  after_textpanel_render_rows();
1213  if(0) printf("======================\n");
1214  //if(jline >= panelsizechars.Y && panelsizechars.Y < self->maxlines){
1215  //if(jrow >= panelsizechars.Y && panelsizechars.Y < self->maxlines){
1216  // //auto expand panel
1217  // //int newheight = (jline)*self->set->rowheight;
1218  // int newheight = (jrow)*self->set->rowheight;
1219  // GUIElement *el = &self->super;
1220  // el->dim.isize.Y = newheight;
1221  // el->needRebaked = TRUE;
1222  // //if(el->scroll.Y == GUI_SCROLL_LIMIT){
1223  // el->pos.offset.Y = min(el->pos.offset.Y,0);
1224  // //el->pos.offset.Y = max(el->pos.offset.Y,min(0,ph-mh));
1225  // //}
1226 
1227  //}
1228  show_ringtext = 0;
1229 }
1230 
1231 
1232 void textpanel_render(void *_self){
1233  //like a layer?
1234  contenttype *c;
1235  ivec4 ivport;
1236  Stack *vportstack;
1237  ttglobal tg;
1238 
1239  contenttype_textpanel *self;
1240  self = (contenttype_textpanel *)_self;
1241  pushnset_viewport(self->t1.viewport);
1242  c = self->t1.contents;
1243  while(c){
1244  c->t1.render(c); // Q. HOW/WHERE TO SIGNAL TO CLEAR JUST Z BUFFER BETWEEN LAYERS
1245  c = c->t1.next;
1246  }
1247  //render self last, as layer over children
1248  if(getShowConsoleText()){
1249  tg = gglobal();
1250  vportstack = (Stack*)tg->Mainloop._vportstack;
1251  ivport = stack_top(ivec4,vportstack);
1252  textpanel_render_blobmethod(self,ivport);
1253  }
1254  popnset_viewport();
1255 }
1256 
1257 
1258 //#endif
1259 
1260 
1261 
1262 
1263 
1264 
1265 
1266 
1267 
1268 
1269 
1270 
1271 
1272 typedef struct contenttype_layer {
1273  tcontenttype t1;
1274  //clears zbuffer between contents, but not clearcolor
1275  //example statusbarHud (SBH) over scene:
1276  // scene rendered first, then SBH; mouse caught first by SBH, if not handled then scene
1278 void layer_render(void *_self){
1279  //just the z-buffer cleared between content
1280  contenttype *c, *self;
1281  self = (contenttype *)_self;
1282  pushnset_viewport(self->t1.viewport);
1283  c = self->t1.contents;
1284  while(c){
1285  c->t1.render(c); // Q. HOW/WHERE TO SIGNAL TO CLEAR JUST Z BUFFER BETWEEN LAYERS
1286  c = c->t1.next;
1287  }
1288  popnset_viewport();
1289 }
1290 int layer_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1291  //layer pick works backward through layers
1292  int iret, n,i;
1293  contenttype *c, *self, *reverse[10];
1294  self = (contenttype *)_self;
1295  c = self->t1.contents;
1296  n=0;
1297  while(c){
1298  reverse[n] = c;
1299  n++;
1300  c = c->t1.next;
1301  if(n > 9) break; //ouch a problem with my fixed-length array technique
1302  }
1303  iret = 0;
1304  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
1305  for(i=0;i<n;i++){
1306  //push viewport
1307  c = reverse[n-i-1];
1308  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID,windex);
1309  //pop viewport
1310  if(iret > 0) break; //handled
1311  }
1312  pop_viewport();
1313  }
1314  return iret;
1315 }
1316 contenttype *new_contenttype_layer(){
1317  contenttype_layer *self = MALLOCV(sizeof(contenttype_layer));
1318  register_contenttype(self);
1319  init_tcontenttype(&self->t1);
1320  self->t1.itype = CONTENT_LAYER;
1321  self->t1.render = layer_render;
1322  self->t1.pick = layer_pick;
1323  return (contenttype*)self;
1324 }
1325 
1326 int emulate_multitouch2(struct Touch *touchlist, int ntouch, int *IDD, int *lastbut, int *mev, unsigned int *button, int x, int y, int *ID, int windex);
1327 void record_multitouch(struct Touch *touchlist, int mev, int butnum, int mouseX, int mouseY, int ID, int windex, int ihandle);
1328 int fwl_get_emulate_multitouch();
1329 //void render_multitouch();
1330 void render_multitouch2(struct Touch* touchlist, int ntouch);
1331 
1332 typedef struct contenttype_multitouch {
1333  tcontenttype t1;
1334  //clears zbuffer between contents, but not clearcolor
1335  //example statusbarHud (SBH) over scene:
1336  // scene rendered first, then SBH; mouse caught first by SBH, if not handled then scene
1337  struct Touch touchlist[20]; //private touchlist here, separate from backend touchlist
1338  int ntouch;
1339  int IDD; //current drag ID - for LMB dragging a specific touch
1340  int lastbut;
1342 void multitouch_render(void *_self){
1343  //just the z-buffer cleared between content
1344  contenttype *c;
1345  contenttype_multitouch *self;
1346  self = (contenttype_multitouch *)_self;
1347  pushnset_viewport(self->t1.viewport);
1348  c = self->t1.contents;
1349  while(c){
1350  c->t1.render(c); // Q. HOW/WHERE TO SIGNAL TO CLEAR JUST Z BUFFER BETWEEN LAYERS
1351  c = c->t1.next;
1352  }
1353  //render self last
1354  // not needed - backend fiducialDraw works better //render_multitouch2(self->touchlist,self->ntouch);
1355  popnset_viewport();
1356 }
1357 int multitouch_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1358  //layer pick works backward through layers
1359  int iret;
1360  contenttype *c;
1361  contenttype_multitouch *self;
1362 
1363  self = (contenttype_multitouch *)_self;
1364  iret = 0;
1365  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
1366  int ihandle;
1367  //record for rendering
1368  ihandle = 0;
1369  if(fwl_get_emulate_multitouch()){
1370  ihandle = emulate_multitouch2(self->touchlist,self->ntouch,&self->IDD,&self->lastbut,&mev,&butnum,mouseX,mouseY,&ID,windex);
1371  iret = ihandle < 0 ? 0 : 1;
1372  }
1373  if(iret == 0){
1374  //then pick children
1375  c = self->t1.contents;
1376  while(c){
1377  //push viewport
1378  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID,windex);
1379  //pop viewport
1380  if(iret > 0) break; //handled
1381  c = c->t1.next;
1382  }
1383  record_multitouch(self->touchlist,mev,butnum,mouseX,mouseY,ID,windex,ihandle);
1384  }
1385  pop_viewport();
1386  }
1387  return iret;
1388 }
1389 contenttype *new_contenttype_multitouch(){
1390  //int i;
1391  contenttype_multitouch *self = MALLOCV(sizeof(contenttype_multitouch));
1392  register_contenttype(self);
1393  init_tcontenttype(&self->t1);
1394  self->t1.itype = CONTENT_MULTITOUCH;
1395  self->t1.render = multitouch_render;
1396  self->t1.pick = multitouch_pick;
1397  self->ntouch = 20;
1398  //for(i=0;i<self->ntouch;i++) self->touchlist[i].ID = -1;
1399  memset(self->touchlist,0,20*sizeof(struct Touch));
1400  self->IDD = -1;
1401  self->lastbut = 0;
1402  return (contenttype*)self;
1403 }
1404 
1405 //emulate 3D mouse with normal navigation + spherical navigation
1406 //use RMB click to toggle between modes
1407 typedef struct contenttype_e3dmouse {
1408  tcontenttype t1;
1409  int sphericalmode;
1410  int navigationMode;
1411  int dragMode;
1412  int waste;
1414 void e3dmouse_render(void *_self){
1415  //render + over contents
1416  // OLDCODE contenttype_e3dmouse *self = (contenttype_e3dmouse*)_self;
1417  content_render(_self);
1418  //render self over top
1419  if(1){
1420  int x,y;
1421  ivec4 ivport;
1422  Stack *vportstack;
1423  ttglobal tg;
1424  tg = gglobal();
1425  vportstack = (Stack*)tg->Mainloop._vportstack;
1426  ivport = stack_top(ivec4,vportstack);
1427  x = ivport.W/2 + ivport.X;
1428  y = ivport.H/2 + ivport.Y;
1429  //fiducialDraw(0, x, y, 0.0f);
1430  fiducialDrawB(CURSOR_DOWN,x,y);
1431 
1432  }
1433 }
1434 int e3dmouse_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1435  //this messy thing is supposed to emulate the case of an HMD scenario:
1436  // 1. the user looks at a drag sensor - centering it in their field of view
1437  // 2. pushing a button on a hand held device to signal 'select'
1438  // 3. looking somewhere else to drag the sensor
1439  // 4. releasing the button to drop the drag sensor
1440  // it emulates by using regular navigation for #1, spherical navigation for #3
1441  //LMB - regular navigation, except with SHIFT to disable sensor nodes
1442  //RMB - toggle on/off spherical
1443  //NONE - spherical navigation, mouseXY = .5
1444  //LMB - in spherical mode: click, with mousexy = .5
1445  int iret, but2, x,y;
1446  unsigned int ID0, ID1;
1447  ivec4 ivport;
1448  Stack *vportstack;
1449  ttglobal tg;
1451  tg = gglobal();
1452  vportstack = (Stack*)tg->Mainloop._vportstack;
1453 
1454  ivport = stack_top(ivec4,vportstack);
1455  x = ivport.W/2 + ivport.X; //viewport center
1456  y = ivport.H/2 + ivport.Y;
1457 
1458 
1459  but2 = LMB;
1460  ID0 = 0; //navigation
1461  ID1 = 1; //sensor dragging
1462  ivport = stack_top(ivec4,vportstack);
1463  if(butnum == RMB){
1464  if(mev == ButtonRelease){
1465  self->sphericalmode = 1 - self->sphericalmode;
1466  if(self->sphericalmode){
1467  printf("turning on spherical mode\n");
1468  self->navigationMode = fwl_getNavMode();
1469  fwl_setNavMode("SPHERICAL");
1470  //tg->Mainloop.AllowNavDrag = TRUE;
1471  //start spherical navigation drag
1472  iret = content_pick(_self,ButtonPress,but2,mouseX,mouseY,ID0,windex);
1473  }else{
1474  printf("turning off spherical mode\n");
1475  fwl_set_viewer_type(self->navigationMode);
1476  //tg->Mainloop.AllowNavDrag = FALSE;
1477  //end spherical navigation drag
1478  iret = content_pick(_self,ButtonRelease,but2,mouseX,mouseY,ID0,windex);
1479  }
1480  self->waste = FALSE;
1481  }else if(mev == ButtonPress){
1482  self->waste = TRUE;
1483  }
1484  return 1; //discard other RMBs
1485  }
1486  if(self->waste) return 1; //its an RMB motionNotify
1487  if(self->sphericalmode){
1488  iret = content_pick(_self,mev,butnum,x,y,ID1,windex);
1489  //printf("mev %d butnum %d x %d y %d ID %d\n",mev,butnum,x,y,ID1);
1490  //tg->Mainloop.SHIFT = TRUE;
1491  iret = content_pick(_self,MotionNotify,0,mouseX,mouseY,ID0,windex);
1492  //tg->Mainloop.SHIFT = FALSE;
1493  }else{
1494  //normal navigation and picking
1495  iret = content_pick(_self,mev,butnum,mouseX,mouseY,ID0,windex);
1496  //printf("mev %d butnum %d x %d y %d ID %d\n",mev,butnum,mouseX,mouseY,ID0);
1497 
1498  }
1499 
1500  return iret;
1501 }
1502 contenttype *new_contenttype_e3dmouse(){
1503  contenttype_e3dmouse *self = MALLOCV(sizeof(contenttype_e3dmouse));
1504  register_contenttype(self);
1505  init_tcontenttype(&self->t1);
1506  self->t1.itype = CONTENT_E3DMOUSE;
1507  self->t1.render = e3dmouse_render;
1508  self->t1.pick = e3dmouse_pick;
1509  self->sphericalmode = 0;
1510  self->dragMode = 0;
1511  self->waste = 0;
1512  return (contenttype*)self;
1513 }
1514 
1515 typedef struct contenttype_quadrant {
1516  tcontenttype t1;
1517  float offset_fraction[2];
1519 void loadIdentityMatrix (double *mat);
1520 void get_view_matrix(double *savePosOri, double *saveView);
1521 static void set_view_matrix(double *savePosOri, double *saveView);
1522 
1523 static void set_quadrant_viewmatrix(double *savePosOri, double *saveView, int iq) {
1524  //iq 2 3
1525  // 0 1
1526  //no comprendo - por que no veo difference.
1527  double viewmatrix[16], tmat[16], pomat[16], vmat[16], bothinverse[16];
1528  double xyz[3], zero[3];
1529 
1530  get_view_matrix(savePosOri,saveView);
1531  if(iq==0) return;
1532  zero[0] = zero[1] = zero[2] = 0.0;
1533  matmultiplyAFFINE(viewmatrix,saveView,savePosOri);
1534  matinverseAFFINE(bothinverse,viewmatrix);
1535 
1536  transformAFFINEd(xyz, zero, bothinverse);
1537 
1538  loadIdentityMatrix (pomat);
1539  loadIdentityMatrix (vmat);
1540  if(0) mattranslate(vmat, -Viewer()->Dist,0.0,0.0);
1541 
1542  mattranslate(tmat,-xyz[0],-xyz[1],-xyz[2]);
1543  matmultiplyAFFINE(vmat,tmat,vmat);
1544 
1545  switch(iq){
1546  case 0: break; //no change to vp
1547  case 1: matrotate(tmat, 0.0, 0.0,0.0,1.0);
1548  break;
1549  case 2: matrotate(tmat, PI * .5, 1.0,0.0,0.0);
1550  break;
1551  case 3: matrotate(tmat, PI * .5, 0.0,1.0,0.0);
1552  break;
1553  default:
1554  break;
1555  }
1556  matmultiplyAFFINE(vmat,vmat,tmat);
1557  set_view_matrix(pomat,vmat);
1558 }
1559 void quadrant_render(void *_self){
1560  //
1561  int i;
1562  contenttype *c;
1563  contenttype_quadrant *self;
1564 
1565  self = (contenttype_quadrant *)_self;
1566  pushnset_viewport(self->t1.viewport); //generic viewport
1567  c = self->t1.contents;
1568  i=0;
1569  while(c){
1570  double savePosOri[16], saveView[16];
1571  float viewport[4];
1572  memcpy(viewport,defaultClipBoundary,4*sizeof(float));
1573  //create quadrant subviewport and push
1574  viewport[0] = i==0 || i==2 ? 0.0f : self->offset_fraction[0]; //left
1575  viewport[1] = i==0 || i==2 ? self->offset_fraction[0] : 1.0f; //right
1576  viewport[2] = i==0 || i==1 ? 0.0f : self->offset_fraction[1]; //bottom
1577  viewport[3] = i==0 || i==1 ? self->offset_fraction[1] : 1.0f; //top
1578  pushnset_viewport(viewport); //quadrant sub-viewport
1579  //printf("quad viewport %f %f %f %f\n",viewport[0],viewport[1],viewport[2],viewport[3]);
1580  //create quadrant viewpoint and push
1581  set_quadrant_viewmatrix(savePosOri, saveView, i);
1582  c->t1.render(c);
1583  //update saved viewpoint and pop quadrant viewpoint
1584  set_view_matrix(savePosOri,saveView);
1585  //pop quadrant subviewport
1586  popnset_viewport();
1587  c = c->t1.next;
1588  i++;
1589  }
1590  popnset_viewport();
1591 }
1592 int quadrant_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1593  //
1594  int iret;
1595  int i;
1596  contenttype *c;
1597  contenttype_quadrant *self;
1598 
1599  self = (contenttype_quadrant *)_self;
1600  iret = 0;
1601  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){ //generic viewport
1602  c = self->t1.contents;
1603  i=0;
1604  while(c){
1605  //create quadrant subviewport and push
1606  float viewport[4];
1607  memcpy(viewport,defaultClipBoundary,4*sizeof(float));
1608  //create quadrant subviewport and push
1609  viewport[0] = i==0 || i==2 ? 0.0f : self->offset_fraction[0]; //left
1610  viewport[1] = i==0 || i==2 ? self->offset_fraction[0] : 1.0f; //right
1611  viewport[2] = i==0 || i==1 ? 0.0f : self->offset_fraction[1]; //bottom
1612  viewport[3] = i==0 || i==1 ? self->offset_fraction[1] : 1.0f; //top
1613  if(checknpush_viewport(viewport,mouseX,mouseY)){ //quadrant sub-viewport
1614  //create quadrant viewpoint and push
1615  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
1616  if(iret > 0) break; //handled
1617  //update saved viewpoint and pop quadrant viewpoint
1618  //pop quadrant subviewport
1619  pop_viewport();
1620  }
1621  c = c->t1.next;
1622  i++;
1623  }
1624  pop_viewport();
1625  }
1626  return iret;
1627 }
1628 contenttype *new_contenttype_quadrant(){
1629  contenttype_quadrant *self = MALLOCV(sizeof(contenttype_quadrant));
1630  register_contenttype(self);
1631  init_tcontenttype(&self->t1);
1632  self->t1.itype = CONTENT_QUADRANT;
1633  self->t1.render = quadrant_render;
1634  self->t1.pick = quadrant_pick;
1635  self->offset_fraction[0] = .5f;
1636  self->offset_fraction[1] = .5f;
1637  return (contenttype*)self;
1638 }
1639 
1640 
1641 
1642 
1643 //STEREO>>
1644 //SIDEBYSIDE >>
1646  tcontenttype t1;
1647  //in theory could put screenbase here, will get from old viewer for now
1649 void loadIdentityMatrix (double *mat);
1650 void get_view_matrix(double *savePosOri, double *saveView);
1651 static void set_view_matrix(double *savePosOri, double *saveView);
1652 void compute_sidebyside_viewport_and_fiducial(int iside, ivec4 *ivport, ivec2 *fidcenter){
1653  int screenwidth2;
1654  int iexpand;
1655  double expansion;
1656  ivec4 vport;
1657  Stack *vportstack;
1658  ttglobal tg = gglobal();
1659  X3D_Viewer *viewer;
1660 
1661  viewer = Viewer();
1662 
1663  vportstack = (Stack*)tg->Mainloop._vportstack;
1664  vport = stack_top(ivec4,vportstack);
1665 
1666  screenwidth2 = vport.W/2;
1667  ivport->W = screenwidth2;
1668  ivport->H = vport.H;
1669  ivport->Y = vport.Y;
1670  if(iside == 0){
1671  ivport->X = vport.X;
1672  }else{
1673  ivport->X = vport.X + screenwidth2;
1674  }
1675  fidcenter->Y = vport.Y + vport.H;
1676 
1677  //its just sidebyside that needs the screen distance adjusted to be slightly less than human eyebase
1678  //(the others can center their viewpoints in the viewports, and center the viewports on the screen)
1679  expansion = viewer->screendist - .5;
1680  iexpand = (GLint)(expansion * ivport->W);
1681 
1682  fidcenter->X = ivport->X + screenwidth2/2;
1683  if(iside == 0)
1684  fidcenter->X -= iexpand;
1685  else
1686  fidcenter->X += iexpand;
1687 
1688 }
1689 void stereo_sidebyside_render(void *_self){
1690  //
1691  int i;
1692  contenttype *c;
1694  X3D_Viewer *viewer;
1695 
1696 
1697  self = (contenttype_stereo_sidebyside *)_self;
1698  viewer = Viewer();
1699  viewer->isStereoB = 1; //we're using the B so old isStereo
1700 
1701  pushnset_viewport(self->t1.viewport); //generic viewport
1702  c = self->t1.contents;
1703  i=0;
1704  while(c){
1705  // OLDCODE ivec4 vport;
1706  ivec4 ivport2;
1707  ivec2 fidcenter;
1708  Stack *vportstack;
1709  ttglobal tg = gglobal();
1710 
1711 
1712  viewer->isideB = i; //set_viewmatrix needs to know
1713 
1714  vportstack = (Stack*)tg->Mainloop._vportstack;
1715  // OLDCODE vport = stack_top(ivec4,vportstack);
1716 
1717  compute_sidebyside_viewport_and_fiducial(viewer->isideB,&ivport2,&fidcenter);
1718 
1719  {
1720  int halfW, vpc;
1721  halfW = ivport2.W / 2;
1722  vpc = halfW + ivport2.X;
1723  viewer->xcenter = (double)(fidcenter.X - vpc)/(double)halfW; //-1 to 1 range with 0 being center, -1 being viewpoint on left side of viewport
1724  pushviewport(vportstack,ivport2);
1725  }
1726  setcurrentviewport(vportstack); //does opengl call
1727 
1728  c->t1.render(c);
1729  //fiducialDraw(1,fidcenter.X,fidcenter.Y,0.0f); //draw a fiducial mark where centre of viewpoint is
1730  fiducialDrawB(CURSOR_FIDUCIALS,fidcenter.X,fidcenter.Y);
1731  //pop stereo_sidebyside subviewport
1732  popnset_viewport();
1733  viewer->xcenter = 0.0;
1734  c = c->t1.next;
1735  i++;
1736  }
1737  viewer->isStereoB = 0;
1738  popnset_viewport();
1739 }
1740 int stereo_sidebyside_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1741  //
1742  int iret;
1743  int i;
1744  contenttype *c;
1746  X3D_Viewer *viewer;
1747 
1748  self = (contenttype_stereo_sidebyside *)_self;
1749  viewer = Viewer();
1750  viewer->isStereoB = 1;
1751 
1752  iret = 0;
1753  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){ //generic viewport
1754  c = self->t1.contents;
1755  i=0;
1756  //for each stereo side ..
1757  while(c){
1758  //.. create stereo_sidebyside subviewport, push, and render
1759  ivec4 ivport2;
1760  ivec2 fidcenter, pt;
1761 
1762  viewer->isideB = i;
1763  compute_sidebyside_viewport_and_fiducial(viewer->isideB,&ivport2,&fidcenter);
1764  //if(checknpush_viewport(viewport,mouseX,mouseY)){ - can't use this convenience function, will do long way
1765  //stereo_sidebyside sub-viewport
1766  pt.X = mouseX;
1767  pt.Y = mouseY;
1768  iret = pointinsideviewport(ivport2,pt);
1769  if(iret){
1770  pushviewport(gglobal()->Mainloop._vportstack,ivport2);
1771  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
1772  if(iret > 0) break; //handled
1773  pop_viewport();
1774  }
1775  c = c->t1.next;
1776  i++;
1777  }
1778  pop_viewport();
1779  }
1780  viewer->isStereoB = 0;
1781  return iret;
1782 }
1783 contenttype *new_contenttype_stereo_sidebyside(){
1785  register_contenttype(self);
1786  init_tcontenttype(&self->t1);
1787  self->t1.itype = CONTENT_STEREO_SIDEBYSIDE;
1788  self->t1.render = stereo_sidebyside_render;
1789  self->t1.pick = stereo_sidebyside_pick;
1790  return (contenttype*)self;
1791 }
1792 
1793 
1794 
1795 
1796 //ANAGLYPH >>
1798  tcontenttype t1;
1799  //in theory could put color sides here, will get from old viewer for now
1801 void loadIdentityMatrix (double *mat);
1802 void clear_shader_table();
1803 void setStereoBufferStyle(int);
1804 void stereo_anaglyph_render(void *_self){
1805  //Feb 13, 2016 - anaglyph isn't rendering properly with FBO stage
1806  // couldn't seem to make these hints work:
1807  // http://www.gamedev.net/topic/664111-blending-problems-on-alpha-enabled-render-target/
1808  //
1809  int i;
1810  contenttype *c;
1812  X3D_Viewer *viewer;
1813 
1814 
1815  self = (contenttype_stereo_anaglyph *)_self;
1816  viewer = Viewer();
1817  viewer->isStereoB = 1; //we're using the B so old isStereo not activated, backend thinks its rendering a mono scene
1818  viewer->anaglyphB = 1; //except we need the shader for luminance = f(R,G,B)
1819  clear_shader_table(); //tiggers reconfiguring shader, so it looks for anaglyphB flag
1820  //setStereoBufferStyle(1);
1821 
1822  pushnset_viewport(self->t1.viewport); //generic viewport
1823  c = self->t1.contents;
1824  i=0;
1825  //glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
1826  Viewer_anaglyph_clearSides(); //clear all channels
1827  //glColorMask(1,1,1,1);
1828  glClearColor(0.0f,0.0f,0.0f,1.0f);
1829  BackEndClearBuffer(2); //scissor test in here
1830  while(c){
1831 
1832  viewer->isideB = i; //set_viewmatrix needs to know
1833  Viewer_anaglyph_setSide(i); //clear just the channels we're going to draw to
1834  viewer->xcenter = 0.0; //no screen shift or fiducials, just center
1835 
1836  c->t1.render(c); //scene will do another backnedclearbuffer but should be constrained to channel mask for side
1837 
1838  c = c->t1.next;
1839  i++;
1840  }
1841  Viewer_anaglyph_clearSides(); //clear all channels
1842  //glColorMask(1,1,1,1);
1843  clear_shader_table();
1844 
1845  viewer->anaglyphB = 0;
1846  viewer->isStereoB = 0;
1847  popnset_viewport();
1848 }
1849 int stereo_anaglyph_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1850  //
1851  int iret;
1852  int i;
1853  contenttype *c;
1855  X3D_Viewer *viewer;
1856 
1857  self = (contenttype_stereo_anaglyph *)_self;
1858  viewer = Viewer();
1859  viewer->isStereoB = 1;
1860 
1861  iret = 0;
1862  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){ //generic viewport
1863  c = self->t1.contents;
1864  i=0;
1865  //for each stereo side, but I'm assuming left side will get it first
1866  while(c){
1867  viewer->isideB = i;
1868  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
1869  if(iret > 0) break; //handled
1870  c = c->t1.next;
1871  i++;
1872  }
1873  pop_viewport();
1874  }
1875  viewer->isStereoB = 0;
1876  return iret;
1877 }
1878 contenttype *new_contenttype_stereo_anaglyph(){
1880  register_contenttype(self);
1881  init_tcontenttype(&self->t1);
1882  self->t1.itype = CONTENT_STEREO_ANAGLYPH;
1883  self->t1.render = stereo_anaglyph_render;
1884  self->t1.pick = stereo_anaglyph_pick;
1885  return (contenttype*)self;
1886 }
1887 
1888 
1889 //UPDOWN >>
1891  tcontenttype t1;
1893 
1894 
1895 void stereo_updown_render(void *_self){
1896  //
1897  int i;
1898  contenttype *c;
1900  X3D_Viewer *viewer;
1901  Stack *vportstack;
1902  ttglobal tg = gglobal();
1903 
1904  self = (contenttype_stereo_updown *)_self;
1905  viewer = Viewer();
1906  viewer->isStereoB = 1; //we're using the B so old isStereo not activated, backend thinks its rendering a mono scene
1907  viewer->updownB = 1; //let setup_projection know to squish the aspect by x 2
1908 
1909  vportstack = (Stack*)tg->Mainloop._vportstack;
1910 
1911 
1912  pushnset_viewport(self->t1.viewport); //generic viewport
1913  c = self->t1.contents;
1914  i=0;
1915  while(c){
1916  ivec4 ivport;
1917  ivport = stack_top(ivec4,vportstack);
1918  ivport.H /= 2;
1919  ivport.Y += (1-i)*ivport.H; //left on top
1920  pushviewport(vportstack,ivport);
1921  setcurrentviewport(vportstack); //does opengl call
1922 
1923  //aspect squishing is still done in setup_projection
1924  viewer->isideB = i; //set_viewmatrix needs to know
1925  viewer->xcenter = 0.0; //no screen eyebase or fiducials, just center
1926 
1927  c->t1.render(c);
1928  popnset_viewport();
1929 
1930  c = c->t1.next;
1931  i++;
1932  }
1933  //glColorMask(1,1,1,1); /*restore, for statusbarHud etc*/ OR:
1934  viewer->updownB = 0;
1935  viewer->isStereoB = 0;
1936  popnset_viewport();
1937 }
1938 int stereo_updown_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
1939  //
1940  int iret;
1941  int i;
1942  contenttype *c;
1944  X3D_Viewer *viewer;
1945  Stack *vportstack;
1946  ttglobal tg = gglobal();
1947 
1948  self = (contenttype_stereo_updown *)_self;
1949  viewer = Viewer();
1950  viewer->isStereoB = 1;
1951  vportstack = (Stack*)tg->Mainloop._vportstack;
1952 
1953  iret = 0;
1954  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){ //generic viewport
1955  c = self->t1.contents;
1956  i=0;
1957  //for each stereo side, but I'm assuming left side will get it first
1958  while(c){
1959  ivec4 ivport;
1960  ivec2 pt;
1961 
1962  viewer->isideB = i;
1963  ivport = stack_top(ivec4,vportstack);
1964  ivport.H /= 2;
1965  ivport.Y += (1-i)*ivport.H; //left on top
1966  pt.X = mouseX;
1967  pt.Y = mouseY;
1968  iret = pointinsideviewport(ivport,pt);
1969  if(iret){
1970 
1971  pushviewport(vportstack,ivport);
1972  setcurrentviewport(vportstack); //does opengl call
1973 
1974  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
1975  popnset_viewport();
1976  if(iret > 0) break; //handled
1977  }
1978  c = c->t1.next;
1979  i++;
1980  }
1981  pop_viewport();
1982  }
1983  viewer->isStereoB = 0;
1984  return iret;
1985 }
1986 contenttype *new_contenttype_stereo_updown(){
1987  contenttype_stereo_updown *self = MALLOCV(sizeof(contenttype_stereo_updown));
1988  register_contenttype(self);
1989  init_tcontenttype(&self->t1);
1990  self->t1.itype = CONTENT_STEREO_UPDOWN;
1991  self->t1.render = stereo_updown_render;
1992  self->t1.pick = stereo_updown_pick;
1993  return (contenttype*)self;
1994 }
1995 
1996 
1997 //SHUTTER >>
1999  tcontenttype t1;
2001 
2002 void setStereoBufferStyleB(int itype, int iside, int ibuffer);
2003 void stereo_shutter_render(void *_self){
2004  //
2005  int i, shutterGlasses;
2006  contenttype *c;
2008  X3D_Viewer *viewer;
2009  // OLDCODE Stack *vportstack;
2010  static double shuttertime;
2011  static int shutterside;
2012  // OLDCODE ttglobal tg = gglobal();
2013 
2014  self = (contenttype_stereo_shutter *)_self;
2015  viewer = Viewer();
2016  viewer->isStereoB = 1; //we're using the B so old isStereo not activated, backend thinks its rendering a mono scene
2017 
2018  // OLDCODE vportstack = (Stack*)tg->Mainloop._vportstack;
2019  shutterGlasses = 2;
2020  if(viewer->haveQuadbuffer)
2021  shutterGlasses = 1;
2022 
2023  pushnset_viewport(self->t1.viewport); //generic viewport
2024  c = self->t1.contents;
2025  i=0;
2026  while(c){
2027 
2028  viewer->isideB = i; //set_viewmatrix needs to know
2029  viewer->xcenter = 0.0; //no screen eyebase or fiducials, just center
2030  if(shutterGlasses == 2) /* flutter mode - like --shutter but no GL_STEREO so alternates */
2031  {
2032  if(TickTime() - shuttertime > 2.0)
2033  {
2034  shuttertime = TickTime();
2035  shutterside = 1 - shutterside;
2036  }
2037  setStereoBufferStyleB(1,i,0);
2038  //p->bufferarray[0]=FW_GL_BACK;
2039  }else{
2040  setStereoBufferStyleB(0,i,0);
2041  shutterside = i;
2042  }
2043 
2044  if(i==shutterside){
2045  c->t1.render(c);
2046  }
2047 
2048  c = c->t1.next;
2049  i++;
2050  }
2051  setStereoBufferStyleB(1,0,0);
2052  viewer->isStereoB = 0;
2053  popnset_viewport();
2054 }
2055 int stereo_shutter_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
2056  //
2057  int iret;
2058  int i;
2059  contenttype *c;
2061  X3D_Viewer *viewer;
2062  // OLDCODE Stack *vportstack;
2063  // OLDCODE ttglobal tg = gglobal();
2064 
2065  self = (contenttype_stereo_shutter *)_self;
2066  viewer = Viewer();
2067  viewer->isStereoB = 1;
2068  // OLDCODE vportstack = (Stack*)tg->Mainloop._vportstack;
2069 
2070  iret = 0;
2071  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){ //generic viewport
2072  c = self->t1.contents;
2073  i=0;
2074  //for each stereo side, but I'm assuming left side will get it first
2075  while(c){
2076  iret = c->t1.pick(c,mev,butnum,mouseX,mouseY,ID, windex);
2077  if(iret > 0) break; //handled
2078 
2079  c = c->t1.next;
2080  i++;
2081  }
2082  pop_viewport();
2083  }
2084  viewer->isStereoB = 0;
2085  return iret;
2086 }
2087 contenttype *new_contenttype_stereo_shutter(){
2088  contenttype_stereo_shutter *self = MALLOCV(sizeof(contenttype_stereo_shutter));
2089  register_contenttype(self);
2090  init_tcontenttype(&self->t1);
2091  self->t1.itype = CONTENT_STEREO_SHUTTER;
2092  self->t1.render = stereo_shutter_render;
2093  self->t1.pick = stereo_shutter_pick;
2094  return (contenttype*)self;
2095 }
2096 
2097 
2098 //<<STEREO
2099 
2100 
2101 
2102 
2103 
2104 
2105 
2106 
2107 
2108 typedef struct contenttype_splitter {
2109  tcontenttype t1;
2110  float offset_fraction;
2111  int offset_pixels;
2112  int orientation; //vertical, horizontal
2114 contenttype *new_contenttype_splitter(){
2115  contenttype_splitter *self = MALLOCV(sizeof(contenttype_splitter));
2116  register_contenttype(self);
2117  init_tcontenttype(&self->t1);
2118  self->t1.itype = CONTENT_SPLITTER;
2119  return (contenttype*)self;
2120 }
2121 
2122 
2123 
2124 enum {
2125  STAGETYPE_BACKBUF,
2126  STAGETYPE_FBO
2127 } stage_type;
2128 typedef struct stage {
2129  tcontenttype t1;
2130  int type; // enum stage_type: fbo or backbuf
2131  unsigned int ibuffer; //gl fbo or backbuffer GL_UINT
2132  unsigned int itexturebuffer; //color buffer, if fbo
2133  unsigned int idepthbuffer; //z-depth buffer, if fbo
2134  //int width, height; //of texture buffer and render buffer
2135  ivec4 ivport; //backbuf stage: sub-area of parent iviewport we are targeting left, width, bottom, height
2136  //fbo stage: size to make the fbo buffer (0,0 offset)
2137  //float[4] clearcolor; FW_GL_CLEAR_COLOR(clearcolor[0],clearcolor[1],clearcolor[2],clearcolor[3]);
2138  BOOL clear_zbuffer;
2139  int even_odd_frame; //just even/odd so we can tell if its already been rendered on this frame
2140  //int initialized;
2141 } stage;
2142 
2143 //mouse coordinates are relative to a stage,
2144 //and because picking is done half in the pick() call stack, and
2145 //half in the render phase, we need to let the render phase know if a touch/pick is
2146 //in its stage.
2147 // pick > touch->stage = current_stageId()
2148 // render > if(touch->stage != current_stageId()) not for me
2149 //So we give each stage an arbitrary unique ID (its void* pointer)
2150 //in theory we could pass it down the pick(,,,,stageId) call stack like windex,
2151 //but stages can be chained so aren't fixed for a pass, so we need a push-n-pop stack for render
2152 //anyway, so we use a stack for pick too.
2153 void push_stageId(void *stageId){
2154  Stack *stagestack = (Stack*)gglobal()->Mainloop._stagestack;
2155  stack_push(void*,stagestack,stageId);
2156 }
2157 void *current_stageId(){
2158  Stack *stagestack = (Stack*)gglobal()->Mainloop._stagestack;
2159  return stack_top(void*,stagestack);
2160 }
2161 void pop_stageId(){
2162  Stack *stagestack = (Stack*)gglobal()->Mainloop._stagestack;
2163  stack_pop(void*,stagestack);
2164 }
2165 
2166 void stage_render(void *_self){
2167  //just the z-buffer cleared between content
2168  Stack *vportstack;
2169 
2170  stage *self = (stage*)_self;
2171  pushnset_framebuffer(self->ibuffer);
2172  vportstack = (Stack*)gglobal()->Mainloop._vportstack;
2173  pushviewport(vportstack,self->ivport);
2174  push_stageId(self);
2175  setcurrentviewport(vportstack); //does opengl call
2176  //for fun/testing, a different clear color for fbos vs gl_back, but not necessary
2177 
2178  if(self->ibuffer != FW_GL_BACK)
2179  glClearColor(.3f,.4f,.5f,1.0f);
2180  else
2181  glClearColor(1.0f,0.0f,0.0f,1.0f);
2182  BackEndClearBuffer(2);
2183  content_render(_self); //the rest of stage render is the same as content render, so we'll delegate
2184  pop_stageId();
2185  popnset_viewport();
2186  popnset_framebuffer();
2187 }
2188 int stage_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
2189  Stack *vportstack;
2190  ivec4 ivport_parent;
2191  int x,y;
2192  int iret;
2193  stage *self = (stage*)_self;
2194 
2195  ivport_parent = get_current_viewport();
2196  x = mouseX - ivport_parent.X;
2197  y = mouseY - ivport_parent.Y;
2198  //pick coords are relative to stage
2199  x = x + self->ivport.X; //should be 0
2200  y = y + self->ivport.Y; //should be 0
2201  vportstack = (Stack*)gglobal()->Mainloop._vportstack;
2202  pushviewport(vportstack,self->ivport);
2203  push_stageId(self);
2204  iret = content_pick(_self,mev,butnum,x,y,ID,windex);
2205  pop_stageId();
2206  pop_viewport();
2207  return iret;
2208 }
2209 
2210 contenttype *new_contenttype_stage(){
2211  stage *self = MALLOCV(sizeof(stage));
2212  register_contenttype(self);
2213  init_tcontenttype(&self->t1);
2214  self->t1.itype = CONTENT_STAGE;
2215  self->t1.render = stage_render;
2216  self->t1.pick = stage_pick;
2217  self->type = STAGETYPE_BACKBUF;
2218  self->ibuffer = FW_GL_BACK;
2219  self->clear_zbuffer = TRUE;
2220  self->ivport = ivec4_init(0,0,100,100);
2221  return (contenttype*)self;
2222 }
2223 //mobile GLES2 via ANGLE has only 16bit depth buffer. not 24 or 32 as with desktop opengl
2224 #ifdef GL_DEPTH_COMPONENT32
2225 #define FW_GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT32
2226 #else
2227 #define FW_GL_DEPTH_COMPONENT GL_DEPTH_COMPONENT16
2228 #endif
2229 contenttype *new_contenttype_stagefbo(int width, int height){
2230  contenttype *_self;
2231  stage *self;
2232  int useMip;
2233 
2234  _self = new_contenttype_stage();
2235  self = (stage*)_self;
2236  self->type = STAGETYPE_FBO;
2237  self->ivport.W = width;
2238  self->ivport.H = height; //can change during render pass
2239 
2240  // before gl 3.1 fbos were an extension
2241  // https://github.com/opengl-tutorials/ogl/blob/2.1_branch/tutorial14_render_to_texture/tutorial14.cpp#L167
2242  if ( !haveFrameBufferObject() ) // GLEW_ARB_framebuffer_object
2243  return _self;
2244 
2245  glGenTextures(1, &self->itexturebuffer);
2246  //bind to set some parameters
2247  glBindTexture(GL_TEXTURE_2D, self->itexturebuffer);
2248  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
2249  useMip = 0;
2250  if(useMip){
2251  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
2252  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4
2253  }else{
2254  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
2255  }
2256  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2257  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2258  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->ivport.W, self->ivport.H, 0, GL_RGBA , GL_UNSIGNED_BYTE, 0);
2259  //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
2260  //unbind - will rebind during render to reset width, height as needed
2261  glBindTexture(GL_TEXTURE_2D, 0);
2262 
2263  glGenFramebuffers(1, &self->ibuffer);
2264  glBindFramebuffer(GL_FRAMEBUFFER, self->ibuffer);
2265 
2266  // create a renderbuffer object to store depth info
2267  // NOTE: A depth renderable image should be attached the FBO for depth test.
2268  // If we don't attach a depth renderable image to the FBO, then
2269  // the rendering output will be corrupted because of missing depth test.
2270  // If you also need stencil test for your rendering, then you must
2271  // attach additional image to the stencil attachement point, too.
2272  glGenRenderbuffers(1, &self->idepthbuffer);
2273  //bind to set some parameters
2274  glBindRenderbuffer(GL_RENDERBUFFER, self->idepthbuffer);
2275  glRenderbufferStorage(GL_RENDERBUFFER, FW_GL_DEPTH_COMPONENT, self->ivport.W, self->ivport.H);
2276  //unbind
2277  glBindRenderbuffer(GL_RENDERBUFFER, 0);
2278 
2279  // attach a texture to FBO color attachement point
2280  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->itexturebuffer, 0);
2281 
2282  // attach a renderbuffer to depth attachment point
2283  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self->idepthbuffer);
2284  //unbind framebuffer till render
2285  glBindFramebuffer(GL_FRAMEBUFFER, 0);
2286 
2287  return _self;
2288 }
2289 void stage_resize(void *_self,int width, int height){
2290  stage *self;
2291  self = (stage*)_self;
2292  if(self->type == STAGETYPE_FBO){
2293  if(width != self->ivport.W || height != self->ivport.H){
2294  self->ivport.W = width;
2295  self->ivport.H = height;
2296  glBindTexture(GL_TEXTURE_2D, self->itexturebuffer);
2297  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self->ivport.W, self->ivport.H, 0, GL_RGBA , GL_UNSIGNED_BYTE, NULL);
2298 
2299  glBindTexture(GL_TEXTURE_2D, 0);
2300 
2301  glBindRenderbuffer(GL_RENDERBUFFER, self->idepthbuffer);
2302  glRenderbufferStorage(GL_RENDERBUFFER, FW_GL_DEPTH_COMPONENT, self->ivport.W, self->ivport.H);
2303 
2304  //unbind
2305  glBindRenderbuffer(GL_RENDERBUFFER, 0);
2306  //printf("stage_resize to %d %d\n",self->ivport.W,self->ivport.H);
2307  if(0){
2308  glBindFramebuffer(GL_FRAMEBUFFER, self->ibuffer);
2309  // attach a texture to FBO color attachement point
2310  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->itexturebuffer, 0);
2311 
2312  // attach a renderbuffer to depth attachment point
2313  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self->idepthbuffer);
2314  //unbind framebuffer till render
2315  glBindFramebuffer(GL_FRAMEBUFFER, 0);
2316  }
2317 
2318  }
2319 
2320  }else{
2321  //GL_BACK stage
2322  self->ivport.W = width;
2323  self->ivport.H = height;
2324  }
2325 
2326 }
2327 
2328 
2329 typedef struct contenttype_texturegrid {
2330  tcontenttype t1;
2331  int nx, ny, nelements, nvert; //number of grid vertices
2332  GLushort *index; //winRT needs short
2333  GLfloat *vert, *vert2, *tex, *norm, dx, tx;
2334  float k1,xc; //optionally used during distort and pick for radial/barrel distortion
2335  int usingDistortions;
2336  GLuint textureID;
2338 
2339 void render_texturegrid(void *_self);
2340 void texturegrid_render(void *_self){
2341  contenttype *c, *self;
2342  self = (contenttype *)_self;
2343  pushnset_viewport(self->t1.viewport);
2344  c = self->t1.contents;
2345  if(c){
2346  //should be just the fbo texture to render
2347  if(c->t1.itype == CONTENT_STAGE){
2348  ivec4 ivport;
2349  Stack* vpstack;
2350  stage *s;
2351 
2352  s = (stage*)c;
2353  vpstack = (Stack*)gglobal()->Mainloop._vportstack;
2354  ivport = stack_top(ivec4,vpstack);
2355  if(s->ivport.W != ivport.W || s->ivport.H != ivport.H)
2356  stage_resize(c,ivport.W,ivport.H);
2357  c->t1.render(c);
2358  }
2359  }
2360  //render self last
2361  render_texturegrid(_self);
2362  popnset_viewport();
2363 }
2364 static GLfloat matrixIdentity[] = {
2365  1.0f, 0.0f, 0.0f, 0.0f,
2366  0.0f, 1.0f, 0.0f, 0.0f,
2367  0.0f, 0.0f, 1.0f, 0.0f,
2368  0.0f, 0.0f, 0.0f, 1.0f
2369 };
2370 int texturegrid_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex);
2371 contenttype *new_contenttype_texturegrid(int nx, int ny){
2372  contenttype_texturegrid *self = MALLOCV(sizeof(contenttype_texturegrid));
2373  register_contenttype(self);
2374  init_tcontenttype(&self->t1);
2375  self->t1.itype = CONTENT_TEXTUREGRID;
2376  self->t1.render = texturegrid_render;
2377  self->t1.pick = texturegrid_pick;
2378  self->k1 = 0.0f;
2379  self->xc = 0.0f;
2380  self->nx = nx;
2381  self->ny = ny;
2382  {
2383  //generate an nxn grid, of object size [-1,1]x[-1,1] = 2x2, complete with vertices, normals, texture coords and triangles
2384  int i,j,k; //,n;
2385  GLushort *index;
2386  GLfloat *vert, *vert2, *tex, *norm;
2387  GLfloat dx,dy, tx,ty;
2388  //n = p->ngridsize;
2389  index = (GLushort*)MALLOCV((nx-1)*(ny-1)*2*3 *sizeof(GLushort));
2390  vert = (GLfloat*)MALLOCV(nx*ny*3*sizeof(GLfloat));
2391  vert2 = (GLfloat*)MALLOCV(nx*ny*3*sizeof(GLfloat));
2392  tex = (GLfloat*)MALLOCV(nx*ny*2*sizeof(GLfloat));
2393  norm = (GLfloat*)MALLOCV(nx*ny*3*sizeof(GLfloat));
2394  register_contenttype(index);
2395  register_contenttype(vert);
2396  register_contenttype(vert2);
2397  register_contenttype(tex);
2398  register_contenttype(norm);
2399 
2400  //generate vertices
2401  dx = 2.0f / (float)(nx-1);
2402  dy = 2.0f / (float)(ny-1);
2403  tx = 1.0f / (float)(nx-1);
2404  ty = 1.0f / (float)(ny-1);
2405  for(i=0;i<nx;i++)
2406  for(j=0;j<ny;j++){
2407  vert[(i*nx + j)*3 + 0] = -1.0f + j*dy;
2408  vert[(i*nx + j)*3 + 1] = -1.0f + i*dx;
2409  vert[(i*nx + j)*3 + 2] = 0.0f;
2410  tex[(i*nx + j)*2 + 0] = 0.0f + j*ty;
2411  tex[(i*nx + j)*2 + 1] = 0.0f + i*tx;
2412  norm[(i*nx + j)*3 + 0] = 0.0f;
2413  norm[(i*nx + j)*3 + 1] = 0.0f;
2414  norm[(i*nx + j)*3 + 2] = 1.0f;
2415  }
2416 
2417 
2418  //generate triangle indices
2419  k = 0;
2420  for(i=0;i<nx-1;i++)
2421  for(j=0;j<ny-1;j++){
2422  //first triangle
2423  index[k++] = i*nx + j;
2424  index[k++] = i*nx + j + 1;
2425  index[k++] = (i+1)*nx + j + 1;
2426  //second triangle
2427  index[k++] = i*nx + j;
2428  index[k++] = (i+1)*nx + j + 1;
2429  index[k++] = (i+1)*nx + j;
2430  }
2431  self->index = index;
2432  self->norm = norm;
2433  self->tex = tex;
2434  self->vert = vert;
2435  self->vert2 = vert2;
2436  self->nelements = k;
2437  self->nvert = nx*ny;
2438  //copy standard vertices unmodified to vert2 which is used in render
2439  for(i=0;i<self->nvert;i++){
2440  self->vert2[i*3 +0] = self->vert[i*3 +0]; //x
2441  self->vert2[i*3 +1] = self->vert[i*3 +1]; //y
2442  self->vert2[i*3 +2] = self->vert[i*3 +2]; //z
2443  }
2444  }
2445  return (contenttype*)self;
2446 }
2447 void texturegrid_barrel_distort(void *_self, float k1){
2449  self = (contenttype_texturegrid *)_self;
2450  //Modify your vertices here for weird things
2451  if(1){
2452  //barrel distortion used for googleCardboard-like devices with magnifying glass per eye
2453  int i;
2454  for(i=0;i<self->nvert;i++){
2455  float radius2, x, y;
2456  x = self->vert[i*3 +0]; //go back to original coords
2457  y = self->vert[i*3 +1];
2458  radius2 = x*x + y*y;
2459  self->vert2[i*3 +0] = x*(1.0f - k1*radius2);
2460  self->vert2[i*3 +1] = y*(1.0f - k1*radius2);
2461  }
2462  self->k1 = k1;
2463  self->xc = 0.0f;
2464  }
2465 
2466  if(0){
2467  //some other example distortions, not used here
2468  float aspect, scale, xshift, yshift;
2469  int i;
2470  aspect = 1.0; //we'll do window aspect ratio below, using projectionMatrix
2471  xshift = 0.0;
2472  yshift = 0.0;
2473  scale = 1.0; //window coords go from -1 to 1 in x and y, and so do our lazyvert
2474 
2475  for(i=0;i<self->nvert;i++){
2476  self->vert2[i*3 +0] += xshift; //x .0355 empirical
2477  self->vert2[i*3 +1] += yshift; //y .04 empirical
2478  self->vert2[i*3 +0] *= 1.0; //x
2479  self->vert2[i*3 +1] *= aspect; //y
2480  self->vert2[i*3 +0] *= scale; //x
2481  self->vert2[i*3 +1] *= scale; //y
2482  self->vert2[i*3 +2] = self->vert[i*3 +2]; //z
2483  }
2484  }
2485 
2486 
2487 }
2488 void texturegrid_barrel_distort2(void *_self, float xc, float k1){
2489  //xc - fiducial center as .% from left
2490  // so radial/barrel distortion is centered on fiducial to counteract magnifying glass barrel distortion in googleCardboard lens
2491  // this function needs to be called during/just after every adjustment to screendist eyebase
2493  self = (contenttype_texturegrid *)_self;
2494  //Modify your vertices here for weird things
2495  if(1){
2496  //barrel distortion used for googleCardboard-like devices with magnifying glass per eye
2497  int i;
2498  float xc2 = xc * 2.0f - 1.0f; // convert from .% * [0 to 1] to [-1 to 1]
2499  for(i=0;i<self->nvert;i++){
2500  float radius2, x, y;;
2501  x = self->vert[i*3 +0]; //go back to original coords
2502  y = self->vert[i*3 +1];
2503  radius2 = (x-xc2)*(x-xc2) + y*y;
2504  self->vert2[i*3 +0] = x*(1.0f - k1*radius2);
2505  self->vert2[i*3 +1] = y*(1.0f - k1*radius2);
2506  }
2507  self->k1 = k1;
2508  self->xc = xc2;
2509  self->usingDistortions = TRUE;
2510  }
2511 }
2512 void texturegrid_barrel_undistort2(void *_self, ivec4 vport, ivec2 *xy){
2513  //There are a few ways to back-transform/transform-backward (screen to scene) with texture grid:
2514  //1. don't - instead rely on cursor drawn in model space from untransformed mouse
2515  //2. bilinear interpolation using 2 more grids, one for x lookup, one for y lookup
2516  //3. iteration of grid lookup going the other way
2517  //4. iteration of original analytical distortion parameters -ie k1, xc
2518  //each has pros and cons
2519 
2521  float x,y,radius2;
2522  self = (contenttype_texturegrid *)_self;
2523  x = (float)(xy->X - vport.X) / (float)vport.W;
2524  y = (float)(xy->Y - vport.Y) / (float)vport.H;
2525  x = x*2.0f - 1.0f; //convert from 0-1 to -1 to 1
2526  y = y*2.0f - 1.0f;
2527  if(1){
2528  //4. iterate using original analytical parameters and transform
2529  int i;
2530  float xb, yb, xa,ya, deltax, deltay, xc2, k1, tolerance;
2531  xb = x; //initial guess: our mouse cursor position
2532  yb = y;
2533  xc2 = self->xc;
2534  k1 = self->k1;
2535  tolerance = .001f; //in [-1 to 1] .%
2536  for(i=0;i<10;i++){
2537  radius2 = (xb-xc2)*(xb-xc2) + yb*yb;
2538  xa = xb*(1.0f - k1*radius2); //transform our guess forward (from scene to screen)
2539  ya = yb*(1.0f - k1*radius2);
2540  deltax = x - xa; //delta: how much we missed our mouse cursor by
2541  deltay = y - ya;
2542  xb += deltax; //next guess: old guess + delta
2543  yb += deltay;
2544  if(fabs(deltax) + fabs(deltay) < tolerance )break;
2545  }
2546  x = xb;
2547  y = yb;
2548  }
2549  x = (x + 1.0f)*.5f; //convert from -1 to 1 to 0-1
2550  y = (y + 1.0f)*.5f;
2551  xy->X = (int)(x*vport.W) + vport.X;
2552  xy->Y = (int)(y*vport.H) + vport.Y;
2553 }
2554 int texturegrid_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
2555  //convert windoow to fbo
2556  int iret;
2557  contenttype *c;
2559 
2560  self = (contenttype_texturegrid *)_self;
2561  iret = 0;
2562  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
2563  ivec4 ivport;
2564  int x,y;
2565  ivec2 xy;
2566  //ttglobal tg = gglobal();
2567  //ivport = stack_top(ivec4,(Stack*)tg->Mainloop._vportstack);
2568  ivport = get_current_viewport();
2569  //fbo = window - viewport
2570  //x = mouseX;
2571  //y = mouseY;
2572  x = mouseX; // - ivport.X;
2573  y = mouseY; // - ivport.Y;
2574  xy.X = x;
2575  xy.Y = y;
2576  if(self->usingDistortions) texturegrid_barrel_undistort2(self, ivport, &xy );
2577  x = xy.X;
2578  y = xy.Y;
2579  c = self->t1.contents;
2580  while(c){
2581  iret = c->t1.pick(c,mev,butnum,x,y,ID, windex);
2582  if(iret > 0) break; //handled
2583  c = c->t1.next;
2584  }
2585  pop_viewport();
2586  }
2587  return iret;
2588 }
2589 #include "../scenegraph/Component_Shape.h"
2590 void render_texturegrid(void *_self){
2592  int useMip,haveTexture;
2593  //float aspect, scale, xshift, yshift;
2594  GLint positionLoc, texCoordLoc, textureLoc;
2595  GLint textureMatrix0;
2596  GLuint textureID;
2598  self = (contenttype_texturegrid *)_self;
2599 
2600  haveTexture = FALSE;
2601  if(self->t1.contents && self->t1.contents->t1.itype == CONTENT_STAGE){
2602  stage *s = (stage*)self->t1.contents;
2603  if(s->type == STAGETYPE_FBO){
2604  textureID = s->itexturebuffer;
2605  haveTexture = TRUE;
2606  }
2607  }
2608  if(!haveTexture) return; //nothing worth drawing - could do a X texture
2609  //now we load our textured geometry plane/grid to render it
2610 
2611  FW_GL_DEPTHMASK(GL_FALSE);
2612  glDisable(GL_DEPTH_TEST);
2613 
2614 //>>onResize
2615  //Standard vertex process - both sides get this:
2616  //for(i=0;i<self->nvert;i++){
2617  // self->vert2[i*3 +0] = self->vert[i*3 +0]; //x
2618  // self->vert2[i*3 +1] = self->vert[i*3 +1]; //y
2619  // self->vert2[i*3 +2] = self->vert[i*3 +2]; //z
2620  //}
2621 
2622  /*
2623  //Modify your vertices here for weird things, depending on side
2624  aspect = 1.0; //we'll do window aspect ratio below, using projectionMatrix
2625  xshift = 0.0;
2626  yshift = 0.0;
2627  scale = 1.0; //window coords go from -1 to 1 in x and y, and so do our lazyvert
2628 
2629  for(i=0;i<self->nvert;i++){
2630  self->vert2[i*3 +0] += xshift; //x .0355 empirical
2631  self->vert2[i*3 +1] += yshift; //y .04 empirical
2632  self->vert2[i*3 +0] *= 1.0; //x
2633  self->vert2[i*3 +1] *= aspect; //y
2634  self->vert2[i*3 +0] *= scale; //x
2635  self->vert2[i*3 +1] *= scale; //y
2636  self->vert2[i*3 +2] = self->vert[i*3 +2]; //z
2637  }
2638  */
2639 
2640 // //standard vertex process - both sides get this:
2641 // //scale = tg->display.screenRatio;
2642 // scale = 1.0f; // 4.0f/3.0f;
2643 // for(i=0;i<self->nvert;i++){
2644 // self->vert2[i*3 +0] *= scale; //x
2645 // self->vert2[i*3 +1] *= scale; //y
2646 // }
2648 
2649  //use FW shader pipeline
2650  //we'll use a simplified shader -same one we use for DrawCursor- that
2651  //skips all the fancy lighting and material, and just shows texture as diffuse material
2652  scap = getMyShader(ONE_TEX_APPEARANCE_SHADER);
2653  enableGlobalShader(scap);
2654  positionLoc = scap->Vertices;
2655  glVertexAttribPointer (positionLoc, 3, GL_FLOAT,
2656  GL_FALSE, 0, self->vert2 );
2657  // Load the texture coordinate
2658  texCoordLoc = scap->TexCoords[0];
2659  glVertexAttribPointer ( texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, self->tex );
2660  glEnableVertexAttribArray (positionLoc );
2661  glEnableVertexAttribArray ( texCoordLoc);
2662 
2663  // Bind the base map - see above
2664  glActiveTexture ( GL_TEXTURE0 );
2665  glBindTexture ( GL_TEXTURE_2D, textureID );
2666  useMip = 0;
2667  if(useMip)
2668  glGenerateMipmap(GL_TEXTURE_2D);
2669 
2670 
2671  // Set the base map sampler to texture unit to 0
2672  textureLoc = scap->TextureUnit[0];
2673  textureMatrix0 = scap->TextureMatrix[0];
2674  glUniformMatrix4fv(textureMatrix0, 1, GL_FALSE, matrixIdentity);
2675 
2676  glUniform1i ( textureLoc, 0 );
2677  //window coordinates natively go from -1 to 1 in x and y
2678  //but usually the window is rectangular, so to draw a perfect square
2679  //you need to scale the coordinates differently in x and y
2680 
2681  glUniformMatrix4fv(scap->ProjectionMatrix, 1, GL_FALSE, matrixIdentity);
2682 
2683  glUniformMatrix4fv(scap->ModelViewMatrix, 1, GL_FALSE, matrixIdentity); //matrix90); //
2684 
2685  if(0){
2686  glDrawArrays(GL_TRIANGLES,0,self->nelements);
2687  }else{
2688  glDrawElements(GL_TRIANGLES,self->nelements,GL_UNSIGNED_SHORT,self->index);
2689  }
2690 
2691  FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
2692  FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
2693 
2694  restoreGlobalShader();
2695  FW_GL_DEPTHMASK(GL_TRUE);
2696  glEnable(GL_DEPTH_TEST);
2697 
2698  return;
2699 }
2700 
2701 
2702 typedef struct contenttype_orientation {
2703  tcontenttype t1;
2704  int nx, ny, nelements, nvert; //number of grid vertices
2705  GLushort *index; //winRT needs short
2706  GLfloat *vert, *vert2, *tex, *norm, dx, tx;
2707  GLuint textureID;
2709 
2710 void render_orientation(void *_self);
2711 void orientation_render(void *_self){
2712  contenttype *c, *self;
2713  self = (contenttype *)_self;
2714  pushnset_viewport(self->t1.viewport);
2715  c = self->t1.contents;
2716  if(c){
2717  //should be just the fbo texture to render
2718  if(c->t1.itype == CONTENT_STAGE){
2719  ivec4 ivport;
2720  int fbowidth,fboheight;
2721  Stack* vpstack;
2722  stage *s;
2723  ttglobal tg = gglobal();
2724 
2725  s = (stage*)c;
2726  vpstack = (Stack*)tg->Mainloop._vportstack;
2727  ivport = stack_top(ivec4,vpstack);
2728  switch(tg->Mainloop.screenOrientation2){
2729  case 90:
2730  case 270:
2731  //portrait
2732  fbowidth = ivport.H;
2733  fboheight = ivport.W;
2734  break;
2735  case 0:
2736  case 180:
2737  default:
2738  //landscape
2739  fbowidth = ivport.W;
2740  fboheight = ivport.H;
2741  break;
2742  }
2743 
2744  if(s->ivport.W != fbowidth || s->ivport.H != fboheight)
2745  stage_resize(c,fbowidth,fboheight);
2746  c->t1.render(c);
2747  }
2748  }
2749  //render self last
2750  render_orientation(_self);
2751  popnset_viewport();
2752 }
2753 
2754 
2755 int orientation_pick(void *_self, int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
2756  //generic render for intermediate level content types (leaf/terminal content types will have their own render())
2757  int iret;
2758  contenttype *c, *self;
2759 
2760  self = (contenttype *)_self;
2761  iret = 0;
2762  if(checknpush_viewport(self->t1.viewport,mouseX,mouseY)){
2763  ivec4 ivport;
2764  int x,y;
2765  ttglobal tg = gglobal();
2766  ivport = stack_top(ivec4,(Stack*)tg->Mainloop._vportstack);
2767  switch(tg->Mainloop.screenOrientation2){
2768  case 90:
2769  x = ivport.H - mouseY;
2770  y = mouseX;
2771  break;
2772  case 180:
2773  x = ivport.W - mouseX;
2774  y = ivport.H - mouseY;
2775  break;
2776  case 270:
2777  x = mouseY;
2778  y = ivport.W - mouseX;
2779  break;
2780  case 0:
2781  case 360:
2782  default:
2783  //landscape
2784  x = mouseX;
2785  y = mouseY;
2786  break;
2787  }
2788  c = self->t1.contents;
2789  while(c){
2790  iret = c->t1.pick(c,mev,butnum,x,y,ID, windex);
2791  if(iret > 0) break; //handled
2792  c = c->t1.next;
2793  }
2794  pop_viewport();
2795  }
2796  return iret;
2797 }
2798 void render_orientation(void *_self);
2799 //our grid generator goes columnwise. To draw we need indexes.
2800 //1--3
2801 //| /|
2802 //0--2
2803 GLfloat quad1Vert[] = {
2804  -1.0f, -1.0f, 0.0f,
2805  -1.0f, 1.0f, 0.0f,
2806  1.0f, -1.0f, 0.0f,
2807  1.0f, 1.0f, 0.0f,
2808 };
2809 GLfloat quad1Tex[] = {
2810  0.0f, 0.0f,
2811  0.0f, 1.0f,
2812  1.0f, 0.0f,
2813  1.0f, 1.0f,
2814 };
2815 GLushort quad1TriangleInd[] = {
2816  0, 1, 3, 3, 2, 0
2817 };
2818 
2819 contenttype *new_contenttype_orientation(){
2820  contenttype_orientation *self = MALLOCV(sizeof(contenttype_orientation));
2821  register_contenttype(self);
2822  init_tcontenttype(&self->t1);
2823  self->t1.itype = CONTENT_ORIENTATION;
2824  self->t1.render = orientation_render;
2825  self->t1.pick = orientation_pick;
2826  self->nx = 2;
2827  self->ny = 2;
2828 
2829  //simple 2-triangle square
2830  self->vert = quad1Vert;
2831  self->tex = quad1Tex;
2832  self->index = quad1TriangleInd;
2833  self->nelements = 6;
2834  self->nvert = 6;
2835 
2836 
2837  return (contenttype*)self;
2838 }
2839 static GLfloat matrix180[] = {
2840  -1.f, 0.0f, 0.0f, 0.0f,
2841  0.0f,-1.0f, 0.0f, 0.0f,
2842  0.0f, 0.0f, 1.0f, 0.0f,
2843  0.0f, 0.0f, 0.0f, 1.0f
2844 };
2845 static GLfloat matrix270[] = {
2846  0.0f, 1.0f, 0.0f, 0.0f,
2847  -1.f, 0.0f, 0.0f, 0.0f,
2848  0.0f, 0.0f, 1.0f, 0.0f,
2849  0.0f, 0.0f, 0.0f, 1.0f
2850 };
2851 static GLfloat matrix90[] = {
2852  0.0f, -1.0f, 0.0f, 0.0f,
2853  1.0f, 0.0f, 0.0f, 0.0f,
2854  0.0f, 0.0f, 1.0f, 0.0f,
2855  0.0f, 0.0f, 0.0f, 1.0f
2856 };
2857 
2858 
2859 unsigned int getCircleCursorTextureID();
2860 void render_orientation(void *_self){
2862  int haveTexture;
2863  GLint positionLoc, texCoordLoc, textureLoc;
2864  GLint textureMatrix0;
2865  GLuint textureID;
2866  float *orientationMatrix;
2868  self = (contenttype_orientation *)_self;
2869 
2870  haveTexture = FALSE;
2871  if(self->t1.contents && self->t1.contents->t1.itype == CONTENT_STAGE){
2872  stage *s = (stage*)self->t1.contents;
2873  if(s->type == STAGETYPE_FBO){
2874 
2875  textureID = s->itexturebuffer;
2876  //for testing when fbo isn't working (give it a known texture):
2877  //if(0) textureID = getCircleCursorTextureID();
2878  haveTexture = TRUE;
2879  }
2880  }
2881  if(!haveTexture)
2882  return; //nothing worth drawing - could do a X texture
2883  //now we load our textured geometry plane/grid to render it
2884 
2885  switch(gglobal()->Mainloop.screenOrientation2){
2886  case 180: //landscape to upsidedown
2887  orientationMatrix = matrix180;
2888  break;
2889  case 270: //portrait upsidedown
2890  orientationMatrix = matrix270;
2891  break;
2892  case 90: //portrait upsideright
2893  orientationMatrix = matrix90;
2894  break;
2895  case 0: //landscape upsideright
2896  case 360:
2897  default:
2898  //landscape
2899  orientationMatrix = matrixIdentity;
2900  break;
2901  }
2902 
2903  FW_GL_DEPTHMASK(GL_FALSE);
2904  glDisable(GL_DEPTH_TEST);
2905 
2906 
2907  //use FW shader pipeline
2908  //we'll use a simplified shader -same one we use for DrawCursor- that
2909  //skips all the fancy lighting and material, and just shows texture as diffuse material
2910  scap = getMyShader(ONE_TEX_APPEARANCE_SHADER);
2911  enableGlobalShader(scap);
2912  positionLoc = scap->Vertices;
2913  glVertexAttribPointer (positionLoc, 3, GL_FLOAT,
2914  GL_FALSE, 0, self->vert );
2915  // Load the texture coordinate
2916  texCoordLoc = scap->TexCoords[0];
2917  glVertexAttribPointer ( texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, self->tex );
2918  glEnableVertexAttribArray (positionLoc );
2919  glEnableVertexAttribArray ( texCoordLoc);
2920 
2921  // Bind the base map - see above
2922  glActiveTexture ( GL_TEXTURE0 );
2923  glBindTexture ( GL_TEXTURE_2D, textureID );
2924 
2925  // Set the base map sampler to texture unit to 0
2926  textureLoc = scap->TextureUnit[0];
2927  textureMatrix0 = scap->TextureMatrix[0];
2928  glUniformMatrix4fv(textureMatrix0, 1, GL_FALSE, matrixIdentity);
2929 
2930  glUniform1i ( textureLoc, 0 );
2931  //window coordinates natively go from -1 to 1 in x and y
2932  //but usually the window is rectangular, so to draw a perfect square
2933  //you need to scale the coordinates differently in x and y
2934 
2935  glUniformMatrix4fv(scap->ProjectionMatrix, 1, GL_FALSE, matrixIdentity);
2936  glUniformMatrix4fv(scap->ModelViewMatrix, 1, GL_FALSE, orientationMatrix); //matrix90); //
2937 
2938  //desktop glew, angleproject and winRT can do this:
2939  glDrawElements(GL_TRIANGLES, self->nelements, GL_UNSIGNED_SHORT, self->index);// winRT needs GLushort indexes, can't do GL_QUADS
2940 
2941 
2942  FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
2943  FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
2944 
2945  restoreGlobalShader();
2946  FW_GL_DEPTHMASK(GL_TRUE);
2947  glEnable(GL_DEPTH_TEST);
2948 
2949  return;
2950 }
2951 
2952 
2953 
2954 
2955 int frame_increment_even_odd_frame_count(int ieo){
2956  ieo++;
2957  ieo = ieo > 1 ? 0 : 1;
2958  return ieo;
2959 }
2960 
2961 typedef struct targetwindow {
2962  contenttype *stage;
2963  //a target is a window. For example you could have an HMD as one target,
2964  //and desktop screen window as another target, both rendered to on the same frame
2965  void *hwnd; //window handle
2966  BOOL swapbuf; //true if we should swapbuffer on the target
2967  ivec4 ivport; //sub-area of window we are targeting left, width, bottom, height
2968  freewrl_params_t params; //will have gl context switching parameters
2969  struct targetwindow *next;
2970 } targetwindow;
2971 void init_targetwindow(void *_self){
2972  targetwindow *self = (targetwindow *)_self;
2973  self->stage = NULL;
2974  self->next = NULL;
2975  self->swapbuf = TRUE;
2976  self->hwnd = NULL;
2977 }
2978 
2979 //int syntax_test_function(){
2980 // targetwindow w;
2981 // return w.t1.itype;
2982 //}
2983 
2984 
2985 //<<<<=====NEW==Nov27,2015=========
2986 struct pedal_state {
2987  int x,y;
2988  int rx,ry;
2989  int isDown;
2990  int initialized;
2991 };
2992 
2993 typedef struct pMainloop{
2994  //browser
2995  /* are we displayed, or iconic? */
2996  int onScreen;// = TRUE;
2997 
2998  /* do we do event propagation, proximity calcs?? */
2999  int doEvents;// = FALSE;
3000 
3001  #ifdef VERBOSE
3002  char debs[300];
3003  #endif
3004 
3005  char* PluginFullPath;
3006  //
3007  int num_SensorEvents;// = 0;
3008 
3009  /* Viewport data */
3010  GLint viewPort2[10];
3011  GLint viewpointScreenX[2], viewpointScreenY[2]; /*for stereo where we can adjust the viewpoint position on the screen */
3012  /* screen width and height. */
3013 
3014  int maxbuffers;// = 1; /* how many active indexes in bufferarray*/
3015  int bufferarray[2];// = {GL_BACK,0};
3016 
3017  double BrowserStartTime; /* start of calculating FPS */
3018  double BrowserInitTime; /* time of first frame */
3019 
3020  //int quitThread;// = FALSE;
3021  int keypress_wait_for_settle;// = 100; /* JAS - change keypress to wait, then do 1 per loop */
3022  char * keypress_string;//=NULL; /* Robert Sim - command line key sequence */
3023 
3024  struct SensStruct *SensorEvents;// = 0;
3025 
3026  unsigned int loop_count;// = 0;
3027  unsigned int once;
3028  unsigned int slowloop_count;// = 0;
3029  //scene
3030  //window
3031  //2D_inputdevice
3032  int lastDeltax;// = 50;
3033  int lastDeltay;// = 50;
3034  int lastxx;
3035  int lastyy;
3036  int ntouch;// =0;
3037  unsigned int currentTouch;// = -1;
3038  struct Touch touchlist[20];
3039  int EMULATE_MULTITOUCH;// = 1;
3040 
3041  FILE* logfile;
3042  FILE* logerr;
3043  char* logfname;
3044  int logging;
3045  int keySensorMode;
3046  int draw_initialized;
3047  int keywait;
3048  char keywaitstring[25];
3049  int fps_sleep_remainder;
3050  double screenorientationmatrix[16];
3051  double viewtransformmatrix[16];
3052  double posorimatrix[16];
3053  double stereooffsetmatrix[2][16];
3054  int targets_initialized;
3055  targetwindow cwindows[4];
3056  void *hyper_switch[4];
3057  int hyper_case[4];
3058  int nwindow;
3059  int windex; //current window index into twoindows array, valid during render()
3060  Stack *_vportstack;
3061  Stack *_stagestack;
3062  Stack *_framebufferstack;
3063  struct Vector *contenttype_registry;
3064  int mouseDown;
3065  int mouseOver;
3066  struct pedal_state pedalstate;
3067 }* ppMainloop;
3068 void *Mainloop_constructor(){
3069  void *v = MALLOCV(sizeof(struct pMainloop));
3070  memset(v,0,sizeof(struct pMainloop));
3071  return v;
3072 }
3073 void Mainloop_init(struct tMainloop *t){
3074  //public
3075  /* linewidth for lines and points - passed in on command line */
3076  t->gl_linewidth= 1.0f;
3077  //t->TickTime;
3078  //t->lastTime;
3079  t->BrowserFPS = 100.0; /* calculated FPS */
3080  t->BrowserSpeed = 0.0; /* calculated movement speed */
3081  t->BrowserDescription = "libfreewrl opensource virtual reality player library";
3082  t->trisThisLoop = 0;
3083 
3084  /* what kind of file was just parsed? */
3085  t->currentFileVersion = 0;
3086  /* do we have some sensitive nodes in scene graph? */
3087  t->HaveSensitive = FALSE;
3088  //t->currentX[20];
3089  //t->currentY[20]; /* current mouse position.*/
3090  t->clipPlane = 0;
3091 
3092  t->tmpFileLocation = MALLOC(char *, 5);
3093  strcpy(t->tmpFileLocation,"/tmp");
3094  t->replaceWorldRequest = NULL;
3095  t->replaceWorldRequestMulti = NULL;
3096  //private
3097  t->prv = Mainloop_constructor();
3098  {
3099  ppMainloop p = (ppMainloop)t->prv;
3100  int i;
3101  //browser
3102  /* are we displayed, or iconic? */
3103  p->onScreen = TRUE;
3104 
3105  /* do we do event propagation, proximity calcs?? */
3106  p->doEvents = FALSE;
3107 
3108  #ifdef VERBOSE
3109  //static char debs[300];
3110  #endif
3111 
3112  //char* PluginFullPath;
3113  p->num_SensorEvents = 0;
3114 
3115  p->maxbuffers = 1; /* how many active indexes in bufferarray*/
3116  p->bufferarray[0] = FW_GL_BACK;
3117  p->bufferarray[1] = 0;
3118  /* current time and other time related stuff */
3119  //p->BrowserStartTime; /* start of calculating FPS */
3120  p->BrowserInitTime = 0.0; /* time of first frame */
3121 
3122  //p->quitThread = FALSE;
3123  p->keypress_wait_for_settle = 100; /* JAS - change keypress to wait, then do 1 per loop */
3124  p->keypress_string=NULL; /* Robert Sim - command line key sequence */
3125 
3126  p->SensorEvents = 0;
3127 
3128  p->loop_count = 0;
3129  p->slowloop_count = 0;
3130  p->once = 0;
3131 
3132  //scene
3133  //window
3134  //2D_inputdevice
3135  p->lastDeltax = 50;
3136  p->lastDeltay = 50;
3137  //p->lastxx;
3138  //p->lastyy;
3139  p->ntouch =20;
3140  p->currentTouch = 0; //-1;
3141  //p->touchlist[20];
3142  p->EMULATE_MULTITOUCH = 0;
3143  memset(p->touchlist,0,20*sizeof(struct Touch));
3144  // .inUse flag 0 //for(i=0;i<p->ntouch;i++) p->touchlist[i].ID = -1;
3145 
3146  p->logfile = NULL;
3147  p->logerr = NULL;
3148  p->logfname = NULL;
3149  p->logging = 0;
3150  p->keySensorMode = 1; //by default on, so it works 'out of the gate' if Key or StringSensor in scene, then ESC to toggle off
3151  p->draw_initialized = FALSE;
3152  p->keywait = FALSE;
3153  p->keywaitstring[0] = (char)0;
3154  p->fps_sleep_remainder = 0;
3155  p->nwindow = 1;
3156  p->windex = 0;
3157  p->targets_initialized = 0;
3158  for(i=0;i<4;i++) init_targetwindow(&p->cwindows[i]);
3159  //t->twindows = p->twindows;
3160  p->_vportstack = newStack(ivec4);
3161  t->_vportstack = (void *)p->_vportstack; //represents screen pixel area being drawn to
3162  p->_stagestack = newStack(void*);
3163  t->_stagestack = (void *)p->_stagestack; //represents screen pixel area being drawn to
3164  p->_framebufferstack = newStack(int);
3165  t->_framebufferstack = (void*)p->_framebufferstack;
3166  stack_push(int,p->_framebufferstack,FW_GL_BACK);
3167  p->contenttype_registry = NULL;
3168  p->mouseDown = 0;
3169  p->mouseOver = 0;
3170  memset(&p->pedalstate,0,sizeof(struct pedal_state));
3171  }
3172 }
3173 void Mainloop_clear(struct tMainloop *t){
3174  FREE_IF_NZ(t->scene_name);
3175  FREE_IF_NZ(t->scene_suff);
3176  FREE_IF_NZ(t->replaceWorldRequest);
3177  FREE_IF_NZ(t->tmpFileLocation);
3178  {
3179  ppMainloop p = (ppMainloop)t->prv;
3180  FREE_IF_NZ(p->SensorEvents);
3181  deleteVector(ivec4,p->_vportstack);
3182  deleteVector(void*,p->_stagestack);
3183  deleteVector(int,p->_framebufferstack);
3184  free_contenttypes();
3185  deleteVector(contenttype*,p->contenttype_registry);
3186  }
3187 }
3188 
3189 //call hwnd_to_windex in frontend window creation and event handling,
3190 //to convert to more convenient int index.
3191 int getWindex(){
3192  ppMainloop p;
3193  ttglobal tg = gglobal();
3194  p = (ppMainloop)tg->Mainloop.prv;
3195  return p->windex;
3196 }
3197 int fwl_hwnd_to_windex(void *hWnd){
3198  int i;
3199  targetwindow *targets;
3200  ppMainloop p;
3201  ttglobal tg = gglobal();
3202  p = (ppMainloop)tg->Mainloop.prv;
3203 
3204  targets = (targetwindow*)p->cwindows;
3205  for(i=0;i<4;i++){
3206  //the following line assume hwnd is never natively null or 0
3207  if(!targets[i].hwnd){
3208  //not found, create
3209  targets[i].hwnd = hWnd;
3210  targets[i].swapbuf = TRUE;
3211  }
3212  if(targets[i].hwnd == hWnd) return i;
3213  }
3214  return 0;
3215 }
3216 
3217 void get_view_matrix(double *savePosOri, double *saveView) {
3218  //iq 2 3
3219  // 0 1
3220  //no comprendo - por que no veo difference.
3221  bindablestack *bstack;
3222  // OLDCODE ppMainloop p;
3223  ttglobal tg = gglobal();
3224  // OLDCODE p = (ppMainloop)tg->Mainloop.prv;
3225 
3226  bstack = getActiveBindableStacks(tg);
3227  matcopy(saveView,bstack->viewtransformmatrix);
3228  matcopy(savePosOri,bstack->posorimatrix);
3229 
3230 }
3231 static void set_view_matrix(double *savePosOri,double *saveView){
3232  ppMainloop p;
3233  ttglobal tg = gglobal();
3234  p = (ppMainloop)tg->Mainloop.prv;
3235 
3236  if(0){
3237  matcopy(p->viewtransformmatrix,saveView);
3238  matcopy(p->posorimatrix,savePosOri);
3239  }else{
3240  bindablestack *bstack;
3241  bstack = getActiveBindableStacks(tg);
3242  matcopy(bstack->viewtransformmatrix,saveView);
3243  matcopy(bstack->posorimatrix,savePosOri);
3244  }
3245 
3246 }
3247 
3248 void fwl_getWindowSize(int *width, int *height){
3249  //call this one when in target rendering loop (and setScreenDim0()
3250  // has been called with targetwindow-specific dimensions)
3251  //the libfreewrl rendering loop should have setScreenDim0 to the appropriate values
3252  ttglobal tg = gglobal();
3253  *width = tg->display.screenWidth;
3254  *height = tg->display.screenHeight;
3255 }
3256 
3257 void fwl_getWindowSize1(int windex, int *width, int *height){
3258  //call this one when recieving window events, ie mouse events
3259  //windex: index (0-3, 0=default) of targetwindow the window event came in on
3260  ivec4 ivport;
3261  ppMainloop p;
3262  ttglobal tg = gglobal();
3263  p = (ppMainloop)tg->Mainloop.prv;
3264 
3265  ivport = p->cwindows[windex].ivport;
3266  *width = ivport.W;
3267  *height = ivport.H;
3268 }
3269 
3270 
3271 //true statics:
3272 int isBrowserPlugin = FALSE; //I can't think of a scenario where sharing this across instances would be a problem
3273 void fwl_set_emulate_multitouch(int ion){
3274  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
3275  p->EMULATE_MULTITOUCH = ion;
3276  //clear up for a fresh start when toggling emulation on/off
3277  //for(i=0;i<p->ntouch;i++)
3278  // p->touchlist[i].ID = -1;
3279  //p->touchlist[0].ID = 0;
3280 }
3281 int fwl_get_emulate_multitouch(){
3282  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
3283  return p->EMULATE_MULTITOUCH;
3284 }
3285 
3286 /*
3287  we want to run initialize() from the calling thread. NOTE: if
3288  initialize creates VRML/X3D nodes, it will call the ProdCon methods
3289  to do this, and these methods will check to see if nodes, yada,
3290  yada, yada, until we run out of stack. So, we check to see if we
3291  are initializing; if so, don't worry about checking for new scripts
3292  any scripts to initialize here? we do it here, because we may just
3293  have created new scripts during X3D/VRML parsing. Routing in the
3294  Display thread may have noted new scripts, but will ignore them
3295  until we have told it that the scripts are initialized. printf
3296  ("have scripts to initialize in fwl_RenderSceneUpdateScene old %d new
3297  %d\n",max_script_found, max_script_found_and_initialized);
3298 */
3299 
3300 /* we bind bindable nodes on parse in this thread */
3301 #define SEND_BIND_IF_REQUIRED(node) \
3302  if (node != NULL) { /* ConsoleMessage ("sendBind in render"); */ send_bind_to(X3D_NODE(node),1); node = NULL; }
3303 
3304 
3305 
3306 void setup_viewpoint();
3307 void set_viewmatrix();
3308 
3309 /* Function protos */
3310 static void sendDescriptionToStatusBar(struct X3D_Node *CursorOverSensitive);
3311 /* void fwl_do_keyPress(char kp, int type); Now in lib.h */
3312 void render_collisions(int Viewer_type);
3313 int slerp_viewpoint(int itype);
3314 static void render_pre(void);
3315 
3316 static int setup_pickside(int x, int y);
3317 void setup_projection();
3318 void setup_pickray(int x, int y);
3319 struct X3D_Node* getRayHit(void);
3320 void get_hyperhit(void);
3321 static void sendSensorEvents(struct X3D_Node *COS,int ev, int butStatus, int status);
3322 #if USE_OSC
3323 void activate_OSCsensors();
3324 #endif
3325 
3326 
3327 /* libFreeWRL_get_version()
3328 
3329  Q. where do I get this function ?
3330  A: look in Makefile.am (vtempl will create it automatically in internal_version.c).
3331 
3332 */
3333 
3334 
3335 #if !defined(_MSC_VER)
3336 
3337 /* Doug Sandens windows function; lets make it static here for non-windows */
3338 double Time1970sec(void) {
3339  struct timeval mytime;
3340  gettimeofday(&mytime, NULL);
3341  return (double) mytime.tv_sec + (double)mytime.tv_usec/1000000.0;
3342 }
3343 
3344 
3345 #endif
3346 
3347 
3348 #define DJ_KEEP_COMPILER_WARNING 0
3349 #if DJ_KEEP_COMPILER_WARNING
3350 #define TI(_tv) gettimeofdat(&_tv)
3351 #define TID(_tv) ((double)_tv.tv_sec + (double)_tv.tv_usec/1000000.0)
3352 #endif
3353 
3354 
3355 /* Main eventloop for FreeWRL!!! */
3356 void fwl_do_keyPress0(int key, int type);
3357 void handle0(const int mev, const unsigned int button, const float x, const float y);
3358 void fwl_handle_aqua_multi(const int mev, const unsigned int button, int x, int y, int ID, int windex);
3359 
3360 void fwl_RenderSceneUpdateScene0(double dtime);
3361 void splitpath_local_suffix(const char *url, char **local_name, char **suff);
3362 int vpGroupActive(struct X3D_ViewpointGroup *vp_parent);
3363 void fwl_gotoCurrentViewPoint()
3364 {
3365  struct tProdCon *t = &gglobal()->ProdCon;
3366 
3367  struct X3D_Node *cn;
3368  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get(struct X3D_Node*, t->viewpointNodes, t->currboundvpno),cn);
3369 
3370  /* printf ("NVP, %d of %d, looking at %d\n",ind, totviewpointnodes, t->currboundvpno);
3371  printf ("looking at node :%s:\n",X3D_VIEWPOINT(cn)->description->strptr); */
3372 
3373  if (cn && vpGroupActive((struct X3D_ViewpointGroup *) cn)) {
3374  t->setViewpointBindInRender = vector_get(struct X3D_Node*,t->viewpointNodes, t->currboundvpno);
3375  return;
3376  }
3377 }
3378 
3379 int fw_exit(int val)
3380 {
3381  printf("exiting with value=%d hit Enter:",val);
3382  getchar();
3383  exit(val);
3384 }
3385 
3386 void render_statusbar0(void){
3387  #if defined(STATUSBAR_HUD)
3388  /* status bar, if we have one */
3389  finishedWithGlobalShader();
3390  drawStatusBar(); // View update
3391  restoreGlobalShader();
3392  #endif
3393 }
3394 
3395 
3396 //static stage output_stages[2];
3397 //static int noutputstages = 2;
3398 //static contenttype contents[2];
3399 /*
3400 render_stage {
3401  content *data;
3402  //[set buffer ie if 1-buffer fbo technqiue]
3403  //push buffer viewport
3404  vport = childViewport(pvport,s->viewport);
3405  pushviewport(vportstack, vport);
3406  //[clear buffer]
3407  glClear(GL_DEPTH);
3408  data = s->data;
3409  while(data){ // scene [,sbh]
3410  //push content area viewport
3411  for view in views //for now, just vp, future [,side, top, front]
3412  push projection
3413  push / alter view matrix
3414  for eye in eyes
3415  [set buffer ie if 2-buffer fbo technique]
3416  push eye viewport
3417  push or alter view matrix
3418  render from last stage output to this stage output, applying stage-specific
3419  pop
3420  pop
3421  pop
3422  data = data->next;
3423  }
3424  popviewport(vportstack);
3425  setcurrentviewport(vportstack);
3426 }
3427 */
3428 
3429 void fwl_RenderSceneUpdateSceneNORMAL() {
3430  double dtime;
3431  ttglobal tg = gglobal();
3432  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3433 
3434  dtime = Time1970sec();
3435  fwl_RenderSceneUpdateScene0(dtime);
3436  /* actual rendering */
3437  if ( p->onScreen) {
3438  render();
3439  }
3440 
3441 }
3442 
3443 //viewport stuff - see Component_Layering.c
3444 ivec4 childViewport(ivec4 parentViewport, float *clipBoundary);
3445 
3446 //#ifdef MULTI_WINDOW
3447 /* MULTI_WINDOW is for desktop configurations where 2 or more windows are rendered to.
3448  - desktop (windows, linux, mac) only, because it relies on changing the opengl context,
3449  and those are desktop-platform-specific calls. GLES2 (opengl es 2)
3450  doesn't have those functions - assuming a single window - so can't do multi-window.
3451  - an example multi-window configuration:
3452  1) HMD (head mounted display + 2) supervisors screen
3453  - they would go through different stages, rendered to different size windows, different menuing
3454  - and different pickrays -a mouse for supervisor, orientation sensor for HMD
3455  Design Option: instead of #ifdef here, all configs could supply
3456  fv_swapbuffers() and fwChangeGlContext() functions in the front-end modules,
3457  including android/mobile/GLES2 which would stub or implement as appropriate
3458 */
3459 
3460 
3461 /*
3462 for targetwindow in targets //supervisor screen, HMD
3463  for stage in stages
3464  [set buffer ie if 1-buffer fbo technqiue]
3465  [clear buffer]
3466  push buffer viewport
3467  for content in contents // scene [,sbh]
3468  push content area viewport
3469  for view in views //for now, just vp, future [,side, top, front]
3470  push projection
3471  push / alter view matrix
3472  for eye in eyes
3473  [set buffer ie if 2-buffer fbo technique]
3474  push eye viewport
3475  push or alter view matrix
3476  render from last stage output to this stage output, applying stage-specific
3477  pop
3478  pop
3479  pop
3480  pop
3481  get final buffer, or swapbuffers
3482 something similar for pickrays, except pick() instead of render()
3483 - or pickrays (3 per target: [left, right, forehead/mono]), updated on same pass once per frame
3484 
3485 */
3486 
3487 
3488 void targetwindow_set_params(int itargetwindow, freewrl_params_t* params){
3489  targetwindow *twindows, *t;
3490  ttglobal tg = gglobal();
3491  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3492 
3493  twindows = p->cwindows;
3494  twindows[itargetwindow].params = *params;
3495  if(itargetwindow > 0){
3496  twindows[itargetwindow -1].next = &twindows[itargetwindow];
3497  }
3498  p->nwindow = max(p->nwindow,itargetwindow+1);
3499  if(0){
3500  t = twindows;
3501  while(t){
3502  printf("windex=%d t->next = %p\n",itargetwindow,t->next);
3503  t=t->next;
3504  }
3505  printf("hows that?\n");
3506  }
3507 }
3508 freewrl_params_t* targetwindow_get_params(int itargetwindow){
3509  targetwindow *twindows;
3510  ttglobal tg = gglobal();
3511  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3512 
3513  twindows = p->cwindows;
3514  return &twindows[itargetwindow].params;
3515 }
3516 
3517 void fwl_setScreenDim1(int wi, int he, int itargetwindow){
3518  targetwindow *twindows;
3519  ivec4 window_rect;
3520  ttglobal tg = gglobal();
3521  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3522 
3523  window_rect.X = 0;
3524  window_rect.Y = 0;
3525  window_rect.W = wi;
3526  window_rect.H = he;
3527 
3528  twindows = p->cwindows;
3529  twindows[itargetwindow].ivport = window_rect;
3530  //the rest is initialized in the target rendering loop, via fwl_setScreenDim(w,h)
3531 }
3532 
3533 
3534 //=====NEW====>>>
3535 //register contenttypes for automatic freeing at end of run
3536 void register_contenttype(void *ct){
3537  ttglobal tg = gglobal();
3538  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3539  if(!p->contenttype_registry)
3540  p->contenttype_registry = newVector(void *,4);
3541  //if(p->contenttype_registry->n >= p->contenttype_registry->allocn){
3542  // int nalloc = upper_power_of_two(p->contenttype_registry->n + 1);
3543  // p->contenttype_registry->data = REALLOC(p->contenttype_registry->data,nalloc);
3544  // p->contenttype_registry->allocn = nalloc;
3545  //}
3546  vector_pushBack(void*,p->contenttype_registry,ct);
3547 
3548 }
3549 void free_contenttypes(){
3550  ttglobal tg = gglobal();
3551  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3552  if(p->contenttype_registry){
3553  int i;
3554  for(i=0;i<p->contenttype_registry->n;i++){
3555  void *ct;
3556  ct = vector_get(void*,p->contenttype_registry,i);
3557  FREE_IF_NZ(ct);
3558  }
3559  }
3560  //the vector itself will be freed by caller with deleteVector
3561 }
3562 
3563 
3564 
3565 void setup_stagesNORMAL(){
3566  int i;
3567  //OLDCODE targetwindow *twindows;
3568  targetwindow *t;
3569  ttglobal tg = gglobal();
3570  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3571 
3572  //OLDCODE twindows = p->cwindows;
3573  //t = twindows;
3574  //while(t){
3575  for(i=0;i<p->nwindow;i++){
3576  contenttype *cstage, *cswitch, **last; //*cscene, *csbh, *cmultitouch, *cstagefbo, *ctexturegrid, *corientation, *cquadrant;
3577  freewrl_params_t *dp;
3578  //ii = p->nwindow - i -1; //reverse order for experiment
3579  t=&p->cwindows[i];
3580 
3581  //FBOs must be created in the opengl window context where they are going to be used as texture
3582  dp = (freewrl_params_t*)tg->display.params;
3583  if(t->params.context != dp->context){
3584  tg->display.params = (void*)&t->params;
3585  fv_change_GLcontext((freewrl_params_t*)tg->display.params);
3586  //printf("%ld %ld %ld\n",t->params.display,t->params.context,t->params.surface);
3587  }
3588 
3589  cstage = new_contenttype_stage();
3590  cswitch = new_contenttype_switch();
3591  p->hyper_switch[i] = cswitch;
3592  cstage->t1.contents = cswitch;
3593  last = &cswitch->t1.contents;
3594  //contenttype_switch_set_which(cswitch,2); //set in big render loop below, based on hyper_case
3595  p->hyper_case[i] = 8; //which block below 0 - 9
3596 
3597  p->EMULATE_MULTITOUCH = FALSE;
3598  // these prepared ways of using freewrl are put into the switch contenttype cswitch above
3599  // (via chain of next pointers, via *last helper)
3600  {
3601  //0. normal: scene, statusbarHud,
3602  contenttype *cscene, *csbh;
3603 
3604  csbh = new_contenttype_statusbar();
3605  cscene = new_contenttype_scene();
3606 
3607  csbh->t1.contents = cscene;
3608 
3609  *last = csbh; //paste into switch.content
3610  last = &csbh->t1.next;
3611  //tg->Mainloop.AllowNavDrag = TRUE; //experimental approach to allow both navigation and dragging at the same time, with 2 separate touches
3612  }
3613  {
3614  //MAY 18, 2016 MULTITOUCH EMULATION DOESN'T WORK NOW after setup_picking() and onTouch() changes
3615  //1. normal + multitouch emulation, scene, statusbarHud,
3616  contenttype *cmultitouch, *cscene, *csbh;
3617 
3618  cmultitouch = new_contenttype_multitouch();
3619  cscene = new_contenttype_scene();
3620  csbh = new_contenttype_statusbar();
3621 
3622  cmultitouch->t1.contents = csbh;
3623  csbh->t1.contents = cscene;
3624 
3625  *last = cmultitouch; //paste into previous blocks top-level (just below switch) next
3626  last = &cmultitouch->t1.next;
3627  p->EMULATE_MULTITOUCH = TRUE;
3628 
3629  //tg->Mainloop.AllowNavDrag = TRUE; //experimental approach to allow both navigation and dragging at the same time, with 2 separate touches
3630  }
3631  {
3632  //2. TextPanel (dual-ringbuffer, for ConsoleMessage) + CaptionText
3633  contenttype *csbh, *cscene, *ctextpanel, *ctext;
3634  vec4 ccolor;
3635 
3636  csbh = new_contenttype_statusbar();
3637  cscene = new_contenttype_scene();
3638  ctextpanel = new_contenttype_textpanel("VeraMono",8,60,120,TRUE);
3639  ccolor = vec4_init(1.0f,.6f,0.0f,1.0f);
3640  ctext = new_contenttype_captiontext("VeraMono",12,ccolor);
3641  //ctext = new_contenttype_captiontext("freewrl_wingding",12,ccolor);
3642  //ctext = new_contenttype_captiontext("VeraIt",10,ccolor);
3643 
3644  captiontext_setString(ctext, "ABCDEDFGHIJKLMNOPQRSTUVWXYZabcd");
3645  ctext->t1.viewport[0] = .1f;
3646  ctext->t1.viewport[1] = .6f;
3647  ctext->t1.viewport[2] = 1.0f;
3648  ctext->t1.viewport[3] = .5f;
3649 
3650  //ConsoleMessage("Going to register textpanel for ConsoleMessages\n"); //should not show in textpanel
3651  textpanel_register_as_console(ctextpanel);
3652  //ConsoleMessage("Registered textpanel for ConsoleMessages\n"); //should be first message to show in textpanel
3653 
3654  csbh->t1.contents = ctextpanel;
3655  ctextpanel->t1.contents = cscene;
3656  ctextpanel->t1.next = ctext;
3657 
3658  *last = csbh;
3659  last = &csbh->t1.next;
3660 
3661  }
3662  {
3663  //3. captiontext, scene, statusbarHud,
3664  contenttype *cscene, *csbh, *ctext;
3665  vec4 ccolor;
3666 
3667  csbh = new_contenttype_statusbar();
3668  ccolor = vec4_init(1.0f,.6f,0.0f,1.0f);
3669  //ctext = new_contenttype_captiontext("Vera",12,ccolor);
3670  ctext = new_contenttype_captiontext("freewrl_wingding",10,ccolor);
3671 
3672  cscene = new_contenttype_scene();
3673 
3674 
3675  //can put regular and extended chars in the \x hex form (visual studio uses code-page system, not utf8)
3676  //& \x0026
3677  //e grave \x00e8
3678  //e acute \x00e9
3679  //msvc has problem embedding utf8 strings in C code even with \x. C++ better, includes u8"" strings
3680  //captiontext_setString(ctext, "string from captiontext FReEgrl \x0026 Gréen");
3681  captiontext_setString(ctext, "abcdABCDEDFGHIJKLMNOPQRSTUVWXYZ");
3682 
3683  ctext->t1.viewport[0] = .1f;
3684  ctext->t1.viewport[1] = .6f;
3685  ctext->t1.viewport[2] = .4f;
3686  ctext->t1.viewport[3] = .5f;
3687 
3688  csbh->t1.contents = cscene;
3689  cscene->t1.next = ctext;
3690 
3691  *last = csbh;
3692  last = &csbh->t1.next;
3693 
3694  }
3695  {
3696  //4. e3dmouse: multitouch emulation, layer, (e3dmouse > scene), statusbarHud,
3697  contenttype *csbh, *cscene, *ce3dmouse; // UNUSED cmultitouch
3698 
3699  csbh = new_contenttype_statusbar();
3700  ce3dmouse = new_contenttype_e3dmouse();
3701  // UNUSED cmultitouch = new_contenttype_multitouch();
3702  cscene = new_contenttype_scene();
3703 
3704  csbh->t1.contents = ce3dmouse;
3705  ce3dmouse->t1.contents = cscene;
3706  cscene->t1.next = NULL;
3707  *last = csbh;
3708  last = &csbh->t1.next;
3709 
3710  }
3711  {
3712  //5. experimental render to fbo, then fbo to screen
3713  //.. this will allow screen orientation to be re-implemented as a 2-stage render with rotation between
3714  contenttype *csbh, *cscene, *cstagefbo, *ctexturegrid, *cmultitouch;
3715 
3716  cmultitouch = new_contenttype_multitouch();
3717  ctexturegrid = new_contenttype_texturegrid(2,2);
3718  cstagefbo = new_contenttype_stagefbo(512,512);
3719  csbh = new_contenttype_statusbar();
3720  cscene = new_contenttype_scene();
3721 
3722  cmultitouch->t1.contents = ctexturegrid;
3723  ctexturegrid->t1.contents = cstagefbo;
3724  cstagefbo->t1.contents = csbh;
3725  csbh->t1.contents = cscene;
3726 
3727  *last = cmultitouch;
3728  last = &cmultitouch->t1.next;
3729 
3730  }
3731  {
3732  //6. multitouch emulation, orientation, fbo, layer { scene, statusbarHud }
3733  contenttype *csbh, *cscene, *corientation, *cmultitouch, *cstagefbo;
3734 
3735  cmultitouch = new_contenttype_multitouch();
3736  corientation = new_contenttype_orientation();
3737  cstagefbo = new_contenttype_stagefbo(512,512);
3738  csbh = new_contenttype_statusbar();
3739  cscene = new_contenttype_scene();
3740 
3741  cmultitouch->t1.contents = corientation;
3742  corientation->t1.contents = cstagefbo;
3743  cstagefbo->t1.contents = csbh;
3744  csbh->t1.contents = cscene;
3745 
3746  *last = cmultitouch;
3747  last = &cmultitouch->t1.next;
3748 
3749  }
3750  {
3751  //7. rotates just the scene, leaves statusbar un-rotated
3752  //multitouch emulation, layer, {{orientation, fbo, scene}, statusbarHud }
3753  contenttype *csbh, *cscene, *corientation, *cmultitouch, *cstagefbo;
3754 
3755  cmultitouch = new_contenttype_multitouch();
3756  csbh = new_contenttype_statusbar();
3757  corientation = new_contenttype_orientation();
3758  cstagefbo = new_contenttype_stagefbo(512,512);
3759  cscene = new_contenttype_scene();
3760 
3761  cmultitouch->t1.contents = csbh;
3762  csbh->t1.contents = corientation;
3763  corientation->t1.contents = cstagefbo;
3764  cstagefbo->t1.contents = cscene;
3765 
3766  *last = cmultitouch;
3767  last = &cmultitouch->t1.next;
3768 
3769  }
3770  {
3771  //8. stereo chooser: switch + 4 stereo vision modes, sbh, textpanel
3772  contenttype *cscene0, *cscene1, *cscene2;
3773  contenttype *cstereo1, *cstereo2, *cstereo3, *cstereo4, *cswitch0;
3774  contenttype *csbh, *ctextpanel;
3775 
3776  csbh = new_contenttype_statusbar();
3777  ctextpanel = new_contenttype_textpanel("VeraMono",8,60,120,TRUE);
3778  cswitch0 = new_contenttype_switch();
3779  cstereo1 = new_contenttype_stereo_shutter();
3780  cstereo2 = new_contenttype_stereo_sidebyside();
3781  cstereo3 = new_contenttype_stereo_anaglyph(); //anaglyph appears to work
3782  cstereo4 = new_contenttype_stereo_updown();
3783  //0 mono 1 shutter 2 sidebyside 3 analgyph 4 updown
3784  contenttype_switch_set_which_ptr(cswitch0,&tg->Viewer.stereotype);
3785 
3786 
3787  //stereo scenes 0,1
3788  cscene0 = new_contenttype_scene();
3789  cscene1 = new_contenttype_scene();
3790  cscene0->t1.next = cscene1;
3791  //mono scene 2
3792  cscene2 = new_contenttype_scene();
3793 
3794 
3795  //ConsoleMessage("Going to register textpanel for ConsoleMessages\n"); //should not show in textpanel
3796  textpanel_register_as_console(ctextpanel);
3797  //ConsoleMessage("Registered textpanel for ConsoleMessages\n"); //should be first message to show in textpanel
3798 
3799  csbh->t1.contents = ctextpanel;
3800  ctextpanel->t1.contents = cswitch0;
3801  cswitch0->t1.contents = cscene2; //mono scene
3802  cscene2->t1.next = cstereo1; //whichCase 0
3803  cstereo1->t1.contents = cscene0; //same scene0,scene1 stereo pair
3804  cstereo2->t1.contents = cscene0; //2
3805  cstereo3->t1.contents = cscene0; //3
3806  cstereo4->t1.contents = cscene0; //4
3807  cstereo1->t1.next = cstereo2;
3808  cstereo2->t1.next = cstereo3;
3809  cstereo3->t1.next = cstereo4;
3810 
3811  *last = csbh;
3812  last = &csbh->t1.next;
3813 
3814  }
3815  {
3816  //9. sidebyside stereo with per-eye fbo
3817  contenttype *cscene0, *cscene1;
3818  contenttype *cstereo;
3819  contenttype *cstagefbo0, *cstagefbo1;
3820  contenttype *ctexturegrid0, *ctexturegrid1;
3821  contenttype *csbh;
3822 
3823  csbh = new_contenttype_statusbar();
3824  cstereo = new_contenttype_stereo_sidebyside();
3825 
3826  cstagefbo0 = new_contenttype_stagefbo(512,512);
3827  ctexturegrid0 = new_contenttype_texturegrid(5,5);
3828 
3829  cstagefbo1 = new_contenttype_stagefbo(512,512);
3830  ctexturegrid1 = new_contenttype_texturegrid(5,5);
3831  cscene0 = new_contenttype_scene();
3832  cscene1 = new_contenttype_scene();
3833 
3834  if(1){
3835  //googleCardboard barrel distortions to counteract/compensate for magnifying lenses
3836  float xc;
3837  X3D_Viewer *viewer = Viewer();
3838 
3839  //ideally this gets run whenever screendist is changed
3840  xc = 1.0f - (float) viewer->screendist;
3841  texturegrid_barrel_distort2(ctexturegrid0, xc,.1f);
3842  xc = (float)viewer->screendist;
3843  texturegrid_barrel_distort2(ctexturegrid1, xc,.1f);
3844  }
3845 
3846 
3847  csbh->t1.contents = cstereo;
3848  cstereo->t1.contents = ctexturegrid0;
3849  ctexturegrid0->t1.next = ctexturegrid1;
3850  ctexturegrid0->t1.contents = cstagefbo0;
3851  ctexturegrid1->t1.contents = cstagefbo1;
3852  cstagefbo0->t1.contents = cscene0;
3853  cstagefbo1->t1.contents = cscene1;
3854 
3855  *last = csbh;
3856  last = &csbh->t1.next;
3857 
3858  }
3859  {
3860  //10. quadrant
3861  contenttype *cscene0, *cscene1, *cscene2, *cscene3;
3862  contenttype *csbh, *cquadrant; //, *cmultitouch;
3863 
3864  csbh = new_contenttype_statusbar();
3865  cquadrant = new_contenttype_quadrant();
3866 
3867  cscene0 = new_contenttype_scene();
3868  cscene1 = new_contenttype_scene();
3869  cscene2 = new_contenttype_scene();
3870  cscene3 = new_contenttype_scene();
3871 
3872  csbh->t1.contents = cquadrant;
3873  cquadrant->t1.contents = cscene0;
3874  cscene0->t1.next = cscene1;
3875  cscene1->t1.next = cscene2;
3876  cscene2->t1.next = cscene3;
3877 
3878  *last = csbh;
3879  last = &csbh->t1.next; //don't need this line if truely the last, but doesn't hurt to have the address
3880 
3881  }
3882 
3883  t->stage = cstage;
3884 // t = t->next;
3885  }
3886 }
3887 int fwl_hyper_option(char *val){
3888  //keyboard on graphics window: ' ' (spacebar) will get : prompt
3889  //then :hyper_otion,3[Enter] will change the hyperoption for all windows
3890  int i,iopt;
3891  //targetwindow *t;
3892  ttglobal tg = gglobal();
3893  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3894 
3895  iopt = atoi(val);
3896  if(iopt >= 0 && iopt <=10)
3897  for(i=0;i<p->nwindow;i++){
3898  p->hyper_case[i] = iopt;
3899  }
3900  return 1;
3901 }
3902 void initialize_targets_simple(){
3903 
3904  ttglobal tg = gglobal();
3905  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3906 
3907  targetwindow *t = p->cwindows;
3908 
3909  if(!t->stage){
3910  setup_stagesNORMAL();
3911  }
3912 
3913 
3914  p->targets_initialized = 1;
3915 
3916 }
3917 void update_navigation();
3918 void fwl_RenderSceneUpdateSceneTARGETWINDOWS() {
3919  double dtime;
3920  int i;
3921  ivec4 defaultvport;
3922  Stack *vportstack;
3923  targetwindow *t;
3924  ttglobal tg = gglobal();
3925  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3926 
3927  if(!p->targets_initialized)
3928  initialize_targets_simple();
3929 
3930  dtime = Time1970sec();
3931  vportstack = (Stack *)tg->Mainloop._vportstack;
3932  defaultvport = ivec4_init(0,0,100,100);
3933  pushviewport(vportstack,defaultvport);
3934  //update_navigation();
3935  fwl_RenderSceneUpdateScene0(dtime);
3936  popviewport(vportstack);
3937 
3938  //twindows = p->cwindows;
3939  //t = twindows;
3940  p->windex = -1;
3941  for(i=0;i<p->nwindow;i++){
3942  //a targetwindow might be a supervisor's screen, or HMD
3943  freewrl_params_t *dp;
3944  stage *s;
3945  void *hyper_switch;
3946  int hyper_case;
3947 
3948  hyper_switch = p->hyper_switch[i];
3949  hyper_case = p->hyper_case[i];
3950  contenttype_switch_set_which(hyper_switch,hyper_case);
3951 
3952 
3953  t=&p->cwindows[i];
3954  p->windex++;
3955  s = (stage*)(t->stage); // assumes t->stage.t1.type == CONTENTTYPE_STAGE
3956  if(s->type == STAGETYPE_BACKBUF){
3957  s->ivport = t->ivport;
3958  }else{
3959  //if s->type == STAGETYPE_FBO
3960  //s->ivport = f(twindow->ivport) ie you might resize the fbo if your target window is big/small
3961  }
3962  fwl_setScreenDim0(s->ivport.W, s->ivport.H); //or t2->ivport ?
3963  dp = (freewrl_params_t*)tg->display.params;
3964  if(t->params.context != dp->context){
3965  tg->display.params = (void*)&t->params;
3966  fv_change_GLcontext((freewrl_params_t*)tg->display.params);
3967  //printf("%ld %ld %ld\n",t->params.display,t->params.context,t->params.surface);
3968  }
3969  //moved to render doglClearColor();
3970  vportstack = (Stack *)tg->Mainloop._vportstack;
3971  pushviewport(vportstack,t->ivport);
3972  s->t1.render(s);
3973  //get final buffer, or swapbuffers
3974  popviewport(vportstack);
3975  //setcurrentviewport(vportstack);
3976  if(t->swapbuf) { FW_GL_SWAPBUFFERS }
3977 // t = (targetwindow*) t->next;
3978  }
3979  p->windex = 0;
3980 }
3981 
3982 //<<<<<=====NEW=====
3983 int fwl_handle_mouse_multi_yup(int mev, int butnum, int mouseX, int yup, unsigned int ID, int windex){
3984  //this is the pick() for the twindow level
3985  int ihit;
3986  Stack *vportstack;
3987  targetwindow *t;
3988  stage *s;
3989  ttglobal tg = gglobal();
3990  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
3991 
3992  if (mev == MotionNotify) butnum = 0; //a freewrl handle...multiNORMAL convention
3993 
3994  t = &p->cwindows[windex];
3995  s = (stage*)t->stage;
3996  if(!s) return 0; //sometimes mouse events can start before a draw events (where stages are initialized)
3997  if(s->type == STAGETYPE_BACKBUF)
3998  s->ivport = t->ivport; //need to refresh every frame incase there was a resize on the window
3999  vportstack = (Stack *)tg->Mainloop._vportstack;
4000  pushviewport(vportstack,s->ivport);
4001  ihit = s->t1.pick(s,mev,butnum,mouseX,yup,ID,windex);
4002  popviewport(vportstack);
4003  return ihit;
4004 }
4005 
4006 void emulate_multitouch(int mev, unsigned int button, int x, int ydown, int windex)
4007 {
4008  /* CREATE/DELETE a touch with RMB down
4009  GRAB/MOVE a touch with LMB down and drag
4010  ID=0 reserved for 'normal' cursor
4011  */
4012  int i,ifound,ID,y;
4013  struct Touch *touch;
4014  static int buttons[4] = {0,0,0,0};
4015  static int idone = 0;
4016  ppMainloop p;
4017  targetwindow *t;
4018  ttglobal tg = gglobal();
4019  p = (ppMainloop)tg->Mainloop.prv;
4020 
4021  t = &p->cwindows[windex];
4022  //Nov. 2015 changed freewrl mouse from y-down to y-up from here on down:
4023  //all y-up now: sesnsor/picking, explore, statusbarHud, handle0 > all navigations, emulate_multitouch, sidebyside fiducials
4024  y = t->ivport.H - ydown; //screenHeight -y;
4025 
4026  if(!idone){
4027  printf("Use RMB (right mouse button) to create and delete touches\n");
4028  printf("Use LMB to drag touches (+- 5 pixel selection window)\n");
4029  idone = 1;
4030  }
4031  buttons[button] = mev == ButtonPress;
4032  ifound = 0;
4033  ID = -1;
4034  touch = NULL;
4035 
4036  for(i=0;i<p->ntouch;i++){
4037  touch = &p->touchlist[i];
4038  if(touch->ID > -1){
4039  if(touch->windex == windex && touch->stageId == current_stageId())
4040  if((abs(x - touch->rx) < 10) && (abs(y - touch->ry) < 10)){
4041  ifound = 1;
4042  ID = i;
4043  break;
4044  }
4045  }
4046  }
4047 
4048  if( mev == ButtonPress && button == RMB )
4049  {
4050  //if near an existing one, delete
4051  if(ifound && touch){
4052  fwl_handle_mouse_multi_yup(ButtonRelease,LMB,x,y,ID,windex);
4053  //delete
4054  touch->ID = -1;
4055  printf("delete ID=%d windex=%d\n",ID,windex);
4056  }
4057  //else create
4058  if(!ifound){
4059  //create!
4060  for(i=0;i<p->ntouch;i++){
4061  touch = &p->touchlist[i];
4062  if(touch->ID < 0) {
4063  fwl_handle_mouse_multi_yup(mev, LMB, x, y, i,windex);
4064  touch->rx = x;
4065  touch->ry = y;
4066  printf("create ID=%d windex=%d\n",i,windex);
4067  break;
4068  }
4069  }
4070  }
4071  }else if( mev == MotionNotify && buttons[LMB]) {
4072  //if near an existing one, grab it and move it
4073  if(ifound){
4074  fwl_handle_mouse_multi_yup(MotionNotify,0,x,y,ID,windex);
4075  touch = &p->touchlist[ID];
4076  touch->rx = x;
4077  touch->ry = y;
4078  //printf("drag ID=%d \n",ID);
4079  }
4080  }
4081 }
4082 //void render_multitouch(){
4083 // ppMainloop p;
4084 // ttglobal tg = gglobal();
4085 // p = (ppMainloop)tg->Mainloop.prv;
4086 //
4087 // if(p->EMULATE_MULTITOUCH) {
4088 // int i;
4089 // for(i=0;i<p->ntouch;i++){
4090 // if(p->touchlist[i].ID > -1)
4091 // if(p->touchlist[i].windex == p->windex)
4092 // {
4093 // struct Touch *touch;
4094 // touch = &p->touchlist[i];
4095 // cursorDraw(touch->ID,touch->rx,touch->ry,touch->angle);
4096 // }
4097 // }
4098 // }
4099 //}
4100 void render_multitouch2(struct Touch *touchlist, int ntouch){
4101  ppMainloop p;
4102  ttglobal tg = gglobal();
4103  p = (ppMainloop)tg->Mainloop.prv;
4104 
4105  if(p->EMULATE_MULTITOUCH) {
4106  int i;
4107  for(i=0;i<ntouch;i++){
4108  if(touchlist[i].ID > -1)
4109  if(touchlist[i].windex == p->windex ) // && touchlist[i].stageId == current_stageId() )
4110  {
4111  struct Touch *touch;
4112  touch = &touchlist[i];
4113  cursorDraw(touch->ID,touch->rx,touch->ry,touch->angle);
4114  }
4115  }
4116  }
4117 }
4118 void record_multitouch(struct Touch *touchlist, int mev, int butnum, int mouseX, int mouseY, int ID, int windex, int ihandle){
4119  struct Touch *touch;
4120  //ppMainloop p;
4121  //ttglobal tg = gglobal();
4122  //p = (ppMainloop)tg->Mainloop.prv;
4123 
4124  touch = &touchlist[ID];
4125  if(ihandle == -2){
4126  touch->ID = -1;
4127  }else{
4128  touch->rx = mouseX;
4129  touch->ry = mouseY;
4130  touch->windex = windex;
4131  touch->stageId = current_stageId();
4132  touch->buttonState = mev == ButtonPress;
4133  touch->ID = ID; /*will come in handy if we change from array[] to accordian list*/
4134  touch->mev = mev;
4135  touch->angle = 0.0f;
4136  //p->currentTouch = ID;
4137  }
4138 
4139 }
4140 
4141 int emulate_multitouch2(struct Touch *touchlist, int ntouch, int *IDD, int *lastbut, int *mev, unsigned int *button, int x, int y, int *ID, int windex)
4142 {
4143  /* CREATE/DELETE a touch with RMB down
4144  GRAB/MOVE a touch with LMB down and drag
4145  ID=0 reserved for 'normal' cursor
4146  */
4147  int i,ihandle;
4148  struct Touch *touch;
4149  static int idone = 0;
4150  ppMainloop p;
4151  ttglobal tg = gglobal();
4152  p = (ppMainloop)tg->Mainloop.prv;
4153 
4154  if(!idone){
4155  printf("Use RMB (right mouse button) to create and delete touches\n");
4156  printf("Use LMB to drag touches (+- 5 pixel selection window)\n");
4157  idone = 1;
4158  }
4159  touch = NULL;
4160  ihandle = 1;
4161 
4162  if(*mev == ButtonPress && (*button == LMB || *button == RMB)){
4163  //FIND
4164  *IDD = -1;
4165  *lastbut = *button;
4166  for(i=0;i<ntouch;i++){
4167  touch = &touchlist[i];
4168  if(touch->inUse){
4169  if(touch->windex == windex ) //&& touch->stageId == current_stageId())
4170  if((abs(x - touch->rx) < 10) && (abs(y - touch->ry) < 10)){
4171  *IDD = i;
4172  printf("drag found ID %d\n",*IDD);
4173  break;
4174  }
4175  }
4176  }
4177  }
4178 
4179  if(*lastbut == LMB){
4180  if( *mev == MotionNotify ) {
4181  //if near an existing one, grab it and move it
4182  if(*IDD > -1){
4183  //fwl_handle_mouse_multi_yup(MotionNotify,0,x,y,ID,windex);
4184  *mev = MotionNotify;
4185  *button = 0;
4186  *ID = *IDD;
4187  ihandle = -1;
4188  touch = &touchlist[*IDD];
4189  touch->rx = x;
4190  touch->ry = y;
4191  printf("drag ID=%d \n",*IDD);
4192  }
4193  }else if(*mev == ButtonRelease){
4194  *IDD = -1;
4195  }
4196  } else if(*lastbut == RMB){
4197  if( *mev == ButtonPress )
4198  {
4199  //if near an existing one, delete
4200  if(*IDD > -1 && touch){
4201  //fwl_handle_mouse_multi_yup(ButtonRelease,LMB,x,y,ID,windex);
4202  *mev = ButtonRelease;
4203  *button = LMB;
4204  *ID = *IDD;
4205  ihandle = -2; //caller must propagate handle_mouse, then set ID = -1;
4206  //delete
4207  //touch->ID = -1; //this gets overwritten
4208  printf("delete ID=%d windex=%d ihandle=%d\n",*IDD,windex,ihandle);
4209  }
4210  //else create
4211  if(*IDD == -1){
4212  //create!
4213  for(i=1;i<p->ntouch;i++){
4214  touch = &touchlist[i];
4215  if(touch->inUse == FALSE) {
4216  //fwl_handle_mouse_multi_yup(mev, LMB, x, y, i,windex);
4217  *button = LMB;
4218  *ID = i;
4219  *IDD = i;
4220  ihandle = -1;
4221  touch->rx = x;
4222  touch->ry = y;
4223  touch->inUse = TRUE;
4224  printf("create ID=%d windex=%d\n",i,windex);
4225  break;
4226  }
4227  }
4228  }
4229  }
4230  }
4231  //p->currentTouch = *ID;
4232  return ihandle;
4233 }
4234 
4235 
4236 int fwl_handle_mouse_multi(int mev, int butnum, int mouseX, int mouseY, unsigned int ID, int windex){
4237  //this is the pick() for the twindow level
4238  int yup;
4239  targetwindow *t;
4240  ttglobal tg = gglobal();
4241  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
4242 
4243  t = &p->cwindows[windex];
4244  //Nov. 2015 changed freewrl mouse from y-down to y-up from here on down:
4245  //all y-up now: sesnsor/picking, explore, statusbarHud, handle0 > all navigations, emulate_multitouch, sidebyside fiducials
4246  yup = t->ivport.H - mouseY; //screenHeight -y;
4247  fwl_handle_mouse_multi_yup(mev,butnum,mouseX,yup,ID,windex);
4248  return getCursorStyle();
4249 }
4250 int fwl_handle_mouse(int mev, int butnum, int mouseX, int mouseY, int windex){
4251  int cstyle, tactic_up_drag;
4252  static unsigned int ID = 1;
4253  ttglobal tg = gglobal();
4254  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
4255 
4256  //ConsoleMessage("mev %d butnum %d\n",mev,butnum);
4257  ID = 1; //normal, 2=over
4258  //if(mev == ButtonPress) ID++;
4259  tactic_up_drag = 0;
4260  if(tactic_up_drag){
4261  //this was an attempt to restore isOver for desktop, by
4262  //creating a Touch/Drag for when the mouse buttons are up
4263  // but didn't work well (H: can't send 2 mouse events on the same frame
4264  // because we are flushing once per event rather than once per frame)
4265  // Use the Hover button.
4266  switch(mev){
4267  case MotionNotify:
4268  if(!p->mouseDown && !p->mouseOver){
4269  //we are moving. Turn it into an up-drag
4270  p->mouseOver = TRUE;
4271  ID = 2;
4272  mev = ButtonPress;
4273  butnum = 0;
4274  }
4275  if(p->mouseOver){
4276  ID = 2;
4277  butnum = 0;
4278  }
4279  break;
4280  case ButtonPress:
4281  if(p->mouseOver){
4282  //clean up up-drag
4283  fwl_handle_mouse_multi(ButtonRelease, 0, mouseX, mouseY, 2, windex);
4284  p->mouseOver = FALSE;
4285  }
4286  p->mouseDown = TRUE;
4287  break;
4288  default:
4289  break;
4290  }
4291  cstyle = fwl_handle_mouse_multi(mev,butnum,mouseX,mouseY,ID,windex);
4292  if(mev == ButtonRelease){
4293  p->mouseDown = FALSE;
4294  }
4295  }else{
4296  //no tactic up-drag, just normal
4297  cstyle = fwl_handle_mouse_multi(mev,butnum,mouseX,mouseY,ID,windex);
4298  }
4299  return cstyle;
4300 }
4301 int fwl_handle_touch(int mev, unsigned int ID, int mouseX, int mouseY, int windex) {
4302  int cstyle;
4303  int ibut;
4304  // OLDCODE ttglobal tg = gglobal();
4305  // OLDCODE ppMainloop p = (ppMainloop)tg->Mainloop.prv;
4306 
4307  //mobile: touch drags only occur when something is down, so LMB is constant
4308  //localhost: touch drags can have mev = move, with no Press preceding, for a mouse up drag
4309  ibut = LMB;
4310  //if(fwl_getHover()) ibut = 0;
4311  cstyle = fwl_handle_mouse_multi(mev, ibut, mouseX, mouseY, ID, windex);
4312  return cstyle;
4313 }
4314 // mobile devices with accelerometer or gyro pass the raw data in here
4315 // assumed axes: z pointing up from face, x to right on face, y pointing up on face
4316 // method of use: relative drags
4317 void viewer_getpose(double *quat4, double *vec3);
4318 void viewer_setpose(double *quat4, double *vec3);
4319 static int using_sensors_for_navigation = 1; //in theory we could use for other things, or turn off
4320 static int using_magnetic = 0;
4321 static int using_gyro = 1;
4322 //OLDCODE static int using_accelerometer = 0;
4323 
4324 void fwl_handle_gyro(float rx, float ry, float rz) {
4325  if(using_sensors_for_navigation && using_gyro){
4326  double axyz[3], dd[3], quat4[4], vec3[3]; //, ypr[3]; //Axyz[3],
4327  static double dt, lasttime, curtime;
4328  Quaternion qq0, qq2, qq; //q1,
4329  static int initialized = 0;
4330 
4331  if (!initialized) {
4332  lasttime = Time1970sec();
4333  initialized = 1;
4334  }
4335 
4336  curtime = Time1970sec();
4337  dt = curtime - lasttime;
4338  lasttime = curtime;
4339 
4340  viewer_getpose(quat4, vec3);
4341  double2quat(&qq0, quat4);
4342  quaternion_normalize(&qq0);
4343 
4344  axyz[0] = (double)rx;
4345  axyz[2] = -(double)ry;
4346  axyz[1] = -(double)rz;
4347 
4348  //and take out driftie small accelerations
4349  if (fabs(axyz[0]) < .01) axyz[0] = 0.0;
4350  if (fabs(axyz[1]) < .01) axyz[1] = 0.0;
4351  if (fabs(axyz[2]) < .01) axyz[2] = 0.0;
4352  vecscaled(dd, axyz, dt); //funny this is working best
4353  //vecscaled(dd, axyz, .01); //under-rotates - see it with roll
4354  //vecscaled(dd,axyz,PI/180.0); //over-rotates .01745, so .015?
4355 
4356  euler2quat(&qq2, dd[0], dd[1], dd[2]);
4357  quaternion_multiply(&qq, &qq2, &qq0); //cumquat should be in world2vp sense like view
4358  quaternion_normalize(&qq);
4359 
4360  quat2double(quat4, &qq);
4361  viewer_setpose(quat4, vec3);
4362  }
4363 }
4364 
4365 void fwl_handle_accelerometer(float ax, float ay, float az){
4366  //ConsoleMessage("hi from handle_accelerometer %f %f %f\n", ax, ay, az);
4367 }
4368 
4369 void fwl_handle_magnetic(float azimuth, float pitch, float roll) {
4370  if (using_sensors_for_navigation && using_magnetic) {
4371  //doesn't work, but the idea is to use magnetic bearing differences from startup pose
4372  //to rotate the scene. (H: would be better to use sensor fusion, perhaps via bayes or
4373  // kalman filtering or least squares updates)
4374  double rxyz[3], dd[3], Rxyz[3], quat4[4], vec3[3]; //, ypr[3];
4375  static double ddlast[3];
4376  Quaternion qq0, qq2, qq, qqlast; //q1,
4377  static int initialized = 0;
4378 
4379  if (!initialized) {
4380  initialized = 1;
4381  Rxyz[0] = (double)azimuth;
4382  Rxyz[2] = -(double)roll;
4383  Rxyz[1] = -(double)pitch;
4384  vecscaled(ddlast,ddlast,0.0);
4385  }
4386 
4387 
4388  viewer_getpose(quat4, vec3);
4389  double2quat(&qq0, quat4);
4390  quaternion_normalize(&qq0);
4391 
4392  rxyz[0] = (double)azimuth;
4393  rxyz[2] = -(double)roll;
4394  rxyz[1] = -(double)pitch;
4395 
4396  vecdifd(dd,rxyz,Rxyz);
4397  vecscaled(dd, dd, .05);
4398  //take off magnetic from last event
4399  euler2quat(&qqlast,ddlast[0],ddlast[1],ddlast[2]);
4400  quaternion_normalize(&qqlast);
4401  quaternion_inverse(&qqlast,&qqlast);
4402  quaternion_normalize(&qqlast);
4403  quaternion_multiply(&qq0,&qqlast,&qq0);
4404  quaternion_normalize(&qq0);
4405 
4406  //add magnetic from this event
4407  euler2quat(&qq2, dd[0], dd[1], dd[2]);
4408  quaternion_multiply(&qq, &qq2, &qq0); //cumquat should be in world2vp sense like view
4409  quaternion_normalize(&qq);
4410 
4411  quat2double(quat4, &qq);
4412  viewer_setpose(quat4, vec3);
4413  veccopyd(ddlast,dd);
4414  }
4415 }
4416 
4417 #ifdef OLDCODE
4418 OLDCODE void fwl_handle_magnetic_old(float azimuth, float pitch, float roll) {
4419 OLDCODE ConsoleMessage("hi from handle_magnetic %f %f %f\n", azimuth, pitch, roll);
4420 OLDCODE if(using_sensors_for_navigation && using_magnetic){
4421 OLDCODE static int initialized = 0;
4422 OLDCODE static float home_azimuth = 0.0f, home_pitch = 0.0f, home_roll = 0.0f;
4423 OLDCODE static double lasttime, curtime, dt;
4424 OLDCODE Quaternion qq, qq2;
4425 OLDCODE static Quaternion qq0;
4426 OLDCODE float dazimuth,ddazimuth, dpitch, droll; //,ddroll;ddpitch,
4427 OLDCODE double quat4[4], vec3[3], rxyz[3];
4428 OLDCODE static double ypr[3];
4429 OLDCODE static float lazimuth, lpitch, lroll;
4430 OLDCODE //we'll use use azimuth relative * time, and pitch absolute
4431 OLDCODE //assume startup azimuth is home azimuth
4432 OLDCODE // x the axes are mixed up
4433 OLDCODE // x seems to depend on orientation
4434 OLDCODE // x my quat2yawpitch isn't comprehensive enought, need roll to understand
4435 OLDCODE if(!initialized){
4436 OLDCODE home_azimuth = azimuth;
4437 OLDCODE home_pitch = pitch;
4438 OLDCODE home_roll = roll;
4439 OLDCODE lazimuth = azimuth;
4440 OLDCODE lpitch = pitch;
4441 OLDCODE lroll = roll;
4442 OLDCODE lasttime = Time1970sec();
4443 OLDCODE initialized = 1;
4444 OLDCODE if(0){
4445 OLDCODE viewer_getpose(quat4, vec3);
4446 OLDCODE double2quat(&qq0, quat4);
4447 OLDCODE quaternion_normalize(&qq0);
4448 OLDCODE //quat2yawpitch(ypr, &qq0);
4449 OLDCODE quat2euler(ypr,0,&qq0);
4450 OLDCODE //in thoery euler rotations can be extracted sequentially from quaternions:
4451 OLDCODE // qy = quat2yaw(q0) // gets yaw
4452 OLDCODE // q1 = qy.inverse()*q0 //gets pitch+roll
4453 OLDCODE // qp = quat2pitch(q1) // gets pitch
4454 OLDCODE // qr = qp.inverse()*q1 //gets roll
4455 OLDCODE // yaw = qy.toEuler()
4456 OLDCODE // pitch = qp.toEuler()
4457 OLDCODE // roll = qr.toEuler()
4458 OLDCODE // and you would get different value depending on the sequence,
4459 OLDCODE // but when multiplied back together in reverse sequence you would/should get original q0
4460 OLDCODE }
4461 OLDCODE
4462 OLDCODE }
4463 OLDCODE curtime = Time1970sec();
4464 OLDCODE dt = curtime - lasttime;
4465 OLDCODE lasttime = curtime;
4466 OLDCODE
4467 OLDCODE if(1){
4468 OLDCODE viewer_getpose(quat4, vec3);
4469 OLDCODE double2quat(&qq0,quat4);
4470 OLDCODE quaternion_normalize(&qq0);
4471 OLDCODE //quat2yawpitch(ypr,&qq0);
4472 OLDCODE quat2euler(ypr,0,&qq0);
4473 OLDCODE }
4474 OLDCODE //quat2euler(rxyz,0,&qq);
4475 OLDCODE dazimuth = (azimuth - home_azimuth);// * dt; // * .1;
4476 OLDCODE ddazimuth = dazimuth - lazimuth;
4477 OLDCODE lazimuth = dazimuth;
4478 OLDCODE if (fabs(ddazimuth) < .7f)
4479 OLDCODE dazimuth = 0.0;
4480 OLDCODE if(fabs(ddazimuth) < 2.0f)
4481 OLDCODE dazimuth = .01f * dazimuth;
4482 OLDCODE if(fabs(ddazimuth) < 10.0f)
4483 OLDCODE dazimuth = .1f * dazimuth;
4484 OLDCODE
4485 OLDCODE dpitch = (pitch - home_pitch);
4486 OLDCODE droll = (roll - home_roll);
4487 OLDCODE //time based azimuth is doing nothing
4488 OLDCODE //roll throws it off, pitch crazy.
4489 OLDCODE // I don't have the right formula and not sure tinkering will help
4490 OLDCODE rxyz[2] = dazimuth*PI/180.0;
4491 OLDCODE rxyz[1] = (50.0 - droll )*PI/50.0*dt; //180.0;
4492 OLDCODE //rxyz[2] = 0.0;
4493 OLDCODE rxyz[0] = 0.0;
4494 OLDCODE rxyz[1] = 0.0;
4495 OLDCODE //rxyz[1] = droll; // dpitch;
4496 OLDCODE //rxyz[2] = dpitch;
4497 OLDCODE //rxyz[1] = roll*PI/180.0;
4498 OLDCODE //rxyz[0] = azimuth*PI/180.0;
4499 OLDCODE euler2quat(&qq2,rxyz[0],rxyz[1],-rxyz[2]);
4500 OLDCODE quaternion_multiply(&qq,&qq2,&qq0); //cumquat should be in world2vp sense like view
4501 OLDCODE quaternion_normalize(&qq);
4502 OLDCODE
4503 OLDCODE quat2double(quat4,&qq);
4504 OLDCODE viewer_setpose(quat4,vec3);
4505 OLDCODE //home_azimuth = azimuth;
4506 OLDCODE home_pitch = pitch;
4507 OLDCODE //home_roll = roll;
4508 OLDCODE
4509 OLDCODE }
4510 OLDCODE }
4511 #endif // OLDCODE
4512 
4513 void (*fwl_RenderSceneUpdateScenePTR)() = fwl_RenderSceneUpdateSceneTARGETWINDOWS;
4514 //#else //MULTI_WINDOW
4516 //void (*fwl_RenderSceneUpdateScenePTR)() = fwl_RenderSceneUpdateSceneSTAGES;
4517 //#endif //MULTI_WINDOW
4518 
4519 /*rendersceneupdatescene overridden with SnapshotRegressionTesting.c
4520  fwl_RenderSceneUpdateSceneTESTING during regression testing runs
4521 */
4522 void fwl_RenderSceneUpdateScene(void){
4523 
4524  fwl_RenderSceneUpdateScenePTR();
4525 }
4526 void setup_picking();
4527 void setup_projection();
4528 void rbp_run_physics();
4529 void fwl_RenderSceneUpdateScene0(double dtime) {
4530  //Nov 2015 change: just viewport-independent, once-per-frame-scene-updates here
4531  //-functionality relying on a viewport -setup_projection(), setup_picking()- has been
4532  // moved to render() which is now called outside this function
4533  // this will allow quadrant displays and multiple windows to update the scene once per frame here,
4534  // then render to many viewports/windows, pickray from any viewport/window
4535  ttglobal tg = gglobal();
4536  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
4537 
4538  /* HAd an issue with Anaglyph rendering on Android; the cursorDraw routine caused the MODELVIEW matrix
4539  to have the Identity matrix loaded, which caused near/far plane calculations to be dinked.
4540  should be set FW_GL_MATRIX_MODE(GL_MODELVIEW);
4541  FW_GL_LOAD_IDENTITY(); DO NOT LOAD IDENTITY HERE, ELSE near/Far planes screwed up.
4542  if you want to see what happened, load identity matrix here! (uncomment above line)
4543  */
4544 
4545  PRINT_GL_ERROR_IF_ANY("start of renderSceneUpdateScene");
4546 
4547  DEBUG_RENDER("start of MainLoop (parsing=%s) (url loaded=%s)\n",
4548  BOOL_STR(fwl_isinputThreadParsing()), BOOL_STR(resource_is_root_loaded()));
4549 
4550  /* should we do events, or maybe a parser is parsing? */
4551  p->doEvents = (!fwl_isinputThreadParsing()) && (!fwl_isTextureParsing()) && fwl_isInputThreadInitialized();
4552  /* First time through */
4553  //if (p->loop_count == 0) {
4554  if(!p->once){
4555  p->BrowserStartTime = dtime; //Time1970sec();
4556  tg->Mainloop.TickTime = p->BrowserStartTime;
4557  tg->Mainloop.lastTime = tg->Mainloop.TickTime - 0.01; /* might as well not invoke the usleep below */
4558  if(p->BrowserInitTime == 0.0)
4559  p->BrowserInitTime = dtime;
4560  p->once = TRUE;
4561  } else {
4562  /* NOTE: front ends now sync with the monitor, meaning, this sleep is no longer needed unless
4563  something goes totally wrong.
4564  Perhaps could be moved up a level, since mobile controls in frontend, but npapi and activex plugins also need displaythread */
4565  if(!((freewrl_params_t*)(tg->display.params))->frontend_handles_display_thread){
4566  /* some users report their device overheats if frame rate is a zillion, so this will limit it to a target number
4567  statusbarHud options has an option to set.
4568  we see how long it took to do the last loop; now that the frame rate is synced to the
4569  vertical retrace of the screens, we should not get more than 60-70fps. We calculate the
4570  time here, if it is more than 200fps, we sleep for 1/100th of a second - we should NOT
4571  need this, but in case something goes pear-shaped (british expression, there!) we do not
4572  consume thousands of frames per second
4573  frames-per-second = FPS = 1/time-per-frame[s]; [s] means seconds, [ms] millisec [us] microseconds [f] frames
4574  target_time_per_frame[s] = 1[f]/target_FPS[f/s];
4575  suggested_wait_time[s] = target_time_per_frame[s] - elapsed_time_since_last_frame[s];
4576  = 1[f]/target_FPS[f/s] - elapsed_time_since_last_frame[s];
4577  if suggested_wait_time < 0 then we can't keep up, no wait time
4578 
4579  */
4580  double elapsed_time_per_frame, suggested_wait_time, target_time_per_frame, kludgefactor;
4581  int wait_time_micro_sec, target_frames_per_second;
4582  kludgefactor = 2.0; //2 works on win8.1 with intel i5
4583  target_frames_per_second = fwl_get_target_fps();
4584  elapsed_time_per_frame = TickTime() - lastTime();
4585  if(target_frames_per_second > 0)
4586  target_time_per_frame = 1.0/(double)target_frames_per_second;
4587  else
4588  target_time_per_frame = 1.0/30.0;
4589  suggested_wait_time = target_time_per_frame - elapsed_time_per_frame;
4590  suggested_wait_time *= kludgefactor;
4591 
4592  wait_time_micro_sec = (int)(suggested_wait_time * 1000000.0);
4593  if(wait_time_micro_sec > 1)
4594  usleep(wait_time_micro_sec);
4595  }
4596  }
4597 
4598  // Set the timestamp
4599  tg->Mainloop.lastTime = tg->Mainloop.TickTime;
4600  tg->Mainloop.TickTime = dtime; //Time1970sec();
4601 
4602  #if !defined(FRONTEND_DOES_SNAPSHOTS)
4603  // handle snapshots
4604  if (tg->Snapshot.doSnapshot) {
4605  Snapshot();
4606  }
4607  #endif //FRONTEND_DOES_SNAPSHOTS
4608 
4609  OcclusionCulling();
4610 
4611  // any scripts to do??
4612 #ifdef _MSC_VER
4613  if(p->doEvents)
4614 #endif /* _MSC_VER */
4615 
4616  initializeAnyScripts();
4617 
4618 
4619 
4620  // BrowserAction required? eg, anchors, etc
4621 #ifndef DISABLER
4622  if (tg->RenderFuncs.BrowserAction) {
4623  tg->RenderFuncs.BrowserAction = doBrowserAction ();
4624  }
4625 #endif
4626 
4627  //doglClearColor();
4628 
4629  OcclusionStartofRenderSceneUpdateScene();
4630 
4631  startOfLoopNodeUpdates();
4632 
4633  if (p->loop_count == 25) {
4634  tg->Mainloop.BrowserFPS = 25.0 / (TickTime()-p->BrowserStartTime);
4635  setMenuFps((float)tg->Mainloop.BrowserFPS); /* tell status bar to refresh, if it is displayed*/
4636  // printf ("fps %f tris %d, rootnode children %d \n",p->BrowserFPS,p->trisThisLoop, X3D_GROUP(rootNode)->children.n);
4637  //ConsoleMessage("fps %f tris %d\n",tg->Mainloop.BrowserFPS,tg->Mainloop.trisThisLoop);
4638  //printf ("MainLoop, nearPlane %lf farPlane %lf\n",Viewer.nearPlane, Viewer.farPlane);
4639  p->BrowserStartTime = TickTime();
4640  p->loop_count = 1;
4641  } else {
4642  p->loop_count++;
4643  }
4644 
4645  tg->Mainloop.trisThisLoop = 0;
4646 
4647  if(p->slowloop_count == 1009) p->slowloop_count = 0 ;
4648  #if USE_OSC
4649  if ((p->slowloop_count % 256) == 0) {
4650  /* activate_picksensors() ; */
4651  /*
4652  printf("slowloop_count = %d at T=%lf : lastMouseEvent=%d , MotionNotify=%d\n",
4653  p->slowloop_count, TickTime(), p->lastMouseEvent, MotionNotify) ;
4654  */
4655  activate_OSCsensors() ;
4656  } else {
4657  /* deactivate_picksensors() ; */
4658  }
4659  #endif /* USE_OSC */
4660 
4661  p->slowloop_count++ ;
4662 
4663  // handle any events provided on the command line - Robert Sim
4664  if (p->keypress_string && p->doEvents) {
4665  if (p->keypress_wait_for_settle > 0) {
4666  p->keypress_wait_for_settle--;
4667  } else {
4668  // dont do the null...
4669  if (*p->keypress_string) {
4670  // printf ("handling key %c\n",*p->keypress_string);
4671 #if !defined( _MSC_VER ) /*win32 - don't know whats it is suppsoed to do yet */
4672  DEBUG_XEV("CMD LINE GEN EVENT: %c\n", *p->keypress_string);
4673  fwl_do_keyPress(*p->keypress_string,KeyPress);
4674 #endif /* NOT WIN32 */
4675  p->keypress_string++;
4676  } else {
4677  p->keypress_string=NULL;
4678  }
4679  }
4680  }
4681 
4682 #if KEEP_X11_INLIB
4683 
4686  /* REMARK: Do we want to process all pending events ? */
4687 
4688 #if defined(TARGET_X11)
4689  /* We are running our own bare window */
4690  {
4691  int kw;
4692  for(kw=0;kw<p->nwindow;kw++)
4693  {
4694  void * xdpy = p->cwindows[kw].params.display;
4695  //while (XPending(Xdpy)) {
4696  while(XPending(xdpy)) {
4697  XNextEvent(xdpy, &event);
4698  DEBUG_XEV("EVENT through XNextEvent\n");
4699  handle_Xevents(event);
4700  }
4701  }
4702  }
4703 #endif /* TARGET_X11 */
4704 
4705 
4706  PRINT_GL_ERROR_IF_ANY("before xtdispatch");
4707 #if defined(TARGET_MOTIF)
4708  /* any updates to the menu buttons? Because of Linux threading
4709  issues, we try to make all updates come from 1 thread */
4710  frontendUpdateButtons();
4711 
4712  /* do the Xt events here. */
4713  while (XtAppPending(Xtcx)!= 0) {
4714  XtAppNextEvent(Xtcx, &event);
4715 #ifdef XEVENT_VERBOSE
4716  XButtonEvent *bev;
4717  XMotionEvent *mev;
4718  switch (event.type) {
4719  case MotionNotify:
4720  mev = &event.xmotion;
4721  TRACE_MSG("mouse motion event: win=%u, state=%d\n",mev->window, mev->state);
4722  break;
4723  case ButtonPress:
4724  case ButtonRelease:
4725  bev = &event.xbutton;
4726  TRACE_MSG("mouse button event: win=%u, state=%d\n",bev->window, bev->state);
4727  break;
4728  }
4729 #endif /* XEVENT_VERBOSE */
4730 
4731  DEBUG_XEV("EVENT through XtDispatchEvent\n");
4732  XtDispatchEvent (&event);
4733  }
4734 
4735 #endif /* TARGET_MOTIF */
4736 #endif /* KEEP_X11_INLIB */
4737 
4738 
4739  /* Viewer move viewpoint */
4740  handle_tick();
4741 
4742  PRINT_GL_ERROR_IF_ANY("after handle_tick")
4743  /* setup Projection and activate ProximitySensors */
4744  if (p->onScreen)
4745  {
4746  render_pre();
4747  slerp_viewpoint(3); //does explore / lookat vp slerp
4748 
4749  }
4750 
4751  if (p->doEvents) {
4752  /* and just parsed nodes needing binding? */
4753  SEND_BIND_IF_REQUIRED(tg->ProdCon.setViewpointBindInRender)
4754  SEND_BIND_IF_REQUIRED(tg->ProdCon.setFogBindInRender)
4755  SEND_BIND_IF_REQUIRED(tg->ProdCon.setBackgroundBindInRender)
4756  SEND_BIND_IF_REQUIRED(tg->ProdCon.setNavigationBindInRender)
4757 
4758  /* handle ROUTES - at least the ones not generated in do_first() */
4759  do_first(); //propagate events called from do_first
4760 
4761  /* Javascript events processed */
4762  process_eventsProcessed();
4763 
4764  #if !defined(EXCLUDE_EAI)
4765  // the fwlio_SCK* funcs to get data into the system, and calls the fwl_EAI*
4766  // funcs to give the data to the EAI.
4767  //
4768  // Actions are now separate so that file IO is not tightly coupled
4769  // via shared buffers and file descriptors etc. 'The core' now calls
4770  // Although the MIDI code and the EAI code are basically the same
4771  // and one could compress them into a loop, for the moment keep
4772  // them seperate to serve as a example for any extensions...
4773  // handle_EAI();
4774  {
4775  int socketVerbose = fwlio_RxTx_control(CHANNEL_EAI, RxTx_GET_VERBOSITY) ;
4776 
4777  if ( socketVerbose <= 1 || (socketVerbose > 1 && ((p->slowloop_count % 256) == 0)) ) {
4778  if(fwlio_RxTx_control(CHANNEL_EAI, RxTx_REFRESH) == 0) {
4779  /* Nothing to be done, maybe not even running */
4780  if ( socketVerbose > 1 ) {
4781  printf("%s:%d Nothing to be done\n",__FILE__,__LINE__) ;
4782  }
4783  } else {
4784  if ( socketVerbose > 1 ) {
4785  printf("%s:%d Test RxTx_PENDING\n",__FILE__,__LINE__) ;
4786  }
4787  if(fwlio_RxTx_control(CHANNEL_EAI, RxTx_PENDING) > 0) {
4788  char *tempEAIdata;
4789  if ( socketVerbose != 0 ) {
4790  printf("%s:%d Something pending\n",__FILE__,__LINE__) ;
4791  }
4792  tempEAIdata = fwlio_RxTx_getbuffer(CHANNEL_EAI) ;
4793  if(tempEAIdata != (char *)NULL) {
4794  char * replyData;
4795  int EAI_StillToDo;
4796  if ( socketVerbose != 0 ) {
4797  printf("%s:%d Something for EAI to do with buffer addr %p\n",__FILE__,__LINE__,tempEAIdata ) ;
4798  }
4799  // Every incoming command has a reply,
4800  // and the reply is synchronous.
4801  replyData = fwl_EAI_handleBuffer(tempEAIdata);
4802  FREE(tempEAIdata) ;
4803  EAI_StillToDo = 1;
4804  do {
4805  if(replyData != NULL && strlen(replyData) != 0) {
4806  fwlio_RxTx_sendbuffer(__FILE__,__LINE__,CHANNEL_EAI, replyData) ;
4807  FREE(replyData) ;
4808  // Note: fwlio_RxTx_sendbuffer() can also be called async
4809  // due to a listener trigger within routing, but it is
4810  // is up to that caller to clean out its own buffers.
4811  }
4812  EAI_StillToDo = fwl_EAI_allDone();
4813  if(EAI_StillToDo) {
4814  if ( socketVerbose != 0 ) {
4815  printf("%s:%d Something still in EAI buffer? %d\n",__FILE__,__LINE__,EAI_StillToDo ) ;
4816  }
4817  replyData = fwl_EAI_handleRest();
4818  }
4819  } while(EAI_StillToDo) ;
4820  } //temEAIdata
4821  } //fwlio PENDING
4822  } //fwlio REFRESH
4823  } //socketverbose
4824  }
4825  #endif //EXCLUDE_EAI
4826  } //doEvents
4827 
4828 #ifdef RENDERVERBOSE
4829  printf("RENDER STEP----------\n");
4830 #endif
4831 
4832  rbp_run_physics();
4833 
4834  /* ensure depth mask turned on here */
4835  //FW_GL_DEPTHMASK(GL_TRUE);
4836  //PRINT_GL_ERROR_IF_ANY("after depth")
4837 
4838 }
4839 void set_viewmatrix0(int iplace);
4840 struct Touch *currentTouch();
4841 void setup_picking(){
4842  /* Dec 15, 2015 update: variables have been vectorized in this function to match multi-touch,
4843  however multitouch with touch sensors doesn't work yet - you can have ID=0 for navigation
4844  and ID=1 for a single touch/drag. But you can't have 2 touches at the same time:
4845  - sendSensorEvents > get_hyperhit Renderfuncs.hp,.hpp etc needs to also be vectorized
4846  somehow so each drag and hyperdrag is per-touch. Then you could have multiple simaltaneous touches
4847  */
4848  int windex;
4849  ttglobal tg = gglobal();
4850  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
4851 
4852  windex = p->windex;
4853  /* handle_mouse events if clicked on a sensitive node */
4854  if (tg->Mainloop.HaveSensitive && !Viewer()->LookatMode && !tg->Mainloop.SHIFT) {
4855  struct X3D_Node *sensornode;
4856  int x,yup,ktouch,priorclaimants;
4857  struct Touch *touch;
4858 
4859  priorclaimants = TOUCHCLAIMANT_PEDAL;
4860  for(ktouch=0;ktouch<p->ntouch;ktouch++){
4861  touch = &p->touchlist[ktouch];
4862  if(!touch->inUse) continue;
4863 
4864  if(touch->windex != windex) continue; //return;
4865  if(touch->stageId != current_stageId()) continue;
4866 
4867  x = touch->x;
4868  yup = touch->y;
4869  if(touch->claimant == TOUCHCLAIMANT_SENSOR || (touch->claimant == TOUCHCLAIMANT_UNCLAIMED && touch->passed == priorclaimants)) {
4870  //ConsoleMessage("setup_picking x %d y %d ID %d but %d mev %d\n",touch->x,touch->y,touch->ID,touch->buttonState[LMB],touch->mev);
4871  if(setup_pickside(x,yup)){
4872  // There can be multiple paths to a parent transform of a sensor node:
4873  // touch 1:M path M:1 transform/parent 1:M SensorEvent M:1 Sensor
4874  // However, for a given touch, there is only one winning hit,
4875  // and only one winning path through the transform stack:
4876  // touch 1:1 winning_path 1:1 winning-transform/parent 1:1 winning_hitpoint 1:M SensorEvent M:1 Sensor
4877 
4878  setup_projection();
4879  setup_pickray(x,yup);
4880  //setup_viewpoint();
4881  set_viewmatrix0(1);
4882  tg->RenderFuncs.hypersensitive = touch->hypersensitive;
4883  tg->RenderFuncs.hyperhit = touch->hyperhit;
4884  //new shortcut way, skips render_hier on hyper pass
4885  if(!touch->hyperhit){
4886  //sensor pass: on ButtonPress, and isOver
4887  render_hier(rootNode(),VF_Sensitive | VF_Geom);
4888  touch->CursorOverSensitive = getRayHit();
4889  memcpy( touch->justModel, ((struct currayhit *)(tg->RenderFuncs.rayHit))->justModel, 16 * sizeof(double));
4890  memcpy( &touch->hp, tg->RenderFuncs.hp, sizeof(struct point_XYZ));
4891  }else{
4892  //hyperhit pass: already buttondown on a dragsensor and touch or viewpoint moves
4893  touch->CursorOverSensitive = NULL; //hyper pass
4894  memcpy(((struct currayhit *)(tg->RenderFuncs.rayHit))->justModel, touch->justModel, 16 * sizeof(double));
4895  memcpy( tg->RenderFuncs.hp, &touch->hp, sizeof(struct point_XYZ));
4896  }
4897 
4898  //double-check navigation, which may have already started
4899  if(touch->dragStart){
4900  if(touch->CursorOverSensitive || fwl_getHover()){
4901  touch->claimant = TOUCHCLAIMANT_SENSOR;
4902  }else{
4903  touch->passed |= TOUCHCLAIMANT_SENSOR;
4904  }
4905  }
4906  //if (p->CursorOverSensitive)
4907  // ConsoleMessage("setup_picking x %d y %d ID %d but %d mev %d\n", touch->x, touch->y, touch->ID, touch->buttonState[LMB], touch->mev);
4908 
4909  /* for nodes that use an "isOver" eventOut... */
4910  if (touch->lastOver != touch->CursorOverSensitive) {
4911  #ifdef VERBOSE
4912  printf ("%lf over changed, p->lastOver %u p->cursorOverSensitive %u, p->butDown1 %d\n",
4913  TickTime(), (unsigned int) touch->lastOver, (unsigned int) touch->CursorOverSensitive,
4914  touch->ButDown[p->currentCursor][1]);
4915  #endif
4916  //ConsoleMessage("isOver changing\n");
4917  //if (p->ButDown[p->currentCursor][1]==0) {
4918  if (touch->buttonState == 0) { //touch->buttonState[LMB]==0) {
4919 
4920  /* ok, when the user releases a button, cursorOverSensitive WILL BE NULL
4921  until it gets sensed again. So, we use the lastOverButtonPressed flag to delay
4922  sending this flag by one event loop loop. */
4923  if (!touch->lastOverButtonPressed) {
4924  sendSensorEvents(touch->lastOver, overMark, 0, FALSE);
4925  sendSensorEvents(touch->CursorOverSensitive, overMark, 0, TRUE);
4926  touch->lastOver = touch->CursorOverSensitive;
4927  }
4928  touch->lastOverButtonPressed = FALSE;
4929  } else {
4930  touch->lastOverButtonPressed = TRUE;
4931  }
4932  }
4933  #ifdef VERBOSE
4934  if (p->CursorOverSensitive != NULL)
4935  printf("COS %d (%s)\n", (unsigned int) p->CursorOverSensitive, stringNodeType(p->CursorOverSensitive->_nodeType));
4936  #endif /* VERBOSE */
4937 
4938  if(touch->claimant != TOUCHCLAIMANT_SENSOR) continue; //navigation touch
4939 
4940  /* did we have a click of button 1? */
4941  //if (p->ButDown[p->currentCursor][1] && (p->lastPressedOver==NULL)) {
4942  //if (touch->buttonState[LMB] && (touch->lastPressedOver==NULL)) {
4943  if (touch->dragStart && touch->buttonState && (touch->lastPressedOver==NULL)) {
4944  //ConsoleMessage("Not Navigation and 1 down\n");
4945  /* send an event of ButtonPress and isOver=true */
4946  touch->lastPressedOver = touch->CursorOverSensitive;
4947  sendSensorEvents(touch->lastPressedOver, ButtonPress, touch->dragStart, TRUE); //p->ButDown[p->currentCursor][1], TRUE);
4948  }
4949  //if ((p->ButDown[p->currentCursor][1]==0) && p->lastPressedOver!=NULL) {
4950  //if ((touch->buttonState[LMB]==0) && touch->lastPressedOver!=NULL) {
4951  if(touch->dragEnd && touch->lastPressedOver!=NULL) {
4952  //this shuts off hypersensitive
4953  //ConsoleMessage ("Not Navigation and 1 up\n");
4954  /* send an event of ButtonRelease and isOver=true;
4955  an isOver=false event will be sent below if required */
4956  sendSensorEvents(touch->lastPressedOver, ButtonRelease, touch->buttonState, TRUE); //p->ButDown[p->currentCursor][1], TRUE);
4957  touch->lastPressedOver = NULL;
4958  }
4959 
4960  if (TRUE) { // || p->lastMouseEvent[ID] == MotionNotify) {
4961  //ConsoleMessage ("Not Navigation and motion - going into sendSensorEvents\n");
4962  //Dec 18, 2015: we should _always_ come through here even when no mouse motion or events
4963  // because if mouse is (already) down on a dragsensor (planesensor) and something animates
4964  // the viewpoint -keyboard navigation, script, 3D mouse, HMD (head mounted display) then
4965  // we won't have a mouse event but the view matrix will change, causing the pickray
4966  // to move with respect to the dragsensor - in which case the sensor should emit events.
4967  /* TouchSensor hitPoint_changed needs to know if we are over a sensitive node or not */
4968  sendSensorEvents(touch->CursorOverSensitive,MotionNotify, touch->buttonState, TRUE); //p->ButDown[p->currentCursor][1], TRUE);
4969 
4970  /* PlaneSensors, etc, take the last sensitive node pressed over, and a mouse movement */
4971  sendSensorEvents(touch->lastPressedOver,MotionNotify, touch->buttonState, TRUE); //p->ButDown[p->currentCursor][1], TRUE);
4972  //p->lastMouseEvent[ID] = 0 ;
4973  }
4974 
4975  /* do we need to re-define cursor style? */
4976  /* do we need to send an isOver event? */
4977  sensornode = touch->lastPressedOver ? touch->lastPressedOver : touch->CursorOverSensitive;
4978  sendDescriptionToStatusBar(sensornode);
4979  if (touch->CursorOverSensitive!= NULL) {
4980  //setSensorCursor();
4981 
4982  /* is this a new node that we are now over?
4983  don't change the node pointer if we are clicked down */
4984  if ((touch->lastPressedOver==NULL) && (touch->CursorOverSensitive != touch->oldCOS)) {
4985  //sendSensorEvents(p->oldCOS,MapNotify,p->ButDown[p->currentCursor][1], FALSE);
4986  sendSensorEvents(touch->oldCOS,MapNotify,touch->buttonState, FALSE);
4987  //sendSensorEvents(p->CursorOverSensitive,MapNotify,p->ButDown[p->currentCursor][1], TRUE);
4988  sendSensorEvents(touch->CursorOverSensitive,MapNotify,touch->buttonState, TRUE);
4989  touch->oldCOS = touch->CursorOverSensitive;
4990  // sendDescriptionToStatusBar(touch->CursorOverSensitive);
4991  //ConsoleMessage("in oldCOS A\n");
4992  }
4993  } else {
4994  /* hold off on cursor change if dragging a sensor */
4995  //if (touch->lastPressedOver != NULL) {
4996  // setSensorCursor();
4997  //} else {
4998  // setArrowCursor();
4999  //}
5000  /* were we over a sensitive node? */
5001  //if ((p->oldCOS!=NULL) && (p->ButDown[p->currentCursor][1]==0)) {
5002  //if ((touch->oldCOS != NULL) && (touch->buttonState[LMB]==0)) {
5003  if ((touch->oldCOS != NULL) && touch->buttonState == 0) { // touch->dragEnd) {
5004  sendSensorEvents(touch->oldCOS, MapNotify, touch->buttonState, FALSE); //p->ButDown[p->currentCursor][1], FALSE);
5005  /* remove any display on-screen */
5006  // sendDescriptionToStatusBar(NULL);
5007  touch->oldCOS = NULL;
5008  //ConsoleMessage("in oldCOS B\n");
5009  }
5010  }
5011  touch->hypersensitive = tg->RenderFuncs.hypersensitive;
5012  touch->hyperhit = tg->RenderFuncs.hyperhit;
5013  } //setup_pickside
5014  if(touch->dragStart){
5015  touch->dragStart = FALSE; //handled buttonPress above
5016  }
5017  if(touch->dragEnd){
5018  touch->dragEnd = FALSE; //handled buttonRelease above
5019  touch->inUse = FALSE; //garbage collect
5020  //setArrowCursor();
5021  }
5022  } //unclaimed or pick claimed
5023  } //ktouch loop
5024  } /* (!NavigationMode && HaveSensitive) */
5025  else if(Viewer()->LookatMode){
5026  //we need navigation to claim, so viewer_handle_lookat is called
5027  int ktouch, kcount, priorclaimants;
5028  int x, yup;
5029  struct Touch *touch;
5030  priorclaimants = TOUCHCLAIMANT_PEDAL;
5031  kcount = 0;
5032  //pick a target object to travel to
5033  //if(Viewer()->LookatMode == 1)
5034  // setLookatCursor();
5035  //else
5036  // setArrowCursor();
5037  for(ktouch=0;ktouch<p->ntouch;ktouch++){
5038  touch = &p->touchlist[ktouch];
5039  if(!touch->inUse) continue;
5040  if(touch->windex != windex) continue;
5041  if(touch->stageId != current_stageId()) continue;
5042  kcount++;
5043  if(touch->claimant == TOUCHCLAIMANT_UNCLAIMED && touch->passed == priorclaimants)
5044  touch->passed |= TOUCHCLAIMANT_SENSOR;
5045 
5046  if(Viewer()->LookatMode == 2 ){
5047  //p->currentCursor = 0;
5048  x = touch->x;
5049  yup = touch->y;
5050  if(setup_pickside(x,yup)){
5051  setup_projection();
5052  setup_pickray(x,yup);
5053  setup_viewpoint();
5054  set_viewmatrix();
5055  render_hier(rootNode(),VF_Sensitive | VF_Geom);
5056  getRayHitAndSetLookatTarget();
5057  }
5058  }
5059  }
5060 
5061  }else{
5062  //normal or navigation mode
5063  int ktouch, priorclaimants;
5064  struct Touch *touch;
5065  priorclaimants = TOUCHCLAIMANT_PEDAL;
5066  for(ktouch=0;ktouch<p->ntouch;ktouch++){
5067  touch = &p->touchlist[ktouch];
5068  if(!touch->inUse) continue;
5069  if(touch->claimant == TOUCHCLAIMANT_UNCLAIMED && touch->passed == priorclaimants)
5070  touch->passed |= TOUCHCLAIMANT_SENSOR;
5071  }
5072  //setArrowCursor();
5073  }
5074 
5075 }
5076 
5077 
5078 void (*handlePTR)(const int mev, const unsigned int button, const float x, const float y) = handle0;
5079 void handle(const int mev, const unsigned int button, const float x, const float y)
5080 {
5081  handlePTR(mev, button, x, y);
5082 }
5083 
5084 /* get setup for rendering. */
5085 
5086 void SSR_test_cumulative_pose();
5087 static void render_pre() {
5088  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
5089 
5090  /* 1. Set up projection */
5091  // Nov 2015 moved render(): setup_projection(); //FALSE,0,0);
5092 
5093 
5094  /* 2. Headlight, initialized here where we have the modelview matrix to Identity.
5095  FIXME: position of light sould actually be offset a little (towards the center)
5096  when in stereo mode. */
5097 
5098  if (fwl_get_headlight()) {
5099  setLightState(HEADLIGHT_LIGHT,TRUE);
5100  setLightType(HEADLIGHT_LIGHT,2); // DirectionalLight
5101  }
5102 
5103 
5105  //setup_viewpoint();
5106  /* need this to render collisions correctly
5107  x Oct 2015 change: rely on last frame's results for this frames collision*/
5108 
5109 #ifdef SSR_SERVER
5110  //just for a diagnostic test of transforms - replaces modelview matrix with one formed from cumQuat,cumTrans
5111  if(0){
5112  static double toggleTime = 0.0;
5113  static int runTest = 0;
5114  double dtime;
5115  dtime = TickTime();
5116  if(dtime - toggleTime > 5.0){
5117  //alternate between ordinary view and test view every 5 seconds, to visually compare
5118  runTest = 1 - runTest;
5119  toggleTime = dtime;
5120  }
5121  if(runTest) SSR_test_cumulative_pose();
5122  }
5123 #endif
5124 
5125 
5126  /* 4. Collisions */
5127  if (fwl_getCollision() == 1) {
5128  profile_start("collision");
5129  render_collisions(Viewer()->type);
5130  profile_end("collision");
5131  // setup_viewpoint(); //see 5 lines below
5132  }
5133 
5134  /* 3. Viewpoint */
5135  /* unconditionally update viewer position after collision, to*/
5136  /* give accurate info to Proximity sensors.*/
5137  setup_viewpoint(); //Oct 2015: now this is the only setup_viewpoint per frame (set_viewmatrix() does shortcut)
5138 
5139  /* 5. render hierarchy - proximity */
5140  if (p->doEvents)
5141  {
5142  profile_start("hier_prox");
5143  render_hier(rootNode(), VF_Proximity);
5144  profile_end("hier_prox");
5145  }
5146 
5147  //drawStatusBar();
5148  PRINT_GL_ERROR_IF_ANY("GLBackend::render_pre");
5149 }
5150 int pointinsideviewport(ivec4 vp, ivec2 pt);
5151 int setup_pickside0(int x, int y, int *iside, ivec4 *vportleft, ivec4 *vportright){
5152  /* Oct 2015 idea: change which stereo side the pickray is working on,
5153  based on which stereo side the mouse is in
5154  - only makes a difference for updown and sidebyside
5155  - analgyph and quadbuffer use the whole screen, so can use either
5156  -- there's now an explicit userPrefferedPickSide (versus always using right)
5157  */
5158  int sideleft, sideright, userPreferredPickSide, ieither;
5159  ivec4 vport, vportscene;
5160  ivec2 pt;
5161  Stack *vportstack;
5162  X3D_Viewer *viewer;
5163 
5164  ttglobal tg = gglobal();
5165  viewer = Viewer();
5166  userPreferredPickSide = viewer->dominantEye; //0= left, 1= right
5167  ieither = viewer->eitherDominantEye;
5168 
5169  //pt = ivec2_init(x,tg->display.screenHeight - y);
5170  pt = ivec2_init(x,y);
5171  vportstack = (Stack*)tg->Mainloop._vportstack;
5172  vport = stack_top(ivec4,vportstack); //should be same as stack bottom, only one on stack here
5173  vportscene = vport;
5174  vportscene.Y = vport.Y + tg->Mainloop.clipPlane;
5175  vportscene.H = vport.H - tg->Mainloop.clipPlane;
5176 
5177  *vportleft = vportscene;
5178  *vportright = vportscene;
5179  if(viewer->isStereo)
5180  {
5181  if (viewer->sidebyside){
5182  vportleft->W /= 2;
5183  vportright->W /=2;
5184  vportright->X = vportleft->X + vportleft->W;
5185  }
5186  if(viewer->updown) { //overunder
5187  vportscene = vport;
5188  vportscene.H /=2;
5189  *vportright = vportscene;
5190  vportright->Y += tg->Mainloop.clipPlane;
5191  vportright->H -= tg->Mainloop.clipPlane;
5192  *vportleft = *vportright;
5193  //vportright.Y = vportleft.Y + vportright.H;
5194  vportleft->Y += vportscene.H;
5195  }
5196  //analgyph and quadbuffer use full window
5197  }
5198  sideleft = sideright=0;
5199  sideleft = pointinsideviewport(*vportleft,pt);
5200  sideright = pointinsideviewport(*vportright,pt);;
5201  if(sideleft && sideright)
5202  *iside = userPreferredPickSide; //analgyph, quadbuffer
5203  else
5204  *iside = sideleft? 0 : sideright ? 1 : 0;
5205  if(!ieither) *iside = userPreferredPickSide;
5206  return sideleft || sideright; //if the mouse is outside graphics window, stop tracking it
5207 }
5208 static int setup_pickside(int x, int y){
5209  ivec4 vpleft, vpright;
5210  int iside, inside;
5211  iside = 0;
5212  inside = setup_pickside0(x,y,&iside,&vpleft,&vpright);
5213  //Viewer()->iside = iside;
5214  return inside;
5215 }
5216 void fw_gluPerspective_2(GLDOUBLE xcenter, GLDOUBLE fovy, GLDOUBLE aspect, GLDOUBLE zNear, GLDOUBLE zFar);
5217 void setup_projection()
5218 {
5219  /* setup_project transfers values from viewer struct to gl_projection matrix
5220  The values get into viewer 2 ways:
5221  1. parsing > new viewer > defaults -> viewer
5222  2. bound viewpoint -(prep_viewpoint)-> viewer
5223  Then here
5224  viewer -> (setup_projection) -> projection matrix
5225  */
5226  GLDOUBLE fieldofview2;
5227  GLint xvp;
5228  GLint scissorxl,scissorxr;
5229  Stack *vportstack;
5230  ivec4 vport;
5231  ppMainloop p;
5232  X3D_Viewer *viewer;
5233  ttglobal tg = gglobal();
5234  GLsizei screenwidth2; // = tg->display.screenWidth;
5235  GLsizei screenheight, bottom, top;
5236  static int counter = 0;
5237  GLDOUBLE aspect2; // = tg->display.screenRatio;
5238  p = (ppMainloop)tg->Mainloop.prv;
5239  viewer = Viewer();
5240  vportstack = (Stack*)tg->Mainloop._vportstack;
5241  vport = stack_top(ivec4,vportstack); //should be same as stack bottom, only one on stack here
5242 
5243  screenwidth2 = vport.W; //tg->display.screenWidth
5244  xvp = vport.X;
5245  top = vport.Y + vport.H; //or .H - .Y? CHANGE OF MEANING used to be 0 at top of screen, now its more like screenHeight
5246  bottom = vport.Y + tg->Mainloop.clipPlane;
5247  screenheight = top - bottom; //tg->display.screenHeight - bottom;
5248  //printf("sw %d sh %d x %d y %d\n",screenwidth2,screenheight,xvp,bottom);
5249  PRINT_GL_ERROR_IF_ANY("XEvents::start of setup_projection");
5250 
5251  scissorxl = xvp;
5252  scissorxr = xvp + screenwidth2;
5253  fieldofview2 = viewer->fieldofview;
5254 
5255  aspect2 = (double)(scissorxr - scissorxl)/(double)(screenheight);
5256 
5257  if(viewer->type==VIEWER_SPHERICAL)
5258  fieldofview2*=viewer->fovZoom;
5259  if(viewer->isStereo)
5260  {
5261  GLint xl,xr;
5262  xl = xvp;
5263  xr = xvp + screenwidth2;
5264 
5265  if (viewer->sidebyside){
5266  GLint iexpand;
5267  bool expand;
5268  double expansion;
5269  //its just sidebyside that needs the screen distance adjusted to be slightly less than human eyebase
5270  //(the others can center their viewpoints in the viewports, and center the viewports on the screen)
5271  //assume: the viewpoint is centered in the viewport
5272  //there are 2 viewports, one for left and one for right
5273  //so if you want to spread the screen eyebase out,
5274  //you need to expand the viewport(s) horizontally by 2x
5275  // in the direction you want it to move
5276  //for example to move the left viewpoint left, you expand the left viewport
5277  //on the left side by 2x (and move the right side of the right viewport to the right)
5278  //to move the left viewpoint right, move the right side of the left viewport
5279  //to the right by 2x.
5280  //except in sidebyside, that would cause an over-write in the middle, and changes
5281  //to aspect2 ratio can change the field of view
5282  //so for sidebyside, we make the viewports normal screenwidth2 wide and
5283  //use scissor test to crop to the viewports
5284  expand = viewer->screendist > .5f;
5285  expansion = viewer->screendist - .5;
5286  expansion = fabs(expansion);
5287  iexpand = (GLint)(expansion * screenwidth2);
5288 
5289  xr -= screenwidth2/4;
5290  xl -= screenwidth2/4;
5291  scissorxr = xvp + screenwidth2/2;
5292  if(viewer->iside ==1)
5293  {
5294  xl += screenwidth2/2;
5295  xr += screenwidth2/2;
5296  scissorxl += screenwidth2/2;
5297  scissorxr += screenwidth2/2;
5298  }
5299  if(expand)
5300  {
5301  if(viewer->iside ==1)
5302  xr = xr + iexpand;
5303  else
5304  xl = xl - iexpand;
5305  }else{
5306  if(viewer->iside ==1)
5307  xl = xl - iexpand;
5308  else
5309  xr = xr + iexpand;
5310  }
5311 
5312  }
5313  if(viewer->updown) //overunder
5314  {
5315  //if there's statusabarHud statusbar to be drawn, reserve space in both viewports
5316  screenheight = vport.H; // tg->display.screenHeight;
5317  screenheight /= 2;
5318  if (viewer->iside == 0){
5319  bottom += screenheight;
5320  }else{
5321  top -= screenheight; //+=
5322  }
5323  screenheight -= tg->Mainloop.clipPlane;
5324  scissorxl = xl;
5325  scissorxr = xr;
5326  counter++;
5327  if(counter == 100)
5328  printf("in setup_projection\n");
5329 
5330  }
5331  aspect2 = (double)(xr - xl)/(double)(screenheight);
5332  xvp = xl;
5333  screenwidth2 = xr-xl;
5334  }
5335  if(viewer->updownB)
5336  aspect2 *= .5;
5337  FW_GL_MATRIX_MODE(GL_PROJECTION);
5338 
5339  /* >>> statusbar hud */
5340  //if(tg->Mainloop.clipPlane != 0 || viewer->updown || viewer->sidebyside)
5341  if(0) if(TRUE) //conttenttypes assume we're going to scissor: statusbar, quadrant
5342  {
5343  /* scissor used to prevent mainloop from glClear()ing the wrong stereo side, and the statusbar area
5344  which is updated only every 10-25 loops */
5345  //FW_GL_SCISSOR(0,tg->Mainloop.clipPlane,tg->display.screenWidth,tg->display.screenHeight);
5346  FW_GL_SCISSOR(scissorxl,bottom,scissorxr-scissorxl,screenheight);
5347  glEnable(GL_SCISSOR_TEST);
5348  }
5349 
5350  /* <<< statusbar hud */
5351  // side-by-side eyebase fiducials (see fiducialDraw())
5352  p->viewpointScreenX[viewer->iside] = xvp + screenwidth2/2;
5353  p->viewpointScreenY[viewer->iside] = top; //yup now //tg->display.screenHeight - top; //fiducial draw still using top-down Y
5354  if (viewer->updown){
5355  FW_GL_VIEWPORT(xvp - screenwidth2 / 2, bottom, screenwidth2 * 2, screenheight);
5356  }
5357  else{
5358  FW_GL_VIEWPORT(xvp, bottom, screenwidth2, screenheight);
5359  }
5360 
5361  FW_GL_LOAD_IDENTITY();
5362 
5363  /* ortho projection or perspective projection? */
5364  if (viewer->ortho) {
5365  double minX, maxX, minY, maxY;
5366  double numerator;
5367 
5368  minX = viewer->orthoField[0];
5369  minY = viewer->orthoField[1];
5370  maxX = viewer->orthoField[2];
5371  maxY = viewer->orthoField[3];
5372 
5373  if (screenheight != 0) {
5374  //aspect ratio correction for ortho
5375  numerator = (maxY - minY) * ((float) screenwidth2) / ((float) screenheight);
5376  maxX = numerator/2.0f;
5377  minX = -(numerator/2.0f);
5378  }
5379 
5380  FW_GL_ORTHO (minX, maxX, minY, maxY,
5381  viewer->nearPlane,viewer->farPlane);
5382 
5383  } else {
5384  /* bounds check */
5385  if ((fieldofview2 <= 0.0) || (fieldofview2 > 180.0))
5386  fieldofview2=45.0;
5387  /* glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); */
5388  //printf ("Before FW_GLU_PERSPECTIVE, np %f fp %f\n",viewer->nearPlane, viewer->farPlane);
5389  if(0) FW_GLU_PERSPECTIVE(fieldofview2, aspect2, viewer->nearPlane,viewer->farPlane);
5390  if(1) fw_gluPerspective_2(viewer->xcenter,fieldofview2, aspect2, viewer->nearPlane,viewer->farPlane);
5391  tg->Mainloop.fieldOfView = (float)fieldofview2;
5392  }
5393  FW_GL_MATRIX_MODE(GL_MODELVIEW);
5394  PRINT_GL_ERROR_IF_ANY("XEvents::setup_projection");
5395 
5396 }
5397 
5398 void getPickrayXY(int *x, int *y){
5399  ttglobal tg = gglobal();
5400  *x = tg->Mainloop.pickray_x;
5401  *y = tg->Mainloop.pickray_y;
5402 
5403 }
5404 void setPickrayXY(int x, int y){
5405  ttglobal tg = gglobal();
5406  tg->Mainloop.pickray_x = x;
5407  tg->Mainloop.pickray_y = y;
5408 }
5409 
5410 void setup_pickray0()
5411 {
5412  //feature-AFFINE_GLU_UNPROJECT
5413  //2015: NEW WAY: leaves proj matrix as normal, and creates a separate affine PICKMATRIX that when multiplied with modelview,
5414  // will point down the pickray (see above for OLD WAY)
5415  // method: uproject 2 points along the ray, one on nearside of frustum (window z = 0)
5416  // one on farside of frustum (window z = 1)
5417  // then the first one is A, second one is B
5418  // create a translation matrix T to get from 0,0,0 to A (non-zero for ortho viewpoint)
5419  // create a rotation matrix R to get from A toward B
5420  // pickmatrix = R * T
5421  //Jan 2016 issue: with the new Layering/Layout component, all the unproject stuff changes
5422  // when traveling up/down the render_hier: viewport changes with Viewport standalone node
5423  // and viewport field of layer and layoutlayer; the projection matrix and viewpoint changes with
5424  // the push/pop of binding stacks for each Layer node; To get it working
5425  // I've had to call this at each layer on the way down and up, in prep_ and fin_Viewpoint
5426  // and likely in prep/fin of layer and layoutlayer for the projection and viewpoint changes
5427  // Therefore attempts below to avoid glu_unproject calls by capturing prepared matrices
5428  // may need more work to fully optimize.
5429  // Generally: opengl is optimized for transforming geometry into screen space, and when
5430  // going the other way -with a pickray- glu_uproject style inversions are needed.
5431  // Perhaps the function needs to be simplified to do just glu_unprojects, perhaps
5432  // doing a single inverse, and applying to both points ie glu_unproject_matrixOnly()?
5433  double mvident[16], pickMatrix[16], pmi[16], proj[16], R1[16], R2[16], R3[16], T[16];
5434  int viewport[4], x, y;
5435  double A[3], B[3], C[3], a[3], b[3];
5436  double yaw, pitch, yy,xx;
5437  //OLDCODE ttglobal tg = gglobal();
5438 
5439  getPickrayXY(&x,&y);
5440  loadIdentityMatrix(mvident);
5441  FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
5442  FW_GL_GETINTEGERV(GL_VIEWPORT,viewport);
5443  //yy = (float)viewport[3] -y + bottom +top;
5444  //glu_unproject will subtract the viewport from the x,y, if they're all in y-up screen coords
5445  //yy = (float)(tg->display.screenHeight - y); //y-up - bottom
5446  yy = (float)y; //yup
5447  xx = (float)x;
5448  //printf("vp = %d %d %d %d\n",viewport[0],viewport[1],viewport[2],viewport[3]);
5449  //printf("yy %lf vp3 %d y %d vp1 %d sh %d\n",
5450  // yy, viewport[3], y, viewport[1], tg->display.screenHeight);
5451  //nearside point
5452  a[0] = xx; a[1] = yy; a[2] = 0.0;
5453  FW_GLU_UNPROJECT(a[0], a[1], a[2], mvident, proj, viewport,
5454  &A[0],&A[1],&A[2]);
5455  mattranslate(T,A[0],A[1],A[2]);
5456  //farside point
5457  b[0] = xx; b[1] = yy; b[2] = 1.0;
5458  FW_GLU_UNPROJECT(b[0], b[1], b[2], mvident, proj, viewport,
5459  &B[0],&B[1],&B[2]);
5460  vecdifd(C,B,A);
5461  vecnormald(C,C);
5462  if(0) printf("Cdif %f %f %f\n",C[0],C[1],C[2]);
5463  //if(1){
5464  // double hypotenuse = sqrt(C[0]*C[0] + C[2]*C[2]);
5465  // yaw = asin(C[0]/hypotenuse);
5466  // hypotenuse = sqrt(C[1]*C[1] + C[2]*C[2]);
5467  // pitch = asin(C[1]/hypotenuse);
5468  // if(1) printf("asin yaw=%f pitch=%f\n",yaw,pitch);
5469  //}
5470  yaw = atan2(C[0],-C[2]);
5471  matrixFromAxisAngle4d(R1, -yaw, 0.0, 1.0, 0.0);
5472  if(1){
5473  transformAFFINEd(C,C,R1);
5474  if(0) printf("Yawed Cdif %f %f %f\n",C[0],C[1],C[2]);
5475  pitch = atan2(C[1],-C[2]);
5476  }else{
5477  double hypotenuse = sqrt(C[0]*C[0] + C[2]*C[2]);
5478  pitch = atan2(C[1],hypotenuse);
5479  }
5480  if(0) printf("atan2 yaw=%f pitch=%f\n",yaw,pitch);
5481 
5482  pitch = -pitch;
5483  if(0) printf("[yaw=%f pitch=%f\n",yaw,pitch);
5484  if(0){
5485  matrotate(R1, -pitch, 1.0, 0.0, 0.0);
5486  matrotate(R2, -yaw, 0.0, 1.0, 0.0);
5487  }else{
5488  matrixFromAxisAngle4d(R1, pitch, 1.0, 0.0, 0.0);
5489  if(0) printmatrix2(R1,"pure R1");
5490  matrixFromAxisAngle4d(R2, yaw, 0.0, 1.0, 0.0);
5491  if(0) printmatrix2(R2,"pure R2");
5492  }
5493  matmultiplyAFFINE(R3,R1,R2);
5494  if(0) printmatrix2(R3,"R3=R1*R2");
5495  if(1){
5496  matmultiplyAFFINE(pickMatrix,R3, T);
5497  matinverseAFFINE(pmi,pickMatrix);
5498  //matinverseFULL(pmi,pickMatrix); //don't need extra FLOPS
5499  }else{
5500  //direct hacking of matrix, can save a few FLOPs
5501  R3[12] = A[0];
5502  R3[13] = A[1];
5503  R3[14] = A[2];
5504  matcopy(pickMatrix,R3);
5505  matinverseAFFINE(pmi,pickMatrix); //,R3);
5506  if(0)printmatrix2(R3,"R3[12]=A");
5507  }
5508  if(0) printmatrix2(pmi,"inverted");
5509  setPickrayMatrix(0,pickMatrix); //using pickmatrix in upd_ray and get_hyper
5510  setPickrayMatrix(1,pmi); //if using pickmatrix_inverse in upd_ray and get_hyper
5511  if(0){
5512  //Test: transform A,B and they should come out 0,0,x
5513  double rA[3], rB[3];
5514  transformAFFINEd(rA,A,pmi);
5515  transformAFFINEd(rB,B,pmi);
5516  printf(" A %f %f %f B %f %f %f \n",A[0],A[1],A[2],B[0],B[1],B[2]);
5517  printf("rA %f %f %f rB %f %f %f \n",rA[0],rA[1],rA[2],rB[0],rB[1],rB[2]);
5518  }
5519 
5520 }
5521 void upd_ray();
5522 void setup_pickray(int x, int y){
5523  setPickrayXY(x,y);
5524  setup_pickray0();
5525 }
5526 void generate_GeneratedCubeMapTextures();
5527 /* Render the scene */
5528 static void render()
5529 {
5530  int count;
5531  static double shuttertime;
5532  static int shutterside;
5533  X3D_Viewer *viewer;
5534  ppMainloop p;
5535  ttglobal tg = gglobal();
5536  p = (ppMainloop)tg->Mainloop.prv;
5537 
5538  generate_GeneratedCubeMapTextures();
5539  setup_projection();
5540  set_viewmatrix();
5541  update_navigation();
5542  setup_picking();
5543  viewer = Viewer();
5544  doglClearColor();
5545 
5546 
5547  for (count = 0; count < p->maxbuffers; count++) {
5548 
5549  viewer->buffer = (unsigned)p->bufferarray[count];
5550  viewer->iside = count;
5551 
5552  /* turn lights off, and clear buffer bits*/
5553  if(viewer->isStereo)
5554  {
5555 
5556  if(viewer->shutterGlasses == 2) /* flutter mode - like --shutter but no GL_STEREO so alternates */
5557  {
5558  if(TickTime() - shuttertime > 2.0)
5559  {
5560  shuttertime = TickTime();
5561  if(shutterside > 0) shutterside = 0;
5562  else shutterside = 1;
5563  }
5564  if(count != shutterside) continue;
5565  }
5566  if(viewer->anaglyph)
5567  {
5568  //set the channels for backbuffer clearing
5569  if(count == 0)
5570  Viewer_anaglyph_clearSides(); //clear all channels
5571  else
5572  Viewer_anaglyph_setSide(count); //clear just the channels we're going to draw to
5573  }
5574  setup_projection();
5575  BackEndClearBuffer(2); //scissor test in here
5576  if(Viewer()->anaglyph)
5577  Viewer_anaglyph_setSide(count); //set the channels for scenegraph drawing
5578  //setup_viewpoint();
5579  set_viewmatrix();
5580  }
5581  else
5582  BackEndClearBuffer(2);
5583  //BackEndLightsOff();
5584  clearLightTable();//turns all lights off- will turn them on for VF_globalLight and scope-wise for non-global in VF_geom
5585 
5586 
5587  /* turn light #0 off only if it is not a headlight.*/
5588  if (!fwl_get_headlight()) {
5589  setLightState(HEADLIGHT_LIGHT,FALSE);
5590  setLightType(HEADLIGHT_LIGHT,2); // DirectionalLight
5591  }
5592 
5593  /* Other lights*/
5594  PRINT_GL_ERROR_IF_ANY("XEvents::render, before render_hier");
5595 
5596  render_hier(rootNode(), VF_globalLight );
5597  PRINT_GL_ERROR_IF_ANY("XEvents::render, render_hier(VF_globalLight)");
5598  render_hier(rootNode(), VF_Other );
5599 
5600 
5601  /* 4. Nodes (not the blended ones)*/
5602  profile_start("hier_geom");
5603  render_hier(rootNode(), VF_Geom);
5604  profile_end("hier_geom");
5605  PRINT_GL_ERROR_IF_ANY("XEvents::render, render_hier(VF_Geom)");
5606 
5607  /* 5. Blended Nodes*/
5608  if (tg->RenderFuncs.have_transparency) {
5609  /* render the blended nodes*/
5610  render_hier(rootNode(), VF_Geom | VF_Blend);
5611  PRINT_GL_ERROR_IF_ANY("XEvents::render, render_hier(VF_Geom)");
5612  }
5613 
5614  if (viewer->isStereo) {
5615 #ifndef DISABLER
5616  if (viewer->sidebyside){
5617  //cursorDraw(1, p->viewpointScreenX[count], p->viewpointScreenY[count], 0.0f); //draw a fiducial mark where centre of viewpoint is
5618  //fiducialDraw(1,p->viewpointScreenX[count],p->viewpointScreenY[count],0.0f); //draw a fiducial mark where centre of viewpoint is
5619  fiducialDrawB(CURSOR_FIDUCIALS,p->viewpointScreenX[count],p->viewpointScreenY[count]); //draw a fiducial mark where centre of viewpoint is
5620  }
5621 #endif
5622  if (viewer->anaglyph)
5623  glColorMask(1,1,1,1); /*restore, for statusbarHud etc*/
5624  }
5625  } /* for loop */
5626  if(1){
5627  //render last known mouse position as seen by the backend
5628  int ktouch;
5629  //s_shader_capabilities_t *scap;
5630  struct Touch *touch; // = currentTouch(); //&p->touchlist[0];
5631  for(ktouch=0;ktouch<p->ntouch;ktouch++){
5632  touch = &p->touchlist[ktouch];
5633  if(touch->inUse){
5634  //if(touch->windex == current_windex)???
5635  if(touch->stageId == current_stageId()){
5636  //float angleDeg = fwl_getHover() ? 180.0f : 0.0f;
5637  //fiducialDraw(0, touch->x, touch->y, angleDeg);
5638  int cstyle;
5639  cstyle = CURSOR_DOWN;
5640  if(touch->buttonState == 0) cstyle = CURSOR_HOVER;
5641  if(touch->lastOverButtonPressed || touch->CursorOverSensitive)
5642  cstyle = CURSOR_OVER; //could differentiate isOver from touching and picking
5643  fiducialDrawB(cstyle,touch->x,touch->y);
5644  }
5645  }
5646  }
5647  }
5648 
5649 }
5650 
5651 
5652 
5653 static int currentViewerLandPort = 0;
5654 static int rotatingCCW = FALSE;
5655 static double currentViewerAngle = 0.0;
5656 static double requestedViewerAngle = 0.0;
5657 
5658 
5659 void setup_viewpoint_part1() {
5660 /*
5661  Computes view part of modelview matrix and leaves it in modelview.
5662  You would call this before traversing the scenegraph to scene nodes
5663  with render() or render_hier().
5664  The view part includes:
5665  a) screen orientation ie on mobile devices landscape vs portrait (this function)
5666  b) stereovision +- 1/2 base offset (viewer_togl)
5667  c) viewpoint slerping interpolation (viewer_togl)
5668  d) .Pos and .Quat of viewpoint from: (viewer_togl)
5669  1) .position and .orientation specified in scenefile
5670  2) cumulative navigation away from initial bound pose
5671  3) gravity and collision (bumping and wall penetration) adjustments
5672  e) transform stack between scene root and currently bound viewpoint (render_hier(rootnode,VF_Viewpoint))
5673 
5674 */
5675  bindablestack *bstack;
5676  X3D_Viewer *viewer;
5677  // OLDCODE ppMainloop p;
5678  ttglobal tg = gglobal();
5679  // OLDCODE p = (ppMainloop)tg->Mainloop.prv;
5680 
5681  bstack = getActiveBindableStacks(tg);
5682  viewer = Viewer();
5683  FW_GL_MATRIX_MODE(GL_MODELVIEW); /* this should be assumed , here for safety.*/
5684  FW_GL_LOAD_IDENTITY();
5685 
5686  // has a change happened?
5687  if (viewer->screenOrientation != currentViewerLandPort) {
5688  // 4 possible values; 0, 90, 180, 270
5689  //
5690  rotatingCCW = FALSE; // assume, unless told otherwise
5691  switch (currentViewerLandPort) {
5692  case 0: {
5693  rotatingCCW= (Viewer()->screenOrientation == 270);
5694  break;
5695  }
5696  case 90: {
5697  rotatingCCW = (Viewer()->screenOrientation == 0);
5698  break;
5699  }
5700  case 180: {
5701  rotatingCCW = (Viewer()->screenOrientation != 270);
5702  break;
5703  }
5704  case 270: {
5705  rotatingCCW = (Viewer()->screenOrientation != 0);
5706  break;
5707  }
5708  }
5709  currentViewerLandPort = viewer->screenOrientation;
5710  requestedViewerAngle = (double)viewer->screenOrientation;
5711  }
5712 
5713  if (!(APPROX(currentViewerAngle,requestedViewerAngle))) {
5714  if (rotatingCCW) {
5715  //printf ("ccw, cva %lf req %lf\n",currentViewerAngle, requestedViewerAngle);
5716  currentViewerAngle -= 10.0;
5717  if (currentViewerAngle < -5.0) currentViewerAngle = 360.0;
5718  } else {
5719  //printf ("cw, cva %lf req %lf\n",currentViewerAngle, requestedViewerAngle);
5720  currentViewerAngle +=10.0;
5721  if (currentViewerAngle > 365.0) currentViewerAngle = 0.0;
5722  }
5723  }
5724  FW_GL_ROTATE_D (currentViewerAngle,0.0,0.0,1.0);
5725  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->screenorientationmatrix);
5726 
5727 
5728  //capture stereo 1/2 base offsets
5729  //a) save current real stereo settings
5730  bstack->isStereo = viewer->isStereo;
5731  bstack->iside = viewer->iside;
5732  //b) fake each stereo side, capture each side's stereo offset matrix
5733  viewer->isStereo = 1;
5734  viewer->iside = 0;
5735  FW_GL_LOAD_IDENTITY();
5736  set_stereo_offset0();
5737  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->stereooffsetmatrix[0]);
5738 
5739  viewer->iside = 1;
5740  FW_GL_LOAD_IDENTITY();
5741  set_stereo_offset0();
5742  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->stereooffsetmatrix[1]);
5743  viewer->isStereo = 0;
5744  FW_GL_LOAD_IDENTITY();
5745 
5746  //capture cumulative .Pos, .Quat
5747  viewer_togl(viewer->fieldofview);
5748  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->posorimatrix);
5749 
5750  FW_GL_LOAD_IDENTITY();
5751 }
5752 struct X3D_Node *getActiveLayerBoundViewpoint(){
5753  struct X3D_Node *boundvp;
5754  bindablestack *bstack;
5755  ttglobal tg = gglobal();
5756  bstack = getActiveBindableStacks(tg);
5757  boundvp = NULL;
5758  if(bstack){
5759  if(bstack->viewpoint){
5760  if( vectorSize(bstack->viewpoint) > 0){
5761  boundvp = vector_back(struct X3D_Node*,bstack->viewpoint);
5762  }
5763  }
5764  }
5765  return boundvp; //should be Viewpoint, OrthoViewpoint, or GeoViewpoint
5766 }
5767 int render_foundLayerViewpoint(){
5768  //on render_VP pass we want to come out of render_hier as soon as we find our VP
5769  //that will save embarrassing 'adding' effect when bound VP is DEF/USED in multiple
5770  // scenegraph branches, and should also save time on average by not traversing
5771  // the entire scengraph after the vp is found
5772  int iret;
5773  struct X3D_Viewpoint *boundvp;
5774  //there can be a weird effect if the VP is DEF/USEd in 2 branches of the scenegraph
5775  //freewrl adds the 2 positions together - weird no one else does
5776  //so we'll set a flag on the viewpoint node, and if its already updated, we'll skip 2nd, third etc instances.
5777  boundvp = (struct X3D_Viewpoint*)getActiveLayerBoundViewpoint();
5778  //watch it - downcasting Node to one type of Viewpoint,
5779  // .. but could be any of Viewpoint, OrthoViewpoint, GeoViewpoint
5780  // - in perl the 3 types better have _donethispass at same offset, else figure the type and switch-case
5781  // - verified in perl, same offset for _donethispass
5782  iret = 0;
5783  if(boundvp)
5784  iret = boundvp->_donethispass;
5785  return iret;
5786 }
5787 void setup_viewpoint_part2() {
5788 /*
5789  Computes view part of modelview matrix and leaves it in modelview.
5790  You would call this before traversing the scenegraph to scene nodes
5791  with render() or render_hier().
5792  The view part includes:
5793  a) screen orientation ie on mobile devices landscape vs portrait (this function)
5794  b) stereovision +- 1/2 base offset (viewer_togl)
5795  c) viewpoint slerping interpolation (viewer_togl)
5796  d) .Pos and .Quat of viewpoint from: (viewer_togl)
5797  1) .position and .orientation specified in scenefile
5798  2) cumulative navigation away from initial bound pose
5799  3) gravity and collision (bumping and wall penetration) adjustments
5800  e) transform stack between scene root and currently bound viewpoint (render_hier(rootnode,VF_Viewpoint))
5801 
5802 */
5803  // OLDCODE ppMainloop p;
5804  struct X3D_Viewpoint *boundvp;
5805  // OLDCODE ttglobal tg = gglobal();
5806  // OLDCODE p = (ppMainloop)tg->Mainloop.prv;
5807 
5808 
5809  //capture view part of modelview ie scenegraph transforms between scene root and bound viewpoint
5810  profile_start("vp_hier");
5811  //there can be a weird effect if the VP is DEF/USEd in 2 branches of the scenegraph
5812  //freewrl adds the 2 positions together - weird no one else does
5813  //so we'll set a flag on the viewpoint node, and if its already updated, we'll skip 2nd, third etc instances.
5814  //printf("\npart2>>>\n");
5815  boundvp = (struct X3D_Viewpoint*)getActiveLayerBoundViewpoint();
5816  if(boundvp)
5817  boundvp->_donethispass = 0; //used in prep_Viewpoint
5818  render_hier(rootNode(), VF_Viewpoint);
5819  if(boundvp)
5820  boundvp->_donethispass = 0; //used in prep_Viewpoint
5821  //printf("\n<<<part2\n");
5822 
5823  profile_end("vp_hier");
5824 
5825 }
5826 
5827 void setup_viewpoint_part3() {
5828 /*
5829  Computes view part of modelview matrix and leaves it in modelview.
5830  You would call this before traversing the scenegraph to scene nodes
5831  with render() or render_hier().
5832  The view part includes:
5833  a) screen orientation ie on mobile devices landscape vs portrait (this function)
5834  b) stereovision +- 1/2 base offset (viewer_togl)
5835  c) viewpoint slerping interpolation (viewer_togl)
5836  d) .Pos and .Quat of viewpoint from: (viewer_togl)
5837  1) .position and .orientation specified in scenefile
5838  2) cumulative navigation away from initial bound pose
5839  3) gravity and collision (bumping and wall penetration) adjustments
5840  e) transform stack between scene root and currently bound viewpoint (render_hier(rootnode,VF_Viewpoint))
5841 
5842 */
5843  double viewmatrix[16];
5844  bindablestack *bstack;
5845  //OLDCODE X3D_Viewer *viewer;
5846  //OLDCODE ppMainloop p;
5847  ttglobal tg = gglobal();
5848  //OLDCODE p = (ppMainloop)tg->Mainloop.prv;
5849 
5850  bstack = getActiveBindableStacks(tg);
5851  //OLDCODE viewer = Viewer();
5852  PRINT_GL_ERROR_IF_ANY("XEvents::setup_viewpoint");
5853  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->viewtransformmatrix);
5854 
5855  //if(0){
5856  // isStereo = bstack->isStereo;
5857  // iside = bstack->iside;
5858 
5860  // viewer->isStereo = bstack->isStereo;
5861  // viewer->iside = iside;
5862  //}
5863  //multiply it all together, and capture any slerp
5864  //Feb 2016 - I think we should slerp the main/normal position of the viewpoint.
5865  // - then if its stereo, offset by half-base during rendernig or picking
5866  matcopy(viewmatrix,bstack->screenorientationmatrix);
5867  //if(0) if(isStereo)
5868  // matmultiplyAFFINE(viewmatrix,bstack->stereooffsetmatrix[iside],viewmatrix);
5869  matmultiplyAFFINE(viewmatrix,bstack->posorimatrix,viewmatrix);
5870  matmultiplyAFFINE(viewmatrix,bstack->viewtransformmatrix,viewmatrix);
5871  fw_glSetDoublev(GL_MODELVIEW_MATRIX, viewmatrix);
5872 
5873  if(slerp_viewpoint(2)) //just starting block, does vp-bind type slerp
5874  fw_glGetDoublev(GL_MODELVIEW_MATRIX, bstack->viewtransformmatrix);
5875 
5876 }
5877 void setup_viewpoint(){
5878  //printf("\nsetup_viewpoint>>>>>\n");
5879  setup_viewpoint_part1();
5880  setup_viewpoint_part2();
5881  setup_viewpoint_part3();
5882  //printf("\n<<<<setup_viewpoint\n");
5883 
5884 }
5885 
5886 void set_viewmatrix0(int iplace) {
5887  //if we already computed view matrix earlier in the frame via setup_viewpoint,
5888  //and theoretically it hasn't changed since,
5889  //and just want to make sure its set, this is shorter than re-doing setup_viewpoint()
5890  double viewmatrix[16];
5891  bindablestack *bstack;
5892  X3D_Viewer *viewer;
5893  // OLDCODE ppMainloop p;
5894  ttglobal tg = gglobal();
5895  // OLDCODE p = (ppMainloop)tg->Mainloop.prv;
5896 
5897  bstack = getActiveBindableStacks(tg);
5898  viewer = Viewer();
5899  FW_GL_MATRIX_MODE(GL_MODELVIEW); /* this should be assumed , here for safety.*/
5900  matcopy(viewmatrix,bstack->screenorientationmatrix);
5901  if(viewer->isStereoB){
5902  int iside = Viewer()->isideB;
5903  matmultiplyAFFINE(viewmatrix,bstack->stereooffsetmatrix[iside],viewmatrix);
5904  }
5905  if(viewer->isStereo){
5906  int iside = Viewer()->iside;
5907  matmultiplyAFFINE(viewmatrix,bstack->stereooffsetmatrix[iside],viewmatrix);
5908  }
5909  matmultiplyAFFINE(viewmatrix,bstack->posorimatrix,viewmatrix);
5910  matmultiplyAFFINE(viewmatrix,bstack->viewtransformmatrix,viewmatrix);
5911  fw_glSetDoublev(GL_MODELVIEW_MATRIX, viewmatrix);
5912 }
5913 void set_viewmatrix() {
5914  set_viewmatrix0(0);
5915 }
5916 
5917 
5918 char *nameLogFileFolderNORMAL(char *logfilename, int size){
5919  strcat(logfilename,"freewrl_tmp");
5920  fw_mkdir(logfilename);
5921  strcat(logfilename,"/");
5922  strcat(logfilename,"logfile");
5923  return logfilename;
5924 }
5925 char * (*nameLogFileFolderPTR)(char *logfilename, int size) = nameLogFileFolderNORMAL;
5926 
5927 void toggleLogfile()
5928 {
5929  ppMainloop p;
5930  ttglobal tg = gglobal();
5931  p = (ppMainloop)tg->Mainloop.prv;
5932  if(p->logging){
5933  fclose(p->logfile);
5934  //fclose(p->logerr);
5935  p->logging = 0;
5936 #ifdef _MSC_VER
5937  freopen("CON","w",stdout);
5938 #else
5939  //JAS - this does nothing, correct?
5940  // freopen("/dev/tty", "w", stdout);
5941 #endif
5942  //save p->logfname for reopening
5943  printf("logging off\n");
5944  }else{
5945  char *mode = "a+";
5946  if(p->logfname == NULL){
5947  char logfilename[1000];
5948  mode = "w";
5949  logfilename[0] = '\0';
5950  nameLogFileFolderPTR(logfilename, 1000);
5951  strcat(logfilename,".log");
5952  p->logfname = STRDUP(logfilename);
5953  }
5954  printf("logging to %s\n",p->logfname);
5955  p->logfile = freopen(p->logfname, mode, stdout );
5956  //p->logerr = freopen(p->logfname, mode, stderr );
5957  p->logging = 1;
5958  }
5959 }
5960 #if defined(_MSC_VER)
5961 #define strncasecmp _strnicmp
5962 #endif
5963 void fwl_set_logfile(char *lname){
5964  ppMainloop p;
5965  ttglobal tg = gglobal();
5966  p = (ppMainloop)tg->Mainloop.prv;
5967  if (strncasecmp(lname, "-", 1) == 0) {
5968  printf("FreeWRL: output to stdout/stderr\n");
5969  } else {
5970  p->logfname = STRDUP(lname);
5971  toggleLogfile();
5972  }
5973 }
5974 
5975 int unload_broto(struct X3D_Proto* node);
5976 struct X3D_Proto *hasContext(struct X3D_Node* node);
5977 void fwl_clearWorld(){
5978  //clear the scene to empty (and do cleanup on old scene);
5979  int done = 0;
5980  ttglobal tg = gglobal();
5981  {
5982  struct X3D_Node *rn = rootNode();
5983  if(hasContext(rn)){
5984  unload_broto(X3D_PROTO(rn));
5985  printf("unloaded scene as broto\n");
5986  done = 1;
5987  }
5988  }
5989  if(!done){
5990  tg->Mainloop.replaceWorldRequest = NULL;
5991  tg->threads.flushing = true;
5992  }
5993  return;
5994 }
5995 
5996 void sendKeyToKeySensor(const char key, int upDown);
5997 /* handle a keypress. "man freewrl" shows all the recognized keypresses */
5998 
5999 
6000 #define KEYDOWN 2
6001 #define KEYUP 3
6002 // OLD_IPHONE_AQUA #ifdef AQUA
6003 // OLD_IPHONE_AQUA #define KEYPRESS 2
6004 // OLD_IPHONE_AQUA #define isAqua 1
6005 // OLD_IPHONE_AQUA #else
6006 #define KEYPRESS 1
6007 #define isAqua 0
6008 // OLD_IPHONE_AQUA #endif
6009 
6010 char lookup_fly_key(int key);
6011 //#endif
6012 void dump_scenegraph(int method);
6013 void fwl_do_keyPress0(int key, int type) {
6014  int lkp;
6015  ppMainloop p;
6016  ttglobal tg = gglobal();
6017  p = (ppMainloop)tg->Mainloop.prv;
6018 
6019  /* does this X3D file have a KeyDevice node? if so, send it to it */
6020  //printf("fwl_do_keyPress: %c%d\n",kp,type);
6021  if(key == 27 && type == 1)
6022  {
6023  //ESC key to toggle back to freewrl command use of keyboard
6024  p->keySensorMode = 1 - p->keySensorMode; //toggle
6025  }
6026  if (p->keySensorMode && KeySensorNodePresent()) {
6027  sendKeyToKeySensor(key,type); //some keysensor test files show no opengl graphics, so we need a logfile
6028  } else {
6029  int handled = isAqua;
6030 
6031  if(p->keywait){
6032  if(type == KEYPRESS){
6033  //key,value commands
6034  //example: hit spacebar, then at the : prompt type keychord,yawz so it looks on the console:
6035  //:keychord,yawz
6036  //then press enter. Then if you use the arrow keys <> should turn left right, and ^v should go back/forth
6037  //here's a little hack so you can set any (pre-programmed) value from the keyboard in freewrl
6038  //by specifying key,value pair
6039  //to get the commandline, hit spacebar
6040  //then type the key, then the value, then hit Enter.
6041  //don't make mistakes typing - there's no backspace handling yet
6042  int len = strlen(p->keywaitstring);
6043  lkp = key;
6044  len = min(24,len); //dimensioned to 25
6045  if(lkp == '\r'){
6046  fwl_commandline(p->keywaitstring);
6047  p->keywait = FALSE;
6048  p->keywaitstring[0] = '\0';
6049  ConsoleMessage("%c",'\n');
6050  }else{
6051  ConsoleMessage("%c",lkp);
6052  if(lkp == '\b' && len){
6053  p->keywaitstring[len-1] = '\0';
6054  }else{
6055  p->keywaitstring[len] = lkp;
6056  p->keywaitstring[len+1] = '\0';
6057  }
6058  }
6059  }
6060  handled = TRUE;
6061  return;
6062  }
6063 
6064  if(type == KEYPRESS)
6065  {
6066  lkp = key;
6067  //normal key
6068  //if(kp>='A' && kp <='Z') lkp = tolower(kp);
6069  switch (lkp) {
6070  case 'n': { fwl_clearWorld(); break; }
6071  case 'e': { fwl_set_viewer_type (VIEWER_EXAMINE); break; }
6072  case 'w': { fwl_set_viewer_type (VIEWER_WALK); break; }
6073  case 'd': { fwl_set_viewer_type (VIEWER_FLY); break; }
6074  case 'f': { fwl_set_viewer_type (VIEWER_EXFLY); break; }
6075  case 'y': { fwl_set_viewer_type (VIEWER_SPHERICAL); break; }
6076  case 't': { fwl_set_viewer_type(VIEWER_TURNTABLE); break; }
6077  case 'm': { fwl_set_viewer_type(VIEWER_LOOKAT); break; }
6078  case 'g': { fwl_set_viewer_type(VIEWER_EXPLORE); break; }
6079  case 'h': { fwl_toggle_headlight(); break; }
6080  case '/': { print_viewer(); break; }
6081  //case '\\': { dump_scenegraph(); break; }
6082  case '\\': { dump_scenegraph(1); break; }
6083  case '|': { dump_scenegraph(2); break; }
6084  case '=': { dump_scenegraph(3); break; }
6085  case '+': { dump_scenegraph(4); break; }
6086  case '-': { dump_scenegraph(5); break; }
6087  case '`': { toggleLogfile(); break; }
6088  case '$': resource_tree_dump(0, (resource_item_t*)tg->resources.root_res); break;
6089  case '*': resource_tree_list_files(0, (resource_item_t*)tg->resources.root_res); break;
6090  case 'q': { if (!RUNNINGASPLUGIN) {
6091  fwl_doQuit(__FILE__,__LINE__);
6092  }
6093  break;
6094  }
6095  case 'c': { toggle_collision(); break;}
6096  case 'v': {fwl_Next_ViewPoint(); break;}
6097  case 'b': {fwl_Prev_ViewPoint(); break;}
6098  case '.': {profile_print_all(); break;}
6099  case ' ': p->keywait = TRUE; ConsoleMessage("\n%c",':'); p->keywaitstring[0] = '\0'; break;
6100  case ',': toggle_debugging_trigger(); break;
6101 #if !defined(FRONTEND_DOES_SNAPSHOTS)
6102  case 's': {fwl_toggleSnapshot(); break;}
6103  case 'x': {Snapshot(); break;} /* thanks to luis dias mas dec16,09 */
6104 #endif //FRONTEND_DOES_SNAPSHOTS
6105  //case '[': resource_dump(gglobal()->resources.root_res); break; //doesn't show 'tree', just rootres
6106  default:
6107  printf("didn't handle key=[%c][%d] type=%d\n",lkp,(int)lkp,type);
6108  handled = 0;
6109  break;
6110  }
6111  }
6112  if(!handled) {
6113  char kp;
6114  if(type/10 == 0){
6115  kp = (char)key; //normal keyboard key
6116  }else{
6117  kp = lookup_fly_key(key); //actionKey possibly numpad or arrows, convert to a/z
6118  if(!kp){
6119  //not a fly key - is it SHIFT or CTRL? //feature-EXPLORE
6120  int keystate = type % 10 == KEYDOWN ? 1 : 0;
6121  switch(key){
6122  case CTL_KEY:
6123  tg->Mainloop.CTRL = keystate; break;
6124  case SFT_KEY:
6125  tg->Mainloop.SHIFT = keystate; break;
6126  //unwritten convention for web3d browsers > viewpoint changes
6127  case HOME_KEY: if(keystate) fwl_First_ViewPoint(); break;
6128  case END_KEY: if(keystate) fwl_Last_ViewPoint(); break;
6129  case PGUP_KEY: if(keystate) fwl_Prev_ViewPoint(); break;
6130  case PGDN_KEY: if(keystate) fwl_Next_ViewPoint(); break;
6131  default:
6132  break;
6133  }
6134  //printf("CTRL=%d SHIFT=%d\n",tg->Mainloop.CTRL,tg->Mainloop.SHIFT);
6135  }
6136  }
6137  if(kp){
6138  if(tg->Mainloop.SHIFT){
6139  if(type%10 == KEYDOWN && (key == LEFT_KEY || key == RIGHT_KEY)){
6140  int ichord;
6141  //shift arrow left or right changes keychord
6142  ichord = viewer_getKeyChord();
6143  if(key == LEFT_KEY) ichord--;
6144  if(key == RIGHT_KEY) ichord++;
6145  viewer_setKeyChord(ichord);
6146  }
6147  }else{
6148  double keytime = Time1970sec();
6149  if(type%10 == KEYDOWN)
6150  handle_key(kp,keytime); //keydown for fly
6151  if(type%10 == KEYUP)
6152  handle_keyrelease(kp,keytime); //keyup for fly
6153  }
6154  }
6155  }
6156  }
6157 }
6158 int fwl_getShift(){
6159  ttglobal tg = gglobal();
6160  return tg->Mainloop.SHIFT;
6161 }
6162 void fwl_setShift(int ishift){
6163  ttglobal tg = gglobal();
6164  tg->Mainloop.SHIFT = ishift;
6165 }
6166 
6167 int fwl_getCtrl(){
6168  ttglobal tg = gglobal();
6169  return tg->Mainloop.CTRL;
6170 }
6171 
6172 
6173 int platform2web3dActionKey(int platformKey);
6174 
6175 void (*fwl_do_rawKeyPressPTR)(int key, int type) = fwl_do_keyPress0;
6176 void fwl_do_rawKeyPress(int key, int type) {
6177  fwl_do_rawKeyPressPTR(key,type);
6178 }
6179 
6180 void fwl_do_keyPress(char kp, int type) {
6181  //call this from ANDROID, QNX as always
6182  //it will do the old-style action-key lookup
6183  //(desktop win32 and linux can get finer tuning on the keyboard
6184  // with per-platform platform2web3dActionKeyLINUX and WIN32 functions
6185  int actionKey=0;
6186  int key = (int) kp;
6187  ConsoleMessage("key pressed decimal = %d\n",key);
6188  if (type != KEYPRESS) //May 2014 added this if (I think just the raw keys would need virtual key lookup, I have a problem with '.')
6189  actionKey = platform2web3dActionKey(key);
6190  if(actionKey)
6191  fwl_do_rawKeyPress(actionKey,type+10);
6192  else
6193  fwl_do_rawKeyPress(key,type);
6194 }
6195 
6196 
6197 /* go to a viewpoint, hopefully it is one that is in our current list */
6198 void fwl_gotoViewpoint (char *findThisOne) {
6199  int i;
6200  int whichnode = -1;
6201  struct tProdCon *t = &gglobal()->ProdCon;
6202 
6203  /* did we have a "#viewpoint" here? */
6204  if (findThisOne != NULL) {
6205  for (i=0; i<vectorSize(t->viewpointNodes); i++) {
6206  switch ((vector_get(struct X3D_Node*, t->viewpointNodes,i)->_nodeType)) {
6207  case NODE_Viewpoint:
6208  if (strcmp(findThisOne,
6209  X3D_VIEWPOINT(vector_get(struct X3D_Node *,t->viewpointNodes,i))->description->strptr) == 0) {
6210  whichnode = i;
6211  }
6212  break;
6213 
6214 
6215  case NODE_GeoViewpoint:
6216  if (strcmp(findThisOne,
6217  X3D_GEOVIEWPOINT(vector_get(struct X3D_Node *,t->viewpointNodes,i))->description->strptr) == 0) {
6218  whichnode = i;
6219  }
6220  break;
6221 
6222  case NODE_OrthoViewpoint:
6223  if (strcmp(findThisOne,
6224  X3D_ORTHOVIEWPOINT(vector_get(struct X3D_Node *,t->viewpointNodes,i))->description->strptr) == 0) {
6225  whichnode = i;
6226  }
6227  break;
6228 
6229 
6230  }
6231  }
6232 
6233 
6234  /* were we successful at finding this one? */
6235  if (whichnode != -1) {
6236  /* set the initial viewpoint for this file */
6237  t->setViewpointBindInRender = vector_get(struct X3D_Node *,t->viewpointNodes,whichnode);
6238  }
6239  }
6240 }
6241 
6242 void setup_viewpoint_slerp(double *center, double pivot_radius, double vp_radius);
6243 
6244 int getRayHitAndSetLookatTarget() {
6245  /* called from mainloop for LOOKAT navigation:
6246  - take mousexy and treat it like a pickray, similar to, or borrowing VF_Sensitive code
6247  - get the closest shape node* along the pickray and its modelview matrix (similar to sensitive, except all and only shape nodes)
6248  - get the center and size of the picked shape node, and send the viewpoint to it
6249  - return to normal navigation
6250  */
6251  double pivot_radius, vp_radius; //x,y,z,
6252  int i;
6253  //ppMainloop p;
6254  ttglobal tg = gglobal();
6255  //p = (ppMainloop)tg->Mainloop.prv;
6256 
6257  if(tg->RenderFuncs.hitPointDist >= 0) {
6258  struct X3D_Node * node;
6259  struct currayhit * rh = (struct currayhit *)tg->RenderFuncs.rayHit;
6260 
6261  /* is the sensitive node not NULL? */
6262  if (rh->hitNode == NULL) {
6263  Viewer()->LookatMode = 0; //give up, turn off lookat cursor
6264  }else{
6265  //GLDOUBLE matTarget[16];
6266  double center[3], radius; //pos[3],
6267  vp_radius = 10.0;
6268  if(Viewer()->type == VIEWER_LOOKAT){
6269  //use the center of the object, and its radius
6270  GLDOUBLE smin[3], smax[3], shapeMBBmin[3], shapeMBBmax[3];
6271  double viewerdist;
6272  //double dradius, pos[3], distance;
6273  node = rh->hitNode;
6274  for(i=0;i<3;i++)
6275  {
6276  shapeMBBmin[i] = node->_extent[i*2 + 1];
6277  shapeMBBmax[i] = node->_extent[i*2];
6278  }
6279  transformMBB(smin,smax,rh->modelMatrix,shapeMBBmin,shapeMBBmax); //transform shape's MBB into eye space
6280  radius = 0.0;
6281  for(i=0;i<3;i++){
6282  center[i] = (smax[i] + smin[i])*.5;
6283  radius = max(radius,(max(fabs(smax[i]-center[i]),fabs(smin[i]-center[i]))));
6284  }
6285  viewerdist = Viewer()->Dist;
6286  vp_radius = max(viewerdist, radius + 5.0);
6287  //distance = veclengthd(center);
6288  //distance = (distance - dradius)/distance;
6289  //radius = distance;
6290  pivot_radius = 0.0;
6291  //vp_radius = dradius;
6292 
6293  } else if(Viewer()->type == VIEWER_EXPLORE){
6294  //use the pickpoint (think of a large, continuous geospatial terrain shape,
6295  // and you want to examine a specific geographic point on that shape)
6296  pointxyz2double(center,tg->RenderFuncs.hp);
6297  transformAFFINEd(center,center,getPickrayMatrix(0));
6298  pivot_radius = 0.0;
6299  vp_radius = .8 * veclengthd(center);
6300  }
6301  Viewer()->LookatMode = 3; //go to viewpiont transition mode
6302  setup_viewpoint_slerp(center,pivot_radius,vp_radius);
6303  }
6304  }
6305  return Viewer()->LookatMode;
6306 }
6307 void prepare_model_view_pickmatrix_inverse(GLDOUBLE *mvpi);
6308 struct X3D_Node* getRayHit() {
6309  // call from setup_picking() after render_hier(,VF_SENSITIVE) > rayHit() > push_sensor(node*)
6310  // was there a pickray hit on a sensitive node? If so, returns node*, else NULL
6311  // 1. get closest geometry intersection along pickray/bearing in sensor coords
6312  // 2. transform the point from bearing/pickray space (near viewer mousexy) to sensor-local and save in ray_save_posn
6313  // 2. see if its a sensitive node, if so return node*
6314  // 4. if sensitive, split the modelview matrix into view and model,
6315  // so later in get_hyperhit a more recent view matrix can be used with the frozen model matrix
6316  // variables of note:
6317  // ray_save_posn - intersection with scene geometry, in sensor-local coordinates
6318  // - used in do_CyclinderSensor, do_SphereSensor for computing a radius on mouse-down
6319  // RenderFuncs.hp - intersection with scene geometry found closes to viewpoint, in bearing/pickray space
6320 
6321  //double x,y,z;
6322  int i;
6323  struct X3D_Node *retnode;
6324  ppMainloop p;
6325  ttglobal tg = gglobal();
6326  p = (ppMainloop)tg->Mainloop.prv;
6327 
6328  retnode = NULL;
6329 // printf("@");
6330  if(tg->RenderFuncs.hitPointDist >= 0) {
6331  //GLDOUBLE mvpi[16];
6332  //struct point_XYZ tp; //note viewpoint/avatar Z=1 behind the viewer, to match the glu_unproject method WinZ = -1
6333  struct currayhit * rh = (struct currayhit *)tg->RenderFuncs.rayHit;
6334 // printf("#");
6335  if (rh->hitNode == NULL){
6336  return NULL; //this prevents unnecessary matrix inversion non-singularity
6337 // printf("!");
6338  }
6339  //if(0){
6340  //prepare_model_view_pickmatrix_inverse(mvpi);
6341  //transform(&tp,tg->RenderFuncs.hp,mvpi);
6342  //x = tp.x; y = tp.y, z = tp.z;
6343 
6344 
6346  //tg->RenderFuncs.ray_save_posn[0] = (float) x; tg->RenderFuncs.ray_save_posn[1] = (float) y; tg->RenderFuncs.ray_save_posn[2] = (float) z;
6347  //}
6348  /* we POSSIBLY are over a sensitive node - lets go through the sensitive list, and see
6349  if it exists */
6350 
6351  /* is the sensitive node not NULL? */
6352  if (rh->hitNode != NULL)
6353  {
6354  /*
6355  printf ("rayhit, we are over a node, have node %p (%s), posn %lf %lf %lf",
6356  rh->hitNode, stringNodeType(rh->hitNode->_nodeType), x, y, z);
6357  printf(" dist %f \n", rh->hitNode->_dist);
6358  */
6359  for (i=0; i<p->num_SensorEvents; i++) {
6360  if (p->SensorEvents[i].fromnode == rh->hitNode) {
6361  /* printf ("found this node to be sensitive - returning %u\n",rayHit.hitNode); */
6362  retnode = ((struct X3D_Node*) rh->hitNode);
6363  }
6364  }
6365  }
6366  }
6367  //else -1
6368 
6369  if(retnode != NULL){
6370  //split modelview matrix into model + view for re-concatonation in getHyperHit
6371  //assume we are at scene root, and have just done set_viewmatrix() or the equivalent render_hier(VF_VIEWPOINT)
6372  GLDOUBLE viewmatrix[16], viewinverse[16];
6373  struct currayhit *rh;
6374  rh = (struct currayhit *)tg->RenderFuncs.rayHit;
6375 
6376  FW_GL_MATRIX_MODE(GL_MODELVIEW);
6377  fw_glGetDoublev(GL_MODELVIEW_MATRIX, viewmatrix);
6378  matinverseAFFINE(viewinverse,viewmatrix);
6379  matmultiplyAFFINE(rh->justModel,rh->modelMatrix,viewinverse);
6380 // printf("~");
6381 
6382  }
6383 // printf("^ ");
6384  return retnode;
6385 }
6386 
6387 
6388 /* set a node to be sensitive, and record info for this node
6389  A transform can have multiple sensor children.
6390  A sensor can be DEF/USEd under multiple parent transforms.
6391  SensorEvent is like a lookup table to go between them:
6392  parentNode 1:M SensorEvent M:1 sensorNode
6393  parent/sensor
6394 */
6395 void setSensitive(struct X3D_Node *parentNode, struct X3D_Node *datanode) {
6396  void (*myp)(unsigned *);
6397  int i;
6398  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
6399 
6400  switch (datanode->_nodeType) {
6401  /* sibling sensitive nodes - we have a parent node, and we use it! */
6402  case NODE_TouchSensor: myp = (void *)do_TouchSensor; break;
6403  case NODE_GeoTouchSensor: myp = (void *)do_GeoTouchSensor; break;
6404  case NODE_LineSensor: myp = (void *)do_LineSensor; break;
6405  case NODE_PlaneSensor: myp = (void *)do_PlaneSensor; break;
6406  case NODE_CylinderSensor: myp = (void *)do_CylinderSensor; break;
6407  case NODE_SphereSensor: myp = (void *)do_SphereSensor; break;
6408  case NODE_ProximitySensor: /* it is time sensitive only, NOT render sensitive */ return; break;
6409  case NODE_GeoProximitySensor: /* it is time sensitive only, NOT render sensitive */ return; break;
6410 
6411  /* Anchor is a special case, as it has children, so this is the "parent" node. */
6412  case NODE_Anchor: myp = (void *)do_Anchor; parentNode = datanode; break;
6413  default: return;
6414  }
6415  /* printf ("setSensitive parentNode %p type %s data %p type %s\n",parentNode,
6416  stringNodeType(parentNode->_nodeType),datanode,stringNodeType (datanode->_nodeType)); */
6417 
6418  /* is this node already here? */
6419  /* why would it be duplicate? When we parse, we add children to a temp group, then we
6420  pass things over to a rootNode; we could possibly have this duplicated */
6421  for (i=0; i<p->num_SensorEvents; i++) {
6422  if ((p->SensorEvents[i].fromnode == parentNode) &&
6423  (p->SensorEvents[i].datanode == datanode) &&
6424  (p->SensorEvents[i].interpptr == (void *)myp)) {
6425  /* printf ("setSensitive, duplicate, returning\n"); */
6426  return;
6427  }
6428  }
6429 
6430  if (datanode == 0) {
6431  printf ("setSensitive: datastructure is zero for type %s\n",stringNodeType(datanode->_nodeType));
6432  return;
6433  }
6434 
6435  /* record this sensor event for clicking purposes */
6436  p->SensorEvents = REALLOC(p->SensorEvents,sizeof (struct SensStruct) * (p->num_SensorEvents+1));
6437 
6438  /* now, put the function pointer and data pointer into the structure entry */
6439  p->SensorEvents[p->num_SensorEvents].fromnode = parentNode;
6440  p->SensorEvents[p->num_SensorEvents].datanode = datanode;
6441  p->SensorEvents[p->num_SensorEvents].interpptr = (void *)myp;
6442 
6443  /* printf ("saved it in num_SensorEvents %d\n",p->num_SensorEvents); */
6444  p->num_SensorEvents++;
6445 }
6446 
6447 /* we have a sensor event changed, look up event and do it */
6448 /* note, (Geo)ProximitySensor events are handled during tick, as they are time-sensitive only */
6449 static void sendSensorEvents(struct X3D_Node* COS,int ev, int butStatus, int status) {
6450  //COS - cursorOverSensitive - a parent transform node / hitPoint node
6451  int count;
6452  int butStatus2;
6453  ttglobal tg;
6454  ppMainloop p;
6455  tg = gglobal();
6456  p = (ppMainloop)tg->Mainloop.prv;
6457 
6458  /* if we are not calling a valid node, dont do anything! */
6459  if (COS==NULL) return;
6460 
6461  for (count = 0; count < p->num_SensorEvents; count++) {
6462  if (p->SensorEvents[count].fromnode == COS) {
6463  butStatus2 = butStatus;
6464  /* should we set/use hypersensitive mode? */
6465  if (ev==ButtonPress) {
6466  tg->RenderFuncs.hypersensitive = p->SensorEvents[count].fromnode;
6467  tg->RenderFuncs.hyperhit = 1; // 1 means we are starting a hypersensitive drag
6468  get_hyperhit(); //added for touch devices which have no isOver preparation
6469  } else if (ev==ButtonRelease) {
6470  tg->RenderFuncs.hypersensitive = 0;
6471  tg->RenderFuncs.hyperhit = 0; //0 means we finished hypersensitive drag
6472  butStatus2 = 1;
6473  } else if (ev==MotionNotify) {
6474  get_hyperhit();
6475  }
6476 
6477 
6478  p->SensorEvents[count].interpptr(p->SensorEvents[count].datanode, ev,butStatus2, status); //do_PlaneSensor, do_...
6479  /* return; do not do this, incase more than 1 node uses this, eg,
6480  an Anchor with a child of TouchSensor */
6481  }
6482  }
6483 }
6484 
6485 void prepare_model_view_pickmatrix_inverse0(GLDOUBLE *modelMatrix, GLDOUBLE *mvpi);
6486 void prepare_model_view_pickmatrix_inverse(GLDOUBLE *mvpi){
6487  /*prepares a matrix to transform points from eye/pickray/bearing
6488  to gemoetry-local, using the modelview matrix of the pickray-scene intersection point
6489  found closest to the viewer on the last render_hier(VF_SENSITIVE) pass
6490  using the model matrix snapshotted/frozen at the time of the intersection, and the current view matrix
6491  MVPI = inverse(pickray_frozen_model * view)
6492  */
6493  struct currayhit * rh;
6494  //GLDOUBLE *modelview;
6495  GLDOUBLE viewmatrix[16], mv[16]; //viewinverse[16],
6496  ttglobal tg = gglobal();
6497 
6498  rh = (struct currayhit *)tg->RenderFuncs.rayHit;
6499  //modelview = rh->modelMatrix;
6500 
6501  FW_GL_MATRIX_MODE(GL_MODELVIEW);
6502  fw_glGetDoublev(GL_MODELVIEW_MATRIX, viewmatrix);
6503 
6504  matmultiplyAFFINE(mv,rh->justModel,viewmatrix); //rh->justModel
6505 
6506  //modelview = mv;
6507 
6508  prepare_model_view_pickmatrix_inverse0(mv, mvpi);
6509 }
6510 
6511 /* get_hyperhit()
6512  If we have successfully picked a DragSensor sensitive node -intersection recorded in rayHit, and
6513  intersection closest to viewpoint transformed to geometry-local, and sensornode * returned from getRayHit()-
6514  and we are on mousedown or mousemove(drag) events on a dragsensor node:
6515  - transform the bearing/pick-ray from bearing-local^ to sensor-local coordinates
6516  - in a way that is generic for all DragSensor nodes in their do_<Drag>Sensor function
6517  - so they can intersect the bearing/pick-ray with their sensor geometry (Cylinder,Sphere,Plane[,Line])
6518  - and emit events in sensor-local coordinates
6519  - ^bearing-local: currently == pick-viewport-local
6520  But it may be overkill if bearing-local is made to == world, for compatibility with 3D pointing devices
6521 */
6522 void get_hyperhit() {
6523  /*
6524  transforms the last known pickray intersection, and current frame pickray/bearing into sensor-local coordinates of the
6525  intersected geometry, using:
6526  - current frame pickray view matrix (in case mouse has moved)
6527  - current frame view matrix (in case its changed, ie keyboard navigation while mouse-down on a drag sensor,
6528  or HMD head mounted display/mobile motionsensor nav while button-down on a drag sensor: should drag)
6529  - mouse-down-frozen model matrix between world and the sensor's parent (transform,..) node
6530  sensor-local-pickray = frozen_justModel * current_view * pickray_matrix * unit_pickray 0,0,-1
6531  modelviewMatrix = | M V P |
6532 
6533  variables:
6534  struct point_XYZ r1 = {0,0,-1},r2 = {0,0,0},r3 = {0,1,0};
6535  pick-viewport-local axes: r1- along pick-proj axis, r2 viewpoint, r3 y-up axis in case needed
6536  hyp_save_posn, t_r2 - A - (viewpoint 0,0,0 transformed by modelviewMatrix.inverse() to geometry-local space)
6537  hyp_save_norm, t_r1 - B - bearing point (viewport 0,0,-1 used with pick-proj bearing-specific projection matrix)
6538  - norm is not a direction vector, its a point. To get a direction vector: v = (B - A) = (norm - posn)
6539  ray_save_posn - intersection with scene geometry, transformed into in sensor-local coordinates
6540  - used in do_CyclinderSensor, do_SphereSensor for computing a radius on mouse-down
6541  t_r3 - viewport y-up in case needed
6542  */
6543  double x1,y1,z1,x2,y2,z2,x3,y3,z3;
6544  GLDOUBLE mvpi[16];
6545  struct point_XYZ r11 = {0.0,0.0,1.0}; //note viewpoint/avatar Z=1 behind the viewer, to match the glu_unproject method WinZ = -1
6546  struct point_XYZ tp;
6547 
6548  //OLDCODE struct currayhit *rh; //*rhh,
6549  ttglobal tg = gglobal();
6550  //OLDCODE rh = (struct currayhit *)tg->RenderFuncs.rayHit;
6551 
6552  //transform last bearing/pickray-local intersection to sensor-local space
6553  // using current?frozen? modelview and current pickmatrix
6554  // so sensor node can emit events from its do_<sensor node> function in sensor-local coordinates
6555 
6556  prepare_model_view_pickmatrix_inverse(mvpi);
6557  //transform pickray from eye/pickray/bearing to geometry/sensor-local
6558  transform(&tp,&r11,mvpi);
6559  x1 = tp.x; y1 = tp.y; z1 = tp.z;
6560  transform(&tp,&r2,mvpi);
6561  x2 = tp.x; y2 = tp.y; z2 = tp.z;
6562  //transform the last known pickray intersection from eye/pickray/bearling to geometry/sensor-local
6563  transform(&tp,tg->RenderFuncs.hp,mvpi);
6564  x3 = tp.x; y3 = tp.y; z3 = tp.z;
6565  if(0)
6566  printf("get_hyperhit\n");
6567 
6568 
6569  //if(0) ConsoleMessage ("get_hyper %f %f %f, %f %f %f, %f %f %f\n",
6570  // x1,y1,z1,x2,y2,z2,x3,y3,z3);
6571 
6572  /* and save this globally */
6573  //last pickray/bearing ( (0,0,0) (0,0,1)) transformed from eye/pickray/bearing to geometry/sensor local coordinates:
6574  tg->RenderFuncs.hyp_save_posn[0] = (float) x1; tg->RenderFuncs.hyp_save_posn[1] = (float) y1; tg->RenderFuncs.hyp_save_posn[2] = (float) z1;
6575  tg->RenderFuncs.hyp_save_norm[0] = (float) x2; tg->RenderFuncs.hyp_save_norm[1] = (float) y2; tg->RenderFuncs.hyp_save_norm[2] = (float) z2;
6576  //last known pickray intersection in geometry/sensor-local coords, ready for sensor emitting
6577  tg->RenderFuncs.ray_save_posn[0] = (float) x3; tg->RenderFuncs.ray_save_posn[1] = (float) y3; tg->RenderFuncs.ray_save_posn[2] = (float) z3;
6578 }
6579 
6580 
6581 
6582 /* set stereo buffers, if required */
6583 void setStereoBufferStyle(int itype) /*setXEventStereo()*/
6584 {
6585  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
6586  if(itype==0)
6587  {
6588  /* quad buffer crystal eyes style */
6589  p->bufferarray[0]=GL_BACK_LEFT;
6590  p->bufferarray[1]=GL_BACK_RIGHT;
6591  p->maxbuffers=2;
6592  }
6593  else if(itype==1)
6594  {
6595  /*sidebyside and anaglyph type*/
6596  p->bufferarray[0]=FW_GL_BACK;
6597  p->bufferarray[1]=FW_GL_BACK;
6598  p->maxbuffers=2;
6599  }
6600  printf("maxbuffers=%d\n",p->maxbuffers);
6601 }
6602 void setStereoBufferStyleB(int itype, int iside, int ibuffer)
6603 {
6604  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
6605  if(itype==0)
6606  {
6607  /* quad buffer crystal eyes style */
6608  if(iside == 0)
6609  p->bufferarray[ibuffer]=GL_BACK_LEFT;
6610  if(iside == 1)
6611  p->bufferarray[ibuffer]=GL_BACK_RIGHT;
6612  }
6613  else if(itype==1)
6614  {
6615  /*sidebyside and anaglyph type*/
6616  p->bufferarray[ibuffer]=FW_GL_BACK;
6617  }
6618 }
6619 
6620 /* go to the first viewpoint */
6621 /* ok, is this ViewpointGroup active or not? */
6622 int vpGroupActive(struct X3D_ViewpointGroup *vp_parent) {
6623 
6624  /* ok, if this is not a ViewpointGroup, we are ok */
6625  if (vp_parent->_nodeType != NODE_ViewpointGroup) return TRUE;
6626 
6627  if (vp_parent->__proxNode != NULL) {
6628  /* if size == 0,,0,0 we always do the render */
6629  if ((APPROX(0.0,vp_parent->size.c[0])) && (APPROX(0.0,vp_parent->size.c[1])) && (APPROX(0.0,vp_parent->size.c[2]))) {
6630  printf ("size is zero\n");
6631  return TRUE;
6632  }
6633 
6634  return X3D_PROXIMITYSENSOR(vp_parent->__proxNode)->isActive;
6635  }
6636  return TRUE;
6637 }
6638 
6639 /* find if there is another valid viewpoint */
6640 static int moreThanOneValidViewpoint( void) {
6641  int count;
6642  struct tProdCon *t = &gglobal()->ProdCon;
6643 
6644  if (vectorSize(t->viewpointNodes)<=1) return FALSE;
6645 
6646  for (count=0; count < vectorSize(t->viewpointNodes); count++) {
6647  if (count != t->currboundvpno) {
6648  struct Vector *me = vector_get(struct X3D_Node*, t->viewpointNodes,count)->_parentVector;
6649 
6650  /* ok, we have a viewpoint; is its parent a ViewpointGroup? */
6651  if (me != NULL) {
6652 
6653  if (vectorSize(me) > 0) {
6654  struct X3D_Node * vp_parent;
6655 
6656  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get( struct X3D_Node *,
6657  vector_get(struct X3D_Node *,t->viewpointNodes,count)->_parentVector, 0),
6658  vp_parent);
6659  /* printf ("parent found, it is a %s\n",stringNodeType(vp_parent->_nodeType)); */
6660 
6661  /* sigh, find if the ViewpointGroup is active or not */
6662  if(vp_parent)
6663  return vpGroupActive((struct X3D_ViewpointGroup *)vp_parent);
6664  }
6665  }
6666  }
6667  }
6668  return FALSE;
6669 }
6670 
6671 
6672 /* go to the last viewpoint */
6673 void fwl_Last_ViewPoint() {
6674  if (moreThanOneValidViewpoint()) {
6675 
6676  int vp_to_go_to;
6677  int ind;
6678  struct tProdCon *t = &gglobal()->ProdCon;
6679 
6680  /* go to the next viewpoint. Possibly, quite possibly, we might
6681  have to skip one or more if they are in a ViewpointGroup that is
6682  out of proxy */
6683  vp_to_go_to = vectorSize(t->viewpointNodes);
6684  for (ind = 0; ind < vectorSize(t->viewpointNodes); ind--) {
6685  struct X3D_Node *cn;
6686 
6687  vp_to_go_to--;
6688  if (vp_to_go_to<0) vp_to_go_to=vectorSize(t->viewpointNodes)-1;
6689  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get(struct X3D_Node*, t->viewpointNodes,vp_to_go_to),cn);
6690 
6691  /* printf ("NVP, %d of %d, looking at %d\n",ind, totviewpointnodes,vp_to_go_to);
6692  printf ("looking at node :%s:\n",X3D_VIEWPOINT(cn)->description->strptr); */
6693 
6694  if (cn && vpGroupActive((struct X3D_ViewpointGroup *) cn)) {
6695  if(0){
6696  /* whew, we have other vp nodes */
6697  send_bind_to(vector_get(struct X3D_Node*, t->viewpointNodes,t->currboundvpno),0);
6698  t->currboundvpno = vp_to_go_to;
6699  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6700  send_bind_to(vector_get(struct X3D_Node*, t->viewpointNodes,t->currboundvpno),1);
6701 
6702  }else{
6703  /* dug9 - using the display-thread-synchronous gotoViewpoint style
6704  to help order-senstive slerp_viewpoint() process */
6705  /* set the initial viewpoint for this file */
6706  t->setViewpointBindInRender = vector_get(struct X3D_Node*,
6707  t->viewpointNodes,vp_to_go_to);
6708  t->currboundvpno = vp_to_go_to;
6709  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6710  }
6711  return;
6712  }
6713  }
6714  }
6715 }
6716 
6717 
6718 
6719 /* go to the first viewpoint */
6720 void fwl_First_ViewPoint() {
6721  if (moreThanOneValidViewpoint()) {
6722 
6723  int vp_to_go_to;
6724  int ind;
6725  struct tProdCon *t = &gglobal()->ProdCon;
6726 
6727  /* go to the next viewpoint. Possibly, quite possibly, we might
6728  have to skip one or more if they are in a ViewpointGroup that is
6729  out of proxy */
6730  vp_to_go_to = -1;
6731  for (ind = 0; ind < vectorSize(t->viewpointNodes); ind++) {
6732  struct X3D_Node *cn;
6733 
6734  vp_to_go_to++;
6735  if (vp_to_go_to<0) vp_to_go_to=vectorSize(t->viewpointNodes)-1;
6736  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get(
6737  struct X3D_Node* , t->viewpointNodes,vp_to_go_to),cn);
6738 
6739  /* printf ("NVP, %d of %d, looking at %d\n",ind, totviewpointnodes,vp_to_go_to);
6740  printf ("looking at node :%s:\n",X3D_VIEWPOINT(cn)->description->strptr); */
6741 
6742  if (cn && vpGroupActive((struct X3D_ViewpointGroup *) cn)) {
6743  if(0){
6744  /* whew, we have other vp nodes */
6745  send_bind_to(vector_get(struct X3D_Node*,t->viewpointNodes,t->currboundvpno),0);
6746  t->currboundvpno = vp_to_go_to;
6747  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6748  send_bind_to(vector_get(struct X3D_Node*,t->viewpointNodes,t->currboundvpno),1);
6749  }else{
6750  /* dug9 - using the display-thread-synchronous gotoViewpoint style
6751  to help order-senstive slerp_viewpoint() process */
6752  /* set the initial viewpoint for this file */
6753  t->setViewpointBindInRender = vector_get(struct X3D_Node*,t->viewpointNodes,vp_to_go_to);
6754  t->currboundvpno = vp_to_go_to;
6755  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6756 
6757  }
6758 
6759  return;
6760  }
6761  }
6762  }
6763 }
6764 /* go to the next viewpoint */
6765 void fwl_Prev_ViewPoint() {
6766  if (moreThanOneValidViewpoint()) {
6767 
6768  int vp_to_go_to;
6769  int ind;
6770  struct tProdCon *t = &gglobal()->ProdCon;
6771 
6772  /* go to the next viewpoint. Possibly, quite possibly, we might
6773  have to skip one or more if they are in a ViewpointGroup that is
6774  out of proxy */
6775  vp_to_go_to = t->currboundvpno;
6776  for (ind = 0; ind < vectorSize(t->viewpointNodes); ind--) {
6777  struct X3D_Node *cn;
6778 
6779  vp_to_go_to--;
6780  if (vp_to_go_to<0) vp_to_go_to=vectorSize(t->viewpointNodes)-1;
6781  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get(struct X3D_Node*, t->viewpointNodes,vp_to_go_to),cn);
6782 
6783  /* printf ("NVP, %d of %d, looking at %d\n",ind, totviewpointnodes,vp_to_go_to);
6784  printf ("looking at node :%s:\n",X3D_VIEWPOINT(cn)->description->strptr); */
6785 
6786  if (cn && vpGroupActive((struct X3D_ViewpointGroup *) cn)) {
6787 
6788  if(0){
6789  /* whew, we have other vp nodes */
6790  send_bind_to(vector_get(struct X3D_Node*,t->viewpointNodes,t->currboundvpno),0);
6791  t->currboundvpno = vp_to_go_to;
6792  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6793  send_bind_to(vector_get(struct X3D_Node*,t->viewpointNodes,t->currboundvpno),1);
6794  }else{
6795  /* dug9 - using the display-thread-synchronous gotoViewpoint style
6796  to help order-senstive slerp_viewpoint() process */
6797  /* set the initial viewpoint for this file */
6798  t->setViewpointBindInRender = vector_get(struct X3D_Node*,
6799  t->viewpointNodes,vp_to_go_to);
6800  t->currboundvpno = vp_to_go_to;
6801  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6802  }
6803 
6804 
6805  return;
6806  }
6807  }
6808  }
6809 }
6810 
6811 /* go to the next viewpoint */
6812 void fwl_Next_ViewPoint() {
6813  if (moreThanOneValidViewpoint()) {
6814 
6815  int vp_to_go_to;
6816  int ind;
6817  struct tProdCon *t = &gglobal()->ProdCon;
6818 
6819  /* go to the next viewpoint. Possibly, quite possibly, we might
6820  have to skip one or more if they are in a ViewpointGroup that is
6821  out of proxy */
6822  vp_to_go_to = t->currboundvpno;
6823  for (ind = 0; ind < vectorSize(t->viewpointNodes); ind++) {
6824  struct X3D_Node *cn;
6825 
6826  vp_to_go_to++;
6827  if (vp_to_go_to>=vectorSize(t->viewpointNodes)) vp_to_go_to=0;
6828  POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, vector_get(
6829  struct X3D_Node*, t->viewpointNodes,vp_to_go_to),cn);
6830 
6831  /* printf ("NVP, %d of %d, looking at %d\n",ind, totviewpointnodes,vp_to_go_to);
6832  printf ("looking at node :%s:\n",X3D_VIEWPOINT(cn)->description->strptr); */
6833 
6834  if (cn && vpGroupActive((struct X3D_ViewpointGroup *) cn)) {
6835  /* whew, we have other vp nodes */
6836  /* dug9 - using the display-thread-synchronous gotoViewpoint style
6837  to help order-senstive slerp_viewpoint() process */
6838  /* set the initial viewpoint for this file */
6839  t->setViewpointBindInRender = vector_get(
6840  struct X3D_Node*,t->viewpointNodes,vp_to_go_to);
6841  t->currboundvpno = vp_to_go_to;
6842  if (t->currboundvpno>=vectorSize(t->viewpointNodes)) t->currboundvpno=0;
6843 
6844  return;
6845  }
6846  }
6847  }
6848 }
6849 
6850 /* initialization for the OpenGL render, event processing sequence. Should be done in threat that has the OpenGL context */
6851 void fwl_initializeRenderSceneUpdateScene() {
6852 
6853 // OLD_IPHONE_AQUA #ifndef AQUA
6854  ttglobal tg = gglobal();
6855 // OLD_IPHONE_AQUA #endif
6856 
6857  /*
6858  ConsoleMessage("fwl_initializeRenderSceneUpdateScene start\n");
6859  if (rootNode()==NULL) {
6860  ConsoleMessage("fwl_initializeRenderSceneUpdateScene rootNode NULL\n");
6861  } else {
6862  ConsoleMessage("fwl_initializeRenderSceneUpdateScene rootNode %d children \n",rootNode()->children.n);
6863  }
6864  */
6865  new_tessellation();
6866  //fwl_set_viewer_type(VIEWER_EXAMINE);
6867  viewer_postGLinit_init();
6868 
6869 // OLD_IPHONE_AQUA #ifndef AQUA
6870  if( ((freewrl_params_t*)(tg->display.params))->fullscreen && newResetGeometry != NULL) newResetGeometry();
6871 // OLD_IPHONE_AQUA #endif
6872 
6873 }
6874 
6875 /* phases to shutdown:
6876 - stop mainthread from rendering - someone presses 'q'. If we are in here, we aren't rendering
6877 A. worker threads > tell them to flush and stop
6878 B. check if both worker threads have stopped
6879 - exit loop
6880 C. delete instance data
6881 - let the display thread die a peaceful death
6882 */
6883 
6884 
6885 static int workers_waiting(){
6886  BOOL waiting;
6887  ttglobal tg = gglobal();
6888  waiting = tg->threads.ResourceThreadWaiting && tg->threads.TextureThreadWaiting;
6889 
6890  return waiting;
6891 }
6892 static void workers_stop()
6893 {
6894  resitem_queue_exit();
6895  texitem_queue_exit();
6896 }
6897 static int workers_running(){
6898  BOOL more;
6899  ttglobal tg = gglobal();
6900  more = tg->threads.ResourceThreadRunning || tg->threads.TextureThreadRunning;
6901  return more;
6902 }
6903 
6904 int isSceneLoaded()
6905 {
6906  //have all the resoruces been loaded and parsed and the scene is stable?
6907  //some other web3d browseers have a way to tell, and you can delay rendering till all resources are loaded
6908  //freewrl -after 2014 rework by dug9- has been reworked to be 'lazy loading' meaning it might not
6909  //request a resource until it visits a node that needs it, perhaps several times - see load_inline() (extern proto is similar)
6910 
6911  //need: in our case we want SSR (server-side rendering) to loop normally until the scene
6912  //is (lazy?) loaded and parsed and ready, then go into a render-one-frame-for-each-client-request mode
6913  //how do we tell? this may change if we have a more reliable cycle for resources.
6914  //for now we'll check if our worker threads are waiting, and frontend has no res items in its possession (not downloading one)
6915  // p->doEvents = (!fwl_isinputThreadParsing()) && (!fwl_isTextureParsing()) && fwl_isInputThreadInitialized();
6916 
6917  int ret;
6918  double dtime;
6919  //ppProdCon p;
6920  ttglobal tg = gglobal();
6921  ppMainloop p = (ppMainloop)tg->Mainloop.prv;
6922  //p = (ppProdCon) tg->ProdCon.prv;
6923  //ret = 0;
6924  //ret = workers_waiting() && !p->frontend_list_to_get;
6925  //ret = ret && tg->Mainloop.
6926  //printf("[%d %d %p]",tg->threads.ResourceThreadWaiting,tg->threads.TextureThreadWaiting,p->frontend_list_to_get);
6927  ret = (!fwl_isinputThreadParsing()) && (!fwl_isTextureParsing()) && fwl_isInputThreadInitialized();
6928  ret = ret && workers_waiting();
6929  //curtime = TickTime();
6930  dtime = tg->Mainloop.TickTime - p->BrowserInitTime;
6931  ret = ret && (dtime > 10.0); //wait 10 seconds
6932  return ret;
6933 }
6934 
6935 void end_of_run_tests(){
6936  //miscalaneous malloc, buffer, resource cleanup testing at end of run
6937  //press Enter on console after viewing results
6938  if(1){
6939  int i, notfreed, notfreedt;
6940  //see if there are any opengl buffers not freed
6941  notfreed = 0;
6942  notfreedt = 0;
6943  for(i=0;i<100000;i++){
6944  if(glIsBuffer(i)) {notfreed++; printf("b%d ",i);}
6945  if(glIsTexture(i)) {notfreedt++; printf("t%d ",i);}
6946  }
6947  printf("\ngl buffers not freed = %d\n",notfreed);
6948  printf("gl textures not freed = %d\n",notfreedt);
6949  getchar();
6950  }
6951 }
6952 
6953 static void finalizeRenderSceneUpdateScene() {
6954  //C. delete instance data
6955  struct X3D_Node* rn;
6956  ttglobal tg = gglobal();
6957  printf ("finalizeRenderSceneUpdateScene\n");
6958 
6959  /* set geometry to normal size from fullscreen */
6960  if (newResetGeometry != NULL) newResetGeometry();
6961  /* kill any remaining children processes like sound processes or consoles */
6962  killErrantChildren();
6963  /* tested on win32 console program July9,2011 seems OK */
6964  rn = rootNode();
6965  if(rn)
6966  deleteVector(struct X3D_Node*,rn->_parentVector); //perhaps unlink first
6967  freeMallocedNodeFields(rn);
6968  FREE_IF_NZ(rn);
6969  setRootNode(NULL);
6970 #ifdef DEBUG_MALLOC
6971  end_of_run_tests(); //with glew mx, we get the glew context from tg, so have to do the glIsBuffer, glIsTexture before deleting tg
6972 #endif
6973  iglobal_destructor(tg);
6974 #ifdef DEBUG_MALLOC
6975  void scanMallocTableOnQuit(void);
6976  scanMallocTableOnQuit();
6977 #endif
6978 
6979 }
6980 
6981 
6982 int checkReplaceWorldRequest(){
6983  ttglobal tg = gglobal();
6984  if (tg->Mainloop.replaceWorldRequest || tg->Mainloop.replaceWorldRequestMulti){
6985  tg->threads.flushing = true;
6986  }
6987  return tg->threads.flushing;
6988 }
6989 static int exitRequest = 0; //static because we want to exit the process, not just a freewrl instance (I think).
6990 int checkExitRequest(){
6991  return exitRequest;
6992 }
6993 
6994 static int checkQuitRequest(){
6995  ttglobal tg = gglobal();
6996  if ((tg->threads.MainLoopQuit) == 1){
6997  //printf ("checkQuitRequest, setting thread.flushing to true\n");
6998  tg->threads.flushing = true;
6999  }
7000  return tg->threads.MainLoopQuit;
7001 }
7002 void doReplaceWorldRequest()
7003 {
7004  resource_item_t *res,*resm;
7005  char * req;
7006 
7007  ttglobal tg = gglobal();
7008 
7009  req = tg->Mainloop.replaceWorldRequest;
7010  tg->Mainloop.replaceWorldRequest = NULL;
7011  if (req){
7012  //kill_oldWorldB(__FILE__,__LINE__);
7013  res = resource_create_single(req);
7014  //send_resource_to_parser_async(res);
7015  resitem_enqueue(ml_new(res));
7016  FREE_IF_NZ(req);
7017  }
7018  resm = (resource_item_t *)tg->Mainloop.replaceWorldRequestMulti;
7019  if (resm){
7020  tg->Mainloop.replaceWorldRequestMulti = NULL;
7021  //kill_oldWorldB(__FILE__, __LINE__);
7022  resm->new_root = true;
7023  gglobal()->resources.root_res = (void*)resm;
7024  //send_resource_to_parser_async(resm);
7025  resitem_enqueue(ml_new(resm));
7026  }
7027  tg->threads.flushing = false;
7028 }
7029 static int(*view_initialize)() = NULL;
7030 //
7031 //EGL/GLES2 winGLES2.exe with KEEP_FV_INLIB sets frontend_handles_display_thread=true,
7032 // then calls fv_display_initialize() which only creates window in backend if false
7033 #if defined(_ANDROID) || defined(ANDROIDNDK) || defined(WINRT)
7034 int view_initialize0(void){
7035  /* Initialize display - View initialize*/
7036  if (!fv_display_initialize()) {
7037  ERROR_MSG("initFreeWRL: error in display initialization.\n");
7038  return FALSE; //exit(1);
7039  }
7040  return TRUE;
7041 }
7042 #else
7043 int view_initialize0(void){
7044  /* Initialize display - View initialize*/
7045  if (!fv_display_initialize_desktop()) {
7046  ERROR_MSG("initFreeWRL: error in display initialization.\n");
7047  return FALSE; //exit(1);
7048  }
7049  return TRUE;
7050 }
7051 #endif //ANDROID
7052 
7053 
7054 void killNodes();
7055 
7056 /* fwl_draw() call from frontend when frontend_handles_display_thread */
7057 int fwl_draw()
7058 {
7059  int more;
7060  ppMainloop p;
7061  ttglobal tg = gglobal();
7062  fwl_setCurrentHandle(tg, __FILE__, __LINE__);
7063  p = (ppMainloop)tg->Mainloop.prv;
7064 
7065  more = TRUE; //FALSE;
7066  if (!p->draw_initialized){
7067  more = FALSE;
7068  view_initialize = view_initialize0; //defined above, with ifdefs
7069  if (view_initialize)
7070  more = view_initialize();
7071 
7072  if (more){
7073  fwl_initializeRenderSceneUpdateScene(); //Model initialize
7074  }
7075  p->draw_initialized = TRUE;
7076  }
7077  if(more) {//more = TRUE;
7078  switch (tg->threads.MainLoopQuit){
7079  case 0:
7080  case 1:
7081  //PRINTF("event loop\n");
7082  //switch (tg->threads.flushing)
7083  if (tg->threads.flushing ==false)
7084  {
7085  //case 0:
7086  profile_end("frontend");
7087  profile_start("mainloop");
7088  //model: udate yourself
7089  fwl_RenderSceneUpdateScene(); //Model update
7090  profile_end("mainloop");
7091  profile_start("frontend");
7092 
7093  PRINT_GL_ERROR_IF_ANY("XEvents::render");
7094  checkReplaceWorldRequest(); //will set flushing=true
7095  checkQuitRequest(); //will set flushing=true
7096  //break;
7097  } else {
7098  //case 1:
7099  if (workers_waiting()) //one way to tell if workers finished flushing is if their queues are empty, and they are not busy
7100  {
7101 
7102  //kill_oldWorldB(__FILE__, __LINE__); //cleans up old scene while leaving gglobal intact ready to load new scene
7103  reset_Browser(); //rename
7104  tg->threads.flushing = false;
7105  if (tg->threads.MainLoopQuit)
7106  tg->threads.MainLoopQuit++; //quiting takes priority over replacing
7107  else
7108  doReplaceWorldRequest();
7109  } else {
7110  //printf ("fwl_draw, mainLoopQuit %d, workers NOT waiting\n",tg->threads.MainLoopQuit);
7111  tg->threads.MainLoopQuit++;
7112  }
7113  } // end of threads.flushing test
7114 
7115  break;
7116  case 2:
7117  //tell worker threads to stop gracefully
7118  workers_stop();
7119  //killNodes(); //deallocates nodes MarkForDisposed
7120  //killed above kill_oldWorldB(__FILE__,__LINE__);
7121 
7122  tg->threads.MainLoopQuit++;
7123  break;
7124  case 3:
7125  //check if worker threads have exited
7126  more = workers_running();
7127  if (more == 0)
7128  {
7129  finalizeRenderSceneUpdateScene();
7130  }
7131  break;
7132  } // end of case
7133  } // end of if (more) clause
7134 
7135 
7136  return more;
7137 }
7138 
7139 
7140 void fwl_initialize_parser()
7141 {
7142  /* create the root node */
7143  if (rootNode() == NULL) {
7144  setRootNode( createNewX3DNode (NODE_Proto) );
7145  /*remove this node from the deleting list*/
7146  doNotRegisterThisNodeForDestroy(X3D_NODE(rootNode()));
7147  }
7148 }
7149 
7150 void fwl_init_SnapSeq() {
7151 #ifdef DOSNAPSEQUENCE
7152 /* need to re-implement this for OSX generating QTVR */
7153  set_snapsequence(TRUE);
7154 #endif
7155 }
7156 
7157 
7158 void fwl_set_LineWidth(float lwidth) {
7159  gglobal()->Mainloop.gl_linewidth = lwidth;
7160 }
7161 
7162 void fwl_set_KeyString(const char* kstring)
7163 {
7164  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
7165  p->keypress_string = STRDUP(kstring);
7166 }
7167 
7168 
7169 
7170 /* if we had an exit(EXIT_FAILURE) anywhere in this C code - it means
7171  a memory error. So... print out a standard message to the
7172  console. */
7173 void outOfMemory(const char *msg) {
7174  ConsoleMessage ("FreeWRL has encountered a memory allocation problem\n"\
7175  "and is exiting. -- %s--",msg);
7176  usleep(10 * 1000);
7177  exit(EXIT_FAILURE);
7178 }
7179 
7180 void _disposeThread(void *globalcontext);
7181 
7182 /* quit key pressed, or Plugin sends SIGQUIT */
7183 void fwl_doQuitInstance(void *tg_remote)
7184 {
7185  ttglobal tg = gglobal();
7186 printf ("fwl_doQuitInstance called\n");
7187 
7188  if (tg_remote == tg)
7189  {
7190  fwl_doQuit(__FILE__,__LINE__);
7191  fwl_draw();
7192  workers_stop();
7193  fwl_clearCurrentHandle();
7194 #ifdef DISABLER
7195  pthread_create(&tg->threads.disposeThread, NULL, (void *(*)(void *))&_disposeThread, tg);
7196 #endif
7197  }
7198 }
7199 
7200 void __iglobal_destructor(ttglobal tg);
7201 
7202 void _disposeThread(void *globalcontext)
7203 {
7204  int more;
7205  ttglobal tg = globalcontext;
7206  fwl_setCurrentHandle(tg, __FILE__, __LINE__);
7207  more = 0;
7208  while((more = workers_running()) && more > 0)
7209  {
7210  usleep(100);
7211  }
7212  if (more == 0)
7213  {
7214  markForDispose(rootNode(), TRUE);
7215  killNodes(); //deallocates nodes MarkForDisposed
7216 
7217 
7218  finalizeRenderSceneUpdateScene();
7219 #ifdef DISABLER
7220 #if defined(WRAP_MALLOC) || defined(DEBUG_MALLOC)
7221  freewrlFreeAllRegisteredAllocations();
7222  freewrlDisposeMemTable();
7223 #endif
7224  __iglobal_destructor(tg);
7225 #endif
7226  }
7227 }
7228 
7229 void fwl_doQuit(char *fl, int ln)
7230 {
7231  ttglobal tg = gglobal();
7232  tg->threads.MainLoopQuit = max(1,tg->threads.MainLoopQuit); //make sure we don't go backwards in the quit process with a double 'q'
7233  // printf ("fwl_doQuit - setting MainLoopQuit to %d from file %s:%d\n", tg->threads.MainLoopQuit,
7234  // fl,ln);
7235 }
7236 
7237 void fwl_doQuitAndWait(){
7238  pthread_t displaythread;
7239  ttglobal tg = gglobal();
7240  displaythread = tg->threads.DispThrd;
7241  fwl_doQuit(__FILE__,__LINE__);
7242  pthread_join(displaythread,NULL);
7243 
7244 }
7245 // tmp files are on a per-invocation basis on Android, and possibly other locations.
7246 // note that the "tempnam" function will accept NULL as the directory on many platforms,
7247 // so this function does not really need to be called on many platforms.
7248 void fwl_tmpFileLocation(char *tmpFileLocation) {
7249  ttglobal tg;
7250  if (tmpFileLocation == NULL) return;
7251  tg = gglobal();
7252  FREE_IF_NZ(tg->Mainloop.tmpFileLocation);
7253  tg->Mainloop.tmpFileLocation = MALLOC(char *,strlen(tmpFileLocation)+1);
7254  strcpy(tg->Mainloop.tmpFileLocation,tmpFileLocation);
7255 }
7256 
7257 void close_internetHandles();
7258 void freewrlDie (const char *format) {
7259  ConsoleMessage ("Catastrophic error: %s\n",format);
7260  fwl_doQuit(__FILE__,__LINE__);
7261 }
7262 
7263 //with multi-touch, touches are flowing in and around, but you normally have 20 or fewer fingers on the screen
7264 //at one time (IIRC smarttech has a touch table for kids that takes 20)
7265 //rather than fragment memory with a lot of malloc and free, these functions recycle touches
7266 struct Touch * AllocTouch(unsigned int ID){
7267  //call on mouse/touch DOWN
7268  int i;
7269  ppMainloop p;
7270  ttglobal tg = gglobal();
7271  p = (ppMainloop)tg->Mainloop.prv;
7272  for(i=0;i<p->ntouch;i++)
7273  if(p->touchlist[i].ID == ID && p->touchlist[i].inUse){
7274  //memset(&p->touchlist[i],0,sizeof(struct Touch));
7275  p->touchlist[i].inUse = 2; //2 == dragging
7276  return &p->touchlist[i];
7277  }
7278  for(i=0;i<p->ntouch;i++)
7279  if(!p->touchlist[i].inUse){
7280  memset(&p->touchlist[i],0,sizeof(struct Touch));
7281  p->touchlist[i].inUse = 2; //2 == dragging
7282  p->touchlist[i].ID = ID;
7283  return &p->touchlist[i];
7284  }
7285  return NULL;
7286 }
7287 struct Touch * GetTouch(unsigned int ID){
7288  //call on mouse/touch MOVE
7289  int i;
7290  ppMainloop p;
7291  ttglobal tg = gglobal();
7292  p = (ppMainloop)tg->Mainloop.prv;
7293  for(i=0;i<p->ntouch;i++)
7294  if(p->touchlist[i].ID == ID && (p->touchlist[i].inUse || ID == 0) ){
7295  return &p->touchlist[i];
7296  }
7297  return NULL;
7298 }
7299 void ReleaseTouch(unsigned int ID){
7300  //call on mouse/touch UP
7301  //it needs to hang around for a frame for logic setup_picking
7302  int i;
7303  ppMainloop p;
7304  ttglobal tg = gglobal();
7305  p = (ppMainloop)tg->Mainloop.prv;
7306  for(i=0;i<p->ntouch;i++)
7307  if(p->touchlist[i].ID == ID ){
7308  p->touchlist[i].inUse = 0;
7309  return;
7310  }
7311 }
7312 //void FreeTouches(){
7313 // //call a frame after touch UP
7314 // //cleans up released drags that hung around for a frame for setup_picking
7315 // int i;
7316 // ppMainloop p;
7317 // ttglobal tg = gglobal();
7318 // p = (ppMainloop)tg->Mainloop.prv;
7319 // for(i=0;i<p->ntouch;i++)
7320 // if(p->touchlist[i].inUse == 1){
7321 // memset(&p->touchlist[i],0,sizeof(struct Touch));
7322 // }
7323 //}
7324 // Definition of current touch:
7325 // when the event is DOWN / PRESS and there is no currentTouch, then the ID
7326 // of this DOWN event becomes the currentTouch until RELEASE/UP for its ID.
7327 // then currentTouch is free'ed up (set to 0)
7328 // ID is unsigned, and 0 means no touch is occuring, 1 to MAXINT (4 billion) is a valid touch ID
7329 void setCurrentTouchID(unsigned int ID){
7330  ppMainloop p;
7331  ttglobal tg = gglobal();
7332  p = (ppMainloop)tg->Mainloop.prv;
7333  p->currentTouch = ID;
7334 }
7335 struct Touch *currentTouch(){
7336  ppMainloop p;
7337  ttglobal tg = gglobal();
7338  p = (ppMainloop)tg->Mainloop.prv;
7339  //printf("currentTouch %d\n",p->currentTouch);
7340  //if(p->currentTouch == -1) p->currentTouch = 0;
7341  return GetTouch(p->currentTouch);
7342 }
7343 
7344 void handle_pedal(int mev, int x, int y, ivec4 vport){
7345  ppMainloop p;
7346  struct pedal_state *pstate;
7347  ttglobal tg = gglobal();
7348  p = (ppMainloop)tg->Mainloop.prv;
7349  pstate = &p->pedalstate;
7350  if(mev == ButtonPress) {
7351  if(!pstate->initialized){
7352  pstate->x = x;
7353  pstate->y = y;
7354  pstate->initialized = TRUE;
7355  }
7356  pstate->rx = x; //x;
7357  pstate->ry = y; //y;
7358  pstate->isDown = TRUE;
7359  } else if (mev == MotionNotify) {
7360  if(pstate->isDown){
7361  pstate->x += x - pstate->rx;
7362  pstate->y += y - pstate->ry;
7363  pstate->rx = x;
7364  pstate->ry = y;
7365  }
7366  } else if(mev == ButtonRelease) {
7367  if(pstate->isDown){
7368  pstate->x += x - pstate->rx;
7369  pstate->y += y - pstate->ry;
7370  }
7371  pstate->isDown = FALSE;
7372  }
7373  //printf("hovpd %d %d\n",pstate->x,pstate->y);
7374 }
7375 void viewer_setNextDragChord();
7376 void fwl_handle_aqua_multiNORMAL(const int mev, const unsigned int button, int x, int y, unsigned int ID, int windex) {
7377  int ibutton, passed, claimant;
7378  float fx, fy;
7379  struct Touch *touch;
7380  Stack *vportstack;
7381  ivec4 vport;
7382  ppMainloop p;
7383  ttglobal tg = gglobal();
7384  p = (ppMainloop)tg->Mainloop.prv;
7385 
7386  //ID = 0; //good way to enforce single-touch for testing
7387  /* save this one... This allows Sensors to get mouse movements if required. */
7388  //p->lastMouseEvent[ID] = mev;
7389  //ConsoleMessage("m %d b %d i %d x %d y %d\n",mev,button,ID,x,y);
7390  //winRT but =1 when mev = motion, others but = 0 when mev = motion.
7391  //make winRT the same as the others:
7392  if(button == RMB){
7393  //May 2016 - officially no more RMB for any kind of navigation
7394  //for fun, lets use desktop RMB to change fly chord
7395  if(mev == ButtonPress){
7396  viewer_setNextDragChord();
7397  }
7398  return;
7399  }
7400  ibutton = button;
7401  if(fwl_getHover()) ibutton = 0; //so called up-drag or isOver / hover mode
7402 
7403  //if (mev == MotionNotify && ibutton !=0)
7404  // ibutton = 0; //moved to fw_handle_mouse_multi_yup for winRT mouse
7405 
7406  vportstack = (Stack*)tg->Mainloop._vportstack;
7407  vport = stack_top(ivec4,vportstack);
7408  if(0){
7409  printf("multiNORMAL x %d y %d fx %f fy %f vp %d %d %d %d\n",x,y,fx,fy,vport.X,vport.W,vport.Y,vport.H);
7410  }
7411  if (0){
7412  ConsoleMessage("fwl_handle_aqua in MainLoop; mev %d but %d x %d y %d ID %d ",
7413  mev, ibutton, x, y, ID);
7414  ConsoleMessage("wndx %d swi %d shi %d ", windex, vport.W, vport.H); //screenWidth, screenHeight);
7415  if (mev == ButtonPress) ConsoleMessage("ButtonPress\n");
7416  else if (mev == ButtonRelease) ConsoleMessage("ButtonRelease\n");
7417  else if (mev == MotionNotify) ConsoleMessage("MotionNotify\n");
7418  else ConsoleMessage("event %d\n", mev);
7419  }
7420  //FreeTouches(); //call often, once per event OK, or once per frame, to garbage collect isUsed = FALSE
7421  // Order of new touch claimants: pedal, sensor, navigation, none/hover
7422  //
7423  //
7424  passed = TOUCHCLAIMANT_PEDAL;
7425  claimant = TOUCHCLAIMANT_UNCLAIMED;
7426  if (fwl_getPedal()) {
7427  handle_pedal(mev, x, y, vport);
7428  if(fwl_getHover()){
7429  ibutton = 0;
7430  passed = 0;
7431  claimant = TOUCHCLAIMANT_PEDAL;
7432  }
7433  }
7434 
7435  /* save the current x and y positions for picking. */
7436  if(mev == ButtonPress){
7437  //welcome, a new touch / start of drag
7438  //android multi_touch can send in two mev=4 and two mev=5 for the first touch when doing 2+ touches
7439  // H: one is regular, and one POINTER
7440  // if 2, then keep using the first one
7441  touch = GetTouch(ID);
7442  if(!touch){
7443  //if(touch) touch->inUse = FALSE;
7444  touch = AllocTouch(ID);
7445  if(currentTouch()->ID == 0) {
7446  //there is no other current touch that we are in the middle of,
7447  //so this becomes the current touch
7448  setCurrentTouchID(ID);
7449  }
7450  touch->windex = windex;
7451  touch->stageId = current_stageId();
7452  touch->buttonState = ibutton ? 1 : 0; //mev == ButtonPress; 0=hover/isOver/up-drag mode, 1=normal down-drag, stays constant for whole drag
7453  touch->claimant = claimant;
7454  touch->passed = passed;
7455  touch->dragStart = TRUE; //cleared by claimant when they've consumed the start
7456  }
7457  }else{
7458  touch = GetTouch(ID);
7459  }
7460  if(touch == NULL){
7461  //May 4, 2016 change: we now ignore mouse-up mouse moves / hovers / isOver
7462  //ConsoleMessage("null touch ");
7463  return;
7464  }
7465 
7466  //touch = &p->touchlist[ID];
7467  if(fwl_getPedal()){
7468  touch->x = p->pedalstate.x;
7469  touch->y = p->pedalstate.y;
7470  //printf("pedal %d %d\n",touch->x,touch->y);
7471  }else {
7472  touch->x = x;
7473  touch->y = y;
7474  //printf("norml %d %d\n",touch->x,touch->y);
7475  }
7476  fx = (float)(touch->x - vport.X) / (float)vport.W;
7477  fy = (float)(touch->y - vport.Y) / (float)vport.H;
7478  touch->fx = fx;
7479  touch->fy = fy;
7480  touch->mev = mev;
7481  touch->angle = 0.0f;
7482  // this isn't necessarily the current touch if there are multiple touches //p->currentTouch = ID; // pick/dragsensors can use 0-19
7483  if(mev == ButtonRelease){
7484  if(touch->ID == ID)
7485  p->currentTouch = 0;
7486  touch->dragEnd = TRUE;
7487  if(touch->claimant == TOUCHCLAIMANT_PEDAL) touch->inUse = FALSE;
7488  }
7489  return;
7490 }
7491 void update_navigation(){
7492  //update_navigation - this will be for unclaimed touches from last iteration
7493  //this code should be called from a function once per frame (not once per event)
7494  // (need to process ButtonRelease from previous event)
7495  // Q. why is there no windex or stageId filtering in here?
7496  int i;
7497  struct Touch *curTouch;
7498  ppMainloop p;
7499  ttglobal tg = gglobal();
7500  p = (ppMainloop)tg->Mainloop.prv;
7501 
7502  for(i=0;i<p->ntouch;i++){
7503  curTouch = &p->touchlist[i];
7504  if(curTouch->inUse){
7505  //yes incoming touch _is_ the current touch
7506  //nav always uses current touch //ID==0
7507  int priorclaimants = TOUCHCLAIMANT_PEDAL | TOUCHCLAIMANT_SENSOR;
7508  if(curTouch->claimant == TOUCHCLAIMANT_UNCLAIMED && curTouch->passed == priorclaimants ){
7509  //see if we want to claim it for navigation
7510  if(!fwl_getHover())
7511  curTouch->claimant = TOUCHCLAIMANT_NAVIGATION;
7512  else
7513  curTouch->passed |= TOUCHCLAIMANT_NAVIGATION;
7514  }
7515  if(curTouch->claimant == TOUCHCLAIMANT_NAVIGATION){
7516  int imev, ibut;
7517  ibut = curTouch->buttonState;
7518  if (curTouch->dragStart || (curTouch->dragEnd)) {
7519  if(curTouch->dragStart) imev = ButtonPress;
7520  if(curTouch->dragEnd) imev = ButtonRelease;
7521  handle(imev, ibut, curTouch->fx,curTouch->fy);
7522  curTouch->dragStart = FALSE;
7523  if(curTouch->dragEnd) curTouch->inUse = FALSE; //garbage collect
7524  curTouch->dragEnd = FALSE;
7525  } else {
7526  imev = MotionNotify;
7527  handle (imev, ibut, curTouch->fx, curTouch->fy);
7528  }
7529  }
7530  }
7531  }
7532 }
7533 
7534 /* mobile devices - set screen orientation */
7535 /* "0" is "normal" orientation; degrees clockwise; note that face up and face down not
7536  coded; assume only landscape/portrait style orientations */
7537 
7538 void fwl_setOrientation (int orient) {
7539  //this original version affects the 3D view matrix
7540  switch (orient) {
7541  case 0:
7542  case 90:
7543  case 180:
7544  case 270:
7545  {
7546  Viewer()->screenOrientation = orient;
7547  break;
7548  }
7549  default: {
7550  ConsoleMessage ("invalid orientation %d\n",orient);
7551  Viewer()->screenOrientation = 0;
7552  }
7553  }
7554 }
7555 int fwl_getOrientation(){
7556  return Viewer()->screenOrientation;
7557 }
7558 
7559 void fwl_setOrientation2 (int orient) {
7560  //this Dec 2015 version affects 2D screen orientation via a contenttype_orientation widget
7561  switch (orient) {
7562  case 0:
7563  case 90:
7564  case 180:
7565  case 270:
7566  {
7567  gglobal()->Mainloop.screenOrientation2 = orient;
7568  //ConsoleMessage ("set orientation2 %d\n",orient);
7569  break;
7570  }
7571  default: {
7572  ConsoleMessage ("invalid orientation2 %d\n",orient);
7573  gglobal()->Mainloop.screenOrientation2 = 0;
7574  }
7575  }
7576 }
7577 int fwl_getOrientation2(){
7578  //ConsoleMessage ("get orientation2 %d\n",gglobal()->Mainloop.screenOrientation2);
7579  return gglobal()->Mainloop.screenOrientation2;
7580 }
7581 
7582 void setIsPlugin() {
7583 
7584  RUNNINGASPLUGIN = TRUE;
7585 
7586  // Save local working directory
7587  /*
7588  {
7589  FILE* tmpfile;
7590  char tmppath[512];
7591  system("pwd > /tmp/freewrl_filename");
7592  tmpfile = fopen("/tmp/freewrl_filename", "r");
7593 
7594  if (tmpfile) {
7595  fgets(tmppath, 512, tmpfile);
7596  }
7597  BrowserFullPath = STRDUP(tmppath);
7598  fclose(tmpfile);
7599  //system("rm /tmp/freewrl_filename");
7600  tmpfile = fopen("/tmp/after", "w");
7601  if (tmpfile) {
7602  fprintf(tmpfile, "%s\n", BrowserFullPath);
7603  }
7604  fclose(tmpfile);
7605  }
7606  */
7607 
7608 }
7609 
7610 // OLD_IPHONE_AQUA #ifdef AQUA
7611 // OLD_IPHONE_AQUA
7612 // OLD_IPHONE_AQUA int aquaPrintVersion() {
7613 // OLD_IPHONE_AQUA printf ("FreeWRL version: %s\n", libFreeWRL_get_version());
7614 // OLD_IPHONE_AQUA exit(EXIT_SUCCESS);
7615 // OLD_IPHONE_AQUA }
7616 // OLD_IPHONE_AQUA
7617 // OLD_IPHONE_AQUA #endif
7618 
7619 /* if we are visible, draw the OpenGL stuff, if not, don not bother */
7620 void setDisplayed (int state) {
7621  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
7622 
7623  #ifdef VERBOSE
7624  if (state) printf ("WE ARE DISPLAYED\n");
7625  else printf ("we are now iconic\n");
7626  #endif
7627  p->onScreen = state;
7628 }
7629 
7630 void fwl_init_EaiVerbose() {
7631  //eaiverbose = TRUE;
7632 #if !defined(EXCLUDE_EAI)
7633  gglobal()->EAI_C_CommonFunctions.eaiverbose = TRUE;
7634  fwlio_RxTx_control(CHANNEL_EAI, RxTx_MOREVERBOSE); /* RxTx_SILENT */
7635 #endif
7636 
7637 }
7638 
7639 #ifdef OLDCODE
7640 #if defined (_ANDROID)
7641 
7642 void fwl_Android_replaceWorldNeeded() {
7643  int i;
7644  // OLD_IPHONE_AQUA #ifndef AQUA
7645  char mystring[20];
7646  // OLD_IPHONE_AQUA #endif
7647  struct VRMLParser *globalParser = (struct VRMLParser *)gglobal()->CParse.globalParser;
7648 
7649  /* get rid of sensor events */
7650  resetSensorEvents();
7651 
7652  /* make the root_res equal NULL - this throws away all old resource info */
7653  gglobal()->resources.root_res = NULL;
7654  Android_reset_viewer_to_defaults();
7655 
7656  struct tProdCon *t = &gglobal()->ProdCon;
7657 
7658  // if we have a bound vp; if the old world did not have a vp, there will be nothing to send_bind_to
7659  if (vectorSize(t->viewpointNodes) > t->currboundvpno) {
7660  send_bind_to(vector_get(struct X3D_Node*, t->viewpointNodes,t->currboundvpno),0);
7661  }
7662 
7663  if (rootNode() != NULL) {
7664 
7665  /* mark all rootNode children for Dispose */
7666  for (i=0; i<proto->__children.n; i++) {
7667  markForDispose(proto->__children.p[i], TRUE);
7668  }
7669 
7670  /* stop rendering. This should be done when the new resource is loaded, and new_root is set,
7671  but lets do it here just to make sure */
7672  proto->__children.n = 0; // no children, but _sortedChildren not made;
7673  proto->_change ++; // force the rootNode()->_sortedChildren to be made
7674  }
7675 
7676  /* close the Console Message system, if required. */
7677  closeConsoleMessage();
7678 
7679  /* occlusion testing - zero total count, but keep MALLOC'd memory around */
7680  zeroOcclusion();
7681 
7682  /* clock events - stop them from ticking */
7683  kill_clockEvents();
7684 
7685  /* kill DEFS, handles */
7686  //if we do this here, we have a problem, as the parser is already killed and cleaned up.
7687  //EAI_killBindables();
7688 
7689  kill_bindables();
7690  killKeySensorNodeList();
7691 
7692  /* stop routing */
7693  kill_routing();
7694 
7695  /* tell the statusbar that it needs to reinitialize */
7696  //kill_status();
7697  setMenuStatus(NULL);
7698 
7699  /* any user defined Shader nodes - ComposedShader, PackagedShader, ProgramShader?? */
7700  kill_userDefinedShaders();
7701 
7702  /* free scripts */
7703 
7704  kill_javascript();
7705 
7706 
7707  #if !defined(EXCLUDE_EAI)
7708  /* free EAI */
7709  if (kill_EAI) {
7710  /* shutdown_EAI(); */
7711  fwlio_RxTx_control(CHANNEL_EAI, RxTx_STOP) ;
7712  }
7713  #endif //EXCLUDE_EAI
7714 
7715  // OLD_IPHONE_AQUA #ifndef AQUA
7716  sprintf (mystring, "QUIT");
7717  Sound_toserver(mystring);
7718  // OLD_IPHONE_AQUA #endif
7719 
7720  /* reset any VRML Parser data */
7721  if (globalParser != NULL) {
7722  parser_destroyData(globalParser);
7723  //globalParser = NULL;
7724  gglobal()->CParse.globalParser = NULL;
7725  }
7726 
7727  kill_X3DDefs();
7728 
7729  /* tell statusbar that we have none */
7730  viewer_default();
7731  setMenuStatus("NONE");
7732 }
7733 #endif
7734 #endif //OLDCODE
7735 
7736 
7737 #if !defined(_ANDROID) || defined(ANDROIDNDK)
7738 
7739 // JAS - Do not know if these are still required.
7740 
7741 /* called from the standalone OSX front end and the OSX plugin */
7742 char *strBackslash2fore(char *);
7743 void fwl_replaceWorldNeeded(char* str)
7744 {
7745  ConsoleMessage("file to load: %s\n",str);
7746  FREE_IF_NZ(gglobal()->Mainloop.replaceWorldRequest);
7747  gglobal()->Mainloop.replaceWorldRequest = strBackslash2fore(STRDUP(str));
7748 }
7749 void fwl_replaceWorldNeededRes(resource_item_t *multiResWithParent){
7750  gglobal()->Mainloop.replaceWorldRequestMulti = (void*)(multiResWithParent);
7751 }
7752 
7753 
7754 void fwl_reload()
7755 {
7756  ConsoleMessage("fwl_reload called");
7757 }
7758 
7759 #endif //NOT _ANDROID
7760 
7761 
7762 /* send the description to the statusbar line */
7763 void sendDescriptionToStatusBar(struct X3D_Node *CursorOverSensitive) {
7764  int tmp;
7765  char *ns;
7766  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
7767 
7768  if (CursorOverSensitive == NULL) update_status(NULL);
7769  else {
7770 
7771  ns = NULL;
7772  for (tmp=0; tmp<p->num_SensorEvents; tmp++) {
7773  if (p->SensorEvents[tmp].fromnode == CursorOverSensitive) {
7774  switch (p->SensorEvents[tmp].datanode->_nodeType) {
7775  case NODE_Anchor: ns = ((struct X3D_Anchor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7776  case NODE_LineSensor: ns = ((struct X3D_LineSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7777  case NODE_PlaneSensor: ns = ((struct X3D_PlaneSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7778  case NODE_SphereSensor: ns = ((struct X3D_SphereSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7779  case NODE_TouchSensor: ns = ((struct X3D_TouchSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7780  case NODE_GeoTouchSensor: ns = ((struct X3D_GeoTouchSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7781  case NODE_CylinderSensor: ns = ((struct X3D_CylinderSensor *)p->SensorEvents[tmp].datanode)->description->strptr; break;
7782  default: {printf ("sendDesc; unknown node type %d\n",p->SensorEvents[tmp].datanode->_nodeType);}
7783  }
7784  /* if there is no description, put the node type on the screen */
7785  if (ns == NULL) {ns = "(over sensitive)";}
7786  else if (ns[0] == '\0') ns = (char *)stringNodeType(p->SensorEvents[tmp].datanode->_nodeType);
7787 
7788  /* send this string to the screen */
7789  update_status(ns);
7790  }
7791  }
7792  }
7793 }
7794 
7795 
7796 /* We have a new file to load, lets get rid of the old world sensor events, and run with it */
7797 void resetSensorEvents(void) {
7798  int ktouch;
7799  ppMainloop p = (ppMainloop)gglobal()->Mainloop.prv;
7800 
7801  for(ktouch=0;ktouch<20;ktouch++){
7802  struct Touch *touch;
7803  touch = &p->touchlist[ktouch];
7804  if(touch->inUse){
7805  if (touch->oldCOS != NULL)
7806  sendSensorEvents(touch->oldCOS,MapNotify,touch->buttonState, FALSE);
7807  //sendSensorEvents(p->oldCOS,MapNotify,p->ButDown[p->currentCursor][1], FALSE);
7808  }
7809  /* remove any display on-screen */
7810  sendDescriptionToStatusBar(NULL);
7811  memset(touch,0,sizeof(struct Touch));
7812  FREE_IF_NZ(p->SensorEvents);
7813  }
7814  p->num_SensorEvents = 0;
7815  gglobal()->RenderFuncs.hypersensitive = NULL;
7816  gglobal()->RenderFuncs.hyperhit = 0;
7817 
7818 }
Definition: Viewer.h:196
Initialization.
Definition: libFreeWRL.h:71
Definition: MainLoop.c:727
Definition: Vector.h:36
Definition: display.c:66
Definition: display.c:65
Definition: Viewer.h:174