diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index b80e258af9fd..f628d03d9a67 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -74,18 +74,28 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { return pinned; } -Ref SceneState::get_remap_resource(const Ref &p_resource, HashMap, Ref> &remap_cache, const Ref &p_fallback, Node *p_for_scene) { +Ref SceneState::get_remap_resource(const Ref &p_resource, HashMap, Ref>> &remap_cache, const Ref &p_fallback, Node *p_for_scene, bool p_reuse_uid) { ERR_FAIL_COND_V(p_resource.is_null(), Ref()); - Ref remap_resource; - // Find the shared copy of the source resource. - HashMap, Ref>::Iterator R = remap_cache.find(p_resource); + HashMap, Ref>::Iterator R = remap_cache[p_for_scene].find(p_resource); if (R) { - remap_resource = R->value; - } else if (p_fallback.is_valid() && p_fallback->is_local_to_scene() && p_fallback->get_class() == p_resource->get_class()) { - // Simply copy the data from the source resource to update the fallback resource that was previously set. + return R->value; + } + + bool reuse_fallback = p_fallback.is_valid() && p_fallback->is_local_to_scene() && p_fallback->get_class() == p_resource->get_class(); + if (reuse_fallback) { + // The fallback resource can only be mapped at most once when it is valid. + for (const KeyValue, Ref> &E : remap_cache[p_for_scene]) { + if (E.value == p_fallback) { + reuse_fallback = false; + break; + } + } + } + + if (reuse_fallback) { // Simply copy the data from the source resource to update the fallback resource that was previously set. p_fallback->reset_state(); // May want to reset state. List pi; @@ -104,23 +114,24 @@ Ref SceneState::get_remap_resource(const Ref &p_resource, Ha // This is mainly used when the sub-scene root is reset in the main scene. Ref sub_res_of_from = value; if (sub_res_of_from.is_valid() && sub_res_of_from->is_local_to_scene()) { - value = get_remap_resource(sub_res_of_from, remap_cache, p_fallback->get(E.name), p_fallback->get_local_scene()); + value = get_remap_resource(sub_res_of_from, remap_cache, p_fallback->get(E.name), p_fallback->get_local_scene(), p_reuse_uid); } p_fallback->set(E.name, value); } - - p_fallback->set_scene_unique_id(p_resource->get_scene_unique_id()); // Get the id from the main scene, in case the id changes again when saving the scene. - - remap_cache[p_resource] = p_fallback; - remap_resource = p_fallback; + if (p_reuse_uid) { + p_fallback->set_scene_unique_id(p_resource->get_scene_unique_id()); // Get the id from the main scene, in case the id changes again when saving the scene. + } + remap_cache[p_for_scene][p_resource] = p_fallback; + return p_fallback; } else { // A copy of the source resource is required to overwrite the previous one. - Ref local_dupe = p_resource->duplicate_for_local_scene(p_for_scene, remap_cache); - remap_cache[p_resource] = local_dupe; - remap_resource = local_dupe; + Ref local_dupe = p_resource->duplicate_for_local_scene(p_for_scene, remap_cache[p_for_scene]); + if (p_reuse_uid) { + local_dupe->set_scene_unique_id(p_resource->get_scene_unique_id()); + } + remap_cache[p_for_scene][p_resource] = local_dupe; + return local_dupe; } - - return remap_resource; } Node *SceneState::instantiate(GenEditState p_edit_state) const { @@ -161,6 +172,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty(); HashMap, Ref> resources_local_to_scene; + HashMap, Ref>> resources_local_to_sub_scene; // Record the mappings in sub-scenes. LocalVector deferred_node_paths; @@ -283,7 +295,6 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { const NodeData::Property *nprops = &n.properties[0]; Dictionary missing_resource_properties; - HashMap, Ref> resources_local_to_sub_scene; // Record the mappings in the sub-scene. for (int j = 0; j < nprop_count; j++) { bool valid; @@ -328,8 +339,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - if (n.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene. - value = get_remap_resource(res, resources_local_to_sub_scene, node->get(snames[nprops[j].name]), node); + if (n.type == TYPE_INSTANTIATED) { // For the (root) nodes of sub-scenes, treat them as parts of the sub-scenes. + value = get_remap_resource(res, resources_local_to_sub_scene, node->get(snames[nprops[j].name]), node->get_scene_file_path().is_empty() ? node->get_owner() : node, true); } else { HashMap, Ref>::Iterator E = resources_local_to_scene.find(res); Node *base = i == 0 ? node : ret_nodes[0]; @@ -384,12 +395,6 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (!missing_resource_properties.is_empty()) { node->set_meta(META_MISSING_RESOURCES, missing_resource_properties); } - - for (KeyValue, Ref> &E : resources_local_to_sub_scene) { - if (E.value->get_local_scene() == node) { - E.value->setup_local_to_scene(); // Setup may be required for the resource to work properly. - } - } } //name @@ -504,8 +509,12 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } for (KeyValue, Ref> &E : resources_local_to_scene) { - if (E.value->get_local_scene() == ret_nodes[0]) { - E.value->setup_local_to_scene(); + E.value->setup_local_to_scene(); + } + + for (KeyValue, Ref>> &E : resources_local_to_sub_scene) { + for (KeyValue, Ref> &R : E.value) { + R.value->setup_local_to_scene(); // Setup may be required for the resource to work properly. } } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 4b436a8385ee..4622014b5517 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -136,7 +136,7 @@ class SceneState : public RefCounted { }; static void set_disable_placeholders(bool p_disable); - static Ref get_remap_resource(const Ref &p_resource, HashMap, Ref> &remap_cache, const Ref &p_fallback, Node *p_for_scene); + static Ref get_remap_resource(const Ref &p_resource, HashMap, Ref>> &remap_cache, const Ref &p_fallback, Node *p_for_scene, bool p_reuse_uid = false); int find_node_by_path(const NodePath &p_node) const; Variant get_property_value(int p_node, const StringName &p_property, bool &found) const;