Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix resource_local_to_scene in arrays and dictionaries #87268

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 82 additions & 26 deletions scene/resources/packed_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,44 +327,43 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
//handle resources that are local to scene by duplicating them if needed
Ref<Resource> res = value;
if (res.is_valid()) {
if (res->is_local_to_scene()) {
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];
if (E) {
value = E->value;
} else {
if (p_edit_state == GEN_EDIT_STATE_MAIN) {
//for the main scene, use the resource as is
res->configure_for_local_scene(base, resources_local_to_scene);
resources_local_to_scene[res] = res;
} else {
//for instances, a copy must be made
Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene);
resources_local_to_scene[res] = local_dupe;
value = local_dupe;
}
}
}
//must make a copy, because this res is local to scene
}
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);
}
}
if (value.get_type() == Variant::ARRAY) {
Array set_array = value;
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);

bool is_get_valid = false;
Variant get_value = node->get(snames[nprops[j].name], &is_get_valid);

if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
Array get_array = get_value;
if (!set_array.is_same_typed(get_array)) {
value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
}
if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) {
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
if (value.get_type() == Variant::DICTIONARY) {
Dictionary dictionary = value;
const Array keys = dictionary.keys();
const Array values = dictionary.values();

if (has_local_resource(values) || has_local_resource(keys)) {
Array duplicated_keys = keys.duplicate(true);
Array duplicated_values = values.duplicate(true);

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

dictionary.clear();

for (int dictionary_index = 0; dictionary_index < keys.size(); dictionary_index++) {
KoBeWi marked this conversation as resolved.
Show resolved Hide resolved
dictionary[duplicated_keys[dictionary_index]] = duplicated_values[dictionary_index];
}

value = dictionary;
}
}

bool set_valid = true;
Expand All @@ -379,6 +378,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
if (set_valid) {
node->set(snames[nprops[j].name], value, &valid);
}
if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) {
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor.
}
}
}
if (!missing_resource_properties.is_empty()) {
Expand Down Expand Up @@ -566,6 +568,50 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
return ret_nodes[0];
}

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 {
Ref<Resource> res = p_value;
if (res.is_null() || !res->is_local_to_scene()) {
return p_value;
}

if (p_node_data.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene.
return get_remap_resource(res, p_resources_local_to_sub_scene, p_node->get(p_sname), p_node);
} else {
HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = p_resources_local_to_scene.find(res);
Node *base = p_i == 0 ? p_node : p_ret_nodes[0];
if (E) {
return E->value;
} else if (p_edit_state == GEN_EDIT_STATE_MAIN) { // For the main scene, use the resource as is
res->configure_for_local_scene(base, p_resources_local_to_scene);
p_resources_local_to_scene[res] = res;
return res;
} else { // For instances, a copy must be made.
Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, p_resources_local_to_scene);
p_resources_local_to_scene[res] = local_dupe;
return local_dupe;
}
}
}

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 {
for (int i = 0; i < p_array_to_scan.size(); i++) {
if (p_array_to_scan[i].get_type() == Variant::OBJECT) {
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);
}
}
return p_array_to_scan;
}

bool SceneState::has_local_resource(const Array &p_array) const {
for (int i = 0; i < p_array.size(); i++) {
Ref<Resource> res = p_array[i];
if (res.is_valid() && res->is_local_to_scene()) {
return true;
}
}
return false;
}

static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) {
if (name_map.has(p_string)) {
return name_map[p_string];
Expand Down Expand Up @@ -730,8 +776,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
if (!pinned_props.has(name)) {
bool is_valid_default = false;
Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true);

if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) {
continue;
if (value.get_type() == Variant::ARRAY && has_local_resource(value)) {
// Save anyway
KoBeWi marked this conversation as resolved.
Show resolved Hide resolved
} else if (value.get_type() == Variant::DICTIONARY) {
Dictionary dictionary = value;
if (!has_local_resource(dictionary.values()) && !has_local_resource(dictionary.keys())) {
continue;
}
} else {
continue;
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions scene/resources/packed_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ class SceneState : public RefCounted {
bool can_instantiate() const;
Node *instantiate(GenEditState p_edit_state) const;

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;
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;
bool has_local_resource(const Array &p_array) const;

Ref<SceneState> get_base_scene_state() const;

void update_instance_resource(String p_path, Ref<PackedScene> p_packed_scene);
Expand Down
Loading