FreeWRL / FreeX3D 4.3.0
Component_Networking.c
1/*
2
3
4X3D Networking Component
5
6*/
7
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28
29
30#include <config.h>
31#include <system.h>
32#include <display.h>
33#include <internal.h>
34
35#include "../vrml_parser/Structs.h"
36#include "../vrml_parser/CRoutes.h"
37#include "../main/headers.h"
38
39#include "../input/EAIHeaders.h"
40#include "../input/EAIHelpers.h"
41#include "../opengl/Frustum.h"
42#include "../opengl/OpenGL_Utils.h"
43#include "../opengl/Textures.h"
44
45#include "Component_Networking.h"
46#include "Children.h"
47#include "../scenegraph/RenderFuncs.h"
48
49#include <libFreeWRL.h>
50#include <list.h>
51#include <io_http.h>
52#ifdef WANT_OSC
53 #include <lo/lo.h>
54 #include "ringbuf.h"
55 #define USE_OSC 1
56 #define TRACK_OSC_MSG 0
57#else
58 #define USE_OSC 0
59#endif
60
61#if USE_OSC
62#include "../vrml_parser/CParseGeneral.h"
63#include "../scenegraph/Vector.h"
64#include "../vrml_parser/CFieldDecls.h"
65#include "../world_script/JScript.h"
66#include "../world_script/CScripts.h"
67#include "../world_script/fieldSet.h"
68#include "../vrml_parser/CParseParser.h"
69#include "../vrml_parser/CParseLexer.h"
70#include "../vrml_parser/CParse.h"
71#endif
72
73//OLDCODE #define BUTTON_PRESS_STRING "use_for_buttonPresses"
74
75#if USE_OSC
76/**************** START OF OSC node **************************/
77
78void error(int num, const char *m, const char *path);
79void utilOSCcounts(char *types , int *intCount, int *fltCount, int *strCount, int *blobCount, int *midiCount, int *otherCount);
80
81/* We actually want to keep this one inline, as it offers ease of editing with minimal infrastructure */
82#include "OSCcallbacks.c"
83
84int serverCount=0;
85#define MAX_OSC_SERVERS 32
86int serverPort[MAX_OSC_SERVERS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
87lo_server_thread oscThread[MAX_OSC_SERVERS] ;
88
89static uintptr_t *OSC_Nodes = NULL;
90static int num_OSC_Nodes = 0;
91int curr_OSC_Node = 0;
92int active_OSC_Nodes = FALSE;
93
94void utilOSCcounts(char *types , int *intCount, int *fltCount, int *strCount, int *blobCount, int *midiCount, int *otherCount) {
95 *intCount = 0;
96 *fltCount = 0;
97 *strCount = 0;
98 *blobCount = 0;
99 *midiCount = 0;
100 *otherCount = 0;
101
102 int i,j;
103
104 j=strlen(types) ;
105 /* what amount of storage */
106 for (i=0 ; i < j ; i++) {
107 switch (types[i]) {
108 case 'i':
109 (*intCount)++;
110 break;
111 case 'f':
112 (*fltCount)++;
113 break;
114 case 's':
115 (*strCount)++;
116 break;
117 case 'b':
118 (*blobCount)++;
119 break;
120 case 'm':
121 (*midiCount)++;
122 break;
123 default:
124 (*otherCount)++;
125 break;
126 }
127 }
128}
129
130void activate_OSCsensors() {
131 curr_OSC_Node = 0 ;
132 active_OSC_Nodes = TRUE ;
133 struct X3D_OSC_Sensor *realnode ;
134 char buf[32];
135 int i ;
136 /* what amount of storage */
137 int fltCount;
138 int intCount;
139 int strCount;
140 int blobCount;
141 int midiCount;
142 int otherCount;
143
144 while (active_OSC_Nodes && curr_OSC_Node < num_OSC_Nodes) {
145 realnode = (struct X3D_OSC_Sensor *) OSC_Nodes[curr_OSC_Node] ;
146 if (checkNode(realnode,__FILE__,__LINE__)) {
147 #if TRACK_OSC_MSG
148 printf("activate_OSCsensors : %s,%d node=%p name=%s\n", __FILE__,__LINE__,realnode,realnode->description->strptr) ;
149 #endif
150 if (realnode->_status < 0) {
151 printf("activate_OSCsensors : %s,%d Moving %s to ready.\n", __FILE__,__LINE__,realnode->description->strptr) ;
152 realnode->_status = 0 ;
153 } else if (realnode->_status == 0) {
154 printf("activate_OSCsensors : %s,%d\n", __FILE__,__LINE__) ;
155 printf("activate_OSCsensors : enabled=%d\n", realnode->enabled) ;
156 printf("activate_OSCsensors : gotEvents=%d\n", realnode->gotEvents) ;
157 printf("activate_OSCsensors : description=%s\n",realnode->description->strptr) ;
158 printf("activate_OSCsensors : protocol=%s\n", realnode->protocol->strptr) ;
159 printf("activate_OSCsensors : port=%d\n", realnode->port) ;
160 printf("activate_OSCsensors : filter=%s\n", realnode->filter->strptr) ;
161 printf("activate_OSCsensors : handler=%s\n", realnode->handler->strptr) ;
162/*
16311715 if(allFields) {
16411716 spacer fprintf (fp,"\t_talkToNodes (MFNode):\n");
16511717 for (i=0; i<tmp->_talkToNodes.n; i++) { dump_scene(fp,level+1,tmp->_talkToNodes.p[i]); }
16611718 }
16711719 if(allFields) {
16811720 spacer fprintf (fp,"\t_status (SFInt32) \t%d\n",tmp->_status);
16911721 }
17011722 if(allFields) {
17111723 spacer fprintf (fp,"\t_floatInpFIFO (SFNode):\n"); dump_scene(fp,level+1,tmp->_floatInpFIFO);
17211724 }
17311725 if(allFields) {
17411726 spacer fprintf (fp,"\t_int32OutFIFO (SFNode):\n"); dump_scene(fp,level+1,tmp->_int32OutFIFO);
17511727 }
17611728 spacer fprintf (fp,"\ttalksTo (MFString): \n");
17711729 for (i=0; i<tmp->talksTo.n; i++) { spacer fprintf (fp," %d: \t%s\n",i,tmp->talksTo.p[i]->strptr); }
178*/
179 printf("activate_OSCsensors : talksTo=[ ");
180 for (i=0; i < realnode->talksTo.n; i++) {
181 printf("\"%s\" ",realnode->talksTo.p[i]->strptr);
182 /* This would be a good time to convert the name into an entry in _talkToNodes */
183 struct X3D_Node * myNode;
184 /* myNode = X3DParser_getNodeFromName(realnode->talksTo.p[i]->strptr); */
185 myNode = parser_getNodeFromName(realnode->talksTo.p[i]->strptr);
186 if (myNode != NULL) {
187 printf("(%p) ",(void *)myNode);
188 } else {
189 printf("(..) ");
190 }
191 }
192 printf("] (%d nodes) (Need to fix %s,%d)\n",realnode->talksTo.n , __FILE__,__LINE__);
193 printf("activate_OSCsensors : listenfor=%s , expect %d parameters\n", realnode->listenfor->strptr , (int)strlen(realnode->listenfor->strptr)) ;
194 printf("activate_OSCsensors : FIFOsize=%d\n", realnode->FIFOsize) ;
195 printf("activate_OSCsensors : _status=%d\n", realnode->_status) ;
196
197 if (realnode->FIFOsize > 0) {
198 /* what amount of storage */
199 utilOSCcounts(realnode->listenfor->strptr,&intCount,&fltCount,&strCount,&blobCount,&midiCount,&otherCount);
200 intCount = realnode->FIFOsize * (intCount + midiCount);
201 fltCount = realnode->FIFOsize * fltCount;
202 strCount = realnode->FIFOsize * (strCount + blobCount + otherCount);
203 printf("Allocate %d floats, %d ints for '%s'\n",fltCount,intCount,realnode->description->strptr);
204
205 realnode->_int32InpFIFO = (void *) NewRingBuffer (intCount) ;
206 realnode->_floatInpFIFO = (void *) NewRingBuffer (fltCount) ;
207 realnode->_stringInpFIFO = (void *) NewRingBuffer (strCount) ;
208
209 }
210
211 /* start a new server on the required port */
212 int foundCurrentPort = -1 ;
213 for ( i=0 ; i < num_OSC_Nodes ; i++) {
214 if(realnode->port == serverPort[i]) {
215 foundCurrentPort=i;
216 i = num_OSC_Nodes+1;
217 }
218 }
219 if (foundCurrentPort < 0) {
220 foundCurrentPort = serverCount ;
221 serverPort[foundCurrentPort] = realnode->port ;
222 serverCount++ ;
223
224 sprintf (buf,"%d",realnode->port);
225
226 if (strcmp("TCP",realnode->protocol->strptr)==0) {
227 /* oscThread[foundCurrentPort] = lo_server_thread_new_with_proto(buf, LO_TCP, error); */
228 oscThread[foundCurrentPort] = lo_server_thread_new(buf, error);
229 } else if (strcmp("UNIX",realnode->protocol->strptr)==0) {
230 /* oscThread[foundCurrentPort] = lo_server_thread_new_with_proto(buf, LO_UNIX, error); */
231 oscThread[foundCurrentPort] = lo_server_thread_new(buf, error);
232 } else {
233 /* oscThread[foundCurrentPort] = lo_server_thread_new_with_proto(buf, LO_UDP, error); */
234 oscThread[foundCurrentPort] = lo_server_thread_new(buf, error);
235 }
236 lo_server_thread_start(oscThread[foundCurrentPort]);
237 }
238
239 printf("%d servers; current server is running in slot %d on port %d\n",serverCount,foundCurrentPort,serverPort[foundCurrentPort]) ;
240
241 /* add method (by looking up its name) that will in future use the required path */
242 /* need to re-read the lo code to check that you can register the same callback twice (or more) with different paths) */
243 /* See OSCcallbacks.c */
244 int foundHandler = 0 ;
245 for (i=0 ; i < OSCfuncCount ; i++) {
246 printf("%d/%d : Check %s against %s\n",i,OSCfuncCount,realnode->handler->strptr,OSCfuncNames[i]);
247 if (0 == strcmp(realnode->handler->strptr,OSCfuncNames[i])) {foundHandler = i;}
248 }
249 if (OSCcallbacks[foundHandler] != NULL) {
250 printf("Going to hook '%s' to '%s' handler\n",realnode->description->strptr ,OSCfuncNames[foundHandler]) ;
251 lo_server_thread_add_method(oscThread[foundCurrentPort], realnode->filter->strptr, realnode->listenfor->strptr,
252 (OSCcallbacks[foundHandler]), realnode);
253 }
254
255 realnode->_status = 1 ;
256 /* We only want one OSC node to become active in one slowtick */
257 active_OSC_Nodes = FALSE ;
258 }
259 } // end of checkNode conditional - JAS
260
261 curr_OSC_Node++;
262 }
263}
264
265void error(int num, const char *msg, const char *path)
266{
267 printf("liblo server error %d in path %s: %s\n", num, path, msg);
268}
269
270void add_OSCsensor(struct X3D_Node * node) {
271 uintptr_t *myptr;
272
273 if (node == 0) {
274 printf ("error in registerOSCNode; somehow the node datastructure is zero \n");
275 return;
276 }
277
278 if (node->_nodeType != NODE_OSC_Sensor) return;
279
280 OSC_Nodes = (uintptr_t *) REALLOC (OSC_Nodes,sizeof (uintptr_t *) * (num_OSC_Nodes+1));
281 myptr = OSC_Nodes;
282
283 /* now, put the node pointer into the structure entry */
284 *myptr = (uintptr_t) node;
285
286 num_OSC_Nodes++;
287}
288void remove_OSCsensor(struct X3D_Node * node) {}
289/***************** END OF OSC node ***************************/
290#else
291void add_OSCsensor(struct X3D_Node * node) {}
292void remove_OSCsensor(struct X3D_Node * node) {}
293#endif
294
295int loadstatus_AudioClip(struct X3D_AudioClip *node);
296int loadstatus_Script(struct X3D_Script *script);
297int getFieldFromNodeAndNameC(struct X3D_Node* node,const char *fieldname, int *type, int *kind, int *iifield, int *builtIn, union anyVrml **value, const char **cname);
298void render_LoadSensor (struct X3D_LoadSensor *node) {
299 int count, nwatch;
300 int nowLoading;
301 int nowFinished;
302 struct X3D_Node *cnode, **watchlist;
303 // HAVE TO RECODE MovieTexture struct X3D_MovieTexture *mnode;
304
305 /* if not enabled, do nothing */
306 if (!node) return;
307 if (node->__oldEnabled != node->enabled) {
308 node->__oldEnabled = node->enabled;
309 MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_LoadSensor, enabled));
310 }
311 if (!node->enabled) return;
312
313 /* we only need to look at this once per event loop */
314 //if (!renderstate()->render_geom) return;
315 if (!renderstate()->render_sensitive) return;
316
317 /* do we need to re-generate our internal variables? */
318 if NODE_NEEDS_COMPILING {
319 MARK_NODE_COMPILED
320 node->__loading = 0;
321 node->__finishedloading = 0;
322 node->progress = (float) 0.0;
323 node->__StartLoadTime = 0.0;
324 }
325
326 /* do we actually have any nodes to watch? */
327 nwatch = 0;
328 if (node->watchList.n) {
329 nwatch = node->watchList.n;
330 watchlist = node->watchList.p;
331 }
332 else if (node->children.n) {
333 nwatch = node->children.n;
334 watchlist = node->children.p;
335 }
336 if (nwatch <=0) return;
337
338 /* are all nodes loaded? */
339 if (node->__finishedloading == nwatch) return;
340
341 /* our current status... */
342 nowLoading = 0;
343 nowFinished = 0;
344
345 /* go through node list, and check to see what the status is */
346 /* printf ("have %d nodes to watch\n",node->watchList.n); */
347 for (count = 0; count < nwatch; count ++) {
348
349 cnode = watchlist[count];
350 render_node(cnode); //might not be def/use, this might be the only node list its in
351 /* printf ("node type of node %d is %d\n",count,tnode->_nodeType); */
352 switch (cnode->_nodeType) {
353 case NODE_ImageTexture:
354 {
355 /* printf ("opengl tex is %d\n",tnode->__texture); */
356 /* is this texture thought of yet? */
357 struct X3D_ImageTexture *tnode = (struct X3D_ImageTexture *) cnode;
358
359 nowLoading++;
360 if (fwl_isTextureLoaded(tnode->__textureTableIndex)) {
361 /* is it finished loading? */
362 nowFinished ++;
363 }
364 }
365 break;
366 case NODE_Inline:
367 {
368 struct X3D_Inline *inode;
369 inode = (struct X3D_Inline *) cnode; /* change type to Inline */
370 if(inode->__loadstatus > INLINE_INITIAL_STATE && inode->__loadstatus < INLINE_STABLE)
371 nowLoading++;
372 if(inode->__loadstatus == INLINE_STABLE)
373 nowFinished ++;
374 /* printf ("LoadSensor, Inline %d, type %d loadstatus %d at %d\n",inode,inode->_nodeType,inode->__loadstatus, &inode->__loadstatus); */
375 }
376 break;
377 case NODE_Script:
378 {
379 if(loadstatus_Script(X3D_SCRIPT(cnode)))
380 nowFinished ++;
381 }
382 break;
383 case NODE_ShaderProgram:
384 {
385 struct Shader_Script *shader;
386 shader=(struct Shader_Script *)(X3D_SHADERPROGRAM(cnode)->_shaderUserDefinedFields);
387 if(shader->loaded) nowFinished++;
388 }
389 break;
390 case NODE_PackagedShader:
391 {
392 struct Shader_Script *shader;
393 shader=(struct Shader_Script *)(X3D_PACKAGEDSHADER(cnode)->_shaderUserDefinedFields);
394 if(shader->loaded) nowFinished++;
395 }
396 break;
397 case NODE_ComposedShader:
398 {
399 struct Shader_Script *shader;
400 shader=(struct Shader_Script *)(X3D_COMPOSEDSHADER(cnode)->_shaderUserDefinedFields);
401 if(shader->loaded) nowFinished++;
402 }
403
404 break;
405 case NODE_Effect:
406 {
407 struct Shader_Script *shader;
408 shader=(struct Shader_Script *)(X3D_EFFECT(cnode)->_shaderUserDefinedFields);
409 if(shader->loaded) nowFinished++;
410 }
411
412 break;
413 case NODE_MovieTexture: //july 2016 - ordered fields in movietexture to match audioclip
414 case NODE_AudioClip:
415 {
416 int istate;
417 struct X3D_AudioClip *anode;
418 anode = (struct X3D_AudioClip *) cnode; /* change type to AudioClip */
419 /* AudioClip sourceNumber will be gt -1 if the clip is ok. see code for details */
420 istate = loadstatus_AudioClip(anode);
421 if (istate == 1)
422 nowLoading ++;
423 if(istate == 2)
424 nowFinished++;
425 }
426 break;
427
428 default :{} /* there should never be anything here, but... */
429 }
430 }
431
432
433 /* ok, are we NOW finished loading? */
434 if (nowFinished == nwatch) {
435 node->isActive = 0;
436 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
437
438 node->isLoaded = 1;
439 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isLoaded));
440
441 node->progress = (float) 1.0;
442 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, progress));
443
444 node->loadTime = TickTime();
445 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, loadTime));
446 }
447
448 /* have we NOW started loading? */
449 if ((nowLoading > 0) && (node->__loading == 0)) {
450 /* mark event isActive TRUE */
451 node->isActive = 1;
452 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
453
454
455 node->__StartLoadTime = TickTime();
456 }
457
458 /* what is our progress? */
459 if (node->isActive == 1) {
460 node->progress = (float)(nowFinished)/(float)(nwatch);
461 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, progress));
462 }
463
464 /* remember our status for next time. */
465 node->__loading = nowLoading;
466 node->__finishedloading = nowFinished;
467
468 /* did we run out of time? */
469 if (node->timeOut > 0.0001) { /* we have a timeOut specified */
470 if (node->__StartLoadTime > 0.001) { /* we have a start Time recorded from the isActive = TRUE */
471
472 /* ok, we should look at time outs */
473 if ((TickTime() - node->__StartLoadTime) > node->timeOut) {
474 node->isLoaded = 0;
475 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isLoaded));
476
477 node->isActive = 0;
478 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
479
480 /* and, we will just assume that we have loaded everything next iteration */
481 node->__finishedloading = nwatch;
482 }
483 }
484 }
485}
486
487
488void child_Anchor (struct X3D_Anchor *node) {
489 int nc = (node->children).n;
490 /* any children at all? */
491 if (nc==0) return;
492 /* any visible children? */
493 OCCLUSIONTEST
494
495 /* do we have a local light for a child? */
496 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
497 prep_BBox((struct BBoxFields*)&node->bboxCenter);
498 /* now, just render the non-directionalLight children */
499 normalChildren(node->children);
500 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
501 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
502}
503
504struct X3D_Node *broto_search_DEFname(struct X3D_Proto *context, const char *name);
505struct IMEXPORT *broto_search_IMPORTname(struct X3D_Proto *context, const char *name);
506struct IMEXPORT *broto_search_EXPORTname(struct X3D_Proto *context, const char *name);
507
508struct X3D_Node * broto_search_ALLnames(struct X3D_Proto *context, const char *name, int *source){
509 /*chain-of-command pattern looks in DEFnames and if not found looks in IMPORTS and if found
510 checks Inline's EXPORT table if available, and if found, checks Inline's DEF table to get node*
511 (name,node*) 'mapping':
512 name -> DEF-> IMPORT -> DEF -> inline -> EXPORT -> node*
513 - the Inline may be mentioned by char* name in IMPORT struct, so an exter DEFname lookup is needed to get Inline* node
514 -- that may change/be optimized if stable enough
515 */
516 struct X3D_Node *node;
517 *source = 0; //main scene
518 node = NULL;
519 //check scene's DEF table to see if it has become a normal node mapping
520 node = broto_search_DEFname(context,name);
521 if(!node){
522 //check scene's IMPORT table to see if it's listed there
523 struct IMEXPORT *im;
524 im = broto_search_IMPORTname(context,name);
525 if(im){
526 //if its listed in scene's import table, look to see if the mentioned Inline is loaded
527 struct X3D_Node *nlinenode;
528 *source = 1; //mentioned in IMPORTS
529 nlinenode = broto_search_DEFname(context,im->inlinename);
530 if(nlinenode && nlinenode->_nodeType == NODE_Inline ){
531 struct X3D_Inline *nline = X3D_INLINE(nlinenode);
532 if(nline->__loadstatus == INLINE_IMPORTING || nline->__loadstatus == INLINE_STABLE){
533 //check to see if the loaded inline exports the node
534 struct IMEXPORT *ex = broto_search_EXPORTname(X3D_PROTO(nline),im->mxname);
535 if(ex){
536 node = ex->nodeptr;
537 if(node)
538 *source = 2;
539 if(0){
540 //a script in the inline may have tinkered with the DEFnames, so re-lookup
541 //can't do this: the export can't act as a char* lookup for DEF -> DEFnames -> node*
542 // because executionContext.updateExportedNode(char*,node*) doesn't have a separate DEF and AS)
543 node = broto_search_DEFname(X3D_PROTO(nline),ex->mxname);
544 if(node)
545 *source = 2; //found via IMPORTs
546 }
547 }
548 }
549 }
550 }
551 }
552 return node;
553}
554
555void update_weakRoute(struct X3D_Proto *context, struct brotoRoute *route){
556 /* we re-search for 'weak' (import node) route ends via (name,node*) 'mapping':
557 name -> DEF-> IMPORT -> DEF -> inline -> EXPORT -> DEF -> node*
558 so whatever parser created, whatever tinkering javascript has done to import names,
559 whatever state inline is in, we'll get the latest mapping of name to node*
560 */
561 struct X3D_Node* newnodef, *newnodet;
562 int source, type, kind, ifield, builtIn;
563 const char *cname;
564 union anyVrml *value;
565 int changed = 0;
566
567 newnodef = route->from.node;
568 newnodet = route->to.node;
569 if(route->from.weak){
570 int ic = 0;
571 newnodef = broto_search_ALLnames(context,route->from.cnode,&source);
572 ic = newnodef != route->from.node;
573 changed = changed || ic;
574 if(newnodef && ic) {
575 route->from.weak = 3; //an extra marker indicating wether its currently 'satisified' or unknown
576 getFieldFromNodeAndNameC(newnodef,route->from.cfield,&type,&kind,&ifield,&builtIn, &value, &cname);
577 if(ifield < 0) ConsoleMessage("bad FROM field ROUTE %s.%s TO %s.%s\n",route->from.cnode,route->from.cfield,route->to.cnode,route->to.cfield);
578 route->from.ifield = ifield;
579 route->from.builtIn = builtIn;
580 route->from.ftype = type;
581 route->ft = type;
582 }
583 else route->from.weak = 1;
584 }
585 if(route->to.weak){
586 int ic;
587 newnodet = broto_search_ALLnames(context,route->to.cnode,&source);
588 ic = newnodet != route->to.node;
589 changed = changed || ic;
590 if(newnodet && ic) {
591 route->to.weak = 3; //an extra marker indicating wether its currently 'satisified' or unknown
592 getFieldFromNodeAndNameC(newnodet,route->to.cfield,&type,&kind,&ifield,&builtIn,&value,&cname);
593 if(ifield < 0)
594 ConsoleMessage("bad TO field ROUTE %s.%s TO %s.%s\n",route->from.cnode,route->from.cfield,route->to.cnode,route->to.cfield);
595 route->to.ifield = ifield;
596 route->to.builtIn = builtIn;
597 route->to.ftype = type;
598 route->ft = type;
599 }
600 else route->to.weak = 1;
601 }
602 if(changed){
603 if(route->lastCommand){
604 //its registered, so unregister
605 CRoutes_RemoveSimpleB(route->from.node,route->from.ifield,route->from.builtIn,route->to.node,route->to.ifield,route->to.builtIn,route->ft);
606 route->lastCommand = 0;
607 }
608 route->from.node = newnodef;
609 route->to.node = newnodet;
610 if(route->from.node && route->to.node && route->from.ifield > -1 && route->to.ifield > -1){ //both satisfied
611 route->lastCommand = 1;
612 CRoutes_RegisterSimpleB(route->from.node,route->from.ifield,route->from.builtIn,route->to.node,route->to.ifield,route->to.builtIn,route->ft);
613 }
614 }
615}
616void update_weakRoutes(struct X3D_Proto *context){
617 /* Goal: update any routes relying on imports -registering or unregistering- that change as Inlines are loaded and unloaded,
618 and/or as javascript tinkers with import names or def names
619 Oct 2014 implementation: we don't have a way to recursively update all contexts once per frame.
620 So we need to catch any changes caused by parsing, inline load/unload, and javascript tinkering with DEF and IMPORT names.
621 This function is designed general (and wasteful) enough so that it can be called from anywhere
622 in the current context: during javascript tinkering, during parsing, and (future) during recursive per-frame context updating
623 PROBLEM: if an inline changes one of its exports, nothing triggers this update, because to call update_weakRoutes, it would need to know
624 the importing scene context, which it doesn't.
625 */
626 if(context && context->__ROUTES){
627 //in theory we could have a separate __WEAKROUTE vector with entries that point to any weak __ROUTES so it's not so wasteful,
628 // but then we need to maintain that __WEAKROUTE vector, when adding/removing routes during parsing or javascript.
629 // its handy to keep both strong and weak routes in one __ROUTES array for javascript currentScene.routes.length and routes[i].fromNode etc
630 // for now (Oct 2014) we'll do a big wasteful loop over all routes.
631 int k;
632 for(k=0;k<vectorSize(context->__ROUTES);k++){
633 struct brotoRoute *route = vector_get(struct brotoRoute *,context->__ROUTES,k);
634 if(route->from.weak || route->to.weak){
635 update_weakRoute(context,route);
636 }
637 }
638 }
639}
640struct X3D_Proto *hasContext(struct X3D_Node* node);
641
642int unload_broto(struct X3D_Proto* node);
643/* note that we get the resources in a couple of steps; this tries to keep the scenegraph running */
644void load_Inline (struct X3D_Inline *node) {
645 resource_item_t *res;
646 struct X3D_Proto *context;
647 //printf ("load_Inline, node %p loadStatus %d url %s\n",node,node->__loadstatus,node->url.p[0]->strptr);
648 /* printf ("loading Inline\n"); */
649
650 switch (node->__loadstatus) {
651 case INLINE_INITIAL_STATE: /* nothing happened yet */
652 if(node->load){
653 if (node->url.n == 0) {
654 node->__loadstatus = INLINE_STABLE; /* a "do-nothing" approach */
655 } else {
656 //wrong parent resource? see Component_DIS note on parsing vs rendering _parentResource
657 //parsing: comes from a stack which is pushed and popped
658 //rendering creation of inlines: comes from parent context's _parentResource
659 res = resource_create_multi(&(node->url));
660 res->media_type = resm_unknown;
661 node->__loadstatus = INLINE_REQUEST_RESOURCE;
662 node->__loadResource = res;
663 }
664 }
665 break;
666
667 case INLINE_REQUEST_RESOURCE:
668 res = node->__loadResource;
669 resource_identify(node->_parentResource, res);
670 /* printf ("load_Inline, we have type %s status %s\n",
671 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
672 res->actions = resa_download | resa_load; //not resa_parse which we do below
673 //frontenditem_enqueue(ml_new(res));
674 resitem_enqueue(ml_new(res));
675 //printf("fetching..");
676 node->__loadstatus = INLINE_FETCHING_RESOURCE;
677 break;
678
679 case INLINE_FETCHING_RESOURCE:
680 res = node->__loadResource;
681 //printf ("load_Inline, we have type %s status %s\n",
682 // resourceTypeToString(res->type), resourceStatusToString(res->status));
683 if(res->complete){
684 if (res->status == ress_loaded) {
685 //determined during load process by resource_identify_type(): res->media_type = resm_vrml; //resm_unknown;
686 res->ectx = (void*)node;
687 res->whereToPlaceData = X3D_NODE(node);
688 res->offsetFromWhereToPlaceData = offsetof (struct X3D_Inline, __children);
689 res->actions = resa_process;
690 node->__loadstatus = INLINE_PARSING; // a "do-nothing" approach
691 //tell it to instance (vs library)
692 node->__protoFlags = ciflag_set(node->__protoFlags,1,0);
693 res->complete = FALSE;
694 //send_resource_to_parser(res);
695 //send_resource_to_parser_if_available(res);
696 resitem_enqueue(ml_new(res));
697 //printf("parsing..");
698 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
699 //no hope left
700 //printf ("resource failed to load\n");
701 node->__loadstatus = INLINE_STABLE; // a "do-nothing" approach
702 }
703 }
704 break;
705
706 case INLINE_PARSING:
707 res = node->__loadResource;
708
709 //printf ("inline parsing.... %s\n",resourceStatusToString(res->status));
710 //printf ("res complete %d\n",res->complete);
711 if(res->complete){
712 if (res->status == ress_parsed) {
713 /* this might be a good place to populate parent context IMPORT table with our EXPORT nodes? */
714 node->__loadstatus = INLINE_IMPORTING; //INLINE_STABLE;
715 }
716 }
717
718 break;
719 case INLINE_IMPORTING:
720 //printf("importing..");
721 context = hasContext(node->_executionContext);
722 if(context)
723 update_weakRoutes(context);
724 node->__loadstatus = INLINE_STABLE;
725 break;
726 case INLINE_STABLE:
727 if(!node->load){
728 //printf ("unloading Inline..\n");
729 node->__loadstatus = INLINE_UN_IMPORTING; //INITIAL_STATE;
730 }
731
732 break;
733 case INLINE_UN_IMPORTING:
734 //printf("un-importing..");
735 context = hasContext(node->_executionContext);
736 if(context)
737 update_weakRoutes(context); //remove any imported routes so no dangling route pointers
738 node->__loadstatus = INLINE_UNLOADING;
739 break;
740 case INLINE_UNLOADING:
741 //printf("unloading ..");
742 /* missing code to unload inline
743 The same (missing) cleanup function could also be used to unload scene and protoInstances, and
744 the garbage collection part can be used on protoDeclares, externProtoDeclares,
745 and extern proto library scenes. All these use X3D_Proto == X3D_Inline struct
746 with a few X3D_Proto.__protoFlags distinguishing their use at runtime.
747 A. unregister items registered in global/browser structs
748 a remove registered sensors -need a __sensors array?
749 b. remove registered scripts -see __scripts
750 c. remove registered routes:
751 c.i regular routes -from __ROUTES table
752 c.ii IS construction routes - from __IStable - a function was developed but not yet tested: unregister_IStableRoutes
753 d unregister nodes from table used by startofloopnodeupdates - see createNewX3DNode vs createNewX3DNode0 in generatedCode.c
754 B. deallocate context-specific heap:
755 a nodes allocated -need a context-specific nodes heap
756 a.0 recursively unload sub-contexts: inlines and protoInstances
757 a.1 builtin nodes
758 b. context vectors: __ROUTES, __IMPORTS, __EXPORTS, __DEFnames, __scripts, addChildren, removeChildren, _children
759 c prototypes declared: __protoDeclares, __externProtoDeclares - use same recursive unload
760 d string heap -need a string heap
761 e malloc heap used for elements of __ vectors - need a context-specific malloc and heap ie fmalloc(context,sizeof)
762 C. clear/reset scalar values so Inline can be re-used/re-loaded: (not sure, likely none to worry about)
763 */
764 //node->__children.n = 0; //this hack will make it look like it's unloaded, but chaos results with a subsequent reload
765 unload_broto(X3D_PROTO(node));
766 node->__loadstatus = INLINE_INITIAL_STATE;
767 //printf("unloaded..\n");
768 break;
769 default:
770 break; //if its part way loaded, we'll wait till it finishes.
771 }
772}
773
774void prep_Inline (struct X3D_Inline *node) {
775 if(0)printf("in prep_inline\n");
776 //load_externProtoInstance(node);
777 COMPILE_IF_REQUIRED
778 if ((node->__loadstatus != INLINE_STABLE && node->load) || (node->__loadstatus != INLINE_INITIAL_STATE && !node->load)) {
779 load_Inline(node);
780 }
781}
782/* not sure why we would compile */
783void compile_Inline(struct X3D_Inline *node) {
784 if(0)printf("in compile_inline\n");
785 //unsigned char pflag = ciflag_get(node->__protoFlags,2);
786 //if(pflag == 2){
787 //scene
788 REINITIALIZE_SORTED_NODES_FIELD(node->__children,node->_sortedChildren);
789 //}
790 {
791 int loadchanged, urlchanged;
792 // something in resource fetch or startofloopnodeupdates sets the node changed flag,
793 // (not sure where, but likely to indicate _children have changed and node needs compiling)
794 // and we aren't interested in that here,
795 // just in the url and load fields changing, so we compare to last recorded values
796 loadchanged = urlchanged = 0;
797 loadchanged = node->load != node->__oldload;
798 urlchanged = node->url.n != node->__oldurl.n || node->url.p != node->__oldurl.p;
799 if(loadchanged || urlchanged){
800 //whether we are loading a new url, or unloading, we always start with an unconditional unload
801 node->__loadstatus = INLINE_UN_IMPORTING;
802 if(loadchanged) node->__oldload = node->load;
803 if(urlchanged) node->__oldurl = node->url; //we don't need to strdup the url strings, assuming the old p* from a malloc doesn't get re-used/remalloced for a new url
804 //MARK_NODE_COMPILED
805 }
806 }
807 MARK_NODE_COMPILED
808}
809void prep_unitscale (struct X3D_Proto *ec);
810void fin_unitscale (struct X3D_Proto *ec);
811void child_Inline (struct X3D_Inline *node) {
812
813 //static int usingSortedChildren = 0;
814 //struct Multi_Node * kids;
815 CHILDREN_COUNT
816 //int nc = node->__children.n; //_sortedChildren.n;
817 //LOCAL_LIGHT_SAVE
818
819 RETURN_FROM_CHILD_IF_NOT_FOR_ME
820 push_executionContext(X3D_NODE(node));
821 prep_unitscale(X3D_PROTO(node));
822 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
823 //LOCAL_LIGHT_CHILDREN(node->_sortedChildren);
824 prep_BBox((struct BBoxFields*)&node->bboxCenter);
825
826 normalChildren(node->_sortedChildren);
827
828 fin_BBox((struct X3D_Node*)node,(struct BBoxFields*)&node->bboxCenter,FALSE);
829 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
830 fin_unitscale(X3D_PROTO(node));
831 pop_executionContext();
832 //LOCAL_LIGHT_OFF
833
834}
835