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