1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / Scene Graph sub-project
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 #include <gpac/internal/scenegraph_dev.h>
27 /*MPEG4 & X3D tags (for node tables & script handling)*/
28 #include <gpac/nodes_mpeg4.h>
29 #include <gpac/nodes_x3d.h>
30
31 #ifndef GPAC_DISABLE_VRML
32
33 GF_Route* gf_sg_route_exists(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField);
34
35 GF_EXPORT
gf_sg_route_new(GF_SceneGraph * sg,GF_Node * fromNode,u32 fromField,GF_Node * toNode,u32 toField)36 GF_Route *gf_sg_route_new(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField)
37 {
38 GF_Route *r;
39 if (!sg || !toNode || !fromNode) return NULL;
40
41 if ( (r = gf_sg_route_exists(sg, fromNode, fromField, toNode, toField)) )
42 return r;
43
44 GF_SAFEALLOC(r, GF_Route)
45 if (!r) return NULL;
46 r->FromNode = fromNode;
47 r->FromField.fieldIndex = fromField;
48 r->ToNode = toNode;
49 r->ToField.fieldIndex = toField;
50 r->graph = sg;
51
52 if (!fromNode->sgprivate->interact) {
53 GF_SAFEALLOC(fromNode->sgprivate->interact, struct _node_interactive_ext);
54 if (!fromNode->sgprivate->interact) {
55 GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to create interact storage\n"));
56 gf_free(r);
57 return NULL;
58 }
59 }
60 if (!fromNode->sgprivate->interact->routes) fromNode->sgprivate->interact->routes = gf_list_new();
61 gf_list_add(fromNode->sgprivate->interact->routes, r);
62 gf_list_add(sg->Routes, r);
63 return r;
64 }
65
gf_sg_route_exists(GF_SceneGraph * sg,GF_Node * fromNode,u32 fromField,GF_Node * toNode,u32 toField)66 GF_Route* gf_sg_route_exists(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, GF_Node *toNode, u32 toField)
67 {
68 u32 i = 0;
69 GF_Route* rt;
70 if ( !fromNode->sgprivate->interact || !fromNode->sgprivate->interact->routes )
71 return NULL;
72
73 while ( (rt = (GF_Route*)gf_list_enum(fromNode->sgprivate->interact->routes, &i) )) {
74 if ( rt->FromField.fieldIndex == fromField && rt->ToNode == toNode && rt->ToField.fieldIndex == toField )
75 return rt;
76 }
77 return NULL;
78 }
79
80 GF_EXPORT
gf_sg_route_del(GF_Route * r)81 void gf_sg_route_del(GF_Route *r)
82 {
83 GF_SceneGraph *sg;
84
85 /*remove declared routes*/
86 gf_list_del_item(r->graph->Routes, r);
87 /*remove route from node - do this regardless of setup state since the route is registered upon creation*/
88 if (r->FromNode && r->FromNode->sgprivate->interact && r->FromNode->sgprivate->interact->routes) {
89 gf_list_del_item(r->FromNode->sgprivate->interact->routes, r);
90 if (!gf_list_count(r->FromNode->sgprivate->interact->routes)) {
91 gf_list_del(r->FromNode->sgprivate->interact->routes);
92 r->FromNode->sgprivate->interact->routes = NULL;
93 }
94 }
95 /*special case for script events: notify desdctruction*/
96 if (r->ToNode && (r->ToField.fieldType==GF_SG_VRML_SCRIPT_FUNCTION) && r->ToField.on_event_in) {
97 r->is_setup = 0;
98 r->FromNode = NULL;
99 if (!r->graph->pOwningProto) r->ToField.on_event_in(r->ToNode, r);
100 }
101
102 r->is_setup = 0;
103 sg = r->graph;
104 while (sg->parent_scene) sg = sg->parent_scene;
105 gf_list_add(sg->routes_to_destroy, r);
106 gf_list_del_item(sg->routes_to_activate, r);
107 }
108
109 GF_EXPORT
gf_sg_route_del_by_id(GF_SceneGraph * sg,u32 routeID)110 GF_Err gf_sg_route_del_by_id(GF_SceneGraph *sg,u32 routeID)
111 {
112 GF_Route *r;
113 if(!sg) return GF_BAD_PARAM;
114 r = gf_sg_route_find(sg, routeID);
115 if (!r) return GF_BAD_PARAM;
116 gf_sg_route_del(r);
117 return GF_OK;
118 }
119
gf_sg_destroy_routes(GF_SceneGraph * sg)120 void gf_sg_destroy_routes(GF_SceneGraph *sg)
121 {
122 while (gf_list_count(sg->routes_to_destroy) ) {
123 GF_Route *r = (GF_Route *)gf_list_get(sg->routes_to_destroy, 0);
124 gf_list_rem(sg->routes_to_destroy, 0);
125 gf_sg_route_unqueue(sg, r);
126 if (r->name) gf_free(r->name);
127 gf_free(r);
128 }
129 }
130
131
gf_sg_route_queue(GF_SceneGraph * sg,GF_Route * r)132 void gf_sg_route_queue(GF_SceneGraph *sg, GF_Route *r)
133 {
134 u32 now;
135 if (!sg) return;
136
137 /*get the top level scene (that's the only reliable one regarding simulatioin tick)*/
138 while (sg->parent_scene) sg = sg->parent_scene;
139 /*a single route may not be activated more than once in a simulation tick*/
140 now = 1 + sg->simulation_tick;
141 if (r->lastActivateTime >= now) return;
142 r->lastActivateTime = now;
143 gf_list_add(sg->routes_to_activate, r);
144 }
145
146 /*activate all routes in the order they where triggered*/
147 GF_EXPORT
gf_sg_activate_routes(GF_SceneGraph * sg)148 void gf_sg_activate_routes(GF_SceneGraph *sg)
149 {
150 GF_Route *r;
151 GF_Node *targ;
152 if (!sg) return;
153
154 sg->simulation_tick++;
155 gf_sg_destroy_routes(sg);
156
157 while (gf_list_count(sg->routes_to_activate)) {
158 r = (GF_Route *)gf_list_get(sg->routes_to_activate, 0);
159 gf_list_rem(sg->routes_to_activate, 0);
160 if (r) {
161 targ = r->ToNode;
162 if (gf_sg_route_activate(r)) {
163 #ifdef GF_SELF_REPLACE_ENABLE
164 if (sg->graph_has_been_reset) {
165 sg->graph_has_been_reset = 0;
166 return;
167 }
168 #endif
169 if (r->is_setup) gf_node_changed(targ, &r->ToField);
170 }
171 }
172 }
173 }
174
gf_sg_route_unqueue(GF_SceneGraph * sg,GF_Route * r)175 void gf_sg_route_unqueue(GF_SceneGraph *sg, GF_Route *r)
176 {
177 /*get the top level scene*/
178 while (sg->parent_scene) sg = sg->parent_scene;
179 /*remove route from queue list*/
180 gf_list_del_item(sg->routes_to_activate, r);
181 }
182
183 GF_EXPORT
gf_sg_route_find(GF_SceneGraph * sg,u32 RouteID)184 GF_Route *gf_sg_route_find(GF_SceneGraph *sg, u32 RouteID)
185 {
186 GF_Route *r;
187 u32 i=0;
188 while ((r = (GF_Route*)gf_list_enum(sg->Routes, &i))) {
189 if (r->ID == RouteID) return r;
190 }
191 return NULL;
192 }
193
194 GF_EXPORT
gf_sg_route_find_by_name(GF_SceneGraph * sg,char * name)195 GF_Route *gf_sg_route_find_by_name(GF_SceneGraph *sg, char *name)
196 {
197 GF_Route *r;
198 u32 i;
199 if (!sg || !name) return NULL;
200
201 i=0;
202 while ((r = (GF_Route*)gf_list_enum(sg->Routes, &i))) {
203 if (r->name && !strcmp(r->name, name)) return r;
204 }
205 return NULL;
206 }
207
208 GF_EXPORT
gf_sg_route_set_id(GF_Route * route,u32 ID)209 GF_Err gf_sg_route_set_id(GF_Route *route, u32 ID)
210 {
211 GF_Route *ptr;
212 if (!route || !ID) return GF_BAD_PARAM;
213
214 ptr = gf_sg_route_find(route->graph, ID);
215 if (ptr) return GF_BAD_PARAM;
216 route->ID = ID;
217 return GF_OK;
218 }
219
220 #if 0 //unused
221 u32 gf_sg_route_get_id(GF_Route *route)
222 {
223 return route->ID;
224 }
225 #endif
226
227 GF_EXPORT
gf_sg_route_set_name(GF_Route * route,char * name)228 GF_Err gf_sg_route_set_name(GF_Route *route, char *name)
229 {
230 GF_Route *ptr;
231 if (!name || !route) return GF_BAD_PARAM;
232 ptr = gf_sg_route_find_by_name(route->graph, name);
233 if (ptr) return GF_BAD_PARAM;
234 if (route->name) gf_free(route->name);
235 route->name = gf_strdup(name);
236 return GF_OK;
237 }
238
239 GF_EXPORT
gf_sg_route_get_name(GF_Route * route)240 char *gf_sg_route_get_name(GF_Route *route)
241 {
242 return route->name;
243 }
244
gf_sg_route_setup(GF_Route * r)245 void gf_sg_route_setup(GF_Route *r)
246 {
247 gf_node_get_field(r->FromNode, r->FromField.fieldIndex, &r->FromField);
248 gf_node_get_field(r->ToNode, r->ToField.fieldIndex, &r->ToField);
249 switch (r->FromField.fieldType) {
250 case GF_SG_VRML_MFNODE:
251 if (r->ToField.fieldType != GF_SG_VRML_MFNODE) return;
252 break;
253 case GF_SG_VRML_SFNODE:
254 if (r->ToField.fieldType != GF_SG_VRML_SFNODE) return;
255 break;
256 }
257 r->is_setup = 1;
258 }
259
260 /*send event out of proto - all ISed fields are ignored*/
gf_node_event_out_proto(GF_Node * node,u32 FieldIndex)261 void gf_node_event_out_proto(GF_Node *node, u32 FieldIndex)
262 {
263 u32 i;
264 GF_Route *r;
265 if (!node) return;
266
267 if (!node->sgprivate->interact) return;
268
269 //search for routes to activate in the order they where declared
270 i=0;
271 while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) {
272 if (r->IS_route) continue;
273 if (r->FromNode != node) continue;
274 if (r->FromField.fieldIndex != FieldIndex) continue;
275 gf_sg_route_queue(node->sgprivate->scenegraph, r);
276 }
277 }
278
gf_sg_route_activate(GF_Route * r)279 Bool gf_sg_route_activate(GF_Route *r)
280 {
281 Bool ret;
282 /*URL/String conversion clone*/
283 void VRML_FieldCopyCast(void *dest, u32 dst_field_type, void *orig, u32 ori_field_type);
284 assert(r->FromNode);
285 if (!r->is_setup) {
286 gf_sg_route_setup(r);
287 if (!r->is_setup) return 0;
288 /*special case when initing ISed routes on eventOuts: skip*/
289 if (r->IS_route) {
290 if (r->FromField.eventType == GF_SG_EVENT_OUT) return 0;
291 if (r->ToField.eventType == GF_SG_EVENT_OUT) return 0;
292 }
293 if (r->IS_route && ((r->ToNode->sgprivate->tag==TAG_MPEG4_Script)
294 #ifndef GPAC_DISABLE_X3D
295 || (r->ToNode->sgprivate->tag==TAG_X3D_Script)
296 #endif
297 ) && ((r->ToField.eventType==GF_SG_EVENT_IN) /*|| (r->ToField.eventType==GF_SG_EVENT_FIELD)*/)
298 && r->FromField.eventType==GF_SG_EVENT_IN) {
299 return 0;
300 }
301 }
302 #ifndef GPAC_DISABLE_LOG
303 if (gf_log_tool_level_on(GF_LOG_INTERACT, GF_LOG_DEBUG)) {
304 if (r->IS_route) {
305 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Event] executing %s.%s IS %s.%s", gf_node_get_name(r->FromNode), r->FromField.name, gf_node_get_name(r->ToNode), r->ToField.name));
306 } else {
307 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Event] executing ROUTE %s.%s TO %s.%s", gf_node_get_name(r->FromNode), r->FromField.name, gf_node_get_name(r->ToNode), r->ToField.name));
308 }
309 if (r->FromField.fieldType==GF_SG_VRML_SFBOOL) {
310 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("\tBOOL VAL: %d\n", *((SFBool*)r->FromField.far_ptr)));
311 } else if (r->FromField.fieldType==GF_SG_VRML_SFINT32) {
312 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("\tINT VAL: %d\n", *((SFInt32*)r->FromField.far_ptr)));
313 } else {
314 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("\n"));
315 }
316 }
317 #endif
318
319 ret = 1;
320 switch (r->FromField.fieldType) {
321 case GF_SG_VRML_SFNODE:
322 if (* (GF_Node **) r->ToField.far_ptr != * (GF_Node **) r->FromField.far_ptr) {
323 GF_Node *n = * (GF_Node **) r->ToField.far_ptr;
324 /*delete instance*/
325 if (n) gf_node_unregister(n, r->ToNode);
326 /*and use the node*/
327 * (GF_Node **) r->ToField.far_ptr = * (GF_Node **) r->FromField.far_ptr;
328 n = * (GF_Node **) r->FromField.far_ptr;
329 gf_node_register(n, r->ToNode);
330 }
331 break;
332
333 /*move all pointers to dest*/
334 case GF_SG_VRML_MFNODE:
335 {
336 GF_ChildNodeItem *last = NULL;
337 GF_ChildNodeItem *orig = *(GF_ChildNodeItem **)r->FromField.far_ptr;
338
339 /*empty list*/
340 gf_node_unregister_children(r->ToNode, *(GF_ChildNodeItem **)r->ToField.far_ptr );
341 *(GF_ChildNodeItem **)r->ToField.far_ptr = NULL;
342
343 while (orig) {
344 gf_node_list_add_child_last( (GF_ChildNodeItem **)r->ToField.far_ptr, orig->node, &last);
345 gf_node_register(orig->node, r->ToNode);
346 orig = orig->next;
347 }
348 }
349 break;
350
351 default:
352 if (r->ToField.fieldType==r->FromField.fieldType) {
353 /*if unchanged don't invalidate dst node*/
354 if (gf_sg_vrml_field_equal(r->ToField.far_ptr, r->FromField.far_ptr, r->FromField.fieldType)) {
355 ret = 0;
356 } else {
357 gf_sg_vrml_field_copy(r->ToField.far_ptr, r->FromField.far_ptr, r->FromField.fieldType);
358 }
359 }
360 /*typecast URL <-> string if needed*/
361 else {
362 VRML_FieldCopyCast(r->ToField.far_ptr, r->ToField.fieldType, r->FromField.far_ptr, r->FromField.fieldType);
363 }
364 break;
365 }
366
367 //don't notify dest change for generic function since the dest is not a node
368 if (r->ToField.fieldType==GF_SG_VRML_GENERIC_FUNCTION) {
369 ret = 0;
370 }
371
372 #ifndef GPAC_DISABLE_LOG
373 if (gf_log_tool_level_on(GF_LOG_INTERACT, GF_LOG_DEBUG)) {
374 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Route] field copy/casted\n"));
375 }
376 #endif
377
378 //if this is a supported eventIn call watcher
379 if (r->ToField.on_event_in) {
380 r->ToField.on_event_in(r->ToNode, r);
381 }
382 //if this is a script eventIn call directly script
383 else if (((r->ToNode->sgprivate->tag==TAG_MPEG4_Script)
384 #ifndef GPAC_DISABLE_X3D
385 || (r->ToNode->sgprivate->tag==TAG_X3D_Script)
386 #endif
387 ) && ((r->ToField.eventType==GF_SG_EVENT_IN) /*|| (r->ToField.eventType==GF_SG_EVENT_FIELD)*/) ) {
388 gf_sg_script_event_in(r->ToNode, &r->ToField);
389 }
390 //check if ISed or not - this will notify the node of any changes
391 else {
392 gf_sg_proto_propagate_event(r->ToNode, r->ToField.fieldIndex, r->FromNode);
393 /*if not an ISed field, propagate (otherwise ROUTE is executed just below)*/
394 if (r->ToField.eventType != GF_SG_EVENT_EXPOSED_FIELD)
395 gf_sg_proto_propagate_event(r->ToNode, r->ToField.fieldIndex, r->FromNode);
396 /*only happen on proto, an eventOut may route to an eventOut*/
397 if (r->IS_route && r->ToField.eventType==GF_SG_EVENT_OUT)
398 gf_node_event_out(r->ToNode, r->ToField.fieldIndex);
399 }
400
401 /*and signal routes on exposed fields if field changed*/
402 if (r->ToField.eventType == GF_SG_EVENT_EXPOSED_FIELD) {
403 if (r->IS_route)
404 gf_node_event_out_proto(r->ToNode, r->ToField.fieldIndex);
405 else
406 gf_node_event_out(r->ToNode, r->ToField.fieldIndex);
407 }
408
409 #ifndef GPAC_DISABLE_LOG
410 if (gf_log_tool_level_on(GF_LOG_INTERACT, GF_LOG_DEBUG)) {
411 GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[VRML Route] done executing (res %d)\n", ret));
412 }
413 #endif
414
415 return ret;
416 }
417
418
419 GF_EXPORT
gf_node_event_out(GF_Node * node,u32 FieldIndex)420 void gf_node_event_out(GF_Node *node, u32 FieldIndex)
421 {
422 u32 i;
423 GF_Route *r;
424 if (!node) return;
425
426 /*node has no routes*/
427 if (!node->sgprivate->interact || !node->sgprivate->interact->routes) return;
428
429 //search for routes to activate in the order they where declared
430 i=0;
431 while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) {
432 if (r->FromNode != node) continue;
433 if (r->FromField.fieldIndex != FieldIndex) continue;
434
435 /*no postpone for IS routes*/
436 if (r->IS_route) {
437 if (gf_sg_route_activate(r))
438 gf_node_changed(r->ToNode, &r->ToField);
439 }
440 //queue
441 else {
442 gf_sg_route_queue(node->sgprivate->scenegraph, r);
443 }
444 }
445 }
446
447 GF_EXPORT
gf_node_event_out_str(GF_Node * node,const char * eventName)448 void gf_node_event_out_str(GF_Node *node, const char *eventName)
449 {
450 u32 i;
451 GF_Route *r;
452
453 /*node has no routes*/
454 if (!node->sgprivate->interact || !node->sgprivate->interact->routes) return;
455
456 //search for routes to activate in the order they where declared
457 i=0;
458 while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) {
459 if (!r->is_setup) gf_sg_route_setup(r);
460 if (stricmp(r->FromField.name, eventName)) continue;
461
462 //no postpone
463 if (r->IS_route) {
464 gf_sg_route_activate(r);
465 }
466 //queue
467 else {
468 gf_sg_route_queue(node->sgprivate->scenegraph, r);
469 }
470 }
471 }
472
473 typedef struct
474 {
475 GF_Route r;
476 void ( *route_callback) (void *param, GF_FieldInfo *from_field);
477 } GF_RouteToFunction;
478
on_route_to_function(GF_Node * node,GF_Route * r)479 static void on_route_to_function(GF_Node *node, GF_Route *r)
480 {
481 GF_RouteToFunction *rf = (GF_RouteToFunction *)r;
482 rf->route_callback(r->ToNode, &r->FromField);
483 }
484
485 GF_EXPORT
gf_sg_route_new_to_callback(GF_SceneGraph * sg,GF_Node * fromNode,u32 fromField,void * cbk,void (* route_callback)(void * param,GF_FieldInfo * from_field))486 void gf_sg_route_new_to_callback(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, void *cbk, void ( *route_callback) (void *param, GF_FieldInfo *from_field) )
487 {
488 GF_Route *r;
489 GF_RouteToFunction *rf;
490 GF_SAFEALLOC(rf, GF_RouteToFunction);
491 if (!rf) return;
492 rf->route_callback = route_callback;
493
494 r = (GF_Route *)rf;
495 r->FromNode = fromNode;
496 r->FromField.fieldIndex = fromField;
497 gf_node_get_field(r->FromNode, fromField, &r->FromField);
498
499 r->ToNode = (GF_Node *) cbk;
500 r->ToField.fieldType = GF_SG_VRML_GENERIC_FUNCTION;
501 r->ToField.on_event_in = on_route_to_function;
502 r->ToField.eventType = GF_SG_EVENT_IN;
503 r->ToField.far_ptr = NULL;
504
505 r->is_setup = 1;
506 r->graph = sg;
507
508 if (!fromNode->sgprivate->interact) {
509 GF_SAFEALLOC(fromNode->sgprivate->interact, struct _node_interactive_ext);
510 if (!fromNode->sgprivate->interact) {
511 GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to create interact storage\n"));
512 gf_free(r);
513 return;
514 }
515 }
516 if (!fromNode->sgprivate->interact->routes) fromNode->sgprivate->interact->routes = gf_list_new();
517 gf_list_add(fromNode->sgprivate->interact->routes, r);
518 gf_list_add(fromNode->sgprivate->scenegraph->Routes, r);
519 }
520
521
522 #endif /*GPAC_DISABLE_VRML*/
523
524