Skip to content

Commit 07b011a

Browse files
author
Wyxaldir
committed
Fixes #71243. Resources that have local_to_scene enabled now properly work inside arrays and dictionaries.
1 parent 107f296 commit 07b011a

File tree

2 files changed

+86
-26
lines changed

2 files changed

+86
-26
lines changed

scene/resources/packed_scene.cpp

+82-26
Original file line numberDiff line numberDiff line change
@@ -327,44 +327,43 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
327327
//handle resources that are local to scene by duplicating them if needed
328328
Ref<Resource> res = value;
329329
if (res.is_valid()) {
330-
if (res->is_local_to_scene()) {
331-
if (n.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene.
332-
value = get_remap_resource(res, resources_local_to_sub_scene, node->get(snames[nprops[j].name]), node);
333-
} else {
334-
HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res);
335-
Node *base = i == 0 ? node : ret_nodes[0];
336-
if (E) {
337-
value = E->value;
338-
} else {
339-
if (p_edit_state == GEN_EDIT_STATE_MAIN) {
340-
//for the main scene, use the resource as is
341-
res->configure_for_local_scene(base, resources_local_to_scene);
342-
resources_local_to_scene[res] = res;
343-
} else {
344-
//for instances, a copy must be made
345-
Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene);
346-
resources_local_to_scene[res] = local_dupe;
347-
value = local_dupe;
348-
}
349-
}
350-
}
351-
//must make a copy, because this res is local to scene
352-
}
330+
value = make_local_resource(value, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state);
353331
}
354332
}
355333
if (value.get_type() == Variant::ARRAY) {
356334
Array set_array = value;
335+
value = setup_resources_in_array(set_array, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state);
336+
357337
bool is_get_valid = false;
358338
Variant get_value = node->get(snames[nprops[j].name], &is_get_valid);
339+
359340
if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
360341
Array get_array = get_value;
361342
if (!set_array.is_same_typed(get_array)) {
362343
value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
363344
}
364345
}
365346
}
366-
if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) {
367-
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
347+
if (value.get_type() == Variant::DICTIONARY) {
348+
Dictionary dictionary = value;
349+
const Array keys = dictionary.keys();
350+
const Array values = dictionary.values();
351+
352+
if (has_local_resource(values) || has_local_resource(keys)) {
353+
Array duplicated_keys = keys.duplicate(true);
354+
Array duplicated_values = values.duplicate(true);
355+
356+
duplicated_keys = setup_resources_in_array(duplicated_keys, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state);
357+
duplicated_values = setup_resources_in_array(duplicated_values, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state);
358+
359+
dictionary.clear();
360+
361+
for (int dictionary_index = 0; dictionary_index < keys.size(); dictionary_index++) {
362+
dictionary[duplicated_keys[dictionary_index]] = duplicated_values[dictionary_index];
363+
}
364+
365+
value = dictionary;
366+
}
368367
}
369368

370369
bool set_valid = true;
@@ -379,6 +378,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
379378
if (set_valid) {
380379
node->set(snames[nprops[j].name], value, &valid);
381380
}
381+
if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) {
382+
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor.
383+
}
382384
}
383385
}
384386
if (!missing_resource_properties.is_empty()) {
@@ -566,6 +568,50 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
566568
return ret_nodes[0];
567569
}
568570

571+
Variant SceneState::make_local_resource(Variant &p_value, const SceneState::NodeData &p_node_data, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const {
572+
Ref<Resource> res = p_value;
573+
if (res.is_null() || !res->is_local_to_scene()) {
574+
return p_value;
575+
}
576+
577+
if (p_node_data.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene.
578+
return get_remap_resource(res, p_resources_local_to_sub_scene, p_node->get(p_sname), p_node);
579+
} else {
580+
HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = p_resources_local_to_scene.find(res);
581+
Node *base = p_i == 0 ? p_node : p_ret_nodes[0];
582+
if (E) {
583+
return E->value;
584+
} else if (p_edit_state == GEN_EDIT_STATE_MAIN) { // For the main scene, use the resource as is
585+
res->configure_for_local_scene(base, p_resources_local_to_scene);
586+
p_resources_local_to_scene[res] = res;
587+
return res;
588+
} else { // For instances, a copy must be made.
589+
Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, p_resources_local_to_scene);
590+
p_resources_local_to_scene[res] = local_dupe;
591+
return local_dupe;
592+
}
593+
}
594+
}
595+
596+
Array SceneState::setup_resources_in_array(Array &p_array_to_scan, const SceneState::NodeData &p_n, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const {
597+
for (int i = 0; i < p_array_to_scan.size(); i++) {
598+
if (p_array_to_scan[i].get_type() == Variant::OBJECT) {
599+
p_array_to_scan[i] = make_local_resource(p_array_to_scan[i], p_n, p_resources_local_to_sub_scene, p_node, p_sname, p_resources_local_to_scene, p_i, p_ret_nodes, p_edit_state);
600+
}
601+
}
602+
return p_array_to_scan;
603+
}
604+
605+
bool SceneState::has_local_resource(const Array &p_array) const {
606+
for (int i = 0; i < p_array.size(); i++) {
607+
Ref<Resource> res = p_array[i];
608+
if (res.is_valid() && res->is_local_to_scene()) {
609+
return true;
610+
}
611+
}
612+
return false;
613+
}
614+
569615
static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) {
570616
if (name_map.has(p_string)) {
571617
return name_map[p_string];
@@ -730,8 +776,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
730776
if (!pinned_props.has(name)) {
731777
bool is_valid_default = false;
732778
Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true);
779+
733780
if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) {
734-
continue;
781+
if (value.get_type() == Variant::ARRAY && has_local_resource(value)) {
782+
// Save anyway
783+
} else if (value.get_type() == Variant::DICTIONARY) {
784+
Dictionary dictionary = value;
785+
if (!has_local_resource(dictionary.values()) && !has_local_resource(dictionary.keys())) {
786+
continue;
787+
}
788+
} else {
789+
continue;
790+
}
735791
}
736792
}
737793

scene/resources/packed_scene.h

+4
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ class SceneState : public RefCounted {
157157
bool can_instantiate() const;
158158
Node *instantiate(GenEditState p_edit_state) const;
159159

160+
Array setup_resources_in_array(Array &array_to_scan, const SceneState::NodeData &n, HashMap<Ref<Resource>, Ref<Resource>> &resources_local_to_sub_scene, Node *node, const StringName sname, HashMap<Ref<Resource>, Ref<Resource>> &resources_local_to_scene, int i, Node **ret_nodes, SceneState::GenEditState p_edit_state) const;
161+
Variant make_local_resource(Variant &value, const SceneState::NodeData &p_node_data, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap<Ref<Resource>, Ref<Resource>> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const;
162+
bool has_local_resource(const Array &p_array) const;
163+
160164
Ref<SceneState> get_base_scene_state() const;
161165

162166
void update_instance_resource(String p_path, Ref<PackedScene> p_packed_scene);

0 commit comments

Comments
 (0)