diff options
21 files changed, 640 insertions, 424 deletions
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 3f70810a7f..d3197efc6b 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TileMap" inherits="Node2D" deprecated="Use multiple [TileMapLayer] nodes instead." keywords="gridmap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="TileMap" inherits="Node2D" deprecated="Use multiple [TileMapLayer] nodes instead. To convert a TileMap to a set of TileMapLayer nodes, open the TileMap bottom panel with the node selected, click the toolbox icon in the top-right corner and choose 'Extract TileMap layers as individual TileMapLayer nodes'." keywords="gridmap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> Node for 2D tile-based maps. </brief_description> diff --git a/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp index d01ae5a73f..8fa495f5c4 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -85,6 +85,11 @@ const GUID CLSID_D3D12SDKConfigurationGodot = { 0x7cda6aca, 0xa03e, 0x49c8, { 0x RenderingContextDriverD3D12::RenderingContextDriverD3D12() {} RenderingContextDriverD3D12::~RenderingContextDriverD3D12() { + // Let's release manually everything that may still be holding + // onto the DLLs before freeing them. + device_factory.Reset(); + dxgi_factory.Reset(); + if (lib_d3d12) { FreeLibrary(lib_d3d12); } diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 11fea8b728..80c4c49c87 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -141,6 +141,16 @@ void EditorSelectionHistory::add_object(ObjectID p_object, const String &p_prope current_elem_idx++; } +void EditorSelectionHistory::replace_object(ObjectID p_old_object, ObjectID p_new_object) { + for (HistoryElement &element : history) { + for (int index = 0; index < element.path.size(); index++) { + if (element.path[index].object == p_old_object) { + element.path.write[index].object = p_new_object; + } + } + } +} + int EditorSelectionHistory::get_history_len() { return history.size(); } diff --git a/editor/editor_data.h b/editor/editor_data.h index 42b2d2ed0c..524c93807b 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -74,6 +74,7 @@ public: // Adds an object to the selection history. A property name can be passed if the target is a subresource of the given object. // If the object should not change the main screen plugin, it can be set as inspector only. void add_object(ObjectID p_object, const String &p_property = String(), bool p_inspector_only = false); + void replace_object(ObjectID p_old_object, ObjectID p_new_object); int get_history_len(); int get_history_pos(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b6d316dd1..f24fa344ae 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1042,11 +1042,17 @@ void EditorNode::_resources_reimporting(const Vector<String> &p_resources) { // because if a mesh is present in an inherited scene, the resource will be modified in // the inherited scene. Then, get_modified_properties_for_node will return the mesh property, // which will trigger a recopy of the previous mesh, preventing the reload. + scenes_modification_table.clear(); + List<String> scenes; for (const String &res_path : p_resources) { if (ResourceLoader::get_resource_type(res_path) == "PackedScene") { - preload_reimporting_with_path_in_edited_scenes(res_path); + scenes.push_back(res_path); } } + + if (scenes.size() > 0) { + preload_reimporting_with_path_in_edited_scenes(scenes); + } } void EditorNode::_resources_reimported(const Vector<String> &p_resources) { @@ -1080,9 +1086,10 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) { for (const String &E : scenes) { reload_scene(E); - reload_instances_with_path_in_edited_scenes(E); } + reload_instances_with_path_in_edited_scenes(); + _set_current_scene_nocheck(current_tab); } @@ -3870,17 +3877,18 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) { changing_scene = true; editor_data.save_edited_scene_state(editor_selection, &editor_history, _get_main_scene_state()); - if (get_editor_data().get_edited_scene_root()) { - if (get_editor_data().get_edited_scene_root()->get_parent() == scene_root) { - scene_root->remove_child(get_editor_data().get_edited_scene_root()); - } - } + Node *old_scene = get_editor_data().get_edited_scene_root(); editor_selection->clear(); editor_data.set_edited_scene(p_idx); Node *new_scene = editor_data.get_edited_scene_root(); + // Remove the scene only if it's a new scene, preventing performance issues when adding and removing scenes. + if (old_scene && new_scene != old_scene && old_scene->get_parent() == scene_root) { + scene_root->remove_child(old_scene); + } + if (Popup *p = Object::cast_to<Popup>(new_scene)) { p->show(); } @@ -4154,21 +4162,23 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * p_node->get_property_list(&pinfo); for (const PropertyInfo &E : pinfo) { if (E.usage & PROPERTY_USAGE_STORAGE) { + bool node_reference = (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE); + if (p_node_references_only && !node_reference) { + continue; + } bool is_valid_revert = false; Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_node, E.name, &is_valid_revert); Variant current_value = p_node->get(E.name); if (is_valid_revert) { if (PropertyUtils::is_property_value_different(p_node, current_value, revert_value)) { // If this property is a direct node reference, save a NodePath instead to prevent corrupted references. - if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + if (node_reference) { Node *target_node = Object::cast_to<Node>(current_value); if (target_node) { modified_property_map[E.name] = p_node->get_path_to(target_node); } } else { - if (!p_node_references_only) { - modified_property_map[E.name] = current_value; - } + modified_property_map[E.name] = current_value; } } } @@ -4178,6 +4188,27 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * return modified_property_map; } +HashMap<StringName, Variant> EditorNode::get_modified_properties_reference_to_nodes(Node *p_node, List<Node *> &p_nodes_referenced_by) { + HashMap<StringName, Variant> modified_property_map; + + List<PropertyInfo> pinfo; + p_node->get_property_list(&pinfo); + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + if (E.type != Variant::OBJECT || E.hint != PROPERTY_HINT_NODE_TYPE) { + continue; + } + Variant current_value = p_node->get(E.name); + Node *target_node = Object::cast_to<Node>(current_value); + if (target_node && p_nodes_referenced_by.find(target_node)) { + modified_property_map[E.name] = p_node->get_path_to(target_node); + } + } + } + + return modified_property_map; +} + void EditorNode::update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification) { if (p_node) { // First, attempt to restore the script property since it may affect the get_property_list method. @@ -4264,28 +4295,6 @@ void EditorNode::update_node_from_node_modification_entry(Node *p_node, Modifica } } -void EditorNode::update_node_reference_modification_table_for_node( - Node *p_root, - Node *p_node, - List<Node *> p_excluded_nodes, - HashMap<NodePath, ModificationNodeEntry> &p_modification_table) { - if (!p_excluded_nodes.find(p_node)) { - HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); - - if (!modified_properties.is_empty()) { - ModificationNodeEntry modification_node_entry; - modification_node_entry.property_table = modified_properties; - - p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; - } - - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *child = p_node->get_child(i); - update_node_reference_modification_table_for_node(p_root, child, p_excluded_nodes, p_modification_table); - } - } -} - bool EditorNode::is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimported_root, Node *p_node) { if (p_node == p_reimported_root) { return false; @@ -4328,11 +4337,39 @@ bool EditorNode::is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimp void EditorNode::get_preload_scene_modification_table( Node *p_edited_scene, Node *p_reimported_root, - Node *p_node, HashMap<NodePath, ModificationNodeEntry> &p_modification_table) { - // Only take the nodes that are in the base imported scene. The additional nodes will be managed - // after the resources are reimported. It's not important to check which property has - // changed on nodes that will not be reimported because they are not part of the reimported scene. - if (!is_additional_node_in_scene(p_edited_scene, p_reimported_root, p_node)) { + Node *p_node, InstanceModificationsEntry &p_instance_modifications) { + if (is_additional_node_in_scene(p_edited_scene, p_reimported_root, p_node)) { + // Only save additional nodes which have an owner since this was causing issues transient ownerless nodes + // which get recreated upon scene tree entry. + // For now instead, assume all ownerless nodes are transient and will have to be recreated. + if (p_node->get_owner()) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true); + if (p_node->get_owner() == p_edited_scene) { + AdditiveNodeEntry new_additive_node_entry; + new_additive_node_entry.node = p_node; + new_additive_node_entry.parent = p_reimported_root->get_path_to(p_node->get_parent()); + new_additive_node_entry.owner = p_node->get_owner(); + new_additive_node_entry.index = p_node->get_index(); + + Node2D *node_2d = Object::cast_to<Node2D>(p_node); + if (node_2d) { + new_additive_node_entry.transform_2d = node_2d->get_transform(); + } + Node3D *node_3d = Object::cast_to<Node3D>(p_node); + if (node_3d) { + new_additive_node_entry.transform_3d = node_3d->get_transform(); + } + + p_instance_modifications.addition_list.push_back(new_additive_node_entry); + } + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; + + p_instance_modifications.modifications[p_reimported_root->get_path_to(p_node)] = modification_node_entry; + } + } + } else { HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false); // Find all valid connections to other nodes. @@ -4391,58 +4428,56 @@ void EditorNode::get_preload_scene_modification_table( modification_node_entry.connections_from = valid_connections_from; modification_node_entry.groups = groups; - p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry; + p_instance_modifications.modifications[p_reimported_root->get_path_to(p_node)] = modification_node_entry; } } for (int i = 0; i < p_node->get_child_count(); i++) { - Node *child = p_node->get_child(i); - get_preload_scene_modification_table(p_edited_scene, p_reimported_root, child, p_modification_table); + get_preload_scene_modification_table(p_edited_scene, p_reimported_root, p_node->get_child(i), p_instance_modifications); } } -void EditorNode::update_reimported_diff_data_for_additional_nodes( - Node *p_edited_scene, - Node *p_reimported_root, +void EditorNode::get_preload_modifications_reference_to_nodes( + Node *p_root, Node *p_node, - HashMap<NodePath, ModificationNodeEntry> &p_modification_table, - List<AdditiveNodeEntry> &p_addition_list) { - if (is_additional_node_in_scene(p_edited_scene, p_reimported_root, p_node)) { - // Only save additional nodes which have an owner since this was causing issues transient ownerless nodes - // which get recreated upon scene tree entry. - // For now instead, assume all ownerless nodes are transient and will have to be recreated. - if (p_node->get_owner()) { - HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true); - if (p_node->get_owner() == p_edited_scene) { - AdditiveNodeEntry new_additive_node_entry; - new_additive_node_entry.node = p_node; - new_additive_node_entry.parent = p_reimported_root->get_path_to(p_node->get_parent()); - new_additive_node_entry.owner = p_node->get_owner(); - new_additive_node_entry.index = p_node->get_index(); + List<Node *> &p_excluded_nodes, + List<Node *> &p_instance_list_with_children, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table) { + if (!p_excluded_nodes.find(p_node)) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_reference_to_nodes(p_node, p_instance_list_with_children); - Node2D *node_2d = Object::cast_to<Node2D>(p_node); - if (node_2d) { - new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent()); - } - Node3D *node_3d = Object::cast_to<Node3D>(p_node); - if (node_3d) { - new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); - } + if (!modified_properties.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; - p_addition_list.push_back(new_additive_node_entry); - } - if (!modified_properties.is_empty()) { - ModificationNodeEntry modification_node_entry; - modification_node_entry.property_table = modified_properties; + p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; + } - p_modification_table[p_reimported_root->get_path_to(p_node)] = modification_node_entry; - } + for (int i = 0; i < p_node->get_child_count(); i++) { + get_preload_modifications_reference_to_nodes(p_root, p_node->get_child(i), p_excluded_nodes, p_instance_list_with_children, p_modification_table); } } +} +void EditorNode::get_children_nodes(Node *p_node, List<Node *> &p_nodes) { for (int i = 0; i < p_node->get_child_count(); i++) { Node *child = p_node->get_child(i); - update_reimported_diff_data_for_additional_nodes(p_edited_scene, p_reimported_root, child, p_modification_table, p_addition_list); + p_nodes.push_back(child); + get_children_nodes(child, p_nodes); + } +} + +void EditorNode::replace_history_reimported_nodes(Node *p_original_root_node, Node *p_new_root_node, Node *p_node) { + NodePath scene_path_to_node = p_original_root_node->get_path_to(p_node); + Node *new_node = p_new_root_node->get_node_or_null(scene_path_to_node); + if (new_node) { + editor_history.replace_object(p_node->get_instance_id(), new_node->get_instance_id()); + } else { + editor_history.replace_object(p_node->get_instance_id(), ObjectID()); + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + replace_history_reimported_nodes(p_original_root_node, p_new_root_node, p_node->get_child(i)); } } @@ -5816,25 +5851,6 @@ void EditorNode::reload_scene(const String &p_path) { scene_tabs->set_current_tab(current_tab); } -void EditorNode::get_edited_scene_map(const String &p_instance_path, HashMap<int, List<Node *>> &p_edited_scene_map) { - // Walk through each opened scene to get a global list of all instances which match - // the current reimported scenes. - for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { - if (editor_data.get_scene_path(i) == p_instance_path) { - continue; - } - Node *edited_scene_root = editor_data.get_edited_scene_root(i); - - if (edited_scene_root) { - List<Node *> valid_nodes; - find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, p_instance_path, valid_nodes); - if (valid_nodes.size() > 0) { - p_edited_scene_map[i] = valid_nodes; - } - } - } -} - void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list) { String scene_file_path = p_node->get_scene_file_path(); @@ -5857,386 +5873,423 @@ void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node * } for (int i = 0; i < p_node->get_child_count(); i++) { - Node *child = p_node->get_child(i); - find_all_instances_inheriting_path_in_node(p_root, child, p_instance_path, p_instance_list); + find_all_instances_inheriting_path_in_node(p_root, p_node->get_child(i), p_instance_path, p_instance_list); } } -void EditorNode::preload_reimporting_with_path_in_edited_scenes(const String &p_instance_path) { - HashMap<int, List<Node *>> edited_scene_map; - scenes_modification_table.clear(); +void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List<String> &p_scenes) { + EditorProgress progress("preload_reimporting_scene", TTR("Preparing scenes for reload"), editor_data.get_edited_scene_count()); + + int original_edited_scene_idx = editor_data.get_edited_scene(); - get_edited_scene_map(p_instance_path, edited_scene_map); + // Walk through each opened scene to get a global list of all instances which match + // the current reimported scenes. + for (int current_scene_idx = 0; current_scene_idx < editor_data.get_edited_scene_count(); current_scene_idx++) { + progress.step(vformat(TTR("Analyzing scene %s"), editor_data.get_scene_title(current_scene_idx)), current_scene_idx); - if (edited_scene_map.size() > 0) { - scenes_modification_table.clear(); + Node *edited_scene_root = editor_data.get_edited_scene_root(current_scene_idx); + + if (edited_scene_root) { + SceneModificationsEntry scene_motifications; - int original_edited_scene_idx = editor_data.get_edited_scene(); - Node *original_edited_scene_root = editor_data.get_edited_scene_root(); + for (const String &instance_path : p_scenes) { + if (editor_data.get_scene_path(current_scene_idx) == instance_path) { + continue; + } - // Prevent scene roots with the same name from being in the tree at the same time. - scene_root->remove_child(original_edited_scene_root); + List<Node *> instance_list; + find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instance_list); + if (instance_list.size() > 0) { + editor_data.set_edited_scene(current_scene_idx); - 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); + List<Node *> instance_list_with_children; + for (Node *original_node : instance_list) { + InstanceModificationsEntry instance_modifications; - // Make sure the node is in the tree so that editor_selection can add node smoothly. - scene_root->add_child(current_edited_scene); + // Fetching all the modified properties of the nodes reimported scene. + get_preload_scene_modification_table(edited_scene_root, original_node, original_node, instance_modifications); - for (Node *original_node : edited_scene_map_elem.value) { - // Fetching all the modified properties of the nodes reimported scene. - HashMap<NodePath, ModificationNodeEntry> modification_table; - get_preload_scene_modification_table(current_edited_scene, original_node, original_node, modification_table); + instance_modifications.original_node = original_node; + instance_modifications.instance_path = instance_path; + scene_motifications.instance_list.push_back(instance_modifications); - if (modification_table.size() > 0) { - NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node); - scenes_modification_table[current_scene_idx][scene_path_to_node] = modification_table; + instance_list_with_children.push_back(original_node); + get_children_nodes(original_node, instance_list_with_children); + } + + // Search the scene to find nodes that references the nodes will be recreated. + get_preload_modifications_reference_to_nodes(edited_scene_root, edited_scene_root, instance_list, instance_list_with_children, scene_motifications.other_instances_modifications); } } - scene_root->remove_child(current_edited_scene); + if (scene_motifications.instance_list.size() > 0) { + scenes_modification_table[current_scene_idx] = scene_motifications; + } } - - editor_data.set_edited_scene(original_edited_scene_idx); - scene_root->add_child(original_edited_scene_root); } + + editor_data.set_edited_scene(original_edited_scene_idx); + + progress.step(TTR("Preparation done."), editor_data.get_edited_scene_count()); } -void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) { - HashMap<int, List<Node *>> edited_scene_map; - Array replaced_nodes; +void EditorNode::reload_instances_with_path_in_edited_scenes() { + if (scenes_modification_table.size() == 0) { + return; + } + EditorProgress progress("reloading_scene", TTR("Scenes reloading"), editor_data.get_edited_scene_count()); + progress.step(TTR("Reloading..."), 0, true); - get_edited_scene_map(p_instance_path, edited_scene_map); + Error err; + Array replaced_nodes; + HashMap<String, Ref<PackedScene>> local_scene_cache; - if (edited_scene_map.size() > 0) { - // Reload the new instance. - Error err; - Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + // Reload the new instances. + for (KeyValue<int, SceneModificationsEntry> &scene_modifications_elem : scenes_modification_table) { + for (InstanceModificationsEntry instance_modifications : scene_modifications_elem.value.instance_list) { + if (!local_scene_cache.has(instance_modifications.instance_path)) { + Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(instance_modifications.instance_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); - ERR_FAIL_COND(err != OK); - ERR_FAIL_COND(instance_scene_packed_scene.is_null()); + ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(instance_scene_packed_scene.is_null()); - HashMap<String, Ref<PackedScene>> local_scene_cache; - local_scene_cache[p_instance_path] = instance_scene_packed_scene; + local_scene_cache[instance_modifications.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(); + // 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(); + int original_edited_scene_idx = editor_data.get_edited_scene(); - // 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 (KeyValue<int, SceneModificationsEntry> &scene_modifications_elem : scenes_modification_table) { + // Set the current scene. + int current_scene_idx = scene_modifications_elem.key; + SceneModificationsEntry *scene_modifications = &scene_modifications_elem.value; - 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); + editor_data.set_edited_scene(current_scene_idx); + Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx); - // Make sure the node is in the tree so that editor_selection can add node smoothly. + // Make sure the node is in the tree so that editor_selection can add node smoothly. + if (original_edited_scene_idx != current_scene_idx) { + // Prevent scene roots with the same name from being in the tree at the same time. + Node *original_edited_scene_root = editor_data.get_edited_scene_root(original_edited_scene_idx); + if (original_edited_scene_root && original_edited_scene_root->get_name() == current_edited_scene->get_name()) { + scene_root->remove_child(original_edited_scene_root); + } 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); + // 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); + 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); + // 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); + // Update the version + editor_data.is_scene_changed(current_scene_idx); - // Contains modifications in the edited scene which reference nodes inside of any nodes we will be reimporting. - HashMap<NodePath, ModificationNodeEntry> edited_scene_global_modification_table; - update_node_reference_modification_table_for_node(current_edited_scene, current_edited_scene, edited_scene_map_elem.value, edited_scene_global_modification_table); + for (InstanceModificationsEntry instance_modifications : scene_modifications->instance_list) { + Node *original_node = instance_modifications.original_node; + String original_node_file_path = original_node->get_scene_file_path(); + Ref<PackedScene> instance_scene_packed_scene = local_scene_cache[instance_modifications.instance_path]; - 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; - Ref<PackedScene> base_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; + // Load a replacement scene for the node. + Ref<PackedScene> current_packed_scene; + Ref<PackedScene> base_packed_scene; + if (original_node_file_path == instance_modifications.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 (!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(); + 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 (current_packed_scene.is_valid()) { - base_packed_scene = current_packed_scene; - } - 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]; - } + // Ensure the inheritance chain is loaded in the correct order so that cache can + // be properly updated. + for (String path : required_load_paths) { + if (current_packed_scene.is_valid()) { + base_packed_scene = current_packed_scene; + } + 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 = nullptr; - - // If we are in a inherit 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 - NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node); - Ref<SceneState> scene_state = current_edited_scene->get_scene_inherited_state(); - if (scene_path_to_node != "." && scene_state.is_valid() && scene_state->get_path() != p_instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) { - Node *root_node = scene_state->instantiate(SceneState::GenEditState::GEN_EDIT_STATE_INSTANCE); - instantiated_node = root_node->get_node(scene_path_to_node); - - if (instantiated_node) { - if (instantiated_node->get_parent()) { - // Remove from the root so we can delete it from memory. - instantiated_node->get_parent()->remove_child(instantiated_node); - // No need of the additional children that could have been added to the node - // in the base scene. That will be managed by the 'addition_list' later. - _remove_all_not_owned_children(instantiated_node, instantiated_node); - memdelete(root_node); - } - } else { - // Should not happen because we checked with find_node_by_path before, just in case. + 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 = nullptr; + + // If we are in a inherit 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 + NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node); + Ref<SceneState> 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) { + Node *root_node = scene_state->instantiate(SceneState::GenEditState::GEN_EDIT_STATE_INSTANCE); + instantiated_node = root_node->get_node(scene_path_to_node); + + if (instantiated_node) { + if (instantiated_node->get_parent()) { + // Remove from the root so we can delete it from memory. + instantiated_node->get_parent()->remove_child(instantiated_node); + // No need of the additional children that could have been added to the node + // in the base scene. That will be managed by the 'addition_list' later. + _remove_all_not_owned_children(instantiated_node, instantiated_node); memdelete(root_node); } + } else { + // Should not happen because we checked with find_node_by_path before, just in case. + memdelete(root_node); } + } - 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. - if (current_edited_scene == original_node) { - if (base_packed_scene.is_valid()) { - instantiated_node = base_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); - } else { - instantiated_node = instance_scene_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); - } + 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. + if (current_edited_scene == original_node) { + if (base_packed_scene.is_valid()) { + instantiated_node = base_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); } else { - instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); + instantiated_node = instance_scene_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); } + } else { + 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; - List<AdditiveNodeEntry> addition_list; - if (scenes_modification_table.has(current_scene_idx) && scenes_modification_table[current_scene_idx].has(scene_path_to_node)) { - modification_table = scenes_modification_table[current_scene_idx][scene_path_to_node]; - } - update_reimported_diff_data_for_additional_nodes(current_edited_scene, original_node, original_node, modification_table, addition_list); + } + ERR_FAIL_NULL(instantiated_node); - // Disconnect all relevant connections, all connections from and persistent connections to. - for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) { - for (Connection conn : modification_table_entry.value.connections_from) { + // Disconnect all relevant connections, all connections from and persistent connections to. + for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : instance_modifications.modifications) { + for (Connection conn : modification_table_entry.value.connections_from) { + conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable); + } + for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) { + Connection conn = cwnp.connection; + if (conn.flags & CONNECT_PERSIST) { conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable); } - for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) { - Connection conn = cwnp.connection; - if (conn.flags & CONNECT_PERSIST) { - conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable); - } - } } + } - // Store all the paths for any selected nodes which are ancestors of the node we're replacing. - List<NodePath> selected_node_paths; - for (Node *selected_node : editor_selection->get_selected_node_list()) { - if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) { - selected_node_paths.push_back(original_node->get_path_to(selected_node)); - editor_selection->remove_node(selected_node); - } + // Store all the paths for any selected nodes which are ancestors of the node we're replacing. + List<NodePath> selected_node_paths; + for (Node *selected_node : editor_selection->get_selected_node_list()) { + if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) { + selected_node_paths.push_back(original_node->get_path_to(selected_node)); + editor_selection->remove_node(selected_node); } + } - // Remove all nodes which were added as additional elements (they will be restored later). - for (AdditiveNodeEntry additive_node_entry : addition_list) { - Node *addition_node = additive_node_entry.node; - addition_node->get_parent()->remove_child(addition_node); - } + // Remove all nodes which were added as additional elements (they will be restored later). + for (AdditiveNodeEntry additive_node_entry : instance_modifications.addition_list) { + Node *addition_node = additive_node_entry.node; + addition_node->get_parent()->remove_child(addition_node); + } - // Clear ownership of the nodes (kind of hack to workaround an issue with - // replace_by when called on nodes in other tabs). - List<Node *> nodes_owned_by_original_node; - original_node->get_owned_by(original_node, &nodes_owned_by_original_node); - for (Node *owned_node : nodes_owned_by_original_node) { - owned_node->set_owner(nullptr); - } + // Clear ownership of the nodes (kind of hack to workaround an issue with + // replace_by when called on nodes in other tabs). + List<Node *> nodes_owned_by_original_node; + original_node->get_owned_by(original_node, &nodes_owned_by_original_node); + for (Node *owned_node : nodes_owned_by_original_node) { + owned_node->set_owner(nullptr); + } - // Delete all the remaining node children. - while (original_node->get_child_count()) { - Node *child = original_node->get_child(0); + // Replace the old nodes in the history with the new ones. + // Otherwise, the history will contain old nodes, and some could still be + // instantiated if used elsewhere, causing the "current edited item" to be + // linked to a node that will be destroyed later. This caused the editor to + // crash when reimporting scenes with animations when "Editable children" was enabled. + replace_history_reimported_nodes(original_node, instantiated_node, original_node); - original_node->remove_child(child); - child->queue_free(); - } + // Delete all the remaining node children. + while (original_node->get_child_count()) { + Node *child = original_node->get_child(0); - // 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); - } + original_node->remove_child(child); + child->queue_free(); + } - 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(); + // 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); + } - // Update the name to match - instantiated_node->set_name(original_node->get_name()); + 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(); - // Is this replacing the edited root node? + // Update the name to match + instantiated_node->set_name(original_node->get_name()); - if (current_edited_scene == original_node) { - // Set the instance as un inherited scene of itself. - instantiated_node->set_scene_inherited_state(instantiated_node->get_scene_instance_state()); - instantiated_node->set_scene_instance_state(nullptr); - instantiated_node->set_scene_file_path(original_node_file_path); - current_edited_scene = instantiated_node; - editor_data.set_edited_scene_root(current_edited_scene); - } + // Is this replacing the edited root node? - // Replace the original node with the instantiated version. - original_node->replace_by(instantiated_node, false); + if (current_edited_scene == original_node) { + // Set the instance as un inherited scene of itself. + instantiated_node->set_scene_inherited_state(instantiated_node->get_scene_instance_state()); + instantiated_node->set_scene_instance_state(nullptr); + instantiated_node->set_scene_file_path(original_node_file_path); + current_edited_scene = instantiated_node; + editor_data.set_edited_scene_root(current_edited_scene); - // Mark the old node for deletion. - original_node->queue_free(); + if (original_edited_scene_idx == current_scene_idx) { + // How that the editor executes a redraw while destroying or progressing the EditorProgress, + // it crashes when the root scene has been replaced because the edited scene + // was freed and no longer in the scene tree. + SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene); + if (get_tree()) { + get_tree()->set_edited_scene_root(current_edited_scene); + } + } + } - // Restore the folded and placeholder state from the original node. - instantiated_node->set_display_folded(original_node_is_displayed_folded); - instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder); + // Replace the original node with the instantiated version. + original_node->replace_by(instantiated_node, false); - if (owner) { - Ref<SceneState> ss_inst = owner->get_scene_instance_state(); - if (ss_inst.is_valid()) { - ss_inst->update_instance_resource(p_instance_path, current_packed_scene); - } + // 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); + instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder); - owner->set_editable_instance(instantiated_node, is_editable); + if (owner) { + Ref<SceneState> ss_inst = owner->get_scene_instance_state(); + if (ss_inst.is_valid()) { + ss_inst->update_instance_resource(instance_modifications.instance_path, current_packed_scene); } - // Attempt to re-add all the additional nodes. - for (AdditiveNodeEntry additive_node_entry : addition_list) { - Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent); + owner->set_editable_instance(instantiated_node, is_editable); + } - if (!parent_node) { - parent_node = current_edited_scene; - } + // 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); - parent_node->add_child(additive_node_entry.node); - parent_node->move_child(additive_node_entry.node, additive_node_entry.index); - // If the additive node's owner was the node which got replaced, update it. - if (additive_node_entry.owner == original_node) { - additive_node_entry.owner = instantiated_node; - } + if (!parent_node) { + parent_node = current_edited_scene; + } - additive_node_entry.node->set_owner(additive_node_entry.owner); + parent_node->add_child(additive_node_entry.node); + parent_node->move_child(additive_node_entry.node, additive_node_entry.index); + // If the additive node's owner was the node which got replaced, update it. + if (additive_node_entry.owner == original_node) { + additive_node_entry.owner = instantiated_node; + } - // If the parent node was lost, attempt to restore the original global transform. - { - Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node); - if (node_2d) { - node_2d->set_transform(additive_node_entry.transform_2d); - } + additive_node_entry.node->set_owner(additive_node_entry.owner); - Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node); - if (node_3d) { - node_3d->set_transform(additive_node_entry.transform_3d); - } + // If the parent node was lost, attempt to restore the original global transform. + { + Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node); + if (node_2d) { + node_2d->set_transform(additive_node_entry.transform_2d); } - } - // Restore the selection. - if (selected_node_paths.size()) { - for (NodePath selected_node_path : selected_node_paths) { - Node *selected_node = instantiated_node->get_node_or_null(selected_node_path); - if (selected_node) { - editor_selection->add_node(selected_node); - } + Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node); + if (node_3d) { + node_3d->set_transform(additive_node_entry.transform_3d); } - editor_selection->update(); } + } - // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. - for (KeyValue<NodePath, ModificationNodeEntry> &E : modification_table) { - NodePath new_current_path = E.key; - Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path); - - update_node_from_node_modification_entry(modifiable_node, E.value); + // Restore the selection. + if (selected_node_paths.size()) { + for (NodePath selected_node_path : selected_node_paths) { + Node *selected_node = instantiated_node->get_node_or_null(selected_node_path); + if (selected_node) { + editor_selection->add_node(selected_node); + } } - // Add the newly instantiated node to the edited scene's replaced node list. - replaced_nodes.push_back(instantiated_node); + editor_selection->update(); } // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. - for (KeyValue<NodePath, ModificationNodeEntry> &E : edited_scene_global_modification_table) { + for (KeyValue<NodePath, ModificationNodeEntry> &E : instance_modifications.modifications) { NodePath new_current_path = E.key; - Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path); + Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path); - if (modifiable_node) { - update_node_from_node_modification_entry(modifiable_node, E.value); - } + update_node_from_node_modification_entry(modifiable_node, E.value); } + // Add the newly instantiated node to the edited scene's replaced node list. + replaced_nodes.push_back(instantiated_node); + } - if (is_unsaved) { - EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id); + // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. + for (KeyValue<NodePath, ModificationNodeEntry> &E : scene_modifications->other_instances_modifications) { + NodePath new_current_path = E.key; + Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path); + + if (modifiable_node) { + update_node_from_node_modification_entry(modifiable_node, E.value); } + } - // Save the current handled scene state. - editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state); - editor_selection->clear(); + if (is_unsaved) { + EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id); + } - // Cleanup the history of the changes. - editor_history.cleanup_history(); + // Save the current handled scene state. + editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state); + editor_selection->clear(); - scene_root->remove_child(current_edited_scene); - } + // Cleanup the history of the changes. + editor_history.cleanup_history(); - // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes. - // To inform anything that depends on them that they should update as appropriate. - _notify_nodes_scene_reimported(this, replaced_nodes); + if (original_edited_scene_idx != current_scene_idx) { + scene_root->remove_child(current_edited_scene); - edited_scene_map.clear(); + // Ensure the current edited scene is re-added if removed earlier because it has the same name + // as the reimported scene. The editor could crash when reloading SceneTreeDock if the current + // edited scene is not in the scene tree. + Node *original_edited_scene_root = editor_data.get_edited_scene_root(original_edited_scene_idx); + if (original_edited_scene_root && !original_edited_scene_root->get_parent()) { + scene_root->add_child(original_edited_scene_root); + } + } + } - editor_data.set_edited_scene(original_edited_scene_idx); + // For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes. + // To inform anything that depends on them that they should update as appropriate. + _notify_nodes_scene_reimported(this, replaced_nodes); - original_edited_scene_root = editor_data.get_edited_scene_root(); - scene_root->add_child(original_edited_scene_root); + editor_data.set_edited_scene(original_edited_scene_idx); - editor_data.restore_edited_scene_state(editor_selection, &editor_history); - } + editor_data.restore_edited_scene_state(editor_selection, &editor_history); scenes_modification_table.clear(); + + progress.step(TTR("Reloading done."), editor_data.get_edited_scene_count()); } void EditorNode::_remove_all_not_owned_children(Node *p_node, Node *p_owner) { diff --git a/editor/editor_node.h b/editor/editor_node.h index 4d55eaf1b2..0877d458a7 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -797,6 +797,7 @@ public: Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only); + HashMap<StringName, Variant> get_modified_properties_reference_to_nodes(Node *p_node, List<Node *> &p_nodes_referenced_by); struct AdditiveNodeEntry { Node *node = nullptr; @@ -820,29 +821,39 @@ public: List<Node::GroupInfo> groups; }; - HashMap<int, HashMap<NodePath, HashMap<NodePath, ModificationNodeEntry>>> scenes_modification_table; + struct InstanceModificationsEntry { + Node *original_node; + String instance_path; + List<Node *> instance_list; + HashMap<NodePath, ModificationNodeEntry> modifications; + List<AdditiveNodeEntry> addition_list; + }; - void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); + struct SceneModificationsEntry { + List<InstanceModificationsEntry> instance_list; + HashMap<NodePath, ModificationNodeEntry> other_instances_modifications; + }; - void update_node_reference_modification_table_for_node( - Node *p_root, - Node *p_node, - List<Node *> p_excluded_nodes, - HashMap<NodePath, ModificationNodeEntry> &p_modification_table); + HashMap<int, SceneModificationsEntry> scenes_modification_table; + + void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification); void get_preload_scene_modification_table( Node *p_edited_scene, Node *p_reimported_root, - Node *p_node, HashMap<NodePath, ModificationNodeEntry> &p_modification_table); + Node *p_node, InstanceModificationsEntry &p_instance_modifications); - void update_reimported_diff_data_for_additional_nodes( - Node *p_edited_scene, - Node *p_reimported_root, + void get_preload_modifications_reference_to_nodes( + Node *p_root, Node *p_node, - HashMap<NodePath, ModificationNodeEntry> &p_modification_table, - List<AdditiveNodeEntry> &p_addition_list); + List<Node *> &p_excluded_nodes, + List<Node *> &p_instance_list_with_children, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table); + void get_children_nodes(Node *p_node, List<Node *> &p_nodes); bool is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimported_root, Node *p_node); + void replace_history_reimported_nodes(Node *p_original_root_node, Node *p_new_root_node, Node *p_node); + bool is_scene_open(const String &p_path); bool is_multi_window_enabled() const; @@ -896,10 +907,9 @@ public: void reload_scene(const String &p_path); - void get_edited_scene_map(const String &p_instance_path, HashMap<int, List<Node *>> &p_edited_scene_map); void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list); - void preload_reimporting_with_path_in_edited_scenes(const String &p_path); - void reload_instances_with_path_in_edited_scenes(const String &p_path); + void preload_reimporting_with_path_in_edited_scenes(const List<String> &p_scenes); + void reload_instances_with_path_in_edited_scenes(); bool is_exiting() const { return exiting; } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 351adc569c..2da9d66d9a 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1096,14 +1096,14 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool default_value = vsnode->get_input_port_default_value(j); } - Button *button = memnew(Button); - hb->add_child(button); - register_default_input_button(p_id, j, button); - button->connect(SceneStringName(pressed), callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, j)); + Button *default_input_btn = memnew(Button); + hb->add_child(default_input_btn); + register_default_input_button(p_id, j, default_input_btn); + default_input_btn->connect(SceneStringName(pressed), callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(default_input_btn, p_id, j)); if (default_value.get_type() != Variant::NIL) { // only a label set_input_port_default_value(p_type, p_id, j, default_value); } else { - button->hide(); + default_input_btn->hide(); } if (j == 0 && custom_editor) { @@ -1144,7 +1144,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool Label *label = memnew(Label); label->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED); // TODO: Implement proper translation switch. label->set_text(name_left); - label->add_theme_style_override(CoreStringName(normal), editor->get_theme_stylebox(SNAME("label_style"), SNAME("VShaderEditor"))); //more compact + label->add_theme_style_override(CoreStringName(normal), editor->get_theme_stylebox(SNAME("label_style"), SNAME("VShaderEditor"))); hb->add_child(label); if (vsnode->is_input_port_default(j, mode) && !port_left_used) { diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp index ee2253b294..b4aa00ee0a 100644 --- a/editor/project_manager/project_dialog.cpp +++ b/editor/project_manager/project_dialog.cpp @@ -351,15 +351,19 @@ void ProjectDialog::_install_path_changed() { } void ProjectDialog::_browse_project_path() { + String path = project_path->get_text(); + if (path.is_empty()) { + path = EDITOR_GET("filesystem/directories/default_project_path"); + } if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) { // Select last ZIP file. - fdialog_project->set_current_path(project_path->get_text()); + fdialog_project->set_current_path(path); } else if ((mode == MODE_NEW || mode == MODE_INSTALL) && create_dir->is_pressed()) { // Select parent directory of project path. - fdialog_project->set_current_dir(project_path->get_text().get_base_dir()); + fdialog_project->set_current_dir(path.get_base_dir()); } else { // Select project path. - fdialog_project->set_current_dir(project_path->get_text()); + fdialog_project->set_current_dir(path); } if (mode == MODE_IMPORT) { @@ -425,6 +429,10 @@ void ProjectDialog::_install_path_selected(const String &p_path) { get_ok_button()->grab_focus(); } +void ProjectDialog::_reset_name() { + project_name->set_text(TTR("New Game Project")); +} + void ProjectDialog::_renderer_selected() { ERR_FAIL_NULL(renderer_button_group->get_pressed_button()); @@ -690,6 +698,7 @@ void ProjectDialog::set_project_path(const String &p_path) { } void ProjectDialog::ask_for_path_and_show() { + _reset_name(); _browse_project_path(); } @@ -714,8 +723,7 @@ void ProjectDialog::show_dialog(bool p_reset_name) { callable_mp(project_name, &LineEdit::select_all).call_deferred(); } else { if (p_reset_name) { - String proj = TTR("New Game Project"); - project_name->set_text(proj); + _reset_name(); } project_path->set_editable(true); diff --git a/editor/project_manager/project_dialog.h b/editor/project_manager/project_dialog.h index 0efe1991ab..b985492f84 100644 --- a/editor/project_manager/project_dialog.h +++ b/editor/project_manager/project_dialog.h @@ -122,6 +122,7 @@ private: void _project_path_selected(const String &p_path); void _install_path_selected(const String &p_path); + void _reset_name(); void _renderer_selected(); void _nonempty_confirmation_ok_pressed(); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index a63b6d4e14..9d8cbb053d 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -2346,7 +2346,7 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme } // VisualShader editor. - p_theme->set_stylebox("label_style", "VShaderEditor", make_empty_stylebox(2, 1, 2, 1)); + p_theme->set_stylebox("label_style", "VShaderEditor", make_empty_stylebox(4, 6, 4, 6)); // StateMachine graph. { diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp index e29cc753c9..fcb477995f 100644 --- a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp +++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "editor/editor_node.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/themes/editor_scale.h" #include "scene/gui/check_box.h" #include "scene/gui/option_button.h" #include "scene/gui/spin_box.h" @@ -290,7 +291,6 @@ void AudioStreamInteractiveTransitionEditor::edit(Object *p_obj) { } int min_w = header_font->get_string_size(name + "XX").width; - tree->set_column_expand(cell_index, false); tree->set_column_custom_minimum_width(cell_index, min_w); max_w = MAX(max_w, min_w); @@ -314,11 +314,10 @@ void AudioStreamInteractiveTransitionEditor::edit(Object *p_obj) { } } - tree->set_column_expand(header_index, false); tree->set_column_custom_minimum_width(header_index, max_w); selection_order.clear(); _update_selection(); - popup_centered_ratio(0.6); + popup_centered_clamped(Size2(900, 450) * EDSCALE); updating = false; _update_transitions(); } @@ -332,6 +331,7 @@ AudioStreamInteractiveTransitionEditor::AudioStreamInteractiveTransitionEditor() tree->set_hide_root(true); tree->add_theme_constant_override("draw_guides", 1); tree->set_select_mode(Tree::SELECT_MULTI); + tree->set_custom_minimum_size(Size2(400, 0) * EDSCALE); split->add_child(tree); tree->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp index d242b06c29..851ad85876 100644 --- a/modules/multiplayer/editor/replication_editor.cpp +++ b/modules/multiplayer/editor/replication_editor.cpp @@ -430,7 +430,7 @@ void ReplicationEditor::_tree_item_edited() { undo_redo->add_do_method(config.ptr(), "property_set_spawn", prop, value); undo_redo->add_undo_method(config.ptr(), "property_set_spawn", prop, !value); undo_redo->add_do_method(this, "_update_value", prop, column, value ? 1 : 0); - undo_redo->add_undo_method(this, "_update_value", prop, column, value ? 1 : 0); + undo_redo->add_undo_method(this, "_update_value", prop, column, value ? 0 : 1); undo_redo->commit_action(); } else if (column == 2) { undo_redo->create_action(TTR("Set sync property")); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 3c63aa4fbf..0a9a4053e3 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -99,6 +99,8 @@ void WSLPeer::Resolver::try_next_candidate(Ref<StreamPeerTCP> &p_tcp) { p_tcp->poll(); StreamPeerTCP::Status status = p_tcp->get_status(); if (status == StreamPeerTCP::STATUS_CONNECTED) { + // On Windows, setting TCP_NODELAY may fail if the socket is still connecting. + p_tcp->set_no_delay(true); ip_candidates.clear(); return; } else if (status == StreamPeerTCP::STATUS_CONNECTING) { @@ -112,7 +114,6 @@ void WSLPeer::Resolver::try_next_candidate(Ref<StreamPeerTCP> &p_tcp) { while (ip_candidates.size()) { Error err = p_tcp->connect_to_host(ip_candidates.pop_front(), port); if (err == OK) { - p_tcp->set_no_delay(true); return; } else { p_tcp->disconnect_from_host(); @@ -311,7 +312,7 @@ void WSLPeer::_do_client_handshake() { ERR_FAIL_COND(tcp.is_null()); // Try to connect to candidates. - if (resolver.has_more_candidates()) { + if (resolver.has_more_candidates() || tcp->get_status() == StreamPeerTCP::STATUS_CONNECTING) { resolver.try_next_candidate(tcp); if (resolver.has_more_candidates()) { return; // Still pending. diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h index af9be8f5ba..383c5c3306 100644 --- a/platform/macos/gl_manager_macos_legacy.h +++ b/platform/macos/gl_manager_macos_legacy.h @@ -62,6 +62,7 @@ class GLManagerLegacy_MacOS { Error create_context(GLWindow &win); + bool framework_loaded = false; bool use_vsync = false; CGLEnablePtr CGLEnable = nullptr; CGLSetParameterPtr CGLSetParameter = nullptr; diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm index 6ce3831d9c..a0d037144e 100644 --- a/platform/macos/gl_manager_macos_legacy.mm +++ b/platform/macos/gl_manager_macos_legacy.mm @@ -32,6 +32,7 @@ #if defined(MACOS_ENABLED) && defined(GLES3_ENABLED) +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> @@ -156,7 +157,7 @@ void GLManagerLegacy_MacOS::window_set_per_pixel_transparency_enabled(DisplaySer } Error GLManagerLegacy_MacOS::initialize() { - return OK; + return framework_loaded ? OK : ERR_CANT_CREATE; } void GLManagerLegacy_MacOS::set_use_vsync(bool p_use) { @@ -186,12 +187,17 @@ NSOpenGLContext *GLManagerLegacy_MacOS::get_context(DisplayServer::WindowID p_wi } GLManagerLegacy_MacOS::GLManagerLegacy_MacOS() { - CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - CFBundleLoadExecutable(framework); - - CGLEnable = (CGLEnablePtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLEnable")); - CGLSetParameter = (CGLSetParameterPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLSetParameter")); - CGLGetCurrentContext = (CGLGetCurrentContextPtr)CFBundleGetFunctionPointerForName(framework, CFSTR("CGLGetCurrentContext")); + NSBundle *framework = [NSBundle bundleWithPath:@"/System/Library/Frameworks/OpenGL.framework"]; + if (framework) { + void *library_handle = dlopen([framework.executablePath UTF8String], RTLD_NOW); + if (library_handle) { + CGLEnable = (CGLEnablePtr)dlsym(library_handle, "CGLEnable"); + CGLSetParameter = (CGLSetParameterPtr)dlsym(library_handle, "CGLSetParameter"); + CGLGetCurrentContext = (CGLGetCurrentContextPtr)dlsym(library_handle, "CGLGetCurrentContext"); + + framework_loaded = CGLEnable && CGLSetParameter && CGLGetCurrentContext; + } + } } GLManagerLegacy_MacOS::~GLManagerLegacy_MacOS() { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 48ade1e5cc..c12b95314e 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -817,6 +817,8 @@ TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) { PackedStringArray TileMap::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); + warnings.push_back(RTR("The TileMap node is deprecated as it is superseded by the use of multiple TileMapLayer nodes.\nTo convert a TileMap to a set of TileMapLayer nodes, open the TileMap bottom panel with this node selected, click the toolbox icon in the top-right corner and choose \"Extract TileMap layers as individual TileMapLayer nodes\".")); + // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; for (const TileMapLayer *layer : layers) { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 3d24b3bbe9..5a4e176e99 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -300,7 +300,7 @@ void Skeleton3D::setup_simulator() { simulator = sim; sim->is_compat = true; sim->set_active(false); // Don't run unneeded process. - add_child(simulator); + add_child(simulator, false, INTERNAL_MODE_BACK); set_animate_physical_bones(animate_physical_bones); } #endif // _DISABLE_DEPRECATED diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index f62421061b..443fe4774a 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -608,7 +608,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) { return; } // Disable clicks under a time threshold to avoid selection right when opening the popup. - if (was_during_grabbed_click && OS::get_singleton()->get_ticks_msec() - popup_time_msec < 150) { + if (was_during_grabbed_click && OS::get_singleton()->get_ticks_msec() - popup_time_msec < 400) { return; } @@ -1064,6 +1064,7 @@ void PopupMenu::_notification(int p_what) { } break; case NOTIFICATION_POST_POPUP: { + popup_time_msec = OS::get_singleton()->get_ticks_msec(); initial_button_mask = Input::get_singleton()->get_mouse_button_mask(); during_grabbed_click = (bool)initial_button_mask; } break; diff --git a/servers/rendering/dummy/storage/light_storage.cpp b/servers/rendering/dummy/storage/light_storage.cpp new file mode 100644 index 0000000000..443e047b37 --- /dev/null +++ b/servers/rendering/dummy/storage/light_storage.cpp @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* light_storage.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "light_storage.h" + +using namespace RendererDummy; + +LightStorage *LightStorage::singleton = nullptr; + +LightStorage *LightStorage::get_singleton() { + return singleton; +} + +LightStorage::LightStorage() { + singleton = this; +} + +LightStorage::~LightStorage() { + singleton = nullptr; +} + +bool LightStorage::free(RID p_rid) { + if (owns_lightmap(p_rid)) { + lightmap_free(p_rid); + return true; + } else if (owns_lightmap_instance(p_rid)) { + lightmap_instance_free(p_rid); + return true; + } + + return false; +} + +/* LIGHTMAP API */ + +RID LightStorage::lightmap_allocate() { + return lightmap_owner.allocate_rid(); +} + +void LightStorage::lightmap_initialize(RID p_lightmap) { + lightmap_owner.initialize_rid(p_lightmap, Lightmap()); +} + +void LightStorage::lightmap_free(RID p_rid) { + lightmap_set_textures(p_rid, RID(), false); + lightmap_owner.free(p_rid); +} + +/* LIGHTMAP INSTANCE */ + +RID LightStorage::lightmap_instance_create(RID p_lightmap) { + LightmapInstance li; + li.lightmap = p_lightmap; + return lightmap_instance_owner.make_rid(li); +} + +void LightStorage::lightmap_instance_free(RID p_lightmap) { + lightmap_instance_owner.free(p_lightmap); +} diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 0a9602b603..c3b63cdbf6 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -36,7 +36,29 @@ namespace RendererDummy { class LightStorage : public RendererLightStorage { +private: + static LightStorage *singleton; + /* LIGHTMAP */ + struct Lightmap { + // dummy lightmap, no data + }; + + mutable RID_Owner<Lightmap, true> lightmap_owner; + /* LIGHTMAP INSTANCE */ + + struct LightmapInstance { + RID lightmap; + }; + + mutable RID_Owner<LightmapInstance> lightmap_instance_owner; + public: + static LightStorage *get_singleton(); + + LightStorage(); + virtual ~LightStorage(); + + bool free(RID p_rid); /* Light API */ virtual RID directional_light_allocate() override { return RID(); } @@ -146,9 +168,11 @@ public: /* LIGHTMAP CAPTURE */ - virtual RID lightmap_allocate() override { return RID(); } - virtual void lightmap_initialize(RID p_rid) override {} - virtual void lightmap_free(RID p_rid) override {} + bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); } + + virtual RID lightmap_allocate() override; + virtual void lightmap_initialize(RID p_rid) override; + virtual void lightmap_free(RID p_rid) override; virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override {} virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} @@ -167,8 +191,10 @@ public: /* LIGHTMAP INSTANCE */ - RID lightmap_instance_create(RID p_lightmap) override { return RID(); } - void lightmap_instance_free(RID p_lightmap) override {} + bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } + + RID lightmap_instance_create(RID p_lightmap) override; + void lightmap_instance_free(RID p_lightmap) override; void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override {} /* SHADOW ATLAS API */ diff --git a/servers/rendering/dummy/storage/utilities.h b/servers/rendering/dummy/storage/utilities.h index 6e8af9afac..ae83547afd 100644 --- a/servers/rendering/dummy/storage/utilities.h +++ b/servers/rendering/dummy/storage/utilities.h @@ -31,6 +31,7 @@ #ifndef UTILITIES_DUMMY_H #define UTILITIES_DUMMY_H +#include "light_storage.h" #include "material_storage.h" #include "mesh_storage.h" #include "servers/rendering/storage/utilities.h" @@ -55,12 +56,16 @@ public: return RS::INSTANCE_MESH; } else if (RendererDummy::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { return RS::INSTANCE_MULTIMESH; + } else if (RendererDummy::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + return RS::INSTANCE_LIGHTMAP; } return RS::INSTANCE_NONE; } virtual bool free(RID p_rid) override { - if (RendererDummy::TextureStorage::get_singleton()->owns_texture(p_rid)) { + if (RendererDummy::LightStorage::get_singleton()->free(p_rid)) { + return true; + } else if (RendererDummy::TextureStorage::get_singleton()->owns_texture(p_rid)) { RendererDummy::TextureStorage::get_singleton()->texture_free(p_rid); return true; } else if (RendererDummy::MeshStorage::get_singleton()->owns_mesh(p_rid)) { |