Browse Source

Duplicate resources pasted to other scenes

kobewi 4 years ago
parent
commit
36494e8526
4 changed files with 131 additions and 1 deletions
  1. 65 1
      editor/scene_tree_dock.cpp
  2. 6 0
      editor/scene_tree_dock.h
  3. 57 0
      scene/main/node.cpp
  4. 3 0
      scene/main/node.h

+ 65 - 1
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<Node::Comparator>();
 
@@ -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);
 
@@ -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() {

+ 6 - 0
editor/scene_tree_dock.h

@@ -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;
@@ -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;

+ 57 - 0
scene/main/node.cpp

@@ -2146,8 +2146,17 @@ 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
@@ -2155,6 +2164,54 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
 
 	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 {

+ 3 - 0
scene/main/node.h

@@ -362,6 +362,9 @@ public:
 	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