Skip to content

Commit

Permalink
Fix recursive resource local to scene
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RedMser committed Jan 11, 2024
1 parent 12ee58d commit 608b5d2
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 27 deletions.
118 changes: 93 additions & 25 deletions core/io/resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -217,29 +268,45 @@ 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);
}

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);

Expand All @@ -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;
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions core/io/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class Resource : public RefCounted {

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();
Expand Down Expand Up @@ -111,8 +114,8 @@ class Resource : public RefCounted {
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;
Expand Down

0 comments on commit 608b5d2

Please sign in to comment.