summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRindbee <idleman@yeah.net>2022-08-29 07:50:36 +0800
committerRindbee <idleman@yeah.net>2023-08-18 19:08:34 +0800
commitbd42d337df4c1187b1b813f2a727823105775207 (patch)
treef7e332b8f10df4765d1535083ad494723f062d3e
parenta278c1b98a81738a35b96a933a6e6cf771f9ab2d (diff)
downloadredot-engine-bd42d337df4c1187b1b813f2a727823105775207.tar.gz
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.
-rw-r--r--scene/resources/packed_scene.cpp72
-rw-r--r--scene/resources/packed_scene.h1
2 files changed, 59 insertions, 14 deletions
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 359c4765a2..a857434b2a 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<Resource> SceneState::get_remap_resource(const Ref<Resource> &p_resource, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache, const Ref<Resource> &p_fallback, Node *p_for_scene) {
+ ERR_FAIL_COND_V(p_resource.is_null(), Ref<Resource>());
+
+ Ref<Resource> remap_resource;
+
+ // Find the shared copy of the source resource.
+ HashMap<Ref<Resource>, Ref<Resource>>::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<PropertyInfo> 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<Resource> 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<Resource> 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<Node *> 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<Resource>, Ref<Resource>> 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<Resource> 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<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;
- }
+ 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<Resource>, Ref<Resource>>::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<Resource>, Ref<Resource>> &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 2be3ac08af..4b436a8385 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<Resource> get_remap_resource(const Ref<Resource> &p_resource, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache, const Ref<Resource> &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;