Coverage Report

Created: 2026-06-08 06:18

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