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