From 86ce15fb7ac1b416d39403ee8226b1a573bc4ec0 Mon Sep 17 00:00:00 2001 From: Saracen Date: Tue, 27 Aug 2024 05:26:54 +0100 Subject: [PATCH] Make reimported models reimport their owner. Changes the behaviour of the scene hot-reload system so that if the scene which needs to be reimported is owned by another instance, reload that instance instead. --- editor/editor_node.cpp | 112 ++++++++++++++++++++----------- editor/editor_node.h | 11 ++- scene/resources/packed_scene.cpp | 19 ------ 3 files changed, 81 insertions(+), 61 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d3fa2ed716f..ac682eed693 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4391,6 +4391,21 @@ bool EditorNode::is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimp return true; } +void EditorNode::get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap &p_table) { + SceneEditorDataEntry new_entry; + new_entry.is_display_folded = p_node->is_displayed_folded(); + + if (p_root != p_node) { + new_entry.is_editable = p_root->is_editable_instance(p_node); + } + + p_table.insert(p_root->get_path_to(p_node), new_entry); + + for (int i = 0; i < p_node->get_child_count(); i++) { + get_scene_editor_data_for_node(p_root, p_node->get_child(i), p_table); + } +} + void EditorNode::get_preload_scene_modification_table( Node *p_edited_scene, Node *p_reimported_root, @@ -4497,7 +4512,7 @@ void EditorNode::get_preload_scene_modification_table( void EditorNode::get_preload_modifications_reference_to_nodes( Node *p_root, Node *p_node, - List &p_excluded_nodes, + HashSet &p_excluded_nodes, List &p_instance_list_with_children, HashMap &p_modification_table) { if (!p_excluded_nodes.find(p_node)) { @@ -5962,12 +5977,14 @@ void EditorNode::reload_scene(const String &p_path) { scene_tabs->set_current_tab(current_tab); } -void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List &p_instance_list) { +void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet &p_instance_list) { String scene_file_path = p_node->get_scene_file_path(); - // This is going to get messy... + bool valid_instance_found = false; + + // Attempt to find all the instances matching path we're going to reload. if (p_node->get_scene_file_path() == p_instance_path) { - p_instance_list.push_back(p_node); + valid_instance_found = true; } else { Node *current_node = p_node; @@ -5975,7 +5992,7 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * while (inherited_state.is_valid()) { String inherited_path = inherited_state->get_path(); if (inherited_path == p_instance_path) { - p_instance_list.push_back(p_node); + valid_instance_found = true; break; } @@ -5983,6 +6000,19 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * } } + // Instead of adding this instance directly, if its not owned by the scene, walk its ancestors + // and find the first node still owned by the scene. This is what we will reloading instead. + if (valid_instance_found) { + Node *current_node = p_node; + while (true) { + if (current_node->get_owner() == p_root || current_node->get_owner() == nullptr) { + p_instance_list.insert(current_node); + break; + } + current_node = current_node->get_parent(); + } + } + for (int i = 0; i < p_node->get_child_count(); i++) { find_all_instances_inheriting_path_in_node(p_root, p_node->get_child(i), p_instance_path, p_instance_list); } @@ -6001,20 +6031,20 @@ void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List instance_list; - find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instance_list); - if (instance_list.size() > 0) { + HashSet instances_to_reimport; + find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instances_to_reimport); + if (instances_to_reimport.size() > 0) { editor_data.set_edited_scene(current_scene_idx); List instance_list_with_children; - for (Node *original_node : instance_list) { + for (Node *original_node : instances_to_reimport) { InstanceModificationsEntry instance_modifications; // Fetching all the modified properties of the nodes reimported scene. @@ -6022,19 +6052,19 @@ void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List 0) { - scenes_modification_table[current_scene_idx] = scene_motifications; + if (scene_modifications.instance_list.size() > 0) { + scenes_modification_table[current_scene_idx] = scene_modifications; } } } @@ -6157,10 +6187,10 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { // Instantiate early so that caches cleared on load in SceneState can be rebuilt early. Node *instantiated_node = nullptr; - // If we are in a inherit scene, it's easier to create a new base scene and + // If we are in a inherited scene, it's easier to create a new base scene and // grab the node from there. // When scene_path_to_node is '.' and we have scene_inherited_state, it's because - // it's a muli-level inheritance scene. We should use + // it's a multi-level inheritance scene. We should use NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node); Ref scene_state = current_edited_scene->get_scene_inherited_state(); if (scene_path_to_node != "." && scene_state.is_valid() && scene_state->get_path() != instance_modifications.instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) { @@ -6184,9 +6214,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { if (!instantiated_node) { // If no base scene was found to create the node, we will use the reimported packed scene directly. - // But, when the current edited scene is the reimported scene, it's because it's a inherited scene - // of the reimported scene. In that case, we will not instantiate current_packed_scene, because - // we would reinstanciate ourself. Using the base scene is better. + // But, when the current edited scene is the reimported scene, it's because it's an inherited scene + // derived from the reimported scene. In that case, we will not instantiate current_packed_scene, because + // we would reinstantiate ourself. Using the base scene is better. if (current_edited_scene == original_node) { if (base_packed_scene.is_valid()) { instantiated_node = base_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); @@ -6242,6 +6272,17 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { // crash when reimporting scenes with animations when "Editable children" was enabled. replace_history_reimported_nodes(original_node, instantiated_node, original_node); + // Reset the editable instance state. + HashMap scene_editor_data_table; + Node *owner = original_node->get_owner(); + if (!owner) { + owner = original_node; + } + + get_scene_editor_data_for_node(owner, original_node, scene_editor_data_table); + + bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder(); + // Delete all the remaining node children. while (original_node->get_child_count()) { Node *child = original_node->get_child(0); @@ -6250,16 +6291,6 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { child->queue_free(); } - // Reset the editable instance state. - bool is_editable = true; - Node *owner = original_node->get_owner(); - if (owner) { - is_editable = owner->is_editable_instance(original_node); - } - - bool original_node_is_displayed_folded = original_node->is_displayed_folded(); - bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder(); - // Update the name to match instantiated_node->set_name(original_node->get_name()); @@ -6290,19 +6321,9 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { // Mark the old node for deletion. original_node->queue_free(); - // Restore the folded and placeholder state from the original node. - instantiated_node->set_display_folded(original_node_is_displayed_folded); + // Restore the placeholder state from the original node. instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder); - if (owner) { - Ref ss_inst = owner->get_scene_instance_state(); - if (ss_inst.is_valid()) { - ss_inst->update_instance_resource(instance_modifications.instance_path, current_packed_scene); - } - - owner->set_editable_instance(instantiated_node, is_editable); - } - // Attempt to re-add all the additional nodes. for (AdditiveNodeEntry additive_node_entry : instance_modifications.addition_list) { Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent); @@ -6334,6 +6355,17 @@ void EditorNode::reload_instances_with_path_in_edited_scenes() { } } + // Restore the scene's editable instance and folded states. + for (HashMap::Iterator I = scene_editor_data_table.begin(); I; ++I) { + Node *node = owner->get_node_or_null(I->key); + if (node) { + if (owner != node) { + owner->set_editable_instance(node, I->value.is_editable); + } + node->set_display_folded(I->value.is_display_folded); + } + } + // Restore the selection. if (selected_node_paths.size()) { for (NodePath selected_node_path : selected_node_paths) { diff --git a/editor/editor_node.h b/editor/editor_node.h index bdf9b26a7ac..52692db74b8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -849,12 +849,19 @@ public: HashMap other_instances_modifications; }; + struct SceneEditorDataEntry { + bool is_editable; + bool is_display_folded; + }; + HashMap scenes_modification_table; List scenes_reimported; List resources_reimported; void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); + void get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap &p_table); + void get_preload_scene_modification_table( Node *p_edited_scene, Node *p_reimported_root, @@ -863,7 +870,7 @@ public: void get_preload_modifications_reference_to_nodes( Node *p_root, Node *p_node, - List &p_excluded_nodes, + HashSet &p_excluded_nodes, List &p_instance_list_with_children, HashMap &p_modification_table); void get_children_nodes(Node *p_node, List &p_nodes); @@ -924,7 +931,7 @@ public: void reload_scene(const String &p_path); - void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List &p_instance_list); + void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet &p_instance_list); void preload_reimporting_with_path_in_edited_scenes(const List &p_scenes); void reload_instances_with_path_in_edited_scenes(); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 27db65bb1ab..f93070aea6a 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -1267,25 +1267,6 @@ Ref SceneState::get_base_scene_state() const { return Ref(); } -void SceneState::update_instance_resource(String p_path, Ref p_packed_scene) { - ERR_FAIL_COND(p_packed_scene.is_null()); - - for (const NodeData &nd : nodes) { - if (nd.instance >= 0) { - if (!(nd.instance & FLAG_INSTANCE_IS_PLACEHOLDER)) { - int instance_id = nd.instance & FLAG_MASK; - Ref original_packed_scene = variants[instance_id]; - if (original_packed_scene.is_valid()) { - if (original_packed_scene->get_path() == p_path) { - variants.remove_at(instance_id); - variants.insert(instance_id, p_packed_scene); - } - } - } - } - } -} - int SceneState::find_node_by_path(const NodePath &p_node) const { ERR_FAIL_COND_V_MSG(node_path_cache.is_empty(), -1, "This operation requires the node cache to have been built.");