浏览代码

Fix recursive resource local to scene

Any resource that contains other local to scene resources inside of
arrays or dictionaries will now be duplicated and configured.

The case where a scene's node has an exported array/dictionary
property containing local to scene resources is NOT handled here.
RedMser 2 年之前
父节点
当前提交
608b5d2e07
共有 2 个文件被更改,包括 98 次插入27 次删除
  1. 93 25
      core/io/resource.cpp
  2. 5 2
      core/io/resource.h

+ 93 - 25
core/io/resource.cpp

@@ -204,7 +204,58 @@ void Resource::reload_from_file() {
 	copy_from(s);
 }
 
-Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) {
+void Resource::_dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
+	switch (r_variant.get_type()) {
+		case Variant::ARRAY: {
+			Array a = r_variant;
+			for (int i = 0; i < a.size(); i++) {
+				_dupe_sub_resources(a[i], p_for_scene, p_remap_cache);
+			}
+		} break;
+		case Variant::DICTIONARY: {
+			Dictionary d = r_variant;
+			List<Variant> keys;
+			d.get_key_list(&keys);
+			for (Variant &k : keys) {
+				if (k.get_type() == Variant::OBJECT) {
+					// Replace in dictionary key.
+					Ref<Resource> sr = k;
+					if (sr.is_valid() && sr->is_local_to_scene()) {
+						if (p_remap_cache.has(sr)) {
+							d[p_remap_cache[sr]] = d[k];
+							d.erase(k);
+						} else {
+							Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
+							d[dupe] = d[k];
+							d.erase(k);
+							p_remap_cache[sr] = dupe;
+						}
+					}
+				} else {
+					_dupe_sub_resources(k, p_for_scene, p_remap_cache);
+				}
+
+				_dupe_sub_resources(d[k], p_for_scene, p_remap_cache);
+			}
+		} break;
+		case Variant::OBJECT: {
+			Ref<Resource> sr = r_variant;
+			if (sr.is_valid() && sr->is_local_to_scene()) {
+				if (p_remap_cache.has(sr)) {
+					r_variant = p_remap_cache[sr];
+				} else {
+					Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
+					r_variant = dupe;
+					p_remap_cache[sr] = dupe;
+				}
+			}
+		} break;
+		default: {
+		}
+	}
+}
+
+Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
 	List<PropertyInfo> plist;
 	get_property_list(&plist);
 
@@ -217,21 +268,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
 		if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
 			continue;
 		}
-		Variant p = get(E.name);
-		if (p.get_type() == Variant::OBJECT) {
-			Ref<Resource> sr = p;
-			if (sr.is_valid()) {
-				if (sr->is_local_to_scene()) {
-					if (remap_cache.has(sr)) {
-						p = remap_cache[sr];
-					} else {
-						Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache);
-						p = dupe;
-						remap_cache[sr] = dupe;
-					}
-				}
-			}
-		}
+		Variant p = get(E.name).duplicate(true);
+
+		_dupe_sub_resources(p, p_for_scene, p_remap_cache);
 
 		r->set(E.name, p);
 	}
@@ -239,7 +278,35 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
 	return r;
 }
 
-void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) {
+void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found) {
+	switch (p_variant.get_type()) {
+		case Variant::ARRAY: {
+			Array a = p_variant;
+			for (int i = 0; i < a.size(); i++) {
+				_find_sub_resources(a[i], p_resources_found);
+			}
+		} break;
+		case Variant::DICTIONARY: {
+			Dictionary d = p_variant;
+			List<Variant> keys;
+			d.get_key_list(&keys);
+			for (const Variant &k : keys) {
+				_find_sub_resources(k, p_resources_found);
+				_find_sub_resources(d[k], p_resources_found);
+			}
+		} break;
+		case Variant::OBJECT: {
+			Ref<Resource> r = p_variant;
+			if (r.is_valid()) {
+				p_resources_found.insert(r);
+			}
+		} break;
+		default: {
+		}
+	}
+}
+
+void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
 	List<PropertyInfo> plist;
 	get_property_list(&plist);
 
@@ -251,14 +318,15 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource
 			continue;
 		}
 		Variant p = get(E.name);
-		if (p.get_type() == Variant::OBJECT) {
-			Ref<Resource> sr = p;
-			if (sr.is_valid()) {
-				if (sr->is_local_to_scene()) {
-					if (!remap_cache.has(sr)) {
-						sr->configure_for_local_scene(p_for_scene, remap_cache);
-						remap_cache[sr] = sr;
-					}
+
+		HashSet<Ref<Resource>> sub_resources;
+		_find_sub_resources(p, sub_resources);
+
+		for (Ref<Resource> sr : sub_resources) {
+			if (sr->is_local_to_scene()) {
+				if (!p_remap_cache.has(sr)) {
+					sr->configure_for_local_scene(p_for_scene, p_remap_cache);
+					p_remap_cache[sr] = sr;
 				}
 			}
 		}

+ 5 - 2
core/io/resource.h

@@ -74,6 +74,9 @@ private:
 
 	SelfList<Resource> remapped_list;
 
+	void _dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
+	void _find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found);
+
 protected:
 	virtual void _resource_path_changed();
 	static void _bind_methods();
@@ -111,8 +114,8 @@ public:
 	String get_scene_unique_id() const;
 
 	virtual Ref<Resource> duplicate(bool p_subresources = false) const;
-	Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache);
-	void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache);
+	Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
+	void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
 
 	void set_local_to_scene(bool p_enable);
 	bool is_local_to_scene() const;