diff options
Diffstat (limited to 'scene/resources/packed_scene.cpp')
-rw-r--r-- | scene/resources/packed_scene.cpp | 198 |
1 files changed, 134 insertions, 64 deletions
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index ac67e6e5e9..514e7eb260 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* packed_scene.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ +/**************************************************************************/ +/* packed_scene.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 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" @@ -71,7 +71,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { } Node *SceneState::instantiate(GenEditState p_edit_state) const { - // nodes where instancing failed (because something is missing) + // Nodes where instantiation failed (because something is missing.) List<Node *> stray_instances; #define NODE_FROM_ID(p_name, p_id) \ @@ -122,7 +122,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { 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()); + WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instantiating: '" + get_path() + "'.").ascii().get_data()); old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); nparent = ret_nodes[0]; } @@ -131,7 +131,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else { // i == 0 is root node. ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name])); - ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); + ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANTIATED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); } Node *node = nullptr; @@ -150,15 +150,15 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else if (n.instance >= 0) { //instance a scene into this node if (n.instance & FLAG_INSTANCE_IS_PLACEHOLDER) { - String path = props[n.instance & FLAG_MASK]; + String scene_path = props[n.instance & FLAG_MASK]; if (disable_placeholders) { - Ref<PackedScene> sdata = ResourceLoader::load(path, "PackedScene"); + Ref<PackedScene> sdata = ResourceLoader::load(scene_path, "PackedScene"); ERR_FAIL_COND_V(!sdata.is_valid(), nullptr); node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE); ERR_FAIL_COND_V(!node, nullptr); } else { InstancePlaceholder *ip = memnew(InstancePlaceholder); - ip->set_instance_path(path); + ip->set_instance_path(scene_path); node = ip; } node->set_scene_instance_load_placeholder(true); @@ -169,7 +169,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_COND_V(!node, nullptr); } - } else if (n.type == TYPE_INSTANCED) { + } else if (n.type == TYPE_INSTANTIATED) { //get the node from somewhere, it likely already exists from another instance if (parent) { node = parent->_get_child_by_name(snames[n.name]); @@ -279,25 +279,36 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); - - if (E) { - value = E->value; + // In a situation where a local-to-scene resource is used in a child node of a non-editable instance, + // we need to avoid the parent scene from overriding the resource potentially also used in the root + // of the instantiated scene. That would to the instance having two different instances of the resource. + // Since at this point it's too late to propagate the resource instance in the parent scene to all the relevant + // nodes in the instance (and that would require very complex bookkepping), what we do instead is + // tampering the resource object already there with the values from the node in the parent scene and + // then tell this node to reference that resource. + if (n.instance >= 0) { + Ref<Resource> node_res = node->get(snames[nprops[j].name]); + if (node_res.is_valid()) { + node_res->copy_from(res); + node_res->configure_for_local_scene(node, resources_local_to_scene); + value = node_res; + } } else { + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); Node *base = i == 0 ? node : ret_nodes[0]; - - if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) { - //for the main scene, use the resource as is - res->configure_for_local_scene(base, resources_local_to_scene); - resources_local_to_scene[res] = res; - + if (E) { + value = E->value; } else { - //for instances, a copy must be made - Node *base2 = i == 0 ? node : ret_nodes[0]; - Ref<Resource> local_dupe = res->duplicate_for_local_scene(base2, resources_local_to_scene); - resources_local_to_scene[res] = local_dupe; - res = local_dupe; - value = local_dupe; + if (p_edit_state == GEN_EDIT_STATE_MAIN) { + //for the main scene, use the resource as is + res->configure_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = res; + } else { + //for instances, a copy must be made + Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = local_dupe; + value = local_dupe; + } } } //must make a copy, because this res is local to scene @@ -334,7 +345,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { node->add_to_group(snames[n.groups[j]], true); } - if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { + if (n.instance >= 0 || n.type != TYPE_INSTANTIATED || i == 0) { //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { @@ -371,7 +382,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + // We only want to deal with pinned flag if instantiating as pure main (no instance, no inheriting.) if (p_edit_state == GEN_EDIT_STATE_MAIN) { _sanitize_node_pinned_properties(node); } else { @@ -398,7 +409,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { - E.value->setup_local_to_scene(); + if (E.value->get_local_scene() == ret_nodes[0]) { + E.value->setup_local_to_scene(); + } } //do connections @@ -437,7 +450,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { callable = callable.bindp(argptrs, binds.size()); } - cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags); + cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED)); } //Node *s = ret_nodes[0]; @@ -652,7 +665,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { - //this node is not part of an instancing process, so save the type + //This node is not part of an instantiation process, so save the type. if (missing_node != nullptr) { // It's a missing node (type non existent on load). nd.type = _nm_get_string(missing_node->get_original_class(), name_map); @@ -662,7 +675,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated - nd.type = TYPE_INSTANCED; + nd.type = TYPE_INSTANTIATED; } // determine whether to save this node or not @@ -925,8 +938,8 @@ Error SceneState::pack(Node *p_scene) { // 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); + String scene_path = scene->get_scene_inherited_state()->get_path(); + Ref<PackedScene> instance = ResourceLoader::load(scene_path); if (instance.is_valid()) { base_scene_idx = _vm_get_variant(instance, variant_map); } @@ -992,6 +1005,37 @@ void SceneState::clear() { base_scene_idx = -1; } +Error SceneState::copy_from(const Ref<SceneState> &p_scene_state) { + ERR_FAIL_COND_V(p_scene_state.is_null(), ERR_INVALID_PARAMETER); + + clear(); + + for (const StringName &E : p_scene_state->names) { + names.append(E); + } + for (const Variant &E : p_scene_state->variants) { + variants.append(E); + } + for (const SceneState::NodeData &E : p_scene_state->nodes) { + nodes.append(E); + } + for (const SceneState::ConnectionData &E : p_scene_state->connections) { + connections.append(E); + } + for (KeyValue<NodePath, int> &E : p_scene_state->node_path_cache) { + node_path_cache.insert(E.key, E.value); + } + for (const NodePath &E : p_scene_state->node_paths) { + node_paths.append(E); + } + for (const NodePath &E : p_scene_state->editable_instances) { + editable_instances.append(E); + } + base_scene_idx = p_scene_state->base_scene_idx; + + return OK; +} + Ref<SceneState> SceneState::get_base_scene_state() const { if (base_scene_idx >= 0) { Ref<PackedScene> ps = variants[base_scene_idx]; @@ -1338,7 +1382,7 @@ int SceneState::get_node_count() const { 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) { + if (nodes[p_idx].type == TYPE_INSTANTIATED) { return StringName(); } return names[nodes[p_idx].type]; @@ -1533,7 +1577,7 @@ Array SceneState::get_connection_binds(int p_idx) const { return binds; } -bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method) { +bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method, bool p_no_inheritance) { // this method cannot be const because of this Ref<SceneState> ss = this; @@ -1565,6 +1609,10 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p } } + if (p_no_inheritance) { + break; + } + ss = ss->get_base_scene_state(); } while (ss.is_valid()); @@ -1720,6 +1768,28 @@ void PackedScene::clear() { state->clear(); } +void PackedScene::reload_from_file() { + String path = get_path(); + if (!path.is_resource_file()) { + return; + } + + Ref<PackedScene> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + if (!s.is_valid()) { + return; + } + + // Backup the loaded_state + Ref<SceneState> loaded_state = s->get_state(); + // This assigns a new state to s->state + // We do this because of the next step + s->recreate_state(); + // This has a side-effect to clear s->state + copy_from(s); + // Then, we copy the backed-up loaded_state to state + state->copy_from(loaded_state); +} + bool PackedScene::can_instantiate() const { return state->can_instantiate(); } @@ -1742,7 +1812,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { s->set_scene_file_path(get_path()); } - s->notification(Node::NOTIFICATION_INSTANCED); + s->notification(Node::NOTIFICATION_SCENE_INSTANTIATED); return s; } @@ -1779,7 +1849,7 @@ void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); - ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); + ClassDB::bind_method(D_METHOD("_set_bundled_scene", "scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); |