Переглянути джерело

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;