From bd42d337df4c1187b1b813f2a727823105775207 Mon Sep 17 00:00:00 2001 From: Rindbee Date: Mon, 29 Aug 2022 07:50:36 +0800 Subject: [PATCH] Fix the behavior of the resource property of the sub-scene root node on instantiation The sub-scene root node will be set successively in the sub-scene and the main scene. The PR is simply to determine intent from the record. Mainly the cases when `resource_local_to_scene` is enabled in main scene. When updating resources according to the records of the main scene, use the `scene_unique_id` in the main scene to prevent the ID of the resource from changing continuously when saving the scene. --- scene/resources/packed_scene.cpp | 72 +++++++++++++++++++++++++------- scene/resources/packed_scene.h | 1 + 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 359c4765a2e..a857434b2a7 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -74,6 +74,55 @@ 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) { + 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); + 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. + + p_fallback->reset_state(); // May want to reset state. + + List pi; + p_resource->get_property_list(&pi); + for (const PropertyInfo &E : pi) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + if (E.name == "resource_path") { + continue; // Do not change path. + } + + Variant value = p_resource->get(E.name); + + // The local-to-scene subresource instance is preserved, thus maintaining the previous sharing relationship. + // 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()); + } + + 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; + } 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; + } + + return remap_resource; +} + Node *SceneState::instantiate(GenEditState p_edit_state) const { // Nodes where instantiation failed (because something is missing.) List stray_instances; @@ -234,6 +283,7 @@ 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; @@ -278,20 +328,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - // 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 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; - } + 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); } else { HashMap, Ref>::Iterator E = resources_local_to_scene.find(res); Node *base = i == 0 ? node : ret_nodes[0]; @@ -346,6 +384,12 @@ 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 diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 2be3ac08afd..4b436a8385e 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -136,6 +136,7 @@ public: }; 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); int find_node_by_path(const NodePath &p_node) const; Variant get_property_value(int p_node, const StringName &p_property, bool &found) const;