Skip to content

Commit

Permalink
Duplicate resources pasted to other scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
KoBeWi committed Feb 12, 2021
1 parent abe548d commit 36494e8
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
66 changes: 65 additions & 1 deletion editor/scene_tree_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Node::Comparator>();

Expand Down Expand Up @@ -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<RES, RES> 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<RES, RES> remap;
for (List<Node *>::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<Node *>::Element *E = node_clipboard.front(); E; E = E->next()) {
Node *node = E->get();
Map<const Node *, Node *> duplimap;
Node *dup = node->duplicate_from_editor(duplimap);

Node *dup = node->duplicate_from_editor(duplimap, resource_remap);

ERR_CONTINUE(!dup);

Expand Down Expand Up @@ -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<RES, RES> &r_remap) {
List<PropertyInfo> props;
p_node->get_property_list(&props);

for (List<PropertyInfo>::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<RES, RES> &r_remap) {
r_remap[p_resource] = p_resource->duplicate();

List<PropertyInfo> props;
p_resource->get_property_list(&props);

for (List<PropertyInfo>::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() {
Expand Down
6 changes: 6 additions & 0 deletions editor/scene_tree_dock.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ class SceneTreeDock : public VBoxContainer {

EditorData *editor_data;
EditorSelection *editor_selection;

List<Node *> node_clipboard;
String clipboard_source_scene;
HashMap<String, Map<RES, RES>> clipboard_resource_remap;

ScriptCreateDialog *script_create_dialog;
AcceptDialog *accept;
Expand Down Expand Up @@ -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<RES, RES> &r_remap);
void _create_remap_for_resource(RES p_resource, Map<RES, RES> &r_remap);

bool profile_allow_editing;
bool profile_allow_script_editing;
Expand Down
57 changes: 57 additions & 0 deletions scene/main/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2146,15 +2146,72 @@ Node *Node::duplicate(int p_flags) const {

#ifdef TOOLS_ENABLED
Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
return duplicate_from_editor(r_duplimap, Map<RES, RES>());
}

Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &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
_duplicate_signals(this, dupe);

return dupe;
}

void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const {
List<PropertyInfo> props;
p_node->get_property_list(&props);

for (List<PropertyInfo>::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<RES, RES> &p_resource_remap) const {
List<PropertyInfo> props;
p_resource->get_property_list(&props);

for (List<PropertyInfo>::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<Node *, Node *> &p_reown_map) const {
Expand Down
3 changes: 3 additions & 0 deletions scene/main/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ class Node : public Object {
Node *duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const;
#ifdef TOOLS_ENABLED
Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const;
Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const;
void remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const;
void remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const;
#endif

// used by editors, to save what has changed only
Expand Down

0 comments on commit 36494e8

Please sign in to comment.