d8223ffa75
That year should bring the long-awaited OpenGL ES 3.0 compatible renderer
with state-of-the-art rendering techniques tuned to work as low as middle
end handheld devices - without compromising with the possibilities given
for higher end desktop games of course. Great times ahead for the Godot
community and the gamers that will play our games!
(cherry picked from commit c7bc44d5ad
)
1810 lines
46 KiB
C++
1810 lines
46 KiB
C++
/*************************************************************************/
|
|
/* packed_scene.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* http://www.godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
#include "packed_scene.h"
|
|
#include "globals.h"
|
|
#include "io/resource_loader.h"
|
|
#include "scene/3d/spatial.h"
|
|
#include "scene/gui/control.h"
|
|
#include "scene/2d/node_2d.h"
|
|
#include "scene/main/instance_placeholder.h"
|
|
#include "core/core_string_names.h"
|
|
#define PACK_VERSION 2
|
|
|
|
bool SceneState::can_instance() const {
|
|
|
|
return nodes.size()>0;
|
|
}
|
|
|
|
|
|
Node *SceneState::instance(bool p_gen_edit_state) const {
|
|
|
|
// nodes where instancing failed (because something is missing)
|
|
List<Node*> stray_instances;
|
|
|
|
#define NODE_FROM_ID(p_name,p_id)\
|
|
Node *p_name;\
|
|
if (p_id&FLAG_ID_IS_PATH) {\
|
|
NodePath np=node_paths[p_id&FLAG_MASK];\
|
|
p_name=ret_nodes[0]->_get_node(np);\
|
|
} else {\
|
|
ERR_FAIL_INDEX_V(p_id&FLAG_MASK,nc,NULL);\
|
|
p_name=ret_nodes[p_id&FLAG_MASK];\
|
|
}
|
|
|
|
int nc = nodes.size();
|
|
ERR_FAIL_COND_V(nc==0,NULL);
|
|
|
|
const StringName*snames=NULL;
|
|
int sname_count=names.size();
|
|
if (sname_count)
|
|
snames=&names[0];
|
|
|
|
const Variant*props=NULL;
|
|
int prop_count=variants.size();
|
|
if (prop_count)
|
|
props=&variants[0];
|
|
|
|
//Vector<Variant> properties;
|
|
|
|
const NodeData *nd = &nodes[0];
|
|
|
|
Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc );
|
|
|
|
bool gen_node_path_cache=p_gen_edit_state && node_path_cache.empty();
|
|
|
|
for(int i=0;i<nc;i++) {
|
|
|
|
const NodeData &n=nd[i];
|
|
|
|
Node *parent=NULL;
|
|
|
|
if (i>0) {
|
|
|
|
NODE_FROM_ID(nparent,n.parent);
|
|
#ifdef DEBUG_ENABLED
|
|
if (!nparent && n.parent&FLAG_ID_IS_PATH) {
|
|
|
|
WARN_PRINT(String("Parent path '"+String(node_paths[n.parent&FLAG_MASK])+"' for node '"+String(snames[n.name])+"' has vanished when instancing: '"+get_path()+"'.").ascii().get_data());
|
|
|
|
}
|
|
#endif
|
|
parent=nparent;
|
|
}
|
|
|
|
Node *node=NULL;
|
|
|
|
|
|
if (i==0 && base_scene_idx>=0) {
|
|
//scene inheritance on root node
|
|
//print_line("scene inherit");
|
|
Ref<PackedScene> sdata = props[ base_scene_idx ];
|
|
ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
|
|
node = sdata->instance(p_gen_edit_state);
|
|
ERR_FAIL_COND_V(!node,NULL);
|
|
if (p_gen_edit_state) {
|
|
node->set_scene_inherited_state(sdata->get_state());
|
|
}
|
|
|
|
} else if (n.instance>=0) {
|
|
//instance a scene into this node
|
|
//print_line("instance");
|
|
if (n.instance&FLAG_INSTANCE_IS_PLACEHOLDER) {
|
|
|
|
String path = props[n.instance&FLAG_MASK];
|
|
if (disable_placeholders) {
|
|
|
|
Ref<PackedScene> sdata = ResourceLoader::load(path,"PackedScene");
|
|
ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
|
|
node = sdata->instance(p_gen_edit_state);
|
|
ERR_FAIL_COND_V(!node,NULL);
|
|
} else {
|
|
InstancePlaceholder *ip = memnew( InstancePlaceholder );
|
|
ip->set_instance_path(path);
|
|
node=ip;
|
|
}
|
|
node->set_scene_instance_load_placeholder(true);
|
|
} else {
|
|
Ref<PackedScene> sdata = props[ n.instance&FLAG_MASK ];
|
|
ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
|
|
node = sdata->instance(p_gen_edit_state);
|
|
ERR_FAIL_COND_V(!node,NULL);
|
|
|
|
}
|
|
|
|
} else if (n.type==TYPE_INSTANCED) {
|
|
//print_line("instanced");
|
|
//get the node from somewhere, it likely already exists from another instance
|
|
if (parent) {
|
|
node=parent->_get_child_by_name(snames[n.name]);
|
|
#ifdef DEBUG_ENABLED
|
|
if (!node) {
|
|
WARN_PRINT(String("Node '"+String(ret_nodes[0]->get_path_to(parent))+"/"+String(snames[n.name])+"' was modified from inside a instance, but it has vanished.").ascii().get_data());
|
|
}
|
|
#endif
|
|
}
|
|
} else if (ObjectTypeDB::is_type_enabled(snames[n.type])) {
|
|
//print_line("created");
|
|
//node belongs to this scene and must be created
|
|
Object * obj = ObjectTypeDB::instance(snames[ n.type ]);
|
|
if (!obj || !obj->cast_to<Node>()) {
|
|
if (obj) {
|
|
memdelete(obj);
|
|
obj=NULL;
|
|
}
|
|
WARN_PRINT(String("Warning node of type "+snames[n.type].operator String()+" does not exist.").ascii().get_data());
|
|
if (n.parent>=0 && n.parent<nc && ret_nodes[n.parent]) {
|
|
if (ret_nodes[n.parent]->cast_to<Spatial>()) {
|
|
obj = memnew( Spatial );
|
|
} else if (ret_nodes[n.parent]->cast_to<Control>()) {
|
|
obj = memnew( Control );
|
|
} else if (ret_nodes[n.parent]->cast_to<Node2D>()) {
|
|
obj = memnew( Node2D );
|
|
}
|
|
|
|
}
|
|
if (!obj) {
|
|
obj = memnew( Node );
|
|
}
|
|
}
|
|
|
|
node = obj->cast_to<Node>();
|
|
|
|
}
|
|
|
|
|
|
if (node) {
|
|
// may not have found the node (part of instanced scene and removed)
|
|
// if found all is good, otherwise ignore
|
|
|
|
//properties
|
|
int nprop_count=n.properties.size();
|
|
if (nprop_count) {
|
|
|
|
const NodeData::Property* nprops=&n.properties[0];
|
|
|
|
for(int j=0;j<nprop_count;j++) {
|
|
|
|
bool valid;
|
|
ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL );
|
|
ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL );
|
|
|
|
if (snames[ nprops[j].name ]==CoreStringNames::get_singleton()->_script) {
|
|
//work around to avoid old script variables from disappearing, should be the proper fix to:
|
|
//https://github.com/godotengine/godot/issues/2958
|
|
|
|
//store old state
|
|
List<Pair<StringName,Variant> > old_state;
|
|
if (node->get_script_instance()) {
|
|
node->get_script_instance()->get_property_state(old_state);
|
|
}
|
|
|
|
node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
|
|
|
|
//restore old state for new script, if exists
|
|
for (List<Pair<StringName,Variant> >::Element *E=old_state.front();E;E=E->next()) {
|
|
node->set(E->get().first,E->get().second);
|
|
}
|
|
} else {
|
|
|
|
node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
|
|
}
|
|
}
|
|
}
|
|
|
|
//name
|
|
|
|
//groups
|
|
for(int j=0;j<n.groups.size();j++) {
|
|
|
|
ERR_FAIL_INDEX_V( n.groups[j], sname_count, NULL );
|
|
node->add_to_group( snames[ n.groups[j] ], true );
|
|
}
|
|
|
|
if (n.instance>=0 || n.type!=TYPE_INSTANCED || i==0) {
|
|
//if node was not part of instance, must set it's name, parenthood and ownership
|
|
if (i>0) {
|
|
if (parent) {
|
|
parent->_add_child_nocheck(node,snames[n.name]);
|
|
} else {
|
|
//it may be possible that an instanced scene has changed
|
|
//and the node has nowhere to go anymore
|
|
stray_instances.push_back(node); //can't be added, go to stray list
|
|
}
|
|
} else {
|
|
node->_set_name_nocheck( snames[ n.name ] );
|
|
}
|
|
}
|
|
|
|
if (n.owner>=0) {
|
|
|
|
NODE_FROM_ID(owner,n.owner);
|
|
if (owner)
|
|
node->_set_owner_nocheck(owner);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ret_nodes[i]=node;
|
|
|
|
if (node && gen_node_path_cache && ret_nodes[0]) {
|
|
NodePath n = ret_nodes[0]->get_path_to(node);
|
|
node_path_cache[n]=i;
|
|
}
|
|
}
|
|
|
|
|
|
//do connections
|
|
|
|
int cc = connections.size();
|
|
const ConnectionData *cdata = connections.ptr();
|
|
|
|
for(int i=0;i<cc;i++) {
|
|
|
|
const ConnectionData &c=cdata[i];
|
|
//ERR_FAIL_INDEX_V( c.from, nc, NULL );
|
|
//ERR_FAIL_INDEX_V( c.to, nc, NULL );
|
|
|
|
NODE_FROM_ID(cfrom,c.from);
|
|
NODE_FROM_ID(cto,c.to);
|
|
|
|
if (!cfrom || !cto)
|
|
continue;
|
|
|
|
Vector<Variant> binds;
|
|
if (c.binds.size()) {
|
|
binds.resize(c.binds.size());
|
|
for(int j=0;j<c.binds.size();j++)
|
|
binds[j]=props[ c.binds[j] ];
|
|
}
|
|
|
|
|
|
cfrom->connect( snames[ c.signal], cto, snames[ c.method], binds,CONNECT_PERSIST|c.flags );
|
|
}
|
|
|
|
//Node *s = ret_nodes[0];
|
|
|
|
//remove nodes that could not be added, likely as a result that
|
|
while(stray_instances.size()) {
|
|
memdelete(stray_instances.front()->get());
|
|
stray_instances.pop_front();;
|
|
}
|
|
|
|
for(int i=0;i<editable_instances.size();i++) {
|
|
Node *ei = ret_nodes[0]->_get_node(editable_instances[i]);
|
|
if (ei) {
|
|
ret_nodes[0]->set_editable_instance(ei,true);
|
|
}
|
|
}
|
|
|
|
return ret_nodes[0];
|
|
|
|
}
|
|
|
|
|
|
static int _nm_get_string(const String& p_string, Map<StringName,int> &name_map) {
|
|
|
|
if (name_map.has(p_string))
|
|
return name_map[p_string];
|
|
|
|
int idx = name_map.size();
|
|
name_map[p_string]=idx;
|
|
return idx;
|
|
}
|
|
|
|
static int _vm_get_variant(const Variant& p_variant, HashMap<Variant,int,VariantHasher> &variant_map) {
|
|
|
|
if (variant_map.has(p_variant))
|
|
return variant_map[p_variant];
|
|
|
|
int idx = variant_map.size();
|
|
variant_map[p_variant]=idx;
|
|
return idx;
|
|
}
|
|
|
|
Error SceneState::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
|
|
|
|
|
|
// this function handles all the work related to properly packing scenes, be it
|
|
// instanced or inherited.
|
|
// given the complexity of this process, an attempt will be made to properly
|
|
// document it. if you fail to understand something, please ask!
|
|
|
|
//discard nodes that do not belong to be processed
|
|
if (p_node!=p_owner && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
|
|
return OK;
|
|
|
|
// save the child instanced scenes that are chosen as editable, so they can be restored
|
|
// upon load back
|
|
if (p_node!=p_owner && p_node->get_filename()!=String() && p_owner->is_editable_instance(p_node))
|
|
editable_instances.push_back(p_owner->get_path_to(p_node));
|
|
|
|
NodeData nd;
|
|
|
|
nd.name=_nm_get_string(p_node->get_name(),name_map);
|
|
nd.instance=-1; //not instanced by default
|
|
|
|
// if this node is part of an instanced scene or sub-instanced scene
|
|
// we need to get the corresponding instance states.
|
|
// with the instance states, we can query for identical properties/groups
|
|
// and only save what has changed
|
|
|
|
List<PackState> pack_state_stack;
|
|
|
|
bool instanced_by_owner=true;
|
|
|
|
{
|
|
Node *n=p_node;
|
|
|
|
while(n) {
|
|
|
|
if (n==p_owner) {
|
|
|
|
Ref<SceneState> state = n->get_scene_inherited_state();
|
|
if (state.is_valid()) {
|
|
int node = state->find_node_by_path(n->get_path_to(p_node));
|
|
if (node>=0) {
|
|
//this one has state for this node, save
|
|
PackState ps;
|
|
ps.node=node;
|
|
ps.state=state;
|
|
pack_state_stack.push_back(ps);
|
|
instanced_by_owner=false;
|
|
}
|
|
}
|
|
|
|
if (p_node->get_filename()!=String() && p_node->get_owner()==p_owner && instanced_by_owner) {
|
|
|
|
if (p_node->get_scene_instance_load_placeholder()) {
|
|
//it's a placeholder, use the placeholder path
|
|
nd.instance=_vm_get_variant(p_node->get_filename(),variant_map);
|
|
nd.instance|=FLAG_INSTANCE_IS_PLACEHOLDER;
|
|
} else {
|
|
//must instance ourselves
|
|
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
|
|
if (!instance.is_valid()) {
|
|
return ERR_CANT_OPEN;
|
|
}
|
|
|
|
nd.instance=_vm_get_variant(instance,variant_map);
|
|
}
|
|
}
|
|
n=NULL;
|
|
} else {
|
|
if (n->get_filename()!=String()) {
|
|
//is an instance
|
|
Ref<SceneState> state = n->get_scene_instance_state();
|
|
if (state.is_valid()) {
|
|
int node = state->find_node_by_path(n->get_path_to(p_node));
|
|
if (node>=0) {
|
|
//this one has state for this node, save
|
|
PackState ps;
|
|
ps.node=node;
|
|
ps.state=state;
|
|
pack_state_stack.push_back(ps);
|
|
}
|
|
}
|
|
|
|
}
|
|
n=n->get_owner();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
|
|
Ref<SceneState> base_scene = p_node->get_scene_inherited_state(); //for inheritance
|
|
Ref<SceneState> instance_state;
|
|
int instance_state_node=-1;
|
|
|
|
if (base_scene.is_valid() && (p_node==p_owner || p_node->get_owner()==p_owner)) {
|
|
//scene inheritance in use, see if this node is actually inherited
|
|
NodePath path = p_owner->get_path_to(p_node);
|
|
instance_state_node = base_scene->find_node_by_path(path);
|
|
if (instance_state_node>=0) {
|
|
instance_state=base_scene;
|
|
}
|
|
}
|
|
|
|
// check that this is a directly instanced scene from the scene being packed, if so
|
|
// this information must be saved. Of course, if using scene instancing and this node
|
|
// does belong to base scene, ignore.
|
|
|
|
if (instance_state.is_null() && p_node!=p_owner && p_node->get_owner()==p_owner && p_node->get_filename()!="") {
|
|
|
|
//instanced, only direct sub-scnes are supported of course
|
|
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
|
|
if (!instance.is_valid()) {
|
|
return ERR_CANT_OPEN;
|
|
}
|
|
|
|
nd.instance=_vm_get_variant(instance,variant_map);
|
|
|
|
} else {
|
|
|
|
nd.instance=-1;
|
|
}
|
|
|
|
// finally, if this does not belong to scene inheritance, check
|
|
// if it belongs to scene instancing
|
|
|
|
if (instance_state.is_null() && p_node!=p_owner) {
|
|
//if not affected by scene inheritance, this may be
|
|
if (p_node->get_owner()==p_owner && p_node->get_filename()!=String()) {
|
|
instance_state=p_node->get_scene_instance_state();
|
|
if (instance_state.is_valid()) {
|
|
instance_state_node=instance_state->find_node_by_path(p_node->get_path_to(p_node));
|
|
}
|
|
|
|
} else if (p_node->get_owner()!=p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
|
|
instance_state=p_node->get_owner()->get_scene_instance_state();
|
|
if (instance_state.is_valid()) {
|
|
instance_state_node=instance_state->find_node_by_path(p_node->get_owner()->get_path_to(p_node));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// all setup, we then proceed to check all properties for the node
|
|
// and save the ones that are worth saving
|
|
|
|
List<PropertyInfo> plist;
|
|
p_node->get_property_list(&plist);
|
|
|
|
for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
|
|
|
|
|
|
if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
|
|
continue;
|
|
}
|
|
|
|
String name = E->get().name;
|
|
Variant value = p_node->get( E->get().name );
|
|
|
|
|
|
bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one());
|
|
|
|
// if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) {
|
|
// continue;
|
|
// }
|
|
|
|
|
|
|
|
//print_line("PASSED!");
|
|
//print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE));
|
|
//print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one()));
|
|
|
|
if (pack_state_stack.size()) {
|
|
// we are on part of an instanced subscene
|
|
// or part of instanced scene.
|
|
// only save what has been changed
|
|
// only save changed properties in instance
|
|
|
|
if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE || E->get().name=="__meta__") {
|
|
//property has requested that no instance state is saved, sorry
|
|
//also, meta won't be overriden or saved
|
|
continue;
|
|
}
|
|
|
|
bool exists=false;
|
|
Variant original;
|
|
|
|
for (List<PackState>::Element *F=pack_state_stack.back();F;F=F->prev()) {
|
|
//check all levels of pack to see if the property exists somewhere
|
|
const PackState &ps=F->get();
|
|
|
|
original = ps.state->get_property_value(ps.node,E->get().name,exists);
|
|
if (exists) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (exists) {
|
|
|
|
//check if already exists and did not change
|
|
if (value.get_type()==Variant::REAL && original.get_type()==Variant::REAL) {
|
|
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
|
|
float a = value;
|
|
float b = original;
|
|
|
|
if (Math::abs(a-b)<CMP_EPSILON)
|
|
continue;
|
|
} else if (bool(Variant::evaluate(Variant::OP_EQUAL,value,original))) {
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!exists && isdefault) {
|
|
//does not exist in original node, but it's the default value
|
|
//so safe to skip too.
|
|
continue;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
if (isdefault) {
|
|
//it's the default value, no point in saving it
|
|
continue;
|
|
}
|
|
}
|
|
|
|
NodeData::Property prop;
|
|
prop.name=_nm_get_string( name,name_map);
|
|
prop.value=_vm_get_variant( value, variant_map);
|
|
nd.properties.push_back(prop);
|
|
|
|
}
|
|
|
|
|
|
// save the groups this node is into
|
|
// discard groups that come from the original scene
|
|
|
|
List<Node::GroupInfo> groups;
|
|
p_node->get_groups(&groups);
|
|
for(List<Node::GroupInfo>::Element *E=groups.front();E;E=E->next()) {
|
|
Node::GroupInfo &gi=E->get();
|
|
|
|
if (!gi.persistent)
|
|
continue;
|
|
// if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name))
|
|
// continue; //group was instanced, don't add here
|
|
|
|
bool skip=false;
|
|
for (List<PackState>::Element *F=pack_state_stack.front();F;F=F->next()) {
|
|
//check all levels of pack to see if the group was added somewhere
|
|
const PackState &ps=F->get();
|
|
if (ps.state->is_node_in_group(ps.node,gi.name)) {
|
|
skip=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (skip)
|
|
continue;
|
|
|
|
nd.groups.push_back(_nm_get_string(gi.name,name_map));
|
|
}
|
|
|
|
|
|
// save the right owner
|
|
// for the saved scene root this is -1
|
|
// for nodes of the saved scene this is 0
|
|
// for nodes of instanced scenes this is >0
|
|
|
|
if (p_node==p_owner) {
|
|
//saved scene root
|
|
nd.owner=-1;
|
|
} else if (p_node->get_owner()==p_owner) {
|
|
//part of saved scene
|
|
nd.owner=0;
|
|
} else {
|
|
|
|
nd.owner=-1;
|
|
#if 0
|
|
// this is pointless, if this was instanced by something else,
|
|
// the owner will already be set.
|
|
|
|
if (node_map.has(p_node->get_owner())) {
|
|
//maybe an existing saved node
|
|
nd.owner=node_map[p_node->get_owner()];
|
|
} else {
|
|
//not saved, use nodepath map
|
|
int sidx;
|
|
if (nodepath_map.has(p_node->get_owner())) {
|
|
sidx=nodepath_map[p_node->get_owner()];
|
|
} else {
|
|
sidx=nodepath_map.size();
|
|
nodepath_map[p_node->get_owner()]=sidx;
|
|
}
|
|
|
|
nd.owner=FLAG_ID_IS_PATH|sidx;
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
// Save the right type. If this node was created by an instance
|
|
// then flag that the node should not be created but reused
|
|
if (pack_state_stack.empty()) {
|
|
//this node is not part of an instancing process, so save the type
|
|
nd.type=_nm_get_string(p_node->get_type(),name_map);
|
|
} else {
|
|
// this node is part of an instanced process, so do not save the type.
|
|
// instead, save that it was instanced
|
|
nd.type=TYPE_INSTANCED;
|
|
}
|
|
|
|
|
|
// determine whether to save this node or not
|
|
// if this node is part of an instanced sub-scene, we can skip storing it if basically
|
|
// no properties changed and no groups were added to it.
|
|
// below condition is true for all nodes of the scene being saved, and ones in subscenes
|
|
// that hold changes
|
|
|
|
bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
|
|
save_node = save_node || p_node==p_owner; // owner is always saved
|
|
save_node = save_node || (p_node->get_owner()==p_owner && instanced_by_owner); //part of scene and not instanced
|
|
|
|
|
|
int idx = nodes.size();
|
|
int parent_node=NO_PARENT_SAVED;
|
|
|
|
if (save_node) {
|
|
|
|
//don't save the node if nothing and subscene
|
|
|
|
node_map[p_node]=idx;
|
|
|
|
//ok validate parent node
|
|
if (p_parent_idx==NO_PARENT_SAVED) {
|
|
|
|
int sidx;
|
|
if (nodepath_map.has(p_node->get_parent())) {
|
|
sidx=nodepath_map[p_node->get_parent()];
|
|
} else {
|
|
sidx=nodepath_map.size();
|
|
nodepath_map[p_node->get_parent()]=sidx;
|
|
}
|
|
|
|
nd.parent=FLAG_ID_IS_PATH|sidx;
|
|
} else {
|
|
nd.parent=p_parent_idx;
|
|
}
|
|
|
|
parent_node=idx;
|
|
nodes.push_back(nd);
|
|
|
|
}
|
|
|
|
|
|
for(int i=0;i<p_node->get_child_count();i++) {
|
|
|
|
Node *c=p_node->get_child(i);
|
|
Error err = _parse_node(p_owner,c,parent_node,name_map,variant_map,node_map,nodepath_map);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
|
|
|
|
if (p_node!=p_owner && p_node->get_owner() && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
|
|
return OK;
|
|
|
|
|
|
List<MethodInfo> _signals;
|
|
p_node->get_signal_list(&_signals);
|
|
_signals.sort();
|
|
|
|
//ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
|
|
//NodeData &nd = nodes[node_map[p_node]];
|
|
|
|
|
|
for(List<MethodInfo>::Element *E=_signals.front();E;E=E->next()) {
|
|
|
|
List<Node::Connection> conns;
|
|
p_node->get_signal_connection_list(E->get().name,&conns);
|
|
|
|
conns.sort();
|
|
|
|
for(List<Node::Connection>::Element *F=conns.front();F;F=F->next()) {
|
|
|
|
const Node::Connection &c = F->get();
|
|
|
|
if (!(c.flags&CONNECT_PERSIST)) //only persistent connections get saved
|
|
continue;
|
|
|
|
// only connections that originate or end into main saved scene are saved
|
|
// everything else is discarded
|
|
|
|
|
|
Node *target=c.target->cast_to<Node>();
|
|
|
|
|
|
if (!target) {
|
|
continue;
|
|
}
|
|
|
|
//find if this connection already exists
|
|
Node *common_parent = target->find_common_parent_with(p_node);
|
|
|
|
ERR_CONTINUE(!common_parent);
|
|
|
|
if (common_parent!=p_owner && common_parent->get_filename()==String()) {
|
|
common_parent=common_parent->get_owner();
|
|
}
|
|
|
|
bool exists=false;
|
|
|
|
//go through ownership chain to see if this exists
|
|
while(common_parent) {
|
|
|
|
|
|
|
|
Ref<SceneState> ps;
|
|
|
|
if (common_parent==p_owner)
|
|
ps=common_parent->get_scene_inherited_state();
|
|
else
|
|
ps=common_parent->get_scene_instance_state();
|
|
|
|
|
|
if (ps.is_valid()) {
|
|
|
|
NodePath signal_from = common_parent->get_path_to(p_node);
|
|
NodePath signal_to = common_parent->get_path_to(target);
|
|
|
|
if (ps->has_connection(signal_from,c.signal,signal_to,c.method)) {
|
|
exists=true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (common_parent==p_owner)
|
|
break;
|
|
else
|
|
common_parent=common_parent->get_owner();
|
|
}
|
|
|
|
if (exists) { //already exists (comes from instance or inheritance), so don't save
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
{
|
|
Node *nl=p_node;
|
|
|
|
bool exists=false;
|
|
|
|
while(nl) {
|
|
|
|
if (nl==p_owner) {
|
|
|
|
Ref<SceneState> state = nl->get_scene_inherited_state();
|
|
if (state.is_valid()) {
|
|
int from_node = state->find_node_by_path(nl->get_path_to(p_node));
|
|
int to_node = state->find_node_by_path(nl->get_path_to(target));
|
|
|
|
if (from_node>=0 && to_node>=0) {
|
|
//this one has state for this node, save
|
|
if (state->is_connection(from_node,c.signal,to_node,c.method)) {
|
|
exists=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
nl=NULL;
|
|
} else {
|
|
if (nl->get_filename()!=String()) {
|
|
//is an instance
|
|
Ref<SceneState> state = nl->get_scene_instance_state();
|
|
if (state.is_valid()) {
|
|
int from_node = state->find_node_by_path(nl->get_path_to(p_node));
|
|
int to_node = state->find_node_by_path(nl->get_path_to(target));
|
|
|
|
if (from_node>=0 && to_node>=0) {
|
|
//this one has state for this node, save
|
|
if (state->is_connection(from_node,c.signal,to_node,c.method)) {
|
|
exists=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
nl=nl->get_owner();
|
|
}
|
|
}
|
|
|
|
if (exists) {
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int src_id;
|
|
|
|
if (node_map.has(p_node)) {
|
|
src_id=node_map[p_node];
|
|
} else {
|
|
if (nodepath_map.has(p_node)) {
|
|
src_id=FLAG_ID_IS_PATH|nodepath_map[p_node];
|
|
} else {
|
|
int sidx=nodepath_map.size();
|
|
nodepath_map[p_node]=sidx;
|
|
src_id=FLAG_ID_IS_PATH|sidx;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int target_id;
|
|
|
|
if (node_map.has(target)) {
|
|
target_id=node_map[target];
|
|
} else {
|
|
if (nodepath_map.has(target)) {
|
|
target_id=FLAG_ID_IS_PATH|nodepath_map[target];
|
|
} else {
|
|
int sidx=nodepath_map.size();
|
|
nodepath_map[target]=sidx;
|
|
target_id=FLAG_ID_IS_PATH|sidx;
|
|
}
|
|
}
|
|
|
|
ConnectionData cd;
|
|
cd.from=src_id;
|
|
cd.to=target_id;
|
|
cd.method=_nm_get_string(c.method,name_map);
|
|
cd.signal=_nm_get_string(c.signal,name_map);
|
|
cd.flags=c.flags;
|
|
for(int i=0;i<c.binds.size();i++) {
|
|
|
|
cd.binds.push_back( _vm_get_variant(c.binds[i],variant_map));
|
|
}
|
|
connections.push_back(cd);
|
|
}
|
|
}
|
|
|
|
for(int i=0;i<p_node->get_child_count();i++) {
|
|
|
|
Node *c=p_node->get_child(i);
|
|
Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map,nodepath_map);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
Error SceneState::pack(Node *p_scene) {
|
|
ERR_FAIL_NULL_V( p_scene, ERR_INVALID_PARAMETER );
|
|
|
|
|
|
clear();
|
|
|
|
Node *scene = p_scene;
|
|
|
|
Map<StringName,int> name_map;
|
|
HashMap<Variant,int,VariantHasher> variant_map;
|
|
Map<Node*,int> node_map;
|
|
Map<Node*,int> nodepath_map;
|
|
|
|
//if using scene inheritance, pack the scene it inherits from
|
|
if (scene->get_scene_inherited_state().is_valid()) {
|
|
String path = scene->get_scene_inherited_state()->get_path();
|
|
Ref<PackedScene> instance = ResourceLoader::load(path);
|
|
if (instance.is_valid()) {
|
|
|
|
base_scene_idx=_vm_get_variant(instance,variant_map);
|
|
}
|
|
}
|
|
//instanced, only direct sub-scnes are supported of course
|
|
|
|
|
|
Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map,nodepath_map);
|
|
if (err) {
|
|
clear();
|
|
ERR_FAIL_V(err);
|
|
}
|
|
|
|
err = _parse_connections(scene,scene,name_map,variant_map,node_map,nodepath_map);
|
|
if (err) {
|
|
clear();
|
|
ERR_FAIL_V(err);
|
|
}
|
|
|
|
names.resize(name_map.size());
|
|
|
|
for(Map<StringName,int>::Element *E=name_map.front();E;E=E->next()) {
|
|
|
|
names[E->get()]=E->key();
|
|
}
|
|
|
|
variants.resize(variant_map.size());
|
|
const Variant *K=NULL;
|
|
while((K=variant_map.next(K))) {
|
|
|
|
int idx = variant_map[*K];
|
|
variants[idx]=*K;
|
|
}
|
|
|
|
node_paths.resize(nodepath_map.size());
|
|
for(Map<Node*,int>::Element *E=nodepath_map.front();E;E=E->next()) {
|
|
|
|
node_paths[E->get()]=scene->get_path_to(E->key());
|
|
}
|
|
|
|
|
|
return OK;
|
|
}
|
|
|
|
void SceneState::set_path(const String &p_path) {
|
|
|
|
path=p_path;
|
|
}
|
|
|
|
String SceneState::get_path() const{
|
|
|
|
return path;
|
|
}
|
|
|
|
void SceneState::clear() {
|
|
|
|
names.clear();
|
|
variants.clear();
|
|
nodes.clear();
|
|
connections.clear();
|
|
node_path_cache.clear();
|
|
node_paths.clear();
|
|
editable_instances.clear();
|
|
base_scene_idx=-1;
|
|
|
|
}
|
|
|
|
Ref<SceneState> SceneState::_get_base_scene_state() const {
|
|
|
|
if (base_scene_idx>=0) {
|
|
|
|
Ref<PackedScene> ps = variants[base_scene_idx];
|
|
if (ps.is_valid()) {
|
|
return ps->get_state();
|
|
}
|
|
}
|
|
|
|
return Ref<SceneState>();
|
|
}
|
|
|
|
int SceneState::find_node_by_path(const NodePath& p_node) const {
|
|
|
|
if (!node_path_cache.has(p_node)) {
|
|
if (_get_base_scene_state().is_valid()) {
|
|
int idx = _get_base_scene_state()->find_node_by_path(p_node);
|
|
if (idx>=0) {
|
|
if (!base_scene_node_remap.has(idx)) {
|
|
int ridx = nodes.size() + base_scene_node_remap.size();
|
|
base_scene_node_remap[ridx]=idx;
|
|
}
|
|
|
|
return base_scene_node_remap[idx];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int nid = node_path_cache[p_node];
|
|
|
|
if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
|
|
//for nodes that _do_ exist in current scene, still try to look for
|
|
//the node in the instanced scene, as a property may be missing
|
|
//from the local one
|
|
int idx = _get_base_scene_state()->find_node_by_path(p_node);
|
|
base_scene_node_remap[nid]=idx;
|
|
|
|
}
|
|
|
|
return nid;
|
|
}
|
|
Variant SceneState::get_property_value(int p_node, const StringName& p_property, bool &found) const {
|
|
|
|
found=false;
|
|
|
|
ERR_FAIL_COND_V(p_node<0,Variant());
|
|
|
|
if (p_node<nodes.size()) {
|
|
//find in built-in nodes
|
|
int pc = nodes[p_node].properties.size();
|
|
const StringName* namep = names.ptr();
|
|
|
|
const NodeData::Property *p=nodes[p_node].properties.ptr();
|
|
for(int i=0;i<pc;i++) {
|
|
if (p_property==namep[p[i].name]) {
|
|
found=true;
|
|
return variants[p[i].value];
|
|
}
|
|
}
|
|
}
|
|
|
|
//property not found, try on instance
|
|
|
|
if (base_scene_node_remap.has(p_node)) {
|
|
return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node],p_property,found);
|
|
}
|
|
|
|
return Variant();
|
|
}
|
|
|
|
bool SceneState::is_node_in_group(int p_node,const StringName& p_group) const {
|
|
|
|
ERR_FAIL_COND_V(p_node<0,false);
|
|
|
|
if (p_node<nodes.size()) {
|
|
const StringName* namep = names.ptr();
|
|
for(int i=0;i<nodes[p_node].groups.size();i++) {
|
|
if (namep[nodes[p_node].groups[i]]==p_group)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (base_scene_node_remap.has(p_node)) {
|
|
return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node],p_group);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SceneState::disable_placeholders=false;
|
|
|
|
void SceneState::set_disable_placeholders(bool p_disable) {
|
|
|
|
disable_placeholders=p_disable;
|
|
}
|
|
|
|
bool SceneState::is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const {
|
|
|
|
ERR_FAIL_COND_V(p_node<0,false);
|
|
ERR_FAIL_COND_V(p_to_node<0,false);
|
|
|
|
if (p_node<nodes.size() && p_to_node<nodes.size()) {
|
|
|
|
int signal_idx=-1;
|
|
int method_idx=-1;
|
|
for(int i=0;i<names.size();i++) {
|
|
if (names[i]==p_signal) {
|
|
signal_idx=i;
|
|
} else if (names[i]==p_to_method) {
|
|
method_idx=i;
|
|
}
|
|
}
|
|
|
|
if (signal_idx>=0 && method_idx>=0) {
|
|
//signal and method strings are stored..
|
|
|
|
for(int i=0;i<connections.size();i++) {
|
|
|
|
if (connections[i].from==p_node && connections[i].to==p_to_node && connections[i].signal==signal_idx && connections[i].method==method_idx) {
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
|
|
return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node],p_signal,base_scene_node_remap[p_to_node],p_to_method);
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
void SceneState::set_bundled_scene(const Dictionary& d) {
|
|
|
|
|
|
ERR_FAIL_COND( !d.has("names"));
|
|
ERR_FAIL_COND( !d.has("variants"));
|
|
ERR_FAIL_COND( !d.has("node_count"));
|
|
ERR_FAIL_COND( !d.has("nodes"));
|
|
ERR_FAIL_COND( !d.has("conn_count"));
|
|
ERR_FAIL_COND( !d.has("conns"));
|
|
// ERR_FAIL_COND( !d.has("path"));
|
|
|
|
int version=1;
|
|
if (d.has("version"))
|
|
version=d["version"];
|
|
|
|
if (version>PACK_VERSION) {
|
|
ERR_EXPLAIN("Save format version too new!");
|
|
ERR_FAIL();
|
|
}
|
|
|
|
DVector<String> snames = d["names"];
|
|
if (snames.size()) {
|
|
|
|
int namecount = snames.size();
|
|
names.resize(namecount);
|
|
DVector<String>::Read r =snames.read();
|
|
for(int i=0;i<names.size();i++)
|
|
names[i]=r[i];
|
|
}
|
|
|
|
Array svariants = d["variants"];
|
|
|
|
if (svariants.size()) {
|
|
int varcount=svariants.size();
|
|
variants.resize(varcount);
|
|
for(int i=0;i<varcount;i++) {
|
|
|
|
variants[i]=svariants[i];
|
|
}
|
|
|
|
} else {
|
|
variants.clear();
|
|
}
|
|
|
|
nodes.resize(d["node_count"]);
|
|
int nc=nodes.size();
|
|
if (nc) {
|
|
DVector<int> snodes = d["nodes"];
|
|
DVector<int>::Read r = snodes.read();
|
|
int idx=0;
|
|
for(int i=0;i<nc;i++) {
|
|
NodeData &nd = nodes[i];
|
|
nd.parent=r[idx++];
|
|
nd.owner=r[idx++];
|
|
nd.type=r[idx++];
|
|
nd.name=r[idx++];
|
|
nd.instance=r[idx++];
|
|
nd.properties.resize(r[idx++]);
|
|
for(int j=0;j<nd.properties.size();j++) {
|
|
|
|
nd.properties[j].name=r[idx++];
|
|
nd.properties[j].value=r[idx++];
|
|
}
|
|
nd.groups.resize(r[idx++]);
|
|
for(int j=0;j<nd.groups.size();j++) {
|
|
|
|
nd.groups[j]=r[idx++];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
connections.resize(d["conn_count"]);
|
|
int cc=connections.size();
|
|
|
|
if (cc) {
|
|
|
|
DVector<int> sconns = d["conns"];
|
|
DVector<int>::Read r = sconns.read();
|
|
int idx=0;
|
|
for(int i=0;i<cc;i++) {
|
|
ConnectionData &cd = connections[i];
|
|
cd.from=r[idx++];
|
|
cd.to=r[idx++];
|
|
cd.signal=r[idx++];
|
|
cd.method=r[idx++];
|
|
cd.flags=r[idx++];
|
|
cd.binds.resize(r[idx++]);
|
|
|
|
for(int j=0;j<cd.binds.size();j++) {
|
|
|
|
cd.binds[j]=r[idx++];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Array np;
|
|
if (d.has("node_paths")) {
|
|
np=d["node_paths"];
|
|
}
|
|
node_paths.resize(np.size());
|
|
for(int i=0;i<np.size();i++) {
|
|
node_paths[i]=np[i];
|
|
}
|
|
|
|
Array ei;
|
|
if (d.has("editable_instances")) {
|
|
ei=d["editable_instances"];
|
|
}
|
|
|
|
if (d.has("base_scene")) {
|
|
base_scene_idx=d["base_scene"];
|
|
}
|
|
|
|
editable_instances.resize(ei.size());
|
|
for(int i=0;i<editable_instances.size();i++) {
|
|
editable_instances[i]=ei[i];
|
|
}
|
|
|
|
// path=d["path"];
|
|
|
|
}
|
|
|
|
Dictionary SceneState::get_bundled_scene() const {
|
|
|
|
DVector<String> rnames;
|
|
rnames.resize(names.size());
|
|
|
|
if (names.size()) {
|
|
|
|
DVector<String>::Write r=rnames.write();
|
|
|
|
for(int i=0;i<names.size();i++)
|
|
r[i]=names[i];
|
|
}
|
|
|
|
Dictionary d;
|
|
d["names"]=rnames;
|
|
d["variants"]=variants;
|
|
|
|
Vector<int> rnodes;
|
|
d["node_count"]=nodes.size();
|
|
|
|
for(int i=0;i<nodes.size();i++) {
|
|
|
|
const NodeData &nd=nodes[i];
|
|
rnodes.push_back(nd.parent);
|
|
rnodes.push_back(nd.owner);
|
|
rnodes.push_back(nd.type);
|
|
rnodes.push_back(nd.name);
|
|
rnodes.push_back(nd.instance);
|
|
rnodes.push_back(nd.properties.size());
|
|
for(int j=0;j<nd.properties.size();j++) {
|
|
|
|
rnodes.push_back(nd.properties[j].name);
|
|
rnodes.push_back(nd.properties[j].value);
|
|
}
|
|
rnodes.push_back(nd.groups.size());
|
|
for(int j=0;j<nd.groups.size();j++) {
|
|
|
|
rnodes.push_back(nd.groups[j]);
|
|
}
|
|
}
|
|
|
|
d["nodes"]=rnodes;
|
|
|
|
Vector<int> rconns;
|
|
d["conn_count"]=connections.size();
|
|
|
|
for(int i=0;i<connections.size();i++) {
|
|
|
|
const ConnectionData &cd=connections[i];
|
|
rconns.push_back(cd.from);
|
|
rconns.push_back(cd.to);
|
|
rconns.push_back(cd.signal);
|
|
rconns.push_back(cd.method);
|
|
rconns.push_back(cd.flags);
|
|
rconns.push_back(cd.binds.size());
|
|
for(int j=0;j<cd.binds.size();j++)
|
|
rconns.push_back(cd.binds[j]);
|
|
|
|
}
|
|
|
|
d["conns"]=rconns;
|
|
|
|
Array rnode_paths;
|
|
rnode_paths.resize(node_paths.size());
|
|
for(int i=0;i<node_paths.size();i++) {
|
|
rnode_paths[i]=node_paths[i];
|
|
}
|
|
d["node_paths"]=rnode_paths;
|
|
|
|
Array reditable_instances;
|
|
reditable_instances.resize(editable_instances.size());
|
|
for(int i=0;i<editable_instances.size();i++) {
|
|
reditable_instances[i]=editable_instances[i];
|
|
}
|
|
d["editable_instances"]=reditable_instances;
|
|
if (base_scene_idx>=0) {
|
|
d["base_scene"]=base_scene_idx;
|
|
}
|
|
|
|
d["version"]=PACK_VERSION;
|
|
|
|
// d["path"]=path;
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
int SceneState::get_node_count() const {
|
|
|
|
return nodes.size();
|
|
}
|
|
|
|
StringName SceneState::get_node_type(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
|
|
if (nodes[p_idx].type==TYPE_INSTANCED)
|
|
return StringName();
|
|
return names[nodes[p_idx].type];
|
|
}
|
|
|
|
StringName SceneState::get_node_name(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
|
|
return names[nodes[p_idx].name];
|
|
}
|
|
|
|
|
|
bool SceneState::is_node_instance_placeholder(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),false);
|
|
|
|
return nodes[p_idx].instance>=0 && nodes[p_idx].instance&FLAG_INSTANCE_IS_PLACEHOLDER;
|
|
|
|
}
|
|
|
|
Ref<PackedScene> SceneState::get_node_instance(int p_idx) const {
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),Ref<PackedScene>());
|
|
|
|
if (nodes[p_idx].instance>=0) {
|
|
if (nodes[p_idx].instance&FLAG_INSTANCE_IS_PLACEHOLDER)
|
|
return Ref<PackedScene>();
|
|
else
|
|
return variants[nodes[p_idx].instance&FLAG_MASK];
|
|
} else if (nodes[p_idx].parent<0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
|
|
|
|
if (base_scene_idx>=0) {
|
|
return variants[base_scene_idx];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return Ref<PackedScene>();
|
|
|
|
|
|
}
|
|
|
|
String SceneState::get_node_instance_placeholder(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),String());
|
|
|
|
if (nodes[p_idx].instance>=0 && nodes[p_idx].instance&FLAG_INSTANCE_IS_PLACEHOLDER) {
|
|
return variants[nodes[p_idx].instance&FLAG_MASK];
|
|
}
|
|
|
|
return String();
|
|
|
|
}
|
|
|
|
Vector<StringName> SceneState::get_node_groups(int p_idx) const{
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),Vector<StringName>());
|
|
Vector<StringName> groups;
|
|
for(int i=0;i<nodes[p_idx].groups.size();i++) {
|
|
groups.push_back(names[nodes[p_idx].groups[i]]);
|
|
}
|
|
return groups;
|
|
}
|
|
|
|
|
|
NodePath SceneState::get_node_path(int p_idx,bool p_for_parent) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
|
|
|
|
if (nodes[p_idx].parent<0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
|
|
if (p_for_parent) {
|
|
return NodePath();
|
|
} else {
|
|
return NodePath(".");
|
|
}
|
|
}
|
|
|
|
Vector<StringName> sub_path;
|
|
NodePath base_path;
|
|
int nidx=p_idx;
|
|
while(true) {
|
|
if (nodes[nidx].parent==NO_PARENT_SAVED || nodes[nidx].parent<0) {
|
|
|
|
sub_path.insert(0,".");
|
|
break;
|
|
}
|
|
|
|
if (!p_for_parent || p_idx!=nidx) {
|
|
sub_path.insert(0,names[nodes[nidx].name]);
|
|
}
|
|
|
|
if (nodes[nidx].parent&FLAG_ID_IS_PATH) {
|
|
base_path=node_paths[nodes[nidx].parent&FLAG_MASK];
|
|
break;
|
|
} else {
|
|
nidx=nodes[nidx].parent&FLAG_MASK;
|
|
}
|
|
}
|
|
|
|
for(int i=base_path.get_name_count()-1;i>=0;i--) {
|
|
sub_path.insert(0,base_path.get_name(i));
|
|
}
|
|
|
|
if (sub_path.empty()) {
|
|
return NodePath(".");
|
|
}
|
|
|
|
return NodePath(sub_path,false);
|
|
|
|
}
|
|
|
|
int SceneState::get_node_property_count(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),-1);
|
|
return nodes[p_idx].properties.size();
|
|
|
|
}
|
|
StringName SceneState::get_node_property_name(int p_idx,int p_prop) const{
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
|
|
ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),StringName());
|
|
return names[nodes[p_idx].properties[p_prop].name];
|
|
|
|
}
|
|
Variant SceneState::get_node_property_value(int p_idx,int p_prop) const{
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),Variant());
|
|
ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),Variant());
|
|
|
|
return variants[nodes[p_idx].properties[p_prop].value];
|
|
}
|
|
|
|
|
|
NodePath SceneState::get_node_owner_path(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
|
|
if (nodes[p_idx].owner<0 || nodes[p_idx].owner==NO_PARENT_SAVED)
|
|
return NodePath(); //root likely
|
|
if (nodes[p_idx].owner&FLAG_ID_IS_PATH) {
|
|
return node_paths[nodes[p_idx].owner&FLAG_MASK];
|
|
} else {
|
|
return get_node_path(nodes[p_idx].owner&FLAG_MASK);
|
|
}
|
|
}
|
|
|
|
int SceneState::get_connection_count() const {
|
|
|
|
return connections.size();
|
|
}
|
|
NodePath SceneState::get_connection_source(int p_idx) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
|
|
if (connections[p_idx].from&FLAG_ID_IS_PATH) {
|
|
return node_paths[connections[p_idx].from&FLAG_MASK];
|
|
} else {
|
|
return get_node_path(connections[p_idx].from&FLAG_MASK);
|
|
}
|
|
|
|
}
|
|
|
|
StringName SceneState::get_connection_signal(int p_idx) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
|
|
return names[connections[p_idx].signal];
|
|
|
|
}
|
|
NodePath SceneState::get_connection_target(int p_idx) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
|
|
if (connections[p_idx].to&FLAG_ID_IS_PATH) {
|
|
return node_paths[connections[p_idx].to&FLAG_MASK];
|
|
} else {
|
|
return get_node_path(connections[p_idx].to&FLAG_MASK);
|
|
}
|
|
|
|
}
|
|
StringName SceneState::get_connection_method(int p_idx) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
|
|
return names[connections[p_idx].method];
|
|
|
|
}
|
|
|
|
|
|
int SceneState::get_connection_flags(int p_idx) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
|
|
return connections[p_idx].flags;
|
|
}
|
|
|
|
Array SceneState::get_connection_binds(int p_idx) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_idx,connections.size(),Array());
|
|
Array binds;
|
|
for(int i=0;i<connections[p_idx].binds.size();i++) {
|
|
binds.push_back(variants[connections[p_idx].binds[i]]);
|
|
}
|
|
return binds;
|
|
}
|
|
|
|
bool SceneState::has_connection(const NodePath& p_node_from, const StringName& p_signal, const NodePath& p_node_to, const StringName& p_method) const {
|
|
|
|
for(int i=0;i<connections.size();i++) {
|
|
const ConnectionData &c = connections[i];
|
|
|
|
NodePath np_from;
|
|
|
|
if (c.from&FLAG_ID_IS_PATH) {
|
|
np_from=node_paths[c.from&FLAG_MASK];
|
|
} else {
|
|
np_from=get_node_path(c.from);
|
|
}
|
|
|
|
NodePath np_to;
|
|
|
|
if (c.to&FLAG_ID_IS_PATH) {
|
|
np_to=node_paths[c.to&FLAG_MASK];
|
|
} else {
|
|
np_to=get_node_path(c.to);
|
|
}
|
|
|
|
StringName sn_signal=names[c.signal];
|
|
StringName sn_method=names[c.method];
|
|
|
|
if (np_from==p_node_from && sn_signal==p_signal && np_to==p_node_to && sn_method==p_method) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Vector<NodePath> SceneState::get_editable_instances() const {
|
|
return editable_instances;
|
|
}
|
|
//add
|
|
|
|
int SceneState::add_name(const StringName& p_name) {
|
|
|
|
names.push_back(p_name);
|
|
return names.size()-1;
|
|
}
|
|
|
|
int SceneState::find_name(const StringName& p_name) const {
|
|
|
|
for(int i=0;i<names.size();i++) {
|
|
if (names[i]==p_name)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int SceneState::add_value(const Variant& p_value) {
|
|
|
|
variants.push_back(p_value);
|
|
return variants.size()-1;
|
|
}
|
|
|
|
int SceneState::add_node_path(const NodePath& p_path){
|
|
|
|
node_paths.push_back(p_path);
|
|
return (node_paths.size()-1)|FLAG_ID_IS_PATH;
|
|
}
|
|
int SceneState::add_node(int p_parent,int p_owner,int p_type,int p_name, int p_instance){
|
|
|
|
NodeData nd;
|
|
nd.parent=p_parent;
|
|
nd.owner=p_owner;
|
|
nd.type=p_type;
|
|
nd.name=p_name;
|
|
nd.instance=p_instance;
|
|
|
|
nodes.push_back(nd);
|
|
|
|
return nodes.size()-1;
|
|
}
|
|
void SceneState::add_node_property(int p_node,int p_name,int p_value){
|
|
|
|
ERR_FAIL_INDEX(p_node,nodes.size());
|
|
ERR_FAIL_INDEX(p_name,names.size());
|
|
ERR_FAIL_INDEX(p_value,variants.size());
|
|
|
|
NodeData::Property prop;
|
|
prop.name=p_name;
|
|
prop.value=p_value;
|
|
nodes[p_node].properties.push_back(prop);
|
|
}
|
|
void SceneState::add_node_group(int p_node,int p_group){
|
|
|
|
ERR_FAIL_INDEX(p_node,nodes.size());
|
|
ERR_FAIL_INDEX(p_group,names.size());
|
|
nodes[p_node].groups.push_back(p_group);
|
|
|
|
}
|
|
void SceneState::set_base_scene(int p_idx){
|
|
|
|
ERR_FAIL_INDEX(p_idx,variants.size());
|
|
base_scene_idx=p_idx;
|
|
}
|
|
void SceneState::add_connection(int p_from,int p_to, int p_signal, int p_method, int p_flags,const Vector<int>& p_binds){
|
|
|
|
ERR_FAIL_INDEX(p_signal,names.size());
|
|
ERR_FAIL_INDEX(p_method,names.size());
|
|
|
|
for(int i=0;i<p_binds.size();i++) {
|
|
ERR_FAIL_INDEX(p_binds[i],variants.size());
|
|
}
|
|
ConnectionData c;
|
|
c.from=p_from;
|
|
c.to=p_to;
|
|
c.signal=p_signal;
|
|
c.method=p_method;
|
|
c.flags=p_flags;
|
|
c.binds=p_binds;
|
|
connections.push_back(c);
|
|
|
|
}
|
|
void SceneState::add_editable_instance(const NodePath& p_path){
|
|
|
|
editable_instances.push_back(p_path);
|
|
}
|
|
|
|
DVector<String> SceneState::_get_node_groups(int p_idx) const {
|
|
|
|
Vector<StringName> groups = get_node_groups(p_idx);
|
|
DVector<String> ret;
|
|
|
|
for(int i=0;i<groups.size();i++)
|
|
ret.push_back(groups[i]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void SceneState::_bind_methods() {
|
|
|
|
//unbuild API
|
|
|
|
ObjectTypeDB::bind_method(_MD("get_node_count"),&SceneState::get_node_count);
|
|
ObjectTypeDB::bind_method(_MD("get_node_type","idx"),&SceneState::get_node_type);
|
|
ObjectTypeDB::bind_method(_MD("get_node_name","idx"),&SceneState::get_node_name);
|
|
ObjectTypeDB::bind_method(_MD("get_node_path","idx","for_parent"),&SceneState::get_node_path,DEFVAL(false));
|
|
ObjectTypeDB::bind_method(_MD("get_node_owner_path","idx"),&SceneState::get_node_owner_path);
|
|
ObjectTypeDB::bind_method(_MD("is_node_instance_placeholder","idx"),&SceneState::is_node_instance_placeholder);
|
|
ObjectTypeDB::bind_method(_MD("get_node_instance_placeholder","idx"),&SceneState::get_node_instance_placeholder);
|
|
ObjectTypeDB::bind_method(_MD("get_node_instance:PackedScene","idx"),&SceneState::get_node_instance);
|
|
ObjectTypeDB::bind_method(_MD("get_node_groups","idx"),&SceneState::_get_node_groups);
|
|
ObjectTypeDB::bind_method(_MD("get_node_property_count","idx"),&SceneState::get_node_property_count);
|
|
ObjectTypeDB::bind_method(_MD("get_node_property_name","idx","prop_idx"),&SceneState::get_node_property_name);
|
|
ObjectTypeDB::bind_method(_MD("get_node_property_value","idx","prop_idx"),&SceneState::get_node_property_value);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_count"),&SceneState::get_connection_count);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_source","idx"),&SceneState::get_connection_source);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_signal","idx"),&SceneState::get_connection_signal);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_target","idx"),&SceneState::get_connection_target);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_method","idx"),&SceneState::get_connection_method);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_flags","idx"),&SceneState::get_connection_flags);
|
|
ObjectTypeDB::bind_method(_MD("get_connection_binds","idx"),&SceneState::get_connection_binds);
|
|
}
|
|
|
|
SceneState::SceneState() {
|
|
|
|
base_scene_idx=-1;
|
|
last_modified_time=0;
|
|
}
|
|
|
|
|
|
////////////////
|
|
|
|
|
|
|
|
void PackedScene::_set_bundled_scene(const Dictionary& d) {
|
|
|
|
state->set_bundled_scene(d);
|
|
}
|
|
|
|
Dictionary PackedScene::_get_bundled_scene() const {
|
|
|
|
return state->get_bundled_scene();
|
|
}
|
|
|
|
|
|
Error PackedScene::pack(Node *p_scene) {
|
|
|
|
return state->pack(p_scene);
|
|
}
|
|
|
|
void PackedScene::clear() {
|
|
|
|
state->clear();
|
|
}
|
|
|
|
bool PackedScene::can_instance() const {
|
|
|
|
return state->can_instance();
|
|
}
|
|
|
|
Node *PackedScene::instance(bool p_gen_edit_state) const {
|
|
|
|
#ifndef TOOLS_ENABLED
|
|
if (p_gen_edit_state) {
|
|
ERR_EXPLAIN("Edit state is only for editors, does not work without tools compiled");
|
|
ERR_FAIL_COND_V(p_gen_edit_state,NULL);
|
|
}
|
|
#endif
|
|
|
|
Node *s = state->instance(p_gen_edit_state);
|
|
if (!s)
|
|
return NULL;
|
|
|
|
if (p_gen_edit_state) {
|
|
s->set_scene_instance_state(state);
|
|
}
|
|
|
|
if (get_path()!="" && get_path().find("::")==-1)
|
|
s->set_filename(get_path());
|
|
|
|
|
|
s->notification(Node::NOTIFICATION_INSTANCED);
|
|
|
|
return s;
|
|
}
|
|
|
|
void PackedScene::replace_state(Ref<SceneState> p_by) {
|
|
|
|
state=p_by;
|
|
state->set_path(get_path());
|
|
#ifdef TOOLS_ENABLED
|
|
state->set_last_modified_time(get_last_modified_time());
|
|
#endif
|
|
|
|
}
|
|
|
|
void PackedScene::recreate_state() {
|
|
|
|
state = Ref<SceneState>( memnew( SceneState ));
|
|
state->set_path(get_path());
|
|
#ifdef TOOLS_ENABLED
|
|
state->set_last_modified_time(get_last_modified_time());
|
|
#endif
|
|
}
|
|
|
|
Ref<SceneState> PackedScene::get_state() {
|
|
|
|
return state;
|
|
}
|
|
|
|
void PackedScene::set_path(const String& p_path,bool p_take_over) {
|
|
|
|
state->set_path(p_path);
|
|
Resource::set_path(p_path,p_take_over);
|
|
}
|
|
|
|
|
|
void PackedScene::_bind_methods() {
|
|
|
|
ObjectTypeDB::bind_method(_MD("pack","path:Node"),&PackedScene::pack);
|
|
ObjectTypeDB::bind_method(_MD("instance:Node","gen_edit_state"),&PackedScene::instance,DEFVAL(false));
|
|
ObjectTypeDB::bind_method(_MD("can_instance"),&PackedScene::can_instance);
|
|
ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&PackedScene::_set_bundled_scene);
|
|
ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&PackedScene::_get_bundled_scene);
|
|
ObjectTypeDB::bind_method(_MD("get_state:SceneState"),&PackedScene::get_state);
|
|
|
|
ADD_PROPERTY( PropertyInfo(Variant::DICTIONARY,"_bundled"),_SCS("_set_bundled_scene"),_SCS("_get_bundled_scene"));
|
|
|
|
}
|
|
|
|
PackedScene::PackedScene() {
|
|
|
|
state = Ref<SceneState>( memnew( SceneState ));
|
|
|
|
}
|