/src/gpac/src/scenegraph/vrml_proto.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2026 |
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 | | |
27 | | #include <gpac/internal/scenegraph_dev.h> |
28 | | /*MPEG4 & X3D tags (for node tables & script handling)*/ |
29 | | #include <gpac/nodes_mpeg4.h> |
30 | | #include <gpac/nodes_x3d.h> |
31 | | |
32 | | #ifndef GPAC_DISABLE_VRML |
33 | | |
34 | | GF_EXPORT |
35 | | GF_Proto *gf_sg_proto_new(GF_SceneGraph *inScene, u32 ProtoID, char *name, Bool unregistered) |
36 | 0 | { |
37 | 0 | GF_Proto *tmp; |
38 | 0 | if (!inScene) return NULL; |
39 | | |
40 | | /*make sure we don't define a proto already defined in this scope*/ |
41 | 0 | if (!unregistered) { |
42 | 0 | tmp = gf_sg_find_proto(inScene, ProtoID, name); |
43 | 0 | if (tmp) { |
44 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scenegraph] PROTO %s redefined - skipping loading\n", name)); |
45 | 0 | return NULL; |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | 0 | GF_SAFEALLOC(tmp, GF_Proto) |
50 | 0 | if (!tmp) return NULL; |
51 | | |
52 | 0 | tmp->proto_fields = gf_list_new(); |
53 | 0 | tmp->node_code = gf_list_new(); |
54 | 0 | tmp->parent_graph = inScene; |
55 | 0 | tmp->sub_graph = gf_sg_new_subscene(inScene); |
56 | 0 | tmp->instances = gf_list_new(); |
57 | |
|
58 | 0 | if (name) |
59 | 0 | tmp->Name = gf_strdup(name); |
60 | 0 | else |
61 | 0 | tmp->Name = gf_strdup("Unnamed Proto"); |
62 | 0 | tmp->ID = ProtoID; |
63 | 0 | if (!unregistered) { |
64 | 0 | gf_list_add(inScene->protos, tmp); |
65 | 0 | } else { |
66 | 0 | gf_list_add(inScene->unregistered_protos, tmp); |
67 | 0 | } |
68 | 0 | return tmp; |
69 | 0 | } |
70 | | |
71 | | #if 0 |
72 | | /*used for memory handling of scene graph only. move proto from off-graph to in-graph or reverse*/ |
73 | | GF_Err gf_sg_proto_set_in_graph(GF_Proto *proto, GF_SceneGraph *inScene, Bool set_in) |
74 | | { |
75 | | u32 i; |
76 | | GF_Proto *tmp; |
77 | | GF_List *removeFrom; |
78 | | GF_List *insertIn; |
79 | | |
80 | | if (set_in) { |
81 | | removeFrom = proto->parent_graph->unregistered_protos; |
82 | | insertIn = proto->parent_graph->protos; |
83 | | } else { |
84 | | insertIn = proto->parent_graph->unregistered_protos; |
85 | | removeFrom = proto->parent_graph->protos; |
86 | | } |
87 | | |
88 | | gf_list_del_item(removeFrom, proto); |
89 | | |
90 | | i=0; |
91 | | while ((tmp = (GF_Proto*)gf_list_enum(insertIn, &i))) { |
92 | | if (tmp==proto) return GF_OK; |
93 | | if (!set_in) continue; |
94 | | /*if registering, make sure no other proto has the same ID/name*/ |
95 | | if (tmp->ID==proto->ID) return GF_BAD_PARAM; |
96 | | if (!stricmp(tmp->Name, proto->Name)) return GF_BAD_PARAM; |
97 | | } |
98 | | return gf_list_add(insertIn, proto); |
99 | | } |
100 | | #endif |
101 | | |
102 | | GF_EXPORT |
103 | | GF_Err gf_sg_proto_del(GF_Proto *proto) |
104 | 0 | { |
105 | 0 | s32 i; |
106 | |
|
107 | 0 | if (!proto) return GF_OK; |
108 | 0 | i = gf_list_del_item(proto->parent_graph->protos, proto); |
109 | 0 | if (i<0) { |
110 | 0 | gf_list_del_item(proto->parent_graph->unregistered_protos, proto); |
111 | 0 | } |
112 | 0 | if (proto->userpriv && proto->OnDelete) proto->OnDelete(proto->userpriv); |
113 | | |
114 | | /*first destroy the code*/ |
115 | 0 | while (gf_list_count(proto->node_code)) { |
116 | 0 | GF_Node *node = (GF_Node*)gf_list_get(proto->node_code, 0); |
117 | 0 | gf_list_rem(proto->node_code, 0); |
118 | 0 | if (node->sgprivate->referencing_protos) |
119 | 0 | gf_list_del_item(node->sgprivate->referencing_protos, proto); |
120 | 0 | gf_node_unregister(node, NULL); |
121 | 0 | } |
122 | 0 | gf_list_del(proto->node_code); |
123 | | |
124 | | /*delete interface*/ |
125 | 0 | while (gf_list_count(proto->proto_fields)) { |
126 | 0 | GF_ProtoFieldInterface *field = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, 0); |
127 | 0 | if (field->userpriv && field->OnDelete) field->OnDelete(field->userpriv); |
128 | |
|
129 | 0 | if (field->FieldType==GF_SG_VRML_SFNODE) { |
130 | 0 | if (field->def_sfnode_value) |
131 | 0 | gf_node_unregister(field->def_sfnode_value, NULL); |
132 | 0 | } |
133 | 0 | else if (field->FieldType==GF_SG_VRML_MFNODE) { |
134 | 0 | if (field->def_mfnode_value) |
135 | 0 | gf_node_unregister_children(NULL, field->def_mfnode_value); |
136 | 0 | } |
137 | 0 | else if (field->def_value) |
138 | 0 | gf_sg_vrml_field_pointer_del(field->def_value, field->FieldType); |
139 | |
|
140 | 0 | if (field->FieldName) gf_free(field->FieldName); |
141 | | |
142 | | /*QP fields are SF fields, we can safely gf_free() them*/ |
143 | 0 | if (field->qp_max_value) gf_free(field->qp_max_value); |
144 | 0 | if (field->qp_min_value) gf_free(field->qp_min_value); |
145 | 0 | gf_free(field); |
146 | 0 | gf_list_rem(proto->proto_fields, 0); |
147 | 0 | } |
148 | 0 | gf_list_del(proto->proto_fields); |
149 | |
|
150 | 0 | while (gf_list_count(proto->instances)) { |
151 | 0 | GF_ProtoInstance *p = (GF_ProtoInstance *)gf_list_get(proto->instances, 0); |
152 | 0 | gf_list_rem(proto->instances, 0); |
153 | 0 | p->proto_interface = NULL; |
154 | 0 | } |
155 | | |
156 | | /*delete sub graph*/ |
157 | 0 | gf_sg_del(proto->sub_graph); |
158 | | |
159 | |
|
160 | 0 | if (proto->Name) gf_free(proto->Name); |
161 | 0 | gf_sg_mfurl_del(proto->ExternProto); |
162 | 0 | gf_list_del(proto->instances); |
163 | 0 | gf_free(proto); |
164 | 0 | return GF_OK; |
165 | 0 | } |
166 | | |
167 | | GF_EXPORT |
168 | | GF_SceneGraph *gf_sg_proto_get_graph(GF_Proto *proto) |
169 | 0 | { |
170 | 0 | return proto ? proto->sub_graph : NULL; |
171 | 0 | } |
172 | | |
173 | | |
174 | | #if 0 |
175 | | void gf_sg_proto_set_private(GF_Proto *p, void *ptr, void (*OnDelete)(void *ptr) ) |
176 | | { |
177 | | if (p) { |
178 | | p->userpriv = ptr; |
179 | | p->OnDelete = OnDelete; |
180 | | } |
181 | | } |
182 | | |
183 | | void *gf_sg_proto_get_private(GF_Proto *p) |
184 | | { |
185 | | return p ? p->userpriv : NULL; |
186 | | } |
187 | | #endif |
188 | | |
189 | | GF_EXPORT |
190 | | MFURL *gf_sg_proto_get_extern_url(GF_Proto *proto) |
191 | 0 | { |
192 | 0 | return proto ? &proto->ExternProto : NULL; |
193 | 0 | } |
194 | | |
195 | | GF_EXPORT |
196 | | GF_Err gf_sg_proto_add_node_code(GF_Proto *proto, GF_Node *pNode) |
197 | 0 | { |
198 | 0 | if (!proto || !pNode) return GF_BAD_PARAM; |
199 | 0 | if (!pNode->sgprivate->referencing_protos) |
200 | 0 | pNode->sgprivate->referencing_protos = gf_list_new(); |
201 | 0 | gf_list_add(pNode->sgprivate->referencing_protos, proto); |
202 | 0 | return gf_list_add(proto->node_code, pNode); |
203 | 0 | } |
204 | | |
205 | | GF_EXPORT |
206 | | GF_ProtoFieldInterface *gf_sg_proto_field_find_by_name(GF_Proto *proto, char *fieldName) |
207 | 0 | { |
208 | 0 | GF_ProtoFieldInterface *ptr; |
209 | 0 | u32 i=0; |
210 | 0 | while ((ptr = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { |
211 | 0 | if (ptr->FieldName && !strcmp(ptr->FieldName, fieldName)) return ptr; |
212 | 0 | } |
213 | 0 | return NULL; |
214 | 0 | } |
215 | | |
216 | | GF_EXPORT |
217 | | GF_ProtoFieldInterface *gf_sg_proto_field_new(GF_Proto *proto, u32 fieldType, u32 eventType, char *fieldName) |
218 | 0 | { |
219 | 0 | GF_ProtoFieldInterface *tmp; |
220 | |
|
221 | 0 | if (fieldName) { |
222 | 0 | tmp = gf_sg_proto_field_find_by_name(proto, fieldName); |
223 | 0 | if (tmp) return NULL; |
224 | 0 | } |
225 | 0 | GF_SAFEALLOC(tmp, GF_ProtoFieldInterface) |
226 | 0 | if (!tmp) return NULL; |
227 | | |
228 | 0 | tmp->FieldType = fieldType; |
229 | 0 | tmp->EventType = eventType; |
230 | | |
231 | | /*create container - can be NULL if SF node*/ |
232 | 0 | if ( fieldType == GF_SG_VRML_SFNODE) { |
233 | 0 | tmp->def_sfnode_value = NULL; |
234 | 0 | tmp->def_value = &tmp->def_sfnode_value; |
235 | 0 | } else if ( fieldType == GF_SG_VRML_MFNODE) { |
236 | 0 | tmp->def_mfnode_value = NULL; |
237 | 0 | tmp->def_value = &tmp->def_mfnode_value; |
238 | 0 | } else { |
239 | 0 | tmp->def_value = gf_sg_vrml_field_pointer_new(fieldType); |
240 | 0 | } |
241 | |
|
242 | 0 | if (fieldName) tmp->FieldName = gf_strdup(fieldName); |
243 | |
|
244 | 0 | tmp->ALL_index = gf_list_count(proto->proto_fields); |
245 | 0 | tmp->OUT_index = tmp->DEF_index = tmp->IN_index = (u32) -1; |
246 | |
|
247 | 0 | switch (eventType) { |
248 | 0 | case GF_SG_EVENT_EXPOSED_FIELD: |
249 | 0 | tmp->IN_index = proto->NumIn; |
250 | 0 | proto->NumIn ++; |
251 | 0 | tmp->OUT_index = proto->NumOut; |
252 | 0 | proto->NumOut ++; |
253 | 0 | case GF_SG_EVENT_FIELD: |
254 | 0 | tmp->DEF_index = proto->NumDef; |
255 | 0 | proto->NumDef ++; |
256 | 0 | break; |
257 | 0 | case GF_SG_EVENT_IN: |
258 | 0 | tmp->IN_index = proto->NumIn; |
259 | 0 | proto->NumIn ++; |
260 | 0 | break; |
261 | 0 | case GF_SG_EVENT_OUT: |
262 | 0 | tmp->OUT_index = proto->NumOut; |
263 | 0 | proto->NumOut ++; |
264 | 0 | break; |
265 | 0 | } |
266 | | |
267 | 0 | gf_list_add(proto->proto_fields, tmp); |
268 | 0 | return tmp; |
269 | 0 | } |
270 | | |
271 | | #if 0 //unused |
272 | | void gf_sg_proto_field_set_private(GF_ProtoFieldInterface *field, void *ptr, void (*OnDelete)(void *ptr)) |
273 | | { |
274 | | if (field) { |
275 | | field->userpriv = ptr; |
276 | | field->OnDelete = OnDelete; |
277 | | } |
278 | | } |
279 | | |
280 | | void *gf_sg_proto_field_get_private(GF_ProtoFieldInterface *field) |
281 | | { |
282 | | return field ? field->userpriv : NULL; |
283 | | } |
284 | | #endif |
285 | | |
286 | | GF_EXPORT |
287 | | GF_Err gf_sg_proto_field_get_field(GF_ProtoFieldInterface *field, GF_FieldInfo *info) |
288 | 0 | { |
289 | 0 | if (!field || !info) return GF_BAD_PARAM; |
290 | 0 | memset(info, 0, sizeof(GF_FieldInfo)); |
291 | 0 | info->fieldIndex = field->ALL_index; |
292 | 0 | info->fieldType = field->FieldType; |
293 | 0 | info->eventType = field->EventType; |
294 | 0 | info->far_ptr = field->def_value; |
295 | 0 | info->name = field->FieldName; |
296 | 0 | info->NDTtype = NDT_SFWorldNode; |
297 | 0 | return GF_OK; |
298 | 0 | } |
299 | | |
300 | | GF_Err gf_sg_proto_get_field(GF_Proto *proto, GF_Node *node, GF_FieldInfo *info) |
301 | 0 | { |
302 | 0 | GF_ProtoFieldInterface *proto_field; |
303 | 0 | GF_ProtoInstance *inst; |
304 | 0 | GF_ProtoField *field; |
305 | |
|
306 | 0 | if (!proto && !node) return GF_BAD_PARAM; |
307 | | |
308 | 0 | if (proto) { |
309 | 0 | proto_field = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, info->fieldIndex); |
310 | 0 | if (!proto_field) return GF_BAD_PARAM; |
311 | | |
312 | 0 | info->fieldType = proto_field->FieldType; |
313 | 0 | info->eventType = proto_field->EventType; |
314 | 0 | info->fieldIndex = proto_field->ALL_index; |
315 | 0 | info->NDTtype = NDT_SFWorldNode; |
316 | 0 | info->far_ptr = proto_field->def_value; |
317 | 0 | info->name = proto_field->FieldName; |
318 | 0 | return GF_OK; |
319 | 0 | } |
320 | | |
321 | | /*otherwise this is an instantiated proto*/ |
322 | 0 | if (node->sgprivate->tag!=TAG_ProtoNode) return GF_BAD_PARAM; |
323 | | |
324 | 0 | inst = (GF_ProtoInstance *) node; |
325 | 0 | field = (GF_ProtoField*)gf_list_get(inst->fields, info->fieldIndex); |
326 | 0 | if (!field) return GF_BAD_PARAM; |
327 | | |
328 | 0 | info->fieldType = field->FieldType; |
329 | 0 | info->eventType = field->EventType; |
330 | 0 | info->on_event_in = field->on_event_in; |
331 | | /*SF/MF nodes need pointers to field object - cf gf_sg_proto_create_node*/ |
332 | 0 | if (gf_sg_vrml_get_sf_type(field->FieldType) == GF_SG_VRML_SFNODE) { |
333 | 0 | info->far_ptr = &field->field_pointer; |
334 | 0 | } else { |
335 | 0 | info->far_ptr = field->field_pointer; |
336 | 0 | } |
337 | | /*set the name - watchout for deletion case*/ |
338 | 0 | if (inst->proto_interface) { |
339 | 0 | proto_field = (GF_ProtoFieldInterface*)gf_list_get(inst->proto_interface->proto_fields, info->fieldIndex); |
340 | 0 | info->name = proto_field->FieldName; |
341 | 0 | } else { |
342 | 0 | info->name = "ProtoFieldDeleted"; |
343 | 0 | } |
344 | 0 | info->NDTtype = NDT_SFWorldNode; |
345 | 0 | return GF_OK; |
346 | 0 | } |
347 | | |
348 | | s32 gf_sg_proto_get_field_index_by_name(GF_Proto *proto, GF_Node *node, char *name) |
349 | 0 | { |
350 | 0 | u32 i; |
351 | 0 | GF_Proto *__proto=NULL; |
352 | |
|
353 | 0 | if (!node && !proto) return -1; |
354 | 0 | if (node && (node->sgprivate->tag!=TAG_ProtoNode)) return -1; |
355 | | |
356 | 0 | if (proto) |
357 | 0 | __proto = proto; |
358 | 0 | else if (node) |
359 | 0 | __proto = ((GF_ProtoInstance *) node)->proto_interface; |
360 | |
|
361 | 0 | if (!__proto ) return -1; |
362 | | |
363 | 0 | for (i=0; i<gf_list_count(__proto->proto_fields); i++) { |
364 | 0 | GF_ProtoFieldInterface *proto_field = (GF_ProtoFieldInterface*)gf_list_get(__proto->proto_fields, i); |
365 | 0 | if (proto_field->FieldName && !strcmp(proto_field->FieldName, name)) return i; |
366 | 0 | } |
367 | 0 | return -1; |
368 | 0 | } |
369 | | |
370 | | |
371 | | GF_Node *gf_vrml_node_clone(GF_SceneGraph *inScene, GF_Node *orig, GF_Node *cloned_parent, char *inst_id_suffix) |
372 | 0 | { |
373 | 0 | u32 i, count, id; |
374 | 0 | char *szNodeName; |
375 | 0 | Bool is_script; |
376 | 0 | GF_Node *node, *child; |
377 | 0 | GF_ChildNodeItem *list, *last; |
378 | 0 | GF_Route *r1; |
379 | 0 | #ifndef GPAC_DISABLE_BIFS |
380 | 0 | void BIFS_SetupConditionalClone(GF_Node *node, GF_Node *orig); |
381 | 0 | #endif |
382 | 0 | GF_ProtoInstance *proto; |
383 | 0 | GF_FieldInfo field_orig, field; |
384 | | |
385 | | /*this is not a mistake*/ |
386 | 0 | if (!orig) return NULL; |
387 | | |
388 | | /*check for DEF/USE*/ |
389 | 0 | szNodeName = NULL; |
390 | 0 | if (!inst_id_suffix) id = 0; |
391 | 0 | else { |
392 | 0 | const char *orig_name = gf_node_get_name_and_id(orig, &id); |
393 | | /*generate clone IDs based on original one*/ |
394 | 0 | if (inst_id_suffix[0] && id) { |
395 | 0 | id = gf_sg_get_next_available_node_id(inScene); |
396 | 0 | if (orig_name) { |
397 | 0 | szNodeName = gf_strdup(orig_name); |
398 | 0 | gf_dynstrcat(&szNodeName, inst_id_suffix, NULL); |
399 | 0 | } |
400 | 0 | } |
401 | 0 | else if (orig_name) szNodeName = gf_strdup(orig_name); |
402 | 0 | } |
403 | |
|
404 | 0 | if (id) { |
405 | 0 | node = szNodeName ? gf_sg_find_node_by_name(inScene, szNodeName) : gf_sg_find_node(inScene, id); |
406 | | /*node already created, USE*/ |
407 | 0 | if (node) { |
408 | 0 | gf_node_register(node, cloned_parent); |
409 | 0 | if (szNodeName) gf_free(szNodeName); |
410 | 0 | return node; |
411 | 0 | } |
412 | 0 | } |
413 | | /*create a node*/ |
414 | 0 | if (orig->sgprivate->tag == TAG_ProtoNode) { |
415 | 0 | GF_Proto *proto_node = ((GF_ProtoInstance *)orig)->proto_interface; |
416 | | /*create the instance but don't load the code -c we MUST wait for ISed routes to be cloned before*/ |
417 | 0 | node = gf_sg_proto_create_node(inScene, proto_node, (GF_ProtoInstance *) orig); |
418 | 0 | } else { |
419 | 0 | node = gf_node_new(inScene, orig->sgprivate->tag); |
420 | 0 | } |
421 | |
|
422 | 0 | count = gf_node_get_field_count(orig); |
423 | |
|
424 | 0 | is_script = 0; |
425 | 0 | if (orig->sgprivate->tag==TAG_MPEG4_Script) is_script = 1; |
426 | 0 | #ifndef GPAC_DISABLE_X3D |
427 | 0 | else if (orig->sgprivate->tag==TAG_X3D_Script) is_script = 1; |
428 | 0 | #endif |
429 | |
|
430 | 0 | if (is_script) gf_sg_script_prepare_clone(node, orig); |
431 | | |
432 | | |
433 | | /*register node*/ |
434 | 0 | if (id) { |
435 | 0 | gf_node_set_id(node, id, szNodeName); |
436 | 0 | if (szNodeName) gf_free(szNodeName); |
437 | 0 | } |
438 | 0 | gf_node_register(node, cloned_parent); |
439 | | |
440 | | /*copy each field*/ |
441 | 0 | for (i=0; i<count; i++) { |
442 | 0 | gf_node_get_field(orig, i, &field_orig); |
443 | | |
444 | | /*get target ptr*/ |
445 | 0 | gf_node_get_field(node, i, &field); |
446 | |
|
447 | 0 | gf_assert(field.eventType==field_orig.eventType); |
448 | 0 | gf_assert(field.fieldType==field_orig.fieldType); |
449 | | |
450 | | /*duplicate it*/ |
451 | 0 | switch (field.fieldType) { |
452 | 0 | case GF_SG_VRML_SFNODE: |
453 | 0 | child = gf_node_clone(inScene, (* ((GF_Node **) field_orig.far_ptr)), node, inst_id_suffix, 1); |
454 | 0 | *((GF_Node **) field.far_ptr) = child; |
455 | 0 | break; |
456 | 0 | case GF_SG_VRML_MFNODE: |
457 | 0 | last = NULL; |
458 | 0 | list = *( (GF_ChildNodeItem **) field_orig.far_ptr); |
459 | 0 | while (list) { |
460 | 0 | child = gf_node_clone(inScene, list->node, node, inst_id_suffix, 1); |
461 | 0 | gf_node_list_add_child_last((GF_ChildNodeItem **) field.far_ptr, child, &last); |
462 | 0 | list = list->next; |
463 | 0 | } |
464 | 0 | break; |
465 | 0 | case GF_SG_VRML_SFTIME: |
466 | 0 | gf_sg_vrml_field_copy(field.far_ptr, field_orig.far_ptr, field.fieldType); |
467 | 0 | if (!inScene->GetSceneTime) break; |
468 | | /*update SFTime that must be updated when cloning the node*/ |
469 | 0 | if (orig->sgprivate->tag == TAG_ProtoNode) { |
470 | 0 | if (gf_sg_proto_field_is_sftime_offset(orig, &field_orig)) |
471 | 0 | *((SFTime *)field.far_ptr) += inScene->GetSceneTime(inScene->userpriv); |
472 | 0 | } else if (!stricmp(field.name, "startTime") || !stricmp(field_orig.name, "startTime") ) { |
473 | 0 | *((SFTime *)field.far_ptr) += inScene->GetSceneTime(inScene->userpriv); |
474 | 0 | } |
475 | 0 | break; |
476 | 0 | default: |
477 | 0 | gf_sg_vrml_field_clone(field.far_ptr, field_orig.far_ptr, field.fieldType, inScene); |
478 | 0 | break; |
479 | 0 | } |
480 | 0 | } |
481 | | |
482 | 0 | #ifndef GPAC_DISABLE_BIFS |
483 | | /*init node before creating ISed routes so the eventIn handler are in place*/ |
484 | 0 | if (node->sgprivate->tag == TAG_MPEG4_Conditional) |
485 | 0 | BIFS_SetupConditionalClone(node, orig); |
486 | 0 | else |
487 | 0 | #endif |
488 | 0 | if (node->sgprivate->tag != TAG_ProtoNode) gf_node_init(node); |
489 | |
|
490 | 0 | if (!inScene->pOwningProto) return node; |
491 | 0 | proto = inScene->pOwningProto; |
492 | | |
493 | | /*create Routes for ISed fields*/ |
494 | 0 | i=0; |
495 | 0 | while ((r1 = (GF_Route*)gf_list_enum(proto->proto_interface->sub_graph->Routes, &i))) { |
496 | 0 | GF_Route *r2 = NULL; |
497 | | /*locate only ISed routes*/ |
498 | 0 | if (!r1->IS_route) continue; |
499 | | |
500 | | /*eventOut*/ |
501 | 0 | if (r1->FromNode == orig) { |
502 | 0 | r2 = gf_sg_route_new(inScene, node, r1->FromField.fieldIndex, (GF_Node *) proto, r1->ToField.fieldIndex); |
503 | 0 | r2->IS_route = 1; |
504 | 0 | } |
505 | | /*eventIn or exposedField*/ |
506 | 0 | else if (r1->ToNode == orig) { |
507 | 0 | r2 = gf_sg_route_new(inScene, (GF_Node *) proto, r1->FromField.fieldIndex, node, r1->ToField.fieldIndex); |
508 | 0 | r2->IS_route = 1; |
509 | | |
510 | | /*activate the route now so that proto instanciation works properly, otherwise we may load scripts with wrong field values |
511 | | Note: we don't activate eventOut routes upon instanciation since no event has been triggered yet*/ |
512 | 0 | gf_sg_route_activate(r2); |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | | /*remember scripts*/ |
517 | 0 | if (is_script) gf_list_add(proto->scripts_to_load, node); |
518 | | |
519 | | /*this is a proto node, init our internal stuff*/ |
520 | 0 | if (node->sgprivate->tag == TAG_ProtoNode) { |
521 | 0 | node->sgprivate->UserCallback = NULL; |
522 | 0 | node->sgprivate->UserPrivate = NULL; |
523 | | /*NO RENDER, this is filtered at the generic gf_node_traverse to cope with instanciations and externProto*/ |
524 | | /*load code*/ |
525 | 0 | gf_sg_proto_instantiate((GF_ProtoInstance *)node); |
526 | 0 | } |
527 | 0 | return node; |
528 | 0 | } |
529 | | |
530 | | GF_Err gf_sg_proto_get_field_ind_static(GF_Node *Node, u32 inField, u8 IndexMode, u32 *allField) |
531 | 0 | { |
532 | 0 | return gf_sg_proto_get_field_index((GF_ProtoInstance *)Node, inField, IndexMode, allField); |
533 | 0 | } |
534 | | |
535 | | |
536 | | static Bool is_same_proto(GF_Proto *extern_proto, GF_Proto *proto) |
537 | 0 | { |
538 | 0 | u32 i, count; |
539 | | /*VRML allows external protos to have more fields defined that the externProto referencing them*/ |
540 | 0 | if (gf_list_count(extern_proto->proto_fields) > gf_list_count(proto->proto_fields)) return 0; |
541 | 0 | count = gf_list_count(extern_proto->proto_fields); |
542 | 0 | for (i=0; i<count; i++) { |
543 | 0 | GF_ProtoFieldInterface *pf1 = (GF_ProtoFieldInterface*)gf_list_get(extern_proto->proto_fields, i); |
544 | 0 | GF_ProtoFieldInterface *pf2 = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, i); |
545 | 0 | if (pf1->EventType != pf2->EventType) return 0; |
546 | 0 | if (pf1->FieldType != pf2->FieldType) return 0; |
547 | | /*note we don't check names since we're not sure both protos use name coding (MPEG4 only)*/ |
548 | 0 | } |
549 | 0 | return 1; |
550 | 0 | } |
551 | | |
552 | | static GF_Proto *find_proto_by_interface(GF_SceneGraph *sg, GF_Proto *extern_proto) |
553 | 0 | { |
554 | 0 | GF_Proto *proto; |
555 | 0 | u32 i, count; |
556 | |
|
557 | 0 | gf_assert(sg); |
558 | | |
559 | | /*browse all top-level */ |
560 | 0 | i=0; |
561 | 0 | while ((proto = (GF_Proto*)gf_list_enum(sg->protos, &i))) { |
562 | 0 | if (is_same_proto(proto, extern_proto)) return proto; |
563 | 0 | } |
564 | | /*browse all top-level unregistered in reverse order*/ |
565 | 0 | count = gf_list_count(sg->unregistered_protos); |
566 | 0 | for (i=count; i>0; i--) { |
567 | 0 | proto = (GF_Proto*)gf_list_get(sg->unregistered_protos, i-1); |
568 | 0 | if (is_same_proto(proto, extern_proto)) return proto; |
569 | 0 | } |
570 | 0 | return NULL; |
571 | 0 | } |
572 | | |
573 | 0 | #define GF_MAX_PROTO_INSTANTIATION_DEPTH 256 |
574 | | |
575 | | static Bool gf_sg_proto_instantiation_would_cycle(GF_ProtoInstance *proto_node, GF_Proto *proto, u32 *depth_out) |
576 | 0 | { |
577 | 0 | u32 depth = 0; |
578 | 0 | GF_SceneGraph *sg = proto_node && proto_node->sgprivate ? proto_node->sgprivate->scenegraph : NULL; |
579 | 0 | if (!sg) return GF_FALSE; |
580 | | |
581 | 0 | sg = sg->parent_scene; |
582 | 0 | while (sg) { |
583 | 0 | depth++; |
584 | 0 | if (depth > GF_MAX_PROTO_INSTANTIATION_DEPTH) { |
585 | 0 | if (depth_out) *depth_out = depth; |
586 | 0 | return GF_TRUE; |
587 | 0 | } |
588 | 0 | if (sg->pOwningProto && (sg->pOwningProto->proto_interface == proto)) { |
589 | 0 | if (depth_out) *depth_out = depth; |
590 | 0 | return GF_TRUE; |
591 | 0 | } |
592 | 0 | sg = sg->parent_scene; |
593 | 0 | } |
594 | 0 | if (depth_out) *depth_out = depth; |
595 | 0 | return GF_FALSE; |
596 | 0 | } |
597 | | |
598 | | /*performs common initialization of routes ISed fields and protos once everything is loaded*/ |
599 | | void gf_sg_proto_instantiate(GF_ProtoInstance *proto_node) |
600 | 0 | { |
601 | 0 | GF_Node *node, *orig; |
602 | 0 | GF_Route *route, *r2; |
603 | 0 | u32 i, count; |
604 | 0 | GF_Proto *proto = proto_node->proto_interface; |
605 | 0 | GF_Proto *owner = proto; |
606 | |
|
607 | 0 | if (!proto) return; |
608 | | |
609 | 0 | if (owner->ExternProto.count) { |
610 | 0 | GF_ProtoFieldInterface *pfi; |
611 | 0 | GF_SceneGraph *extern_lib; |
612 | 0 | if (!owner->parent_graph->GetExternProtoLib) return; |
613 | 0 | extern_lib = owner->parent_graph->GetExternProtoLib(proto->parent_graph->userpriv, &owner->ExternProto); |
614 | 0 | if (!extern_lib) return; |
615 | | |
616 | | /*this is an hardcoded proto - all routes, node modifications and co are handled internally*/ |
617 | 0 | if (PTR_TO_U_CAST extern_lib == GF_SG_INTERNAL_PROTO) { |
618 | 0 | proto_node->sgprivate->flags |= GF_SG_NODE_DIRTY; |
619 | | // take default values |
620 | 0 | count = gf_list_count(owner->proto_fields); |
621 | 0 | for (i=0; i<count; i++) { |
622 | 0 | GF_ProtoField *pf = (GF_ProtoField *)gf_list_get(proto_node->fields, i); |
623 | 0 | if (!pf->has_been_accessed) { |
624 | 0 | pfi = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, i); |
625 | 0 | gf_sg_vrml_field_copy(pf->field_pointer, pfi->def_value, pfi->FieldType); |
626 | 0 | } |
627 | 0 | } |
628 | 0 | owner->parent_graph->NodeCallback(owner->parent_graph->userpriv, GF_SG_CALLBACK_INIT, (GF_Node *) proto_node, NULL); |
629 | 0 | proto_node->flags |= GF_SG_PROTO_LOADED | GF_SG_PROTO_HARDCODED; |
630 | 0 | return; |
631 | 0 | } |
632 | | /*not loaded yet*/ |
633 | 0 | if (!gf_list_count(extern_lib->protos)) return; |
634 | | |
635 | | /*overwrite this proto by external one*/ |
636 | 0 | proto = NULL; |
637 | | /*start with proto v2 addressing*/ |
638 | 0 | if (owner->ExternProto.vals[0].url) { |
639 | 0 | u32 ID = (u32) -1; |
640 | 0 | char *szName = strrchr(owner->ExternProto.vals[0].url, '#'); |
641 | 0 | if (szName) { |
642 | 0 | szName++; |
643 | 0 | if (sscanf(szName, "%u", &ID)) ID = (u32) -1; |
644 | 0 | } |
645 | | /*if we have the proto name, use it*/ |
646 | 0 | if (owner->Name) szName = owner->Name; |
647 | 0 | proto = gf_sg_find_proto(extern_lib, ID, szName); |
648 | 0 | } |
649 | 0 | if (!proto) proto = gf_sg_find_proto(extern_lib, owner->ID, owner->Name); |
650 | 0 | if (!proto) proto = find_proto_by_interface(extern_lib, owner); |
651 | |
|
652 | 0 | if (proto && !is_same_proto(owner, proto)) { |
653 | 0 | proto = NULL; |
654 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[Scenegraph] fields/types mismatch for PROTO %s - skipping instantiation\n", owner->Name)); |
655 | 0 | } |
656 | | /*couldn't find proto in the given lib, consider the proto as loaded (give up)*/ |
657 | 0 | if (!proto) { |
658 | 0 | proto_node->flags |= GF_SG_PROTO_LOADED; |
659 | 0 | return; |
660 | 0 | } |
661 | | /*cf VRML: once an external proto is loaded, copy back the default field values of the external proto*/ |
662 | 0 | count = gf_list_count(owner->proto_fields); |
663 | 0 | for (i=0; i<count; i++) { |
664 | 0 | GF_ProtoField *pf = (GF_ProtoField *)gf_list_get(proto_node->fields, i); |
665 | 0 | if (!pf->has_been_accessed) { |
666 | 0 | pfi = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, i); |
667 | 0 | gf_sg_vrml_field_copy(pf->field_pointer, pfi->def_value, pfi->FieldType); |
668 | 0 | } else { |
669 | | //pfi = (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, i); |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | | /*unregister from prev and reg with real proto*/ |
674 | 0 | gf_list_del_item(owner->instances, proto_node); |
675 | 0 | gf_list_add(proto->instances, proto_node); |
676 | 0 | } |
677 | | |
678 | | /*OVERRIDE the proto instance (eg don't instantiate an empty externproto...)*/ |
679 | 0 | proto_node->proto_interface = proto; |
680 | |
|
681 | 0 | { |
682 | 0 | u32 chain_depth = 0; |
683 | 0 | if (gf_sg_proto_instantiation_would_cycle(proto_node, proto, &chain_depth)) { |
684 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, |
685 | 0 | ("[Scenegraph] cyclic PROTO instantiation blocked for %s (depth %u)\n", |
686 | 0 | proto->Name ? proto->Name : "<unnamed>", chain_depth)); |
687 | | /* Mark as loaded to avoid repeated attempts on traversal */ |
688 | 0 | proto_node->flags |= GF_SG_PROTO_LOADED; |
689 | 0 | return; |
690 | 0 | } |
691 | 0 | } |
692 | | |
693 | | /*clone all nodes*/ |
694 | 0 | i=0; |
695 | 0 | while ((orig = (GF_Node*)gf_list_enum(proto->node_code, &i))) { |
696 | | /*node is cloned in the new scenegraph and its parent is NULL */ |
697 | 0 | node = gf_node_clone(proto_node->sgprivate->scenegraph, orig, NULL, "", 1); |
698 | 0 | gf_assert(node); |
699 | | |
700 | | /*assign first rendering node*/ |
701 | 0 | if (i==1) proto_node->RenderingNode = node; |
702 | 0 | gf_list_add(proto_node->node_code, node); |
703 | 0 | } |
704 | | |
705 | | /*instantiate routes (not ISed ones)*/ |
706 | 0 | i=0; |
707 | 0 | while ((route = (GF_Route*)gf_list_enum(proto->sub_graph->Routes, &i))) { |
708 | 0 | if (route->IS_route) continue; |
709 | | |
710 | 0 | r2 = gf_sg_route_new(proto_node->sgprivate->scenegraph, |
711 | 0 | gf_sg_find_node(proto_node->sgprivate->scenegraph, gf_node_get_id(route->FromNode) ), |
712 | 0 | route->FromField.fieldIndex, |
713 | 0 | gf_sg_find_node(proto_node->sgprivate->scenegraph, gf_node_get_id(route->ToNode) ), |
714 | 0 | route->ToField.fieldIndex); |
715 | |
|
716 | 0 | if (route->ID) gf_sg_route_set_id(r2, route->ID); |
717 | 0 | if (route->name) gf_sg_route_set_name(r2, route->name); |
718 | 0 | } |
719 | | /*activate all ISed fields so that inits on events is properly done*/ |
720 | 0 | i=0; |
721 | 0 | while ((route = (GF_Route*)gf_list_enum(proto_node->sgprivate->scenegraph->Routes, &i))) { |
722 | 0 | if (!route->IS_route) continue; |
723 | | /*do not activate eventIn to eventIn routes*/ |
724 | 0 | if (route->is_setup) { |
725 | 0 | if ((route->ToField.eventType == GF_SG_EVENT_IN) && (route->FromField.eventType == GF_SG_EVENT_IN) ) continue; |
726 | 0 | } |
727 | 0 | gf_sg_route_activate(route); |
728 | 0 | } |
729 | | /*and load all scripts (this must be done once all fields are routed for the "initialize" method)*/ |
730 | 0 | while (gf_list_count(proto_node->scripts_to_load)) { |
731 | 0 | node = (GF_Node*)gf_list_get(proto_node->scripts_to_load, 0); |
732 | 0 | gf_list_rem(proto_node->scripts_to_load, 0); |
733 | 0 | gf_sg_script_load(node); |
734 | 0 | } |
735 | | /*re-activate all ISed fields pointing to scripts once scripts are loaded (eventIns)*/ |
736 | 0 | i=0; |
737 | 0 | while ((route = (GF_Route*)gf_list_enum(proto_node->sgprivate->scenegraph->Routes, &i))) { |
738 | 0 | if (!route->IS_route || !route->ToNode) continue; |
739 | | /* gf_assert(route->is_setup); |
740 | | if ((route->FromField.eventType == GF_SG_EVENT_OUT) || (route->FromField.eventType == GF_SG_EVENT_IN) ) continue; |
741 | | */ |
742 | | |
743 | 0 | if (route->is_setup) { |
744 | 0 | if ((route->ToField.eventType == GF_SG_EVENT_IN) && (route->FromField.eventType == GF_SG_EVENT_IN) ) continue; |
745 | 0 | } |
746 | | |
747 | 0 | if (route->ToNode->sgprivate->tag==TAG_MPEG4_Script) |
748 | 0 | gf_sg_route_activate(route); |
749 | 0 | #ifndef GPAC_DISABLE_X3D |
750 | 0 | else if (route->ToNode->sgprivate->tag==TAG_X3D_Script) |
751 | 0 | gf_sg_route_activate(route); |
752 | 0 | #endif |
753 | 0 | } |
754 | |
|
755 | | #if 0 |
756 | | /*reset all regular route activation times - if we don't do so, creating a proto by script and then manipulating one of its |
757 | | ISed field may not trigger the proper routes*/ |
758 | | i=0; |
759 | | while ((route = (GF_Route*)gf_list_enum(proto_node->sgprivate->scenegraph->Routes, &i))) { |
760 | | if (!route->IS_route) { |
761 | | route->lastActivateTime = 0; |
762 | | } |
763 | | } |
764 | | #endif |
765 | 0 | proto_node->flags |= GF_SG_PROTO_LOADED; |
766 | 0 | } |
767 | | |
768 | | void gf_sg_proto_mark_field_loaded(GF_Node *proto_inst, GF_FieldInfo *info) |
769 | 0 | { |
770 | 0 | GF_ProtoInstance *inst= (proto_inst->sgprivate->tag==TAG_ProtoNode) ? (GF_ProtoInstance *)proto_inst : NULL; |
771 | 0 | GF_ProtoField *pf = inst ? (GF_ProtoField *)gf_list_get(inst->fields, info->fieldIndex) : NULL; |
772 | 0 | if (pf) pf->has_been_accessed = 1; |
773 | 0 | } |
774 | | |
775 | | GF_Node *gf_sg_proto_create_node(GF_SceneGraph *scene, GF_Proto *proto, GF_ProtoInstance *from_inst) |
776 | 0 | { |
777 | 0 | u32 i; |
778 | 0 | GF_ProtoField *inst, *from_field; |
779 | 0 | GF_ProtoFieldInterface *field; |
780 | 0 | GF_ProtoInstance *proto_node; |
781 | 0 | if (!proto) return NULL; |
782 | | |
783 | 0 | GF_SAFEALLOC(proto_node, GF_ProtoInstance) |
784 | 0 | if (!proto_node) return NULL; |
785 | | |
786 | 0 | gf_node_setup((GF_Node *)proto_node, TAG_ProtoNode); |
787 | 0 | proto_node->node_code = gf_list_new(); |
788 | 0 | proto_node->fields = gf_list_new(); |
789 | 0 | proto_node->scripts_to_load = gf_list_new(); |
790 | |
|
791 | 0 | proto_node->proto_interface = proto; |
792 | 0 | gf_list_add(proto->instances, proto_node); |
793 | |
|
794 | 0 | proto_node->proto_name = gf_strdup(proto->Name); |
795 | | |
796 | | /*create the namespace*/ |
797 | 0 | proto_node->sgprivate->scenegraph = gf_sg_new_subscene(scene); |
798 | | /*set this proto as owner of the new graph*/ |
799 | 0 | proto_node->sgprivate->scenegraph->pOwningProto = proto_node; |
800 | | |
801 | | /*instantiate fields*/ |
802 | 0 | i=0; |
803 | 0 | while ((field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { |
804 | 0 | GF_SAFEALLOC(inst, GF_ProtoField); |
805 | 0 | if (!inst) { |
806 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate proto instance field\n]")); |
807 | 0 | continue; |
808 | 0 | } |
809 | | |
810 | 0 | inst->EventType = field->EventType; |
811 | 0 | inst->FieldType = field->FieldType; |
812 | | |
813 | | /*this is OK to call on GF_Node (returns NULL) and MFNode (returns gf_list_new() )*/ |
814 | 0 | inst->field_pointer = gf_sg_vrml_field_pointer_new(inst->FieldType); |
815 | | |
816 | | /*regular field, duplicate from default value or instantiated one if specified (since |
817 | | a proto may be partially instantiated when used in another proto)*/ |
818 | 0 | if (gf_sg_vrml_get_sf_type(inst->FieldType) != GF_SG_VRML_SFNODE) { |
819 | 0 | from_field = from_inst ? (GF_ProtoField *)gf_list_get(from_inst->fields, i-1) : NULL; |
820 | | /*a partially-instantiated PROTO (from_inst supplies fewer fields than this PROTO |
821 | | interface declares) yields a NULL entry here - fall back to the declared default, |
822 | | matching the from_inst==NULL path*/ |
823 | 0 | if (from_field) { |
824 | 0 | gf_sg_vrml_field_copy(inst->field_pointer, from_field->field_pointer, inst->FieldType); |
825 | 0 | inst->has_been_accessed = from_field->has_been_accessed; |
826 | 0 | } else { |
827 | 0 | gf_sg_vrml_field_copy(inst->field_pointer, field->def_value, inst->FieldType); |
828 | 0 | } |
829 | 0 | } |
830 | | /*No default values for SFNodes as interfaces ...*/ |
831 | 0 | gf_list_add(proto_node->fields, inst); |
832 | 0 | } |
833 | 0 | return (GF_Node *) proto_node; |
834 | 0 | } |
835 | | |
836 | | |
837 | | GF_EXPORT |
838 | | GF_Node *gf_sg_proto_create_instance(GF_SceneGraph *sg, GF_Proto *proto) |
839 | 0 | { |
840 | 0 | return gf_sg_proto_create_node(sg, proto, NULL); |
841 | 0 | } |
842 | | |
843 | | GF_EXPORT |
844 | | GF_Err gf_sg_proto_load_code(GF_Node *node) |
845 | 0 | { |
846 | 0 | GF_ProtoInstance *inst; |
847 | 0 | if (node->sgprivate->tag != TAG_ProtoNode) return GF_BAD_PARAM; |
848 | 0 | inst = (GF_ProtoInstance *) node; |
849 | 0 | if (!inst->proto_interface) return GF_BAD_PARAM; |
850 | 0 | if (inst->flags & GF_SG_PROTO_LOADED) return GF_OK; |
851 | 0 | gf_sg_proto_instantiate(inst); |
852 | 0 | return GF_OK; |
853 | 0 | } |
854 | | |
855 | | |
856 | | u32 gf_sg_proto_get_num_fields(GF_Node *node, u8 code_mode) |
857 | 0 | { |
858 | 0 | GF_ProtoInstance *proto; |
859 | 0 | if (!node) return 0; |
860 | | |
861 | 0 | proto = (GF_ProtoInstance *)node; |
862 | | /*watchout for deletion case*/ |
863 | 0 | switch (code_mode) { |
864 | 0 | case GF_SG_FIELD_CODING_IN: |
865 | 0 | return proto->proto_interface ? proto->proto_interface->NumIn : 0; |
866 | 0 | case GF_SG_FIELD_CODING_OUT: |
867 | 0 | return proto->proto_interface ? proto->proto_interface->NumOut : 0; |
868 | 0 | case GF_SG_FIELD_CODING_DEF: |
869 | 0 | return proto->proto_interface ? proto->proto_interface->NumDef : 0; |
870 | 0 | case GF_SG_FIELD_CODING_ALL: |
871 | 0 | return gf_list_count(proto->proto_interface ? proto->proto_interface->proto_fields : proto->fields); |
872 | | /*BIFS-ANIM not supported*/ |
873 | 0 | case GF_SG_FIELD_CODING_DYN: |
874 | 0 | default: |
875 | 0 | return 0; |
876 | 0 | } |
877 | 0 | } |
878 | | |
879 | | |
880 | | void gf_sg_proto_del_instance(GF_ProtoInstance *inst) |
881 | 0 | { |
882 | 0 | GF_SceneGraph *sg; |
883 | |
|
884 | 0 | while (gf_list_count(inst->fields)) { |
885 | 0 | GF_ProtoField *field = (GF_ProtoField *)gf_list_get(inst->fields, 0); |
886 | 0 | gf_list_rem(inst->fields, 0); |
887 | |
|
888 | 0 | if (field->field_pointer) { |
889 | | /*regular type*/ |
890 | 0 | if ( (field->FieldType!=GF_SG_VRML_SFNODE) && (field->FieldType!=GF_SG_VRML_MFNODE)) { |
891 | 0 | gf_sg_vrml_field_pointer_del(field->field_pointer, field->FieldType); |
892 | 0 | } |
893 | | /*node types: delete instances*/ |
894 | 0 | else { |
895 | 0 | if (field->FieldType == GF_SG_VRML_SFNODE) { |
896 | 0 | gf_node_unregister((GF_Node *) field->field_pointer, (GF_Node *) inst); |
897 | 0 | } else { |
898 | 0 | GF_ChildNodeItem *list = (GF_ChildNodeItem *)field->field_pointer; |
899 | 0 | while (list) { |
900 | 0 | GF_ChildNodeItem *cur = list; |
901 | 0 | gf_node_unregister(list->node, (GF_Node *) inst); |
902 | 0 | list = list->next; |
903 | 0 | gf_free(cur); |
904 | 0 | } |
905 | 0 | } |
906 | 0 | } |
907 | 0 | } |
908 | |
|
909 | 0 | gf_free(field); |
910 | 0 | } |
911 | 0 | gf_list_del(inst->fields); |
912 | | |
913 | | /*destroy the code*/ |
914 | 0 | while (gf_list_count(inst->node_code)) { |
915 | 0 | GF_Node *node = (GF_Node*)gf_list_get(inst->node_code, 0); |
916 | 0 | gf_node_unregister(node, (GF_Node*) inst); |
917 | 0 | gf_list_rem(inst->node_code, 0); |
918 | 0 | } |
919 | |
|
920 | 0 | sg = inst->sgprivate->scenegraph; |
921 | | |
922 | | /*reset the scene graph before destroying the node code list, as unregistering nodes |
923 | | not destroyed in the previous phase (eg, cyclic references such as script and co) will |
924 | | refer to the node-code list*/ |
925 | 0 | gf_sg_reset(sg); |
926 | 0 | sg->pOwningProto = NULL; |
927 | |
|
928 | 0 | gf_free((char *) inst->proto_name); |
929 | 0 | gf_list_del(inst->node_code); |
930 | 0 | gf_assert(!gf_list_count(inst->scripts_to_load)); |
931 | 0 | gf_list_del(inst->scripts_to_load); |
932 | |
|
933 | 0 | if (inst->proto_interface && inst->proto_interface->instances) gf_list_del_item(inst->proto_interface->instances, inst); |
934 | |
|
935 | 0 | gf_node_free((GF_Node *)inst); |
936 | 0 | gf_sg_del(sg); |
937 | 0 | } |
938 | | |
939 | | /*Note on ISed fields: we cannot support fan-in on proto, eg we assume only one eventIn field can receive events |
940 | | thus situations where a proto receives eventIn from outside and the node with ISed eventIn receives event |
941 | | from inside the proto are undefined*/ |
942 | | GF_EXPORT |
943 | | GF_Err gf_sg_proto_field_set_ised(GF_Proto *proto, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex) |
944 | 0 | { |
945 | 0 | GF_Err e; |
946 | 0 | GF_Route *r; |
947 | 0 | GF_FieldInfo field, nodeField; |
948 | 0 | field.fieldIndex = protoFieldIndex; |
949 | 0 | e = gf_sg_proto_get_field(proto, NULL, &field); |
950 | 0 | if (e) return e; |
951 | 0 | e = gf_node_get_field(node, nodeFieldIndex, &nodeField); |
952 | 0 | if (e) return e; |
953 | 0 | if (field.fieldType != nodeField.fieldType) { |
954 | 0 | if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFSTRING) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFURL)) { |
955 | | // e = GF_OK; |
956 | 0 | } else if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFURL) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFSTRING)) { |
957 | | // e = GF_OK; |
958 | 0 | } else { |
959 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] error in IS - node field %s.%s - inType %s - outType %s\n", gf_node_get_class_name(node) , nodeField.name, gf_sg_vrml_get_field_type_name(field.fieldType), gf_sg_vrml_get_field_type_name(nodeField.fieldType))); |
960 | 0 | return GF_SG_INVALID_PROTO; |
961 | 0 | } |
962 | 0 | } |
963 | | |
964 | 0 | GF_SAFEALLOC(r, GF_Route) |
965 | 0 | if (!r) return GF_OUT_OF_MEM; |
966 | 0 | r->IS_route = 1; |
967 | |
|
968 | 0 | if (nodeField.eventType==GF_SG_EVENT_OUT) { |
969 | 0 | r->FromField.fieldIndex = nodeFieldIndex; |
970 | 0 | r->FromNode = node; |
971 | 0 | r->ToField.fieldIndex = protoFieldIndex; |
972 | 0 | r->ToNode = NULL; |
973 | 0 | if (!node->sgprivate->interact) { |
974 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
975 | 0 | if (!node->sgprivate->interact) { |
976 | 0 | return GF_OUT_OF_MEM; |
977 | 0 | } |
978 | 0 | } |
979 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
980 | 0 | gf_list_add(node->sgprivate->interact->routes, r); |
981 | 0 | } else { |
982 | 0 | switch (field.eventType) { |
983 | 0 | case GF_SG_EVENT_FIELD: |
984 | 0 | case GF_SG_EVENT_EXPOSED_FIELD: |
985 | 0 | case GF_SG_EVENT_IN: |
986 | 0 | r->FromField.fieldIndex = protoFieldIndex; |
987 | 0 | r->FromNode = NULL; |
988 | 0 | r->ToField.fieldIndex = nodeFieldIndex; |
989 | 0 | r->ToNode = node; |
990 | | /*create an ISed route for the eventOut part of the exposedFIeld*/ |
991 | 0 | if ((field.eventType==GF_SG_EVENT_EXPOSED_FIELD) && (nodeField.eventType==GF_SG_EVENT_EXPOSED_FIELD)) { |
992 | 0 | GF_Route *r2; |
993 | 0 | GF_SAFEALLOC(r2, GF_Route); |
994 | 0 | if (!r2) { |
995 | 0 | gf_free(r); |
996 | 0 | return GF_OUT_OF_MEM; |
997 | 0 | } |
998 | 0 | r2->IS_route = 1; |
999 | 0 | r2->FromField.fieldIndex = nodeFieldIndex; |
1000 | 0 | r2->FromNode = node; |
1001 | 0 | r2->ToField.fieldIndex = protoFieldIndex; |
1002 | 0 | r2->ToNode = NULL; |
1003 | 0 | r2->graph = proto->sub_graph; |
1004 | 0 | if (!node->sgprivate->interact) { |
1005 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
1006 | 0 | if (!node->sgprivate->interact) return GF_OUT_OF_MEM; |
1007 | 0 | } |
1008 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
1009 | 0 | gf_list_add(node->sgprivate->interact->routes, r2); |
1010 | 0 | gf_list_add(proto->sub_graph->Routes, r2); |
1011 | 0 | } |
1012 | 0 | break; |
1013 | 0 | case GF_SG_EVENT_OUT: |
1014 | 0 | r->FromField.fieldIndex = nodeFieldIndex; |
1015 | 0 | r->FromNode = node; |
1016 | 0 | r->ToField.fieldIndex = protoFieldIndex; |
1017 | 0 | r->ToNode = NULL; |
1018 | 0 | if (!node->sgprivate->interact) { |
1019 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
1020 | 0 | if (!node->sgprivate->interact) return GF_OUT_OF_MEM; |
1021 | 0 | } |
1022 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
1023 | 0 | break; |
1024 | 0 | default: |
1025 | 0 | gf_free(r); |
1026 | 0 | return GF_BAD_PARAM; |
1027 | 0 | } |
1028 | 0 | } |
1029 | 0 | r->graph = proto->sub_graph; |
1030 | 0 | return gf_list_add(proto->sub_graph->Routes, r); |
1031 | 0 | } |
1032 | | |
1033 | | GF_EXPORT |
1034 | | GF_Err gf_sg_proto_instance_set_ised(GF_Node *protoinst, u32 protoFieldIndex, GF_Node *node, u32 nodeFieldIndex) |
1035 | 0 | { |
1036 | 0 | GF_Err e; |
1037 | 0 | GF_Route *r; |
1038 | 0 | GF_FieldInfo field, nodeField; |
1039 | 0 | if (!protoinst) return GF_BAD_PARAM; |
1040 | 0 | if (protoinst->sgprivate->tag != TAG_ProtoNode) return GF_BAD_PARAM; |
1041 | | |
1042 | 0 | e = gf_node_get_field(protoinst, protoFieldIndex, &field); |
1043 | 0 | if (e) return e; |
1044 | 0 | e = gf_node_get_field(node, nodeFieldIndex, &nodeField); |
1045 | 0 | if (e) return e; |
1046 | 0 | if (field.fieldType != nodeField.fieldType) { |
1047 | 0 | if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFSTRING) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFURL)) { |
1048 | | // e = GF_OK; |
1049 | 0 | } else if ((gf_sg_vrml_get_sf_type(field.fieldType)==GF_SG_VRML_SFURL) && (gf_sg_vrml_get_sf_type(nodeField.fieldType) == GF_SG_VRML_SFSTRING)) { |
1050 | | // e = GF_OK; |
1051 | 0 | } else { |
1052 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] error in IS - node field %s.%s - inType %s - outType %s\n", gf_node_get_class_name(node) , nodeField.name, gf_sg_vrml_get_field_type_name(field.fieldType), gf_sg_vrml_get_field_type_name(nodeField.fieldType))); |
1053 | 0 | return GF_SG_INVALID_PROTO; |
1054 | 0 | } |
1055 | 0 | } |
1056 | | |
1057 | 0 | GF_SAFEALLOC(r, GF_Route) |
1058 | 0 | if (!r) return GF_OUT_OF_MEM; |
1059 | 0 | r->IS_route = 1; |
1060 | |
|
1061 | 0 | if (nodeField.eventType==GF_SG_EVENT_OUT) { |
1062 | 0 | r->FromField.fieldIndex = nodeFieldIndex; |
1063 | 0 | r->FromNode = node; |
1064 | 0 | r->ToField.fieldIndex = protoFieldIndex; |
1065 | 0 | r->ToNode = protoinst; |
1066 | 0 | if (!node->sgprivate->interact) { |
1067 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
1068 | 0 | if (!node->sgprivate->interact) return GF_OUT_OF_MEM; |
1069 | 0 | } |
1070 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
1071 | 0 | gf_list_add(node->sgprivate->interact->routes, r); |
1072 | 0 | } else { |
1073 | 0 | switch (field.eventType) { |
1074 | 0 | case GF_SG_EVENT_FIELD: |
1075 | 0 | case GF_SG_EVENT_EXPOSED_FIELD: |
1076 | 0 | case GF_SG_EVENT_IN: |
1077 | 0 | r->FromField.fieldIndex = protoFieldIndex; |
1078 | 0 | r->FromNode = protoinst; |
1079 | 0 | r->ToField.fieldIndex = nodeFieldIndex; |
1080 | 0 | r->ToNode = node; |
1081 | | |
1082 | | /*create an ISed route for the eventOut part of the exposedFIeld*/ |
1083 | 0 | if ((field.eventType==GF_SG_EVENT_EXPOSED_FIELD) && (nodeField.eventType==GF_SG_EVENT_EXPOSED_FIELD)) { |
1084 | 0 | GF_Route *r2; |
1085 | 0 | GF_SAFEALLOC(r2, GF_Route); |
1086 | 0 | if (!r2) { |
1087 | 0 | gf_free(r); |
1088 | 0 | return GF_OUT_OF_MEM; |
1089 | 0 | } |
1090 | 0 | r2->IS_route = 1; |
1091 | 0 | r2->FromField.fieldIndex = nodeFieldIndex; |
1092 | 0 | r2->FromNode = node; |
1093 | 0 | r2->ToField.fieldIndex = protoFieldIndex; |
1094 | 0 | r2->ToNode = protoinst; |
1095 | 0 | r2->graph = node->sgprivate->scenegraph; |
1096 | 0 | if (!node->sgprivate->interact) { |
1097 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
1098 | 0 | if (!node->sgprivate->interact) return GF_OUT_OF_MEM; |
1099 | 0 | } |
1100 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
1101 | 0 | gf_list_add(node->sgprivate->interact->routes, r2); |
1102 | 0 | gf_list_add(r->graph->Routes, r2); |
1103 | 0 | } |
1104 | 0 | break; |
1105 | 0 | case GF_SG_EVENT_OUT: |
1106 | 0 | r->FromField.fieldIndex = nodeFieldIndex; |
1107 | 0 | r->FromNode = node; |
1108 | 0 | r->ToField.fieldIndex = protoFieldIndex; |
1109 | 0 | r->ToNode = protoinst; |
1110 | 0 | if (!node->sgprivate->interact) { |
1111 | 0 | GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext); |
1112 | 0 | if (!node->sgprivate->interact) return GF_OUT_OF_MEM; |
1113 | 0 | } |
1114 | 0 | if (!node->sgprivate->interact->routes) node->sgprivate->interact->routes = gf_list_new(); |
1115 | 0 | gf_list_add(node->sgprivate->interact->routes, r); |
1116 | 0 | break; |
1117 | 0 | default: |
1118 | 0 | gf_free(r); |
1119 | 0 | return GF_BAD_PARAM; |
1120 | 0 | } |
1121 | 0 | } |
1122 | 0 | r->graph = node->sgprivate->scenegraph; |
1123 | 0 | gf_sg_route_activate(r); |
1124 | 0 | return gf_list_add(r->graph->Routes, r); |
1125 | 0 | } |
1126 | | |
1127 | | |
1128 | | GF_Err gf_bifs_proto_field_set_aq_info(GF_ProtoFieldInterface *field, |
1129 | | u32 QP_Type, |
1130 | | u32 hasMinMax, |
1131 | | u32 QPSFType, |
1132 | | void *qp_min_value, |
1133 | | void *qp_max_value, |
1134 | | u32 QP13_NumBits) |
1135 | 0 | { |
1136 | |
|
1137 | 0 | if (!field) return GF_BAD_PARAM; |
1138 | 0 | if (!QP_Type) return GF_OK; |
1139 | 0 | if (!gf_sg_vrml_is_sf_field(QPSFType)) return GF_BAD_PARAM; |
1140 | | |
1141 | 0 | field->QP_Type = QP_Type; |
1142 | 0 | field->hasMinMax = hasMinMax; |
1143 | 0 | if (hasMinMax) { |
1144 | 0 | if (qp_min_value) { |
1145 | 0 | field->qp_min_value = gf_sg_vrml_field_pointer_new(QPSFType); |
1146 | 0 | gf_sg_vrml_field_copy(field->qp_min_value, qp_min_value, QPSFType); |
1147 | 0 | } |
1148 | 0 | if (qp_max_value) { |
1149 | 0 | field->qp_max_value = gf_sg_vrml_field_pointer_new(QPSFType); |
1150 | 0 | gf_sg_vrml_field_copy(field->qp_max_value, qp_max_value, QPSFType); |
1151 | 0 | } |
1152 | 0 | } |
1153 | 0 | field->NumBits = QP13_NumBits; |
1154 | 0 | return GF_OK; |
1155 | 0 | } |
1156 | | |
1157 | | |
1158 | | GF_Err gf_sg_proto_get_field_index(GF_ProtoInstance *proto, u32 index, u32 code_mode, u32 *all_index) |
1159 | 0 | { |
1160 | 0 | u32 i; |
1161 | 0 | GF_ProtoFieldInterface *proto_field; |
1162 | |
|
1163 | 0 | if (!proto || !proto->proto_interface) return GF_BAD_PARAM; |
1164 | | |
1165 | 0 | i=0; |
1166 | 0 | while ((proto_field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_interface->proto_fields, &i))) { |
1167 | 0 | gf_assert(proto_field); |
1168 | 0 | switch (code_mode) { |
1169 | 0 | case GF_SG_FIELD_CODING_IN: |
1170 | 0 | if (proto_field->IN_index == index) { |
1171 | 0 | *all_index = proto_field->ALL_index; |
1172 | 0 | return GF_OK; |
1173 | 0 | } |
1174 | 0 | break; |
1175 | 0 | case GF_SG_FIELD_CODING_OUT: |
1176 | 0 | if (proto_field->OUT_index == index) { |
1177 | 0 | *all_index = proto_field->ALL_index; |
1178 | 0 | return GF_OK; |
1179 | 0 | } |
1180 | 0 | break; |
1181 | 0 | case GF_SG_FIELD_CODING_DEF: |
1182 | 0 | if (proto_field->DEF_index == index) { |
1183 | 0 | *all_index = proto_field->ALL_index; |
1184 | 0 | return GF_OK; |
1185 | 0 | } |
1186 | 0 | break; |
1187 | 0 | case GF_SG_FIELD_CODING_ALL: |
1188 | 0 | if (proto_field->ALL_index == index) { |
1189 | 0 | *all_index = proto_field->ALL_index; |
1190 | 0 | return GF_OK; |
1191 | 0 | } |
1192 | 0 | break; |
1193 | | /*BIFS-ANIM not supported*/ |
1194 | 0 | case GF_SG_FIELD_CODING_DYN: |
1195 | 0 | default: |
1196 | 0 | return GF_BAD_PARAM; |
1197 | 0 | } |
1198 | 0 | } |
1199 | 0 | return GF_BAD_PARAM; |
1200 | 0 | } |
1201 | | |
1202 | | GF_EXPORT |
1203 | | u32 gf_sg_proto_get_field_count(GF_Proto *proto) |
1204 | 0 | { |
1205 | 0 | if (!proto) return 0; |
1206 | 0 | return gf_list_count(proto->proto_fields); |
1207 | 0 | } |
1208 | | |
1209 | | GF_EXPORT |
1210 | | GF_ProtoFieldInterface *gf_sg_proto_field_find(GF_Proto *proto, u32 fieldIndex) |
1211 | 0 | { |
1212 | 0 | if (!proto) return NULL; |
1213 | 0 | return (GF_ProtoFieldInterface*)gf_list_get(proto->proto_fields, fieldIndex); |
1214 | 0 | } |
1215 | | |
1216 | | void gf_sg_proto_propagate_event(GF_Node *node, u32 fieldIndex, GF_Node *from_node) |
1217 | 0 | { |
1218 | 0 | u32 i; |
1219 | 0 | GF_Route *r; |
1220 | 0 | if (!node) return; |
1221 | | /*propagation only for proto*/ |
1222 | 0 | if (node->sgprivate->tag != TAG_ProtoNode) return; |
1223 | | /*with ISed fields*/ |
1224 | 0 | if (!node->sgprivate->interact || !node->sgprivate->interact->routes) return; |
1225 | | /*we only need to propagate ISed for eventIn/exposedField. This means that if the event comes from |
1226 | | the same scene graph as the proto (eg from the proto code) we don't propagate the event*/ |
1227 | 0 | if (from_node->sgprivate->scenegraph == node->sgprivate->scenegraph) return; |
1228 | | |
1229 | | /*for all ISed routes*/ |
1230 | 0 | i=0; |
1231 | 0 | while ((r = (GF_Route*)gf_list_enum(node->sgprivate->interact->routes, &i))) { |
1232 | 0 | if (!r->IS_route) continue; |
1233 | | /*connecting from this node && field to a destination node other than the event source (this will break loops due to exposedFields)*/ |
1234 | 0 | if ((r->FromNode == node) && (r->FromField.fieldIndex == fieldIndex) && (r->ToNode != from_node) ) { |
1235 | 0 | if (gf_sg_route_activate(r)) |
1236 | 0 | gf_node_changed(r->ToNode, &r->ToField); |
1237 | 0 | } |
1238 | 0 | } |
1239 | 0 | } |
1240 | | |
1241 | | |
1242 | | Bool gf_sg_proto_get_aq_info(GF_Node *Node, u32 FieldIndex, u8 *QType, u8 *AType, Fixed *b_min, Fixed *b_max, u32 *QT13_bits) |
1243 | 0 | { |
1244 | 0 | GF_Proto *proto; |
1245 | 0 | u32 i; |
1246 | 0 | GF_ProtoFieldInterface *proto_field; |
1247 | |
|
1248 | 0 | proto = ((GF_ProtoInstance *)Node)->proto_interface; |
1249 | |
|
1250 | 0 | i=0; |
1251 | 0 | while ((proto_field = (GF_ProtoFieldInterface*)gf_list_enum(proto->proto_fields, &i))) { |
1252 | 0 | if (proto_field->ALL_index!=FieldIndex) continue; |
1253 | | |
1254 | 0 | *QType = proto_field->QP_Type; |
1255 | 0 | *AType = proto_field->Anim_Type; |
1256 | 0 | *b_min = FIX_MIN; |
1257 | 0 | *b_max = FIX_MAX; |
1258 | |
|
1259 | 0 | if (proto_field->hasMinMax) { |
1260 | | |
1261 | | /*translate our bounds*/ |
1262 | 0 | switch (gf_sg_vrml_get_sf_type(proto_field->FieldType)) { |
1263 | 0 | case GF_SG_VRML_SFINT32: |
1264 | 0 | *b_min = (SFFloat) * ( (SFInt32 *) proto_field->qp_min_value); |
1265 | 0 | *b_max = (SFFloat) * ( (SFInt32 *) proto_field->qp_max_value); |
1266 | 0 | break; |
1267 | | /*TO DO EVERYWHERE: check on field translation from double to float |
1268 | | during quant bounds*/ |
1269 | 0 | case GF_SG_VRML_SFTIME: |
1270 | 0 | *b_min = (SFFloat) * ( (SFTime *) proto_field->qp_min_value); |
1271 | 0 | *b_max = (SFFloat) * ( (SFTime *) proto_field->qp_max_value); |
1272 | 0 | break; |
1273 | 0 | default: |
1274 | 0 | if (proto_field->qp_min_value) |
1275 | 0 | *b_min = (SFFloat) * ( (SFFloat *) proto_field->qp_min_value); |
1276 | 0 | if (proto_field->qp_max_value) |
1277 | 0 | *b_max = (SFFloat) * ( (SFFloat *) proto_field->qp_max_value); |
1278 | 0 | break; |
1279 | 0 | } |
1280 | |
|
1281 | 0 | } |
1282 | 0 | *QT13_bits = proto_field->NumBits; |
1283 | 0 | return 1; |
1284 | 0 | } |
1285 | 0 | return 0; |
1286 | 0 | } |
1287 | | |
1288 | | |
1289 | | GF_EXPORT |
1290 | | GF_Proto *gf_node_get_proto(GF_Node *node) |
1291 | 0 | { |
1292 | 0 | GF_ProtoInstance *inst; |
1293 | 0 | if (node->sgprivate->tag != TAG_ProtoNode) return NULL; |
1294 | 0 | inst = (GF_ProtoInstance *) node; |
1295 | 0 | return inst->proto_interface; |
1296 | 0 | } |
1297 | | |
1298 | | /*returns the ID of the proto*/ |
1299 | | GF_EXPORT |
1300 | | u32 gf_sg_proto_get_id(GF_Proto *proto) |
1301 | 0 | { |
1302 | 0 | return proto ? proto->ID : 0; |
1303 | 0 | } |
1304 | | |
1305 | | GF_EXPORT |
1306 | | const char *gf_sg_proto_get_class_name(GF_Proto *proto) |
1307 | 0 | { |
1308 | 0 | return (const char *) proto->Name; |
1309 | 0 | } |
1310 | | |
1311 | | u32 gf_sg_proto_get_root_tag(GF_Proto *proto) |
1312 | 0 | { |
1313 | 0 | GF_Node *n; |
1314 | 0 | GF_Proto *orig = proto; |
1315 | 0 | if (!proto) return TAG_UndefinedNode; |
1316 | 0 | n = (GF_Node*)gf_list_get(proto->node_code, 0); |
1317 | 0 | if (!n) return TAG_UndefinedNode; |
1318 | 0 | if (n->sgprivate->tag == TAG_ProtoNode) { |
1319 | 0 | GF_Proto *next = ((GF_ProtoInstance *)n)->proto_interface; |
1320 | 0 | if (!next || next == orig) return TAG_UndefinedNode; |
1321 | 0 | return gf_sg_proto_get_root_tag(next); |
1322 | 0 | } |
1323 | 0 | return n->sgprivate->tag; |
1324 | 0 | } |
1325 | | |
1326 | | static Bool gf_sg_proto_field_is_sftime_offset_internal(GF_Node *node, GF_FieldInfo *field, u32 depth) |
1327 | 0 | { |
1328 | 0 | u32 i; |
1329 | 0 | GF_Route *r; |
1330 | 0 | GF_ProtoInstance *inst; |
1331 | 0 | GF_FieldInfo inf; |
1332 | 0 | if (node->sgprivate->tag != TAG_ProtoNode) return 0; |
1333 | 0 | if (field->fieldType != GF_SG_VRML_SFTIME) return 0; |
1334 | 0 | if (depth > 255) return 0; |
1335 | | |
1336 | 0 | inst = (GF_ProtoInstance *) node; |
1337 | | /*check in interface if this is ISed */ |
1338 | 0 | i=0; |
1339 | 0 | while ((r = (GF_Route*)gf_list_enum(inst->proto_interface->sub_graph->Routes, &i))) { |
1340 | 0 | if (!r->IS_route) continue; |
1341 | | /*only check eventIn/field/exposedField*/ |
1342 | 0 | if (r->FromNode || (r->FromField.fieldIndex != field->fieldIndex)) continue; |
1343 | | |
1344 | 0 | gf_node_get_field(r->ToNode, r->ToField.fieldIndex, &inf); |
1345 | | /*IS to another proto*/ |
1346 | 0 | if (r->ToNode->sgprivate->tag == TAG_ProtoNode) { |
1347 | 0 | if (r->ToNode==node) continue; |
1348 | 0 | return gf_sg_proto_field_is_sftime_offset_internal(r->ToNode, &inf, depth+1); |
1349 | 0 | } |
1350 | | /*IS to a startTime/stopTime field*/ |
1351 | 0 | if (!stricmp(inf.name, "startTime") || !stricmp(inf.name, "stopTime")) return 1; |
1352 | 0 | } |
1353 | 0 | return 0; |
1354 | 0 | } |
1355 | | |
1356 | | GF_EXPORT |
1357 | | Bool gf_sg_proto_field_is_sftime_offset(GF_Node *node, GF_FieldInfo *field) |
1358 | 0 | { |
1359 | 0 | return gf_sg_proto_field_is_sftime_offset_internal(node, field, 0); |
1360 | 0 | } |
1361 | | |
1362 | | GF_EXPORT |
1363 | | GF_Err gf_node_proto_set_grouping(GF_Node *node) |
1364 | 0 | { |
1365 | 0 | if (!node || (node->sgprivate->tag!=TAG_ProtoNode)) return GF_BAD_PARAM; |
1366 | 0 | ((GF_ProtoInstance *)node)->flags |= GF_SG_PROTO_IS_GROUPING; |
1367 | 0 | return GF_OK; |
1368 | 0 | } |
1369 | | |
1370 | | GF_EXPORT |
1371 | | Bool gf_node_proto_is_grouping(GF_Node *node) |
1372 | 0 | { |
1373 | 0 | if (!node || (node->sgprivate->tag!=TAG_ProtoNode)) return 0; |
1374 | 0 | if ( ((GF_ProtoInstance *)node)->flags & GF_SG_PROTO_IS_GROUPING) return 1; |
1375 | 0 | return 0; |
1376 | 0 | } |
1377 | | |
1378 | | GF_EXPORT |
1379 | | GF_Node *gf_node_get_proto_root(GF_Node *node) |
1380 | 0 | { |
1381 | 0 | if (!node || (node->sgprivate->tag!=TAG_ProtoNode)) return NULL; |
1382 | 0 | return ((GF_ProtoInstance *)node)->RenderingNode; |
1383 | 0 | } |
1384 | | |
1385 | | #if 0 //unused |
1386 | | GF_Node *gf_node_get_proto_parent(GF_Node *node) |
1387 | | { |
1388 | | if (!node) return NULL; |
1389 | | if (node->sgprivate->scenegraph->pOwningProto) { |
1390 | | GF_Node *the_node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto; |
1391 | | if (the_node != node) return the_node; |
1392 | | } |
1393 | | return NULL; |
1394 | | } |
1395 | | |
1396 | | Bool gf_node_is_proto_root(GF_Node *node) |
1397 | | { |
1398 | | if (!node) return 0; |
1399 | | if (!node->sgprivate->scenegraph->pOwningProto) return 0; |
1400 | | |
1401 | | if (gf_list_find(node->sgprivate->scenegraph->pOwningProto->node_code, node)>=0) return 1; |
1402 | | return 0; |
1403 | | } |
1404 | | #endif |
1405 | | |
1406 | | |
1407 | | GF_EXPORT |
1408 | | GF_Err gf_node_set_proto_eventin_handler(GF_Node *node, u32 fieldIndex, void (*event_in_cbk)(GF_Node *pThis, struct _route *route) ) |
1409 | 0 | { |
1410 | 0 | GF_ProtoInstance *inst; |
1411 | 0 | GF_ProtoField *field; |
1412 | 0 | if (!node || (node->sgprivate->tag!=TAG_ProtoNode)) return GF_BAD_PARAM; |
1413 | | |
1414 | 0 | inst = (GF_ProtoInstance *) node; |
1415 | 0 | field = (GF_ProtoField*)gf_list_get(inst->fields, fieldIndex); |
1416 | 0 | if (!field) return GF_BAD_PARAM; |
1417 | | |
1418 | 0 | if (field->EventType!=GF_SG_EVENT_IN) return GF_BAD_PARAM; |
1419 | 0 | field->on_event_in = event_in_cbk; |
1420 | 0 | return GF_OK; |
1421 | 0 | } |
1422 | | |
1423 | | |
1424 | | |
1425 | | #endif /*GPAC_DISABLE_VRML*/ |