Coverage Report

Created: 2026-10-26 08:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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*/