diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d70c6000fc8..820e43aa977 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -417,6 +417,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!node_clipboard.is_empty()) { _clear_clipboard(); } + clipboard_source_scene = editor->get_edited_scene()->get_filename(); selection.sort_custom(); @@ -470,10 +471,24 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().create_action(TTR("Paste Node(s)")); editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); + Map resource_remap; + String target_scene = editor->get_edited_scene()->get_filename(); + if (target_scene != clipboard_source_scene) { + if (!clipboard_resource_remap.has(target_scene)) { + Map remap; + for (List::Element *E = node_clipboard.front(); E; E = E->next()) { + _create_remap_for_node(E->get(), remap); + } + clipboard_resource_remap[target_scene] = remap; + } + resource_remap = clipboard_resource_remap[target_scene]; + } + for (List::Element *E = node_clipboard.front(); E; E = E->next()) { Node *node = E->get(); Map duplimap; - Node *dup = node->duplicate_from_editor(duplimap); + + Node *dup = node->duplicate_from_editor(duplimap, resource_remap); ERR_CONTINUE(!dup); @@ -2890,6 +2905,55 @@ void SceneTreeDock::_clear_clipboard() { memdelete(E->get()); } node_clipboard.clear(); + clipboard_resource_remap.clear(); +} + +void SceneTreeDock::_create_remap_for_node(Node *p_node, Map &r_remap) { + List props; + p_node->get_property_list(&props); + + for (List::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant v = p_node->get(E->get().name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (res->get_path() == "" && !r_remap.has(res)) { + _create_remap_for_resource(res, r_remap); + } + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + _create_remap_for_node(p_node->get_child(i), r_remap); + } +} + +void SceneTreeDock::_create_remap_for_resource(RES p_resource, Map &r_remap) { + r_remap[p_resource] = p_resource->duplicate(); + + List props; + p_resource->get_property_list(&props); + + for (List::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant v = p_resource->get(E->get().name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (res->get_path() == "" && !r_remap.has(res)) { + _create_remap_for_resource(res, r_remap); + } + } + } + } } void SceneTreeDock::_bind_methods() { diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 0df833169e9..a042188be6d 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -128,7 +128,10 @@ class SceneTreeDock : public VBoxContainer { EditorData *editor_data; EditorSelection *editor_selection; + List node_clipboard; + String clipboard_source_scene; + HashMap> clipboard_resource_remap; ScriptCreateDialog *script_create_dialog; AcceptDialog *accept; @@ -233,7 +236,10 @@ class SceneTreeDock : public VBoxContainer { void _favorite_root_selected(const String &p_class); void _feature_profile_changed(); + void _clear_clipboard(); + void _create_remap_for_node(Node *p_node, Map &r_remap); + void _create_remap_for_resource(RES p_resource, Map &r_remap); bool profile_allow_editing; bool profile_allow_script_editing; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0c015160328..bda1187ad64 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2146,8 +2146,17 @@ Node *Node::duplicate(int p_flags) const { #ifdef TOOLS_ENABLED Node *Node::duplicate_from_editor(Map &r_duplimap) const { + return duplicate_from_editor(r_duplimap, Map()); +} + +Node *Node::duplicate_from_editor(Map &r_duplimap, const Map &p_resource_remap) const { Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. + if (!p_resource_remap.is_empty()) { + remap_node_resources(dupe, p_resource_remap); + } + // Duplication of signals must happen after all the node descendants have been copied, // because re-targeting of connections from some descendant to another is not possible // if the emitter node comes later in tree order than the receiver @@ -2155,6 +2164,54 @@ Node *Node::duplicate_from_editor(Map &r_duplimap) const { return dupe; } + +void Node::remap_node_resources(Node *p_node, const Map &p_resource_remap) const { + List props; + p_node->get_property_list(&props); + + for (List::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant v = p_node->get(E->get().name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (p_resource_remap.has(res)) { + p_node->set(E->get().name, p_resource_remap[res]); + remap_nested_resources(res, p_resource_remap); + } + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + remap_node_resources(p_node->get_child(i), p_resource_remap); + } +} + +void Node::remap_nested_resources(RES p_resource, const Map &p_resource_remap) const { + List props; + p_resource->get_property_list(&props); + + for (List::Element *E = props.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant v = p_resource->get(E->get().name); + if (v.is_ref()) { + RES res = v; + if (res.is_valid()) { + if (p_resource_remap.has(res)) { + p_resource->set(E->get().name, p_resource_remap[res]); + remap_nested_resources(res, p_resource_remap); + } + } + } + } +} #endif void Node::_duplicate_and_reown(Node *p_new_parent, const Map &p_reown_map) const { diff --git a/scene/main/node.h b/scene/main/node.h index a0dca757913..a5694886802 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -362,6 +362,9 @@ public: Node *duplicate_and_reown(const Map &p_reown_map) const; #ifdef TOOLS_ENABLED Node *duplicate_from_editor(Map &r_duplimap) const; + Node *duplicate_from_editor(Map &r_duplimap, const Map &p_resource_remap) const; + void remap_node_resources(Node *p_node, const Map &p_resource_remap) const; + void remap_nested_resources(RES p_resource, const Map &p_resource_remap) const; #endif // used by editors, to save what has changed only