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