From 9fe902b29622de74db39729b4ca2ebce030cb27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=9D=92=E5=B1=B1?= Date: Thu, 30 May 2024 07:46:36 +0800 Subject: [PATCH] Fix errors when re-importing 3D asset files Some 3D asset files are treated as scenes and may cause some errors when importing. When working through different scene tabs, we need to temporarily add the scene root to the SceneTree to ensure that `editor_selection->add_node()` can work smoothly. This avoids the error message: `ERROR: Condition "!p_node->is_inside_tree()" is true.` This also ensures that no other scenes are accidentally added to the SceneTree causing the wrong display. When there is an inherited scene tab open and it is not the current tab, the new root node is accidentally added as a child node of `scene_root` during replacement. Instantiate the scene early so caches in SceneState that are cleared due to loading are rebuilt early. This avoids numerous error messages: `This operation requires the node cache to have been built.` --- editor/editor_node.cpp | 155 ++++++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 62 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0df4df36bcd9..af06ccb88573 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1067,7 +1067,7 @@ void EditorNode::_resources_reimported(const Vector &p_resources) { reload_instances_with_path_in_edited_scenes(E); } - scene_tabs->set_current_tab(current_tab); + _set_current_scene_nocheck(current_tab); } void EditorNode::_sources_changed(bool p_exist) { @@ -5719,8 +5719,14 @@ void EditorNode::reload_scene(const String &p_path) { if (scene_idx == -1) { if (get_edited_scene()) { + int current_history_id = editor_data.get_current_edited_scene_history_id(); + bool is_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(current_history_id); + // Scene is not open, so at it might be instantiated. We'll refresh the whole scene later. - EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id()); + EditorUndoRedoManager::get_singleton()->clear_history(false, current_history_id); + if (is_unsaved) { + EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id); + } } return; } @@ -5770,7 +5776,6 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * } void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) { - int original_edited_scene_idx = editor_data.get_edited_scene(); HashMap> edited_scene_map; Array replaced_nodes; @@ -5801,14 +5806,34 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins HashMap> local_scene_cache; local_scene_cache[p_instance_path] = instance_scene_packed_scene; + // Save the current scene state/selection in case of lost. + Dictionary editor_state = _get_main_scene_state(); + editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state); + editor_selection->clear(); + + int original_edited_scene_idx = editor_data.get_edited_scene(); + Node *original_edited_scene_root = editor_data.get_edited_scene_root(); + + // Prevent scene roots with the same name from being in the tree at the same time. + scene_root->remove_child(original_edited_scene_root); + for (const KeyValue> &edited_scene_map_elem : edited_scene_map) { // Set the current scene. int current_scene_idx = edited_scene_map_elem.key; editor_data.set_edited_scene(current_scene_idx); Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx); - // Clear the history for this tab (should we allow history to be retained?). - EditorUndoRedoManager::get_singleton()->clear_history(); + // Make sure the node is in the tree so that editor_selection can add node smoothly. + scene_root->add_child(current_edited_scene); + + // Restore the state so that the selection can be updated. + editor_state = editor_data.restore_edited_scene_state(editor_selection, &editor_history); + + int current_history_id = editor_data.get_current_edited_scene_history_id(); + bool is_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(current_history_id); + + // Clear the history for this affected tab. + EditorUndoRedoManager::get_singleton()->clear_history(false, current_history_id); // Update the version editor_data.is_scene_changed(current_scene_idx); @@ -5818,6 +5843,52 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins update_node_reference_modification_table_for_node(current_edited_scene, current_edited_scene, edited_scene_map_elem.value, edited_scene_global_modification_table); for (Node *original_node : edited_scene_map_elem.value) { + String original_node_file_path = original_node->get_scene_file_path(); + + // Load a replacement scene for the node. + Ref current_packed_scene; + if (original_node_file_path == p_instance_path) { + // If the node file name directly matches the scene we're replacing, + // just load it since we already cached it. + current_packed_scene = instance_scene_packed_scene; + } else { + // Otherwise, check the inheritance chain, reloading and caching any scenes + // we require along the way. + List required_load_paths; + + // Do we need to check if the paths are empty? + if (!original_node_file_path.is_empty()) { + required_load_paths.push_front(original_node_file_path); + } + Ref inherited_state = original_node->get_scene_inherited_state(); + while (inherited_state.is_valid()) { + String inherited_path = inherited_state->get_path(); + // Do we need to check if the paths are empty? + if (!inherited_path.is_empty()) { + required_load_paths.push_front(inherited_path); + } + inherited_state = inherited_state->get_base_scene_state(); + } + + // Ensure the inheritance chain is loaded in the correct order so that cache can + // be properly updated. + for (String path : required_load_paths) { + if (!local_scene_cache.find(path)) { + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err); + local_scene_cache[path] = current_packed_scene; + } else { + current_packed_scene = local_scene_cache[path]; + } + } + } + + ERR_FAIL_COND(current_packed_scene.is_null()); + + // Instantiate early so that caches cleared on load in SceneState can be rebuilt early. + Node *instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); + + ERR_FAIL_NULL(instantiated_node); + // Walk the tree for the current node and extract relevant diff data, storing it in the modification table. // For additional nodes which are part of the current scene, they get added to the addition table. HashMap modification_table; @@ -5875,53 +5946,6 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins is_editable = owner->is_editable_instance(original_node); } - // Load a replacement scene for the node. - Ref current_packed_scene; - if (original_node->get_scene_file_path() == p_instance_path) { - // If the node file name directly matches the scene we're replacing, - // just load it since we already cached it. - current_packed_scene = instance_scene_packed_scene; - } else { - // Otherwise, check the inheritance chain, reloading and caching any scenes - // we require along the way. - List required_load_paths; - String scene_path = original_node->get_scene_file_path(); - // Do we need to check if the paths are empty? - if (!scene_path.is_empty()) { - required_load_paths.push_front(scene_path); - } - Ref inherited_state = original_node->get_scene_inherited_state(); - while (inherited_state.is_valid()) { - String inherited_path = inherited_state->get_path(); - // Do we need to check if the paths are empty? - if (!inherited_path.is_empty()) { - required_load_paths.push_front(inherited_path); - } - inherited_state = inherited_state->get_base_scene_state(); - } - - // Ensure the inheritance chain is loaded in the correct order so that cache can - // be properly updated. - for (String path : required_load_paths) { - if (!local_scene_cache.find(path)) { - current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP, &err); - local_scene_cache[path] = current_packed_scene; - } else { - current_packed_scene = local_scene_cache[path]; - } - } - } - - ERR_FAIL_COND(current_packed_scene.is_null()); - - // Instantiate the node. - Node *instantiated_node = nullptr; - if (current_packed_scene.is_valid()) { - instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); - } - - ERR_FAIL_NULL(instantiated_node); - // For clear instance state for path recaching. instantiated_node->set_scene_instance_state(Ref()); @@ -5932,7 +5956,6 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins instantiated_node->set_name(original_node->get_name()); // Is this replacing the edited root node? - String original_node_file_path = original_node->get_scene_file_path(); if (current_edited_scene == original_node) { instantiated_node->set_scene_instance_state(original_node->get_scene_instance_state()); @@ -5943,13 +5966,7 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins instantiated_node->set_scene_inherited_state(state); instantiated_node->set_scene_file_path(String()); } - editor_data.set_edited_scene_root(instantiated_node); current_edited_scene = instantiated_node; - - if (original_node->is_inside_tree()) { - SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene); - original_node->get_tree()->set_edited_scene_root(instantiated_node); - } } // Replace the original node with the instantiated version. @@ -6046,8 +6063,18 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins } } + if (is_unsaved) { + EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id); + } + + // Save the current handled scene state. + editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state); + editor_selection->clear(); + // Cleanup the history of the changes. editor_history.cleanup_history(); + + scene_root->remove_child(current_edited_scene); } // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes. @@ -6055,10 +6082,14 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins _notify_nodes_scene_reimported(this, replaced_nodes); edited_scene_map.clear(); - } - editor_data.set_edited_scene(original_edited_scene_idx); - _edit_current(); + editor_data.set_edited_scene(original_edited_scene_idx); + + original_edited_scene_root = editor_data.get_edited_scene_root(); + scene_root->add_child(original_edited_scene_root); + + editor_data.restore_edited_scene_state(editor_selection, &editor_history); + } } int EditorNode::plugin_init_callback_count = 0;