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;
300 int nowLoading;
301 int nowFinished;
302 struct X3D_Node *cnode;
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 if (node->watchList.n<=0) return;
328
329 /* are all nodes loaded? */
330 if (node->__finishedloading == node->watchList.n) return;
331
332 /* our current status... */
333 nowLoading = 0;
334 nowFinished = 0;
335
336 /* go through node list, and check to see what the status is */
337 /* printf ("have %d nodes to watch\n",node->watchList.n); */
338 for (count = 0; count < node->watchList.n; count ++) {
339
340 cnode = node->watchList.p[count];
341
342 /* printf ("node type of node %d is %d\n",count,tnode->_nodeType); */
343 switch (cnode->_nodeType) {
344 case NODE_ImageTexture:
345 {
346 /* printf ("opengl tex is %d\n",tnode->__texture); */
347 /* is this texture thought of yet? */
348 struct X3D_ImageTexture *tnode = (struct X3D_ImageTexture *) cnode;
349
350 nowLoading++;
351 if (fwl_isTextureLoaded(tnode->__textureTableIndex)) {
352 /* is it finished loading? */
353 nowFinished ++;
354 }
355 }
356 break;
357 case NODE_Inline:
358 {
359 struct X3D_Inline *inode;
360 inode = (struct X3D_Inline *) cnode; /* change type to Inline */
361 if(inode->__loadstatus > INLINE_INITIAL_STATE && inode->__loadstatus < INLINE_STABLE)
362 nowLoading++;
363 if(inode->__loadstatus == INLINE_STABLE)
364 nowFinished ++;
365 /* printf ("LoadSensor, Inline %d, type %d loadstatus %d at %d\n",inode,inode->_nodeType,inode->__loadstatus, &inode->__loadstatus); */
366 }
367 break;
368 case NODE_Script:
369 {
370 if(loadstatus_Script(X3D_SCRIPT(cnode)))
371 nowFinished ++;
372 }
373 break;
374 case NODE_ShaderProgram:
375 {
376 struct Shader_Script *shader;
377 shader=(struct Shader_Script *)(X3D_SHADERPROGRAM(cnode)->_shaderUserDefinedFields);
378 if(shader->loaded) nowFinished++;
379 }
380 break;
381 case NODE_PackagedShader:
382 {
383 struct Shader_Script *shader;
384 shader=(struct Shader_Script *)(X3D_PACKAGEDSHADER(cnode)->_shaderUserDefinedFields);
385 if(shader->loaded) nowFinished++;
386 }
387 break;
388 case NODE_ComposedShader:
389 {
390 struct Shader_Script *shader;
391 shader=(struct Shader_Script *)(X3D_COMPOSEDSHADER(cnode)->_shaderUserDefinedFields);
392 if(shader->loaded) nowFinished++;
393 }
394
395 break;
396 case NODE_Effect:
397 {
398 struct Shader_Script *shader;
399 shader=(struct Shader_Script *)(X3D_EFFECT(cnode)->_shaderUserDefinedFields);
400 if(shader->loaded) nowFinished++;
401 }
402
403 break;
404 case NODE_MovieTexture: //july 2016 - ordered fields in movietexture to match audioclip
405 case NODE_AudioClip:
406 {
407 int istate;
408 struct X3D_AudioClip *anode;
409 anode = (struct X3D_AudioClip *) cnode; /* change type to AudioClip */
410 /* AudioClip sourceNumber will be gt -1 if the clip is ok. see code for details */
411 istate = loadstatus_AudioClip(anode);
412 if (istate == 1)
413 nowLoading ++;
414 if(istate == 2)
415 nowFinished++;
416 }
417 break;
418
419 default :{} /* there should never be anything here, but... */
420 }
421 }
422
423
424 /* ok, are we NOW finished loading? */
425 if (nowFinished == node->watchList.n) {
426 node->isActive = 0;
427 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
428
429 node->isLoaded = 1;
430 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isLoaded));
431
432 node->progress = (float) 1.0;
433 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, progress));
434
435 node->loadTime = TickTime();
436 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, loadTime));
437 }
438
439 /* have we NOW started loading? */
440 if ((nowLoading > 0) && (node->__loading == 0)) {
441 /* mark event isActive TRUE */
442 node->isActive = 1;
443 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
444
445
446 node->__StartLoadTime = TickTime();
447 }
448
449 /* what is our progress? */
450 if (node->isActive == 1) {
451 node->progress = (float)(nowFinished)/(float)(node->watchList.n);
452 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, progress));
453 }
454
455 /* remember our status for next time. */
456 node->__loading = nowLoading;
457 node->__finishedloading = nowFinished;
458
459 /* did we run out of time? */
460 if (node->timeOut > 0.0001) { /* we have a timeOut specified */
461 if (node->__StartLoadTime > 0.001) { /* we have a start Time recorded from the isActive = TRUE */
462
463 /* ok, we should look at time outs */
464 if ((TickTime() - node->__StartLoadTime) > node->timeOut) {
465 node->isLoaded = 0;
466 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isLoaded));
467
468 node->isActive = 0;
469 MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_LoadSensor, isActive));
470
471 /* and, we will just assume that we have loaded everything next iteration */
472 node->__finishedloading = node->watchList.n;
473 }
474 }
475 }
476}
477
478
479void child_Anchor (struct X3D_Anchor *node) {
480 int nc = (node->children).n;
481 //LOCAL_LIGHT_SAVE
482
483 /* printf ("child_Anchor node %u, vis %d\n",node,node->_renderFlags & VF_hasVisibleChildren); */
484
485 /* any children at all? */
486 if (nc==0) return;
487
488 /* any visible children? */
489 OCCLUSIONTEST
490
491 #ifdef CHILDVERBOSE
492 printf("RENDER ANCHOR START %d (%d)\n",node, nc);
493 #endif
494
495 /* do we have a local light for a child? */
496 //LOCAL_LIGHT_CHILDREN(node->children);
497 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
498
499 /* now, just render the non-directionalLight children */
500 normalChildren(node->children);
501
502 #ifdef CHILDVERBOSE
503 printf("RENDER ANCHOR END %d\n",node);
504 #endif
505 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
506 //LOCAL_LIGHT_OFF
507}
508
509struct X3D_Node *broto_search_DEFname(struct X3D_Proto *context, const char *name);
510struct IMEXPORT *broto_search_IMPORTname(struct X3D_Proto *context, const char *name);
511struct IMEXPORT *broto_search_EXPORTname(struct X3D_Proto *context, const char *name);
512
513struct X3D_Node * broto_search_ALLnames(struct X3D_Proto *context, const char *name, int *source){
514 /*chain-of-command pattern looks in DEFnames and if not found looks in IMPORTS and if found
515 checks Inline's EXPORT table if available, and if found, checks Inline's DEF table to get node*
516 (name,node*) 'mapping':
517 name -> DEF-> IMPORT -> DEF -> inline -> EXPORT -> node*
518 - the Inline may be mentioned by char* name in IMPORT struct, so an exter DEFname lookup is needed to get Inline* node
519 -- that may change/be optimized if stable enough
520 */
521 struct X3D_Node *node;
522 *source = 0; //main scene
523 node = NULL;
524 //check scene's DEF table to see if it has become a normal node mapping
525 node = broto_search_DEFname(context,name);
526 if(!node){
527 //check scene's IMPORT table to see if it's listed there
528 struct IMEXPORT *im;
529 im = broto_search_IMPORTname(context,name);
530 if(im){
531 //if its listed in scene's import table, look to see if the mentioned Inline is loaded
532 struct X3D_Node *nlinenode;
533 *source = 1; //mentioned in IMPORTS
534 nlinenode = broto_search_DEFname(context,im->inlinename);
535 if(nlinenode && nlinenode->_nodeType == NODE_Inline ){
536 struct X3D_Inline *nline = X3D_INLINE(nlinenode);
537 if(nline->__loadstatus == INLINE_IMPORTING || nline->__loadstatus == INLINE_STABLE){
538 //check to see if the loaded inline exports the node
539 struct IMEXPORT *ex = broto_search_EXPORTname(X3D_PROTO(nline),im->mxname);
540 if(ex){
541 node = ex->nodeptr;
542 if(node)
543 *source = 2;
544 if(0){
545 //a script in the inline may have tinkered with the DEFnames, so re-lookup
546 //can't do this: the export can't act as a char* lookup for DEF -> DEFnames -> node*
547 // because executionContext.updateExportedNode(char*,node*) doesn't have a separate DEF and AS)
548 node = broto_search_DEFname(X3D_PROTO(nline),ex->mxname);
549 if(node)
550 *source = 2; //found via IMPORTs
551 }
552 }
553 }
554 }
555 }
556 }
557 return node;
558}
559
560void update_weakRoute(struct X3D_Proto *context, struct brotoRoute *route){
561 /* we re-search for 'weak' (import node) route ends via (name,node*) 'mapping':
562 name -> DEF-> IMPORT -> DEF -> inline -> EXPORT -> DEF -> node*
563 so whatever parser created, whatever tinkering javascript has done to import names,
564 whatever state inline is in, we'll get the latest mapping of name to node*
565 */
566 struct X3D_Node* newnodef, *newnodet;
567 int source, type, kind, ifield, builtIn;
568 const char *cname;
569 union anyVrml *value;
570 int changed = 0;
571
572 newnodef = route->from.node;
573 newnodet = route->to.node;
574 if(route->from.weak){
575 int ic = 0;
576 newnodef = broto_search_ALLnames(context,route->from.cnode,&source);
577 ic = newnodef != route->from.node;
578 changed = changed || ic;
579 if(newnodef && ic) {
580 route->from.weak = 3; //an extra marker indicating wether its currently 'satisified' or unknown
581 getFieldFromNodeAndNameC(newnodef,route->from.cfield,&type,&kind,&ifield,&builtIn, &value, &cname);
582 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);
583 route->from.ifield = ifield;
584 route->from.builtIn = builtIn;
585 route->from.ftype = type;
586 route->ft = type;
587 }
588 else route->from.weak = 1;
589 }
590 if(route->to.weak){
591 int ic;
592 newnodet = broto_search_ALLnames(context,route->to.cnode,&source);
593 ic = newnodet != route->to.node;
594 changed = changed || ic;
595 if(newnodet && ic) {
596 route->to.weak = 3; //an extra marker indicating wether its currently 'satisified' or unknown
597 getFieldFromNodeAndNameC(newnodet,route->to.cfield,&type,&kind,&ifield,&builtIn,&value,&cname);
598 if(ifield < 0)
599 ConsoleMessage("bad TO field ROUTE %s.%s TO %s.%s\n",route->from.cnode,route->from.cfield,route->to.cnode,route->to.cfield);
600 route->to.ifield = ifield;
601 route->to.builtIn = builtIn;
602 route->to.ftype = type;
603 route->ft = type;
604 }
605 else route->to.weak = 1;
606 }
607 if(changed){
608 if(route->lastCommand){
609 //its registered, so unregister
610 CRoutes_RemoveSimpleB(route->from.node,route->from.ifield,route->from.builtIn,route->to.node,route->to.ifield,route->to.builtIn,route->ft);
611 route->lastCommand = 0;
612 }
613 route->from.node = newnodef;
614 route->to.node = newnodet;
615 if(route->from.node && route->to.node && route->from.ifield > -1 && route->to.ifield > -1){ //both satisfied
616 route->lastCommand = 1;
617 CRoutes_RegisterSimpleB(route->from.node,route->from.ifield,route->from.builtIn,route->to.node,route->to.ifield,route->to.builtIn,route->ft);
618 }
619 }
620}
621void update_weakRoutes(struct X3D_Proto *context){
622 /* Goal: update any routes relying on imports -registering or unregistering- that change as Inlines are loaded and unloaded,
623 and/or as javascript tinkers with import names or def names
624 Oct 2014 implementation: we don't have a way to recursively update all contexts once per frame.
625 So we need to catch any changes caused by parsing, inline load/unload, and javascript tinkering with DEF and IMPORT names.
626 This function is designed general (and wasteful) enough so that it can be called from anywhere
627 in the current context: during javascript tinkering, during parsing, and (future) during recursive per-frame context updating
628 PROBLEM: if an inline changes one of its exports, nothing triggers this update, because to call update_weakRoutes, it would need to know
629 the importing scene context, which it doesn't.
630 */
631 if(context && context->__ROUTES){
632 //in theory we could have a separate __WEAKROUTE vector with entries that point to any weak __ROUTES so it's not so wasteful,
633 // but then we need to maintain that __WEAKROUTE vector, when adding/removing routes during parsing or javascript.
634 // its handy to keep both strong and weak routes in one __ROUTES array for javascript currentScene.routes.length and routes[i].fromNode etc
635 // for now (Oct 2014) we'll do a big wasteful loop over all routes.
636 int k;
637 for(k=0;k<vectorSize(context->__ROUTES);k++){
638 struct brotoRoute *route = vector_get(struct brotoRoute *,context->__ROUTES,k);
639 if(route->from.weak || route->to.weak){
640 update_weakRoute(context,route);
641 }
642 }
643 }
644}
645struct X3D_Proto *hasContext(struct X3D_Node* node);
646
647
648int unload_broto(struct X3D_Proto* node);
649/* note that we get the resources in a couple of steps; this tries to keep the scenegraph running */
650void load_Inline (struct X3D_Inline *node) {
651 resource_item_t *res;
652 struct X3D_Proto *context;
653 //printf ("load_Inline, node %p loadStatus %d url %s\n",node,node->__loadstatus,node->url.p[0]->strptr);
654 /* printf ("loading Inline\n"); */
655
656 switch (node->__loadstatus) {
657 case INLINE_INITIAL_STATE: /* nothing happened yet */
658 if(node->load){
659 if (node->url.n == 0) {
660 node->__loadstatus = INLINE_STABLE; /* a "do-nothing" approach */
661 } else {
662 //wrong parent resource? see Component_DIS note on parsing vs rendering _parentResource
663 //parsing: comes from a stack which is pushed and popped
664 //rendering creation of inlines: comes from parent context's _parentResource
665 res = resource_create_multi(&(node->url));
666 res->media_type = resm_unknown;
667 node->__loadstatus = INLINE_REQUEST_RESOURCE;
668 node->__loadResource = res;
669 }
670 }
671 break;
672
673 case INLINE_REQUEST_RESOURCE:
674 res = node->__loadResource;
675 resource_identify(node->_parentResource, res);
676 /* printf ("load_Inline, we have type %s status %s\n",
677 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
678 res->actions = resa_download | resa_load; //not resa_parse which we do below
679 //frontenditem_enqueue(ml_new(res));
680 resitem_enqueue(ml_new(res));
681 //printf("fetching..");
682 node->__loadstatus = INLINE_FETCHING_RESOURCE;
683 break;
684
685 case INLINE_FETCHING_RESOURCE:
686 res = node->__loadResource;
687 /* printf ("load_Inline, we have type %s status %s\n",
688 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
689 if(res->complete){
690 if (res->status == ress_loaded) {
691 //determined during load process by resource_identify_type(): res->media_type = resm_vrml; //resm_unknown;
692 res->ectx = (void*)node;
693 res->whereToPlaceData = X3D_NODE(node);
694 res->offsetFromWhereToPlaceData = offsetof (struct X3D_Inline, __children);
695 res->actions = resa_process;
696 node->__loadstatus = INLINE_PARSING; // a "do-nothing" approach
697 //tell it to instance (vs library)
698 node->__protoFlags = ciflag_set(node->__protoFlags,1,0);
699 res->complete = FALSE;
700 //send_resource_to_parser(res);
701 //send_resource_to_parser_if_available(res);
702 resitem_enqueue(ml_new(res));
703 //printf("parsing..");
704 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
705 //no hope left
706 //printf ("resource failed to load\n");
707 node->__loadstatus = INLINE_STABLE; // a "do-nothing" approach
708 }
709 }
710 break;
711
712 case INLINE_PARSING:
713 res = node->__loadResource;
714
715 //printf ("inline parsing.... %s\n",resourceStatusToString(res->status));
716 //printf ("res complete %d\n",res->complete);
717 if(res->complete){
718 if (res->status == ress_parsed) {
719 /* this might be a good place to populate parent context IMPORT table with our EXPORT nodes? */
720 node->__loadstatus = INLINE_IMPORTING; //INLINE_STABLE;
721
722 }
723 }
724
725 break;
726 case INLINE_IMPORTING:
727 //printf("importing..");
728 context = hasContext(node->_executionContext);
729 if(context)
730 update_weakRoutes(context);
731 node->__loadstatus = INLINE_STABLE;
732 break;
733 case INLINE_STABLE:
734 if(!node->load){
735 //printf ("unloading Inline..\n");
736 node->__loadstatus = INLINE_UN_IMPORTING; //INITIAL_STATE;
737 }
738
739 break;
740 case INLINE_UN_IMPORTING:
741 //printf("un-importing..");
742 context = hasContext(node->_executionContext);
743 if(context)
744 update_weakRoutes(context); //remove any imported routes so no dangling route pointers
745 node->__loadstatus = INLINE_UNLOADING;
746 break;
747 case INLINE_UNLOADING:
748 //printf("unloading ..");
749 /* missing code to unload inline
750 The same (missing) cleanup function could also be used to unload scene and protoInstances, and
751 the garbage collection part can be used on protoDeclares, externProtoDeclares,
752 and extern proto library scenes. All these use X3D_Proto == X3D_Inline struct
753 with a few X3D_Proto.__protoFlags distinguishing their use at runtime.
754 A. unregister items registered in global/browser structs
755 a remove registered sensors -need a __sensors array?
756 b. remove registered scripts -see __scripts
757 c. remove registered routes:
758 c.i regular routes -from __ROUTES table
759 c.ii IS construction routes - from __IStable - a function was developed but not yet tested: unregister_IStableRoutes
760 d unregister nodes from table used by startofloopnodeupdates - see createNewX3DNode vs createNewX3DNode0 in generatedCode.c
761 B. deallocate context-specific heap:
762 a nodes allocated -need a context-specific nodes heap
763 a.0 recursively unload sub-contexts: inlines and protoInstances
764 a.1 builtin nodes
765 b. context vectors: __ROUTES, __IMPORTS, __EXPORTS, __DEFnames, __scripts, addChildren, removeChildren, _children
766 c prototypes declared: __protoDeclares, __externProtoDeclares - use same recursive unload
767 d string heap -need a string heap
768 e malloc heap used for elements of __ vectors - need a context-specific malloc and heap ie fmalloc(context,sizeof)
769 C. clear/reset scalar values so Inline can be re-used/re-loaded: (not sure, likely none to worry about)
770 */
771 //node->__children.n = 0; //this hack will make it look like it's unloaded, but chaos results with a subsequent reload
772 unload_broto(X3D_PROTO(node));
773 node->__loadstatus = INLINE_INITIAL_STATE;
774 //printf("unloaded..\n");
775 break;
776 default:
777 break; //if its part way loaded, we'll wait till it finishes.
778 }
779}
780
781void prep_Inline (struct X3D_Inline *node) {
782 if(0)printf("in prep_inline\n");
783 //load_externProtoInstance(node);
784 COMPILE_IF_REQUIRED
785 if ((node->__loadstatus != INLINE_STABLE && node->load) || (node->__loadstatus != INLINE_INITIAL_STATE && !node->load)) {
786 load_Inline(node);
787 }
788 RECORD_DISTANCE
789
790}
791/* not sure why we would compile */
792void compile_Inline(struct X3D_Inline *node) {
793 if(0)printf("in compile_inline\n");
794 //unsigned char pflag = ciflag_get(node->__protoFlags,2);
795 //if(pflag == 2){
796 //scene
797 REINITIALIZE_SORTED_NODES_FIELD(node->__children,node->_sortedChildren);
798 //}
799 {
800 int loadchanged, urlchanged;
801 // something in resource fetch or startofloopnodeupdates sets the node changed flag,
802 // (not sure where, but likely to indicate _children have changed and node needs compiling)
803 // and we aren't interested in that here,
804 // just in the url and load fields changing, so we compare to last recorded values
805 loadchanged = urlchanged = 0;
806 loadchanged = node->load != node->__oldload;
807 urlchanged = node->url.n != node->__oldurl.n || node->url.p != node->__oldurl.p;
808 if(loadchanged || urlchanged){
809 //whether we are loading a new url, or unloading, we always start with an unconditional unload
810 node->__loadstatus = INLINE_UN_IMPORTING;
811 if(loadchanged) node->__oldload = node->load;
812 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
813 //MARK_NODE_COMPILED
814 }
815 }
816 MARK_NODE_COMPILED
817}
818void prep_unitscale (struct X3D_Proto *ec);
819void fin_unitscale (struct X3D_Proto *ec);
820void child_Inline (struct X3D_Inline *node) {
821
822 //static int usingSortedChildren = 0;
823 //struct Multi_Node * kids;
824 CHILDREN_COUNT
825 //int nc = node->__children.n; //_sortedChildren.n;
826 //LOCAL_LIGHT_SAVE
827
828 RETURN_FROM_CHILD_IF_NOT_FOR_ME
829 prep_unitscale(X3D_PROTO(node));
830 prep_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
831 //LOCAL_LIGHT_CHILDREN(node->_sortedChildren);
832
833 normalChildren(node->_sortedChildren);
834 fin_sibAffectors((struct X3D_Node*)node,&node->__sibAffectors);
835 fin_unitscale(X3D_PROTO(node));
836 //LOCAL_LIGHT_OFF
837
838}