📄 base_scenegraph.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / Scene Graph sub-project * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/internal/scenegraph_dev.h>/*svg proto*/#include <gpac/scenegraph_svg.h>/*MPEG4 tags (for internal nodes)*/#include <gpac/nodes_mpeg4.h>/*X3D tags (for internal nodes)*/#include <gpac/nodes_x3d.h>static void ReplaceDEFNode(GF_Node *FromNode, GF_Node *node, GF_Node *newNode, Bool updateOrderedGroup);#ifndef GPAC_DISABLE_SVGstatic void ReplaceIRINode(GF_Node *FromNode, GF_Node *oldNode, GF_Node *newNode);#endifGF_SceneGraph *gf_sg_new(){ GF_SceneGraph *tmp; GF_SAFEALLOC(tmp, GF_SceneGraph); if (!tmp) return NULL; tmp->protos = gf_list_new(); tmp->unregistered_protos = gf_list_new(); tmp->Routes = gf_list_new(); tmp->routes_to_activate = gf_list_new(); tmp->routes_to_destroy = gf_list_new();#ifndef GPAC_DISABLE_SVG tmp->xlink_hrefs = gf_list_new(); tmp->smil_timed_elements = gf_list_new();#endif#ifdef GPAC_HAS_SPIDERMONKEY tmp->scripts = gf_list_new(); tmp->objects = gf_list_new(); tmp->listeners_to_add = gf_list_new();#endif return tmp;}GF_EXPORTGF_SceneGraph *gf_sg_new_subscene(GF_SceneGraph *scene){ GF_SceneGraph *tmp; if (!scene) return NULL; tmp = gf_sg_new(); if (!tmp) return NULL; tmp->parent_scene = scene; tmp->script_action = scene->script_action; tmp->script_action_cbck = scene->script_action_cbck; tmp->script_load = scene->script_load; /*by default use the same callbacks*/ tmp->userpriv = scene->userpriv; tmp->GetSceneTime = scene->GetSceneTime; tmp->GetExternProtoLib = scene->GetExternProtoLib; tmp->NodeCallback = scene->NodeCallback; return tmp;}GF_EXPORTvoid gf_sg_set_node_callback(GF_SceneGraph *sg, void (*NodeCallback)(void *user_priv, u32 type, GF_Node *node, void *ctxdata) ){ sg->NodeCallback = NodeCallback;}GF_EXPORTvoid gf_sg_set_scene_time_callback(GF_SceneGraph *sg, Double (*GetSceneTime)(void *user_priv)){ sg->GetSceneTime = GetSceneTime;}GF_EXPORTDouble gf_node_get_scene_time(GF_Node *node){ if (!node || !node->sgprivate->scenegraph->GetSceneTime) return 0.0; return node->sgprivate->scenegraph->GetSceneTime(node->sgprivate->scenegraph->userpriv);}GF_EXPORTvoid gf_sg_del(GF_SceneGraph *sg){ if (!sg) return; gf_sg_reset(sg);#ifndef GPAC_DISABLE_SVG gf_list_del(sg->xlink_hrefs); gf_list_del(sg->smil_timed_elements); gf_list_del(sg->listeners_to_add);#endif#ifdef GPAC_HAS_SPIDERMONKEY gf_list_del(sg->scripts); gf_list_del(sg->objects);#endif gf_list_del(sg->Routes); gf_list_del(sg->protos); gf_list_del(sg->unregistered_protos); gf_list_del(sg->routes_to_activate); gf_list_del(sg->routes_to_destroy); free(sg);}/*recursive traverse of the whole graph to check for scope mixes (nodes from an inline graphinserted in a parent graph through bind or routes). We must do this otherwise we're certain to get randomcrashes or mem leaks.*/void SG_GraphRemoved(GF_Node *node, GF_SceneGraph *sg){ u32 i, count; GF_FieldInfo info; u32 tag; tag = node->sgprivate->tag; count = gf_node_get_field_count(node);#ifndef GPAC_DISABLE_SVG if (((tag>= GF_NODE_RANGE_FIRST_SVG) && (tag<= GF_NODE_RANGE_LAST_SVG)) #ifdef GPAC_ENABLE_SVG_SA || ((tag>= GF_NODE_RANGE_FIRST_SVG_SA) && (tag<= GF_NODE_RANGE_LAST_SVG_SA)) #endif#ifdef GPAC_ENABLE_SVG_SANI || ((tag>= GF_NODE_RANGE_FIRST_SVG_SANI) && (tag<= GF_NODE_RANGE_LAST_SVG_SANI))#endif ) { /* TODO */ tag = 0; } else #endif { for (i=0; i<count; i++) { gf_node_get_field(node, i, &info); if (info.fieldType==GF_SG_VRML_SFNODE) { GF_Node *n = *(GF_Node **) info.far_ptr; if (n) { if (n->sgprivate->scenegraph==sg) { /*if root of graph, skip*/ if (sg->RootNode!=n) { gf_node_unregister(n, node); /*don't forget to remove node...*/ *(GF_Node **) info.far_ptr = NULL; } } else { SG_GraphRemoved(n, sg); } } } else if (info.fieldType==GF_SG_VRML_MFNODE) { GF_ChildNodeItem *cur, *prev, *list = *(GF_ChildNodeItem **) info.far_ptr; prev = NULL; while (list) { if (list->node->sgprivate->scenegraph==sg) { gf_node_unregister(list->node, node); if (prev) prev->next = list->next; else *(GF_ChildNodeItem **) info.far_ptr = list->next; cur = list; free(cur); } else { SG_GraphRemoved(list->node, sg); } list = list->next; } } } }}GFINLINE GF_Node *SG_SearchForNode(GF_SceneGraph *sg, GF_Node *node){ NodeIDedItem *reg_node = sg->id_node; while (reg_node) { if (reg_node->node == node) return reg_node->node; reg_node = reg_node->next; } return NULL;}static GFINLINE u32 get_num_id_nodes(GF_SceneGraph *sg){ u32 count = 0; NodeIDedItem *reg_node = sg->id_node; while (reg_node) { count++; reg_node = reg_node->next; } return count;}GF_EXPORTvoid gf_sg_reset(GF_SceneGraph *sg){ u32 type, count; NodeIDedItem *reg_node; if (!sg) return; /*inlined graph, remove any of this graph nodes from the parent graph*/ if (!sg->pOwningProto && sg->parent_scene) { GF_SceneGraph *par = sg->parent_scene; while (par->parent_scene) par = par->parent_scene; if (par->RootNode) SG_GraphRemoved(par->RootNode, sg); }#ifdef GPAC_HAS_SPIDERMONKEY /*scripts are the first source of cylic references in the graph. In order to clean properly force a remove of all script nodes, this will release all references to nodes in JS*/ while (gf_list_count(sg->scripts)) { GF_Node *n = gf_list_get(sg->scripts, 0); gf_list_rem(sg->scripts, 0); /*prevent destroy*/ gf_node_register(n, NULL); /*remove from all parents*/ gf_node_replace(n, NULL, 0); /*FORCE destroy in case the script refers to itself*/ n->sgprivate->num_instances=1; gf_node_unregister(n, NULL); }#endif#ifndef GPAC_DISABLE_SVG /*flush any pending add_listener*/ gf_dom_listener_process_add(sg);#endif if (sg->RootNode) gf_node_unregister(sg->RootNode, NULL); sg->RootNode = NULL; while (gf_list_count(sg->routes_to_activate)) { gf_list_rem(sg->routes_to_activate, 0); } /*destroy all routes*/ while (gf_list_count(sg->Routes)) { GF_Route *r = (GF_Route*)gf_list_get(sg->Routes, 0); /*this will unregister the route from the graph, so don't delete the chain entry*/ gf_sg_route_del(r); } /*WATCHOUT: we may have cyclic dependencies due to 1- a node referencing itself (forbidden in VRML) 2- nodes refered to in commands of conditionals children of this node (MPEG-4 is mute about that) we recursively preocess from last declared DEF node to first one */restart: reg_node = sg->id_node; while (reg_node) { Bool ignore = 0; GF_Node *node = reg_node->node; if (!node) { reg_node = reg_node->next; continue; } /*first replace all instances in parents by NULL WITHOUT UNREGISTERING (to avoid destroying the node). This will take care of nodes referencing themselves*/ { GF_ParentList *nlist = node->sgprivate->parents; type = node->sgprivate->tag;#ifndef GPAC_DISABLE_SVG if (((type>= GF_NODE_RANGE_FIRST_SVG) && (type<= GF_NODE_RANGE_LAST_SVG)) #ifdef GPAC_ENABLE_SVG_SA || ((type>= GF_NODE_RANGE_FIRST_SVG_SA) && (type<= GF_NODE_RANGE_LAST_SVG_SA)) #endif#ifdef GPAC_ENABLE_SVG_SANI || ((type>= GF_NODE_RANGE_FIRST_SVG_SANI) && (type<= GF_NODE_RANGE_LAST_SVG_SANI))#endif ) type = 1; else #endif type = 0; while (nlist) { GF_ParentList *next = nlist->next;#if 0 /*parent is a DEF'ed node, try to clean-up properly?*/ if ((nlist->node!=node) && SG_SearchForNode(sg, nlist->node) != NULL) { ignore = 1; break; }#endif#ifndef GPAC_DISABLE_SVG if (type) { ReplaceIRINode(nlist->node, node, NULL); } else #endif ReplaceDEFNode(nlist->node, reg_node->node, NULL, 0); free(nlist); nlist = next; } if (ignore) { node->sgprivate->parents = nlist; continue; } node->sgprivate->parents = NULL; } //sg->node_registry[i-1] = NULL; count = get_num_id_nodes(sg); node->sgprivate->num_instances = 1; gf_node_unregister(node, NULL); if (count != get_num_id_nodes(sg)) goto restart; reg_node = reg_node->next; } assert(sg->id_node==NULL); /*destroy all proto*/ while (gf_list_count(sg->protos)) { GF_Proto *p = (GF_Proto *)gf_list_get(sg->protos, 0); /*this will unregister the proto from the graph, so don't delete the chain entry*/ gf_sg_proto_del(p); } /*destroy all unregistered proto*/ while (gf_list_count(sg->unregistered_protos)) { GF_Proto *p = (GF_Proto *)gf_list_get(sg->unregistered_protos, 0); /*this will unregister the proto from the graph, so don't delete the chain entry*/ gf_sg_proto_del(p); }#ifndef GPAC_DISABLE_SVG assert(gf_list_count(sg->xlink_hrefs) == 0);#endif /*last destroy all routes*/ gf_sg_destroy_routes(sg); sg->simulation_tick = 0;#ifdef GF_SELF_REPLACE_ENABLE sg->graph_has_been_reset = 1;#endif}GFINLINE GF_Node *SG_SearchForDuplicateNodeID(GF_SceneGraph *sg, u32 nodeID, GF_Node *toExclude){ NodeIDedItem *reg_node = sg->id_node; while (reg_node) { if ((reg_node->node != toExclude) && (reg_node->NodeID == nodeID)) return reg_node->node; reg_node = reg_node->next; } return NULL;}void *gf_node_get_name_address(GF_Node*node){ NodeIDedItem *reg_node; if (!(node->sgprivate->flags & GF_NODE_IS_DEF)) return NULL; reg_node = node->sgprivate->scenegraph->id_node; while (reg_node) { if (reg_node->node == node) return ®_node->NodeName; reg_node = reg_node->next; } return NULL;}void gf_sg_set_private(GF_SceneGraph *sg, void *ptr){ if (sg) sg->userpriv = ptr;}void *gf_sg_get_private(GF_SceneGraph *sg){ return sg ? sg->userpriv : NULL;}GF_EXPORTvoid gf_sg_set_scene_size_info(GF_SceneGraph *sg, u32 width, u32 height, Bool usePixelMetrics){ if (!sg) return; if (width && height) { sg->width = width; sg->height = height; } else { sg->width = sg->height = 0; } sg->usePixelMetrics = usePixelMetrics;}GF_EXPORTBool gf_sg_use_pixel_metrics(GF_SceneGraph *sg){ if (sg) { while (sg->pOwningProto) sg = sg->parent_scene; return sg->usePixelMetrics; } return 0;}GF_EXPORTBool gf_sg_get_scene_size_info(GF_SceneGraph *sg, u32 *width, u32 *height){ if (!sg) return 0; *width = sg->width; *height = sg->height; return (sg->width && sg->height) ? 1 : 0;}GF_EXPORTGF_Node *gf_sg_get_root_node(GF_SceneGraph *sg){ return sg ? sg->RootNode : NULL;}GF_EXPORTvoid gf_sg_set_root_node(GF_SceneGraph *sg, GF_Node *node){ if (sg) sg->RootNode = node;}GFINLINE void remove_node_id(GF_SceneGraph *sg, GF_Node *node){ NodeIDedItem *reg_node = sg->id_node; if (reg_node && (reg_node->node==node)) { sg->id_node = reg_node->next; if (sg->id_node_last==reg_node) sg->id_node_last = reg_node->next; if (reg_node->NodeName) free(reg_node->NodeName); free(reg_node); } else { NodeIDedItem *to_del; while (reg_node->next) { if (reg_node->next->node!=node) { reg_node = reg_node->next; continue; } to_del = reg_node->next; reg_node->next = to_del->next; if (sg->id_node_last==to_del) { sg->id_node_last = reg_node->next ? reg_node->next : reg_node; } if (to_del->NodeName) free(to_del->NodeName); free(to_del); break; } }}GF_EXPORTGF_Err gf_node_unregister(GF_Node *pNode, GF_Node *parentNode){ u32 j; GF_SceneGraph *pSG; GF_Route *r; if (!pNode) return GF_OK; pSG = pNode->sgprivate->scenegraph; /*if this is a proto its is registered in its parent graph, not the current*/ if (pSG && (pNode == (GF_Node*)pSG->pOwningProto)) pSG = pSG->parent_scene; if (parentNode) { GF_ParentList *nlist = pNode->sgprivate->parents; if (nlist) { GF_ParentList *prev = NULL; while (nlist) { if (nlist->node != parentNode) { prev = nlist; nlist = nlist->next; continue; } if (prev) prev->next = nlist->next; else pNode->sgprivate->parents = nlist->next; free(nlist); break; } } } /*unregister the instance*/ assert(pNode->sgprivate->num_instances); pNode->sgprivate->num_instances -= 1; /*this is just an instance removed*/ if (pNode->sgprivate->num_instances) { return GF_OK; } assert(pNode->sgprivate->parents==NULL); if (pSG) { /*if def, remove from sg def table*/ if (pNode->sgprivate->flags & GF_NODE_IS_DEF) { remove_node_id(pSG, pNode); } /*check all routes from or to this node and destroy them - cf spec*/ j=0; while ((r = (GF_Route *)gf_list_enum(pSG->Routes, &j))) { if ( (r->ToNode == pNode) || (r->FromNode == pNode)) { gf_sg_route_del(r); j--; } } } if (pNode->sgprivate->scenegraph && (pNode->sgprivate->scenegraph->RootNode==pNode)) pNode->sgprivate->scenegraph->RootNode = NULL; /*delete the node*/ gf_node_del(pNode); return GF_OK;}GF_EXPORTGF_Err gf_node_register(GF_Node *node, GF_Node *parentNode){ GF_SceneGraph *pSG; if (!node) return GF_OK; pSG = node->sgprivate->scenegraph; /*if this is a proto register to the parent graph, not the current*/ if (pSG && (node == (GF_Node*)pSG->pOwningProto)) pSG = pSG->parent_scene; node->sgprivate->num_instances ++; /*parent may be NULL (top node and proto)*/ if (parentNode) { if (!node->sgprivate->parents) { node->sgprivate->parents = (GF_ParentList*)malloc(sizeof(GF_ParentList));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -