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.`
This commit is contained in:
风青山 2024-05-30 07:46:36 +08:00
parent 25519867f2
commit 9fe902b296
No known key found for this signature in database
GPG key ID: C06116835A98BFFF

View file

@ -1067,7 +1067,7 @@ void EditorNode::_resources_reimported(const Vector<String> &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<int, List<Node *>> edited_scene_map;
Array replaced_nodes;
@ -5801,14 +5806,34 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins
HashMap<String, Ref<PackedScene>> 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<int, List<Node *>> &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<PackedScene> 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<String> 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<SceneState> 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<NodePath, ModificationNodeEntry> 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<PackedScene> 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<String> 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<SceneState> 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<SceneState>());
@ -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;