Browse Source

Merge pull request #25406 from aqnuep/gridmap_improvements

GridMap editor fixes and improvements
Rémi Verschelde 6 years ago
parent
commit
3cfab06080
2 changed files with 286 additions and 144 deletions
  1. 255 132
      modules/gridmap/grid_map_editor_plugin.cpp
  2. 31 12
      modules/gridmap/grid_map_editor_plugin.h

+ 255 - 132
modules/gridmap/grid_map_editor_plugin.cpp

@@ -67,9 +67,6 @@ void GridMapEditor::_menu_option(int p_option) {
 			floor->set_value(floor->get_value() + 1);
 		} break;
 
-		case MENU_OPTION_CONFIGURE: {
-
-		} break;
 		case MENU_OPTION_LOCK_VIEW: {
 
 			int index = options->get_popup()->get_item_index(MENU_OPTION_LOCK_VIEW);
@@ -121,14 +118,15 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_ROTATE_Y: {
 
 			Basis r;
-			if (input_action == INPUT_DUPLICATE) {
+			if (input_action == INPUT_PASTE) {
 
-				r.set_orthogonal_index(selection.duplicate_rot);
+				r.set_orthogonal_index(paste_indicator.orientation);
 				r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0);
-				selection.duplicate_rot = r.get_orthogonal_index();
-				_update_duplicate_indicator();
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
 				break;
 			}
+
 			r.set_orthogonal_index(cursor_rot);
 			r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0);
 			cursor_rot = r.get_orthogonal_index();
@@ -137,12 +135,12 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_ROTATE_X: {
 
 			Basis r;
-			if (input_action == INPUT_DUPLICATE) {
+			if (input_action == INPUT_PASTE) {
 
-				r.set_orthogonal_index(selection.duplicate_rot);
+				r.set_orthogonal_index(paste_indicator.orientation);
 				r.rotate(Vector3(1, 0, 0), -Math_PI / 2.0);
-				selection.duplicate_rot = r.get_orthogonal_index();
-				_update_duplicate_indicator();
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
 				break;
 			}
 
@@ -154,12 +152,12 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_ROTATE_Z: {
 
 			Basis r;
-			if (input_action == INPUT_DUPLICATE) {
+			if (input_action == INPUT_PASTE) {
 
-				r.set_orthogonal_index(selection.duplicate_rot);
+				r.set_orthogonal_index(paste_indicator.orientation);
 				r.rotate(Vector3(0, 0, 1), -Math_PI / 2.0);
-				selection.duplicate_rot = r.get_orthogonal_index();
-				_update_duplicate_indicator();
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
 				break;
 			}
 
@@ -171,6 +169,15 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_BACK_ROTATE_Y: {
 
 			Basis r;
+			if (input_action == INPUT_PASTE) {
+
+				r.set_orthogonal_index(paste_indicator.orientation);
+				r.rotate(Vector3(0, 1, 0), Math_PI / 2.0);
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
+				break;
+			}
+
 			r.set_orthogonal_index(cursor_rot);
 			r.rotate(Vector3(0, 1, 0), Math_PI / 2.0);
 			cursor_rot = r.get_orthogonal_index();
@@ -179,6 +186,15 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_BACK_ROTATE_X: {
 
 			Basis r;
+			if (input_action == INPUT_PASTE) {
+
+				r.set_orthogonal_index(paste_indicator.orientation);
+				r.rotate(Vector3(1, 0, 0), Math_PI / 2.0);
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
+				break;
+			}
+
 			r.set_orthogonal_index(cursor_rot);
 			r.rotate(Vector3(1, 0, 0), Math_PI / 2.0);
 			cursor_rot = r.get_orthogonal_index();
@@ -187,6 +203,15 @@ void GridMapEditor::_menu_option(int p_option) {
 		case MENU_OPTION_CURSOR_BACK_ROTATE_Z: {
 
 			Basis r;
+			if (input_action == INPUT_PASTE) {
+
+				r.set_orthogonal_index(paste_indicator.orientation);
+				r.rotate(Vector3(0, 0, 1), Math_PI / 2.0);
+				paste_indicator.orientation = r.get_orthogonal_index();
+				_update_paste_indicator();
+				break;
+			}
+
 			r.set_orthogonal_index(cursor_rot);
 			r.rotate(Vector3(0, 0, 1), Math_PI / 2.0);
 			cursor_rot = r.get_orthogonal_index();
@@ -194,10 +219,10 @@ void GridMapEditor::_menu_option(int p_option) {
 		} break;
 		case MENU_OPTION_CURSOR_CLEAR_ROTATION: {
 
-			if (input_action == INPUT_DUPLICATE) {
+			if (input_action == INPUT_PASTE) {
 
-				selection.duplicate_rot = 0;
-				_update_duplicate_indicator();
+				paste_indicator.orientation = 0;
+				_update_paste_indicator();
 				break;
 			}
 
@@ -205,28 +230,33 @@ void GridMapEditor::_menu_option(int p_option) {
 			_update_cursor_transform();
 		} break;
 
-		case MENU_OPTION_DUPLICATE_SELECTS: {
-			int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS);
+		case MENU_OPTION_PASTE_SELECTS: {
+			int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS);
 			options->get_popup()->set_item_checked(idx, !options->get_popup()->is_item_checked(idx));
 		} break;
-		case MENU_OPTION_SELECTION_DUPLICATE:
+
+		case MENU_OPTION_SELECTION_DUPLICATE: // fallthrough
+		case MENU_OPTION_SELECTION_CUT: {
 			if (!(selection.active && input_action == INPUT_NONE))
-				return;
-			if (last_mouseover == Vector3(-1, -1, -1)) //nono mouseovering anythin
 				break;
 
-			last_mouseover = selection.begin;
-			VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], Transform(Basis(), grid_ofs));
+			_set_clipboard_data();
 
-			input_action = INPUT_DUPLICATE;
-			selection.click = last_mouseover;
-			selection.current = last_mouseover;
-			selection.duplicate_rot = 0;
-			_update_duplicate_indicator();
-			break;
+			if (p_option == MENU_OPTION_SELECTION_CUT) {
+				_delete_selection();
+			}
+
+			input_action = INPUT_PASTE;
+			paste_indicator.click = selection.begin;
+			paste_indicator.current = selection.begin;
+			paste_indicator.begin = selection.begin;
+			paste_indicator.end = selection.end;
+			paste_indicator.orientation = 0;
+			_update_paste_indicator();
+		} break;
 		case MENU_OPTION_SELECTION_CLEAR: {
 			if (!selection.active)
-				return;
+				break;
 
 			_delete_selection();
 
@@ -315,17 +345,28 @@ void GridMapEditor::_validate_selection() {
 	_update_selection_transform();
 }
 
+void GridMapEditor::_set_selection(bool p_active, const Vector3 p_begin, const Vector3 p_end) {
+
+	selection.active = p_active;
+	selection.begin = p_begin;
+	selection.end = p_end;
+	selection.click = p_begin;
+	selection.current = p_end;
+
+	_update_selection_transform();
+}
+
 bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click) {
 
 	if (!spatial_editor)
 		return false;
 
-	if (selected_palette < 0 && input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE)
+	if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE)
 		return false;
 	Ref<MeshLibrary> mesh_library = node->get_mesh_library();
 	if (mesh_library.is_null())
 		return false;
-	if (input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE && !mesh_library->has_item(selected_palette))
+	if (input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette))
 		return false;
 
 	Camera *camera = p_camera;
@@ -386,13 +427,17 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo
 		cursor_origin = (Vector3(cell[0], cell[1], cell[2]) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size();
 		cursor_visible = true;
 
+		if (input_action == INPUT_SELECT || input_action == INPUT_PASTE) {
+			cursor_visible = false;
+		}
+
 		_update_cursor_transform();
 	}
 
-	if (input_action == INPUT_DUPLICATE) {
+	if (input_action == INPUT_PASTE) {
 
-		selection.current = Vector3(cell[0], cell[1], cell[2]);
-		_update_duplicate_indicator();
+		paste_indicator.current = Vector3(cell[0], cell[1], cell[2]);
+		_update_paste_indicator();
 
 	} else if (input_action == INPUT_SELECT) {
 
@@ -403,7 +448,7 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo
 		_validate_selection();
 
 		return true;
-	} else if (input_action == INPUT_COPY) {
+	} else if (input_action == INPUT_PICK) {
 
 		int item = node->get_cell_item(cell[0], cell[1], cell[2]);
 		if (item >= 0) {
@@ -456,10 +501,9 @@ void GridMapEditor::_delete_selection() {
 			}
 		}
 	}
+	undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end);
+	undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end);
 	undo_redo->commit_action();
-
-	selection.active = false;
-	_validate_selection();
 }
 
 void GridMapEditor::_fill_selection() {
@@ -479,97 +523,124 @@ void GridMapEditor::_fill_selection() {
 			}
 		}
 	}
+	undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end);
+	undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end);
 	undo_redo->commit_action();
+}
 
-	selection.active = false;
-	_validate_selection();
+void GridMapEditor::_clear_clipboard_data() {
+
+	for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
+
+		VisualServer::get_singleton()->free(E->get().instance);
+	}
+
+	clipboard_items.clear();
 }
 
-void GridMapEditor::_update_duplicate_indicator() {
+void GridMapEditor::_set_clipboard_data() {
 
-	if (!selection.active || input_action != INPUT_DUPLICATE) {
+	_clear_clipboard_data();
+
+	Ref<MeshLibrary> meshLibrary = node->get_mesh_library();
+
+	for (int i = selection.begin.x; i <= selection.end.x; i++) {
+
+		for (int j = selection.begin.y; j <= selection.end.y; j++) {
+
+			for (int k = selection.begin.z; k <= selection.end.z; k++) {
+
+				int itm = node->get_cell_item(i, j, k);
+				if (itm == GridMap::INVALID_CELL_ITEM)
+					continue;
+
+				Ref<Mesh> mesh = meshLibrary->get_item_mesh(itm);
+
+				ClipboardItem item;
+				item.cell_item = itm;
+				item.grid_offset = Vector3(i, j, k) - selection.begin;
+				item.orientation = node->get_cell_item_orientation(i, j, k);
+				item.instance = VisualServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world()->get_scenario());
+
+				clipboard_items.push_back(item);
+			}
+		}
+	}
+}
+
+void GridMapEditor::_update_paste_indicator() {
+
+	if (input_action != INPUT_PASTE) {
 
 		Transform xf;
 		xf.basis.set_zero();
-		VisualServer::get_singleton()->instance_set_transform(duplicate_instance, xf);
+		VisualServer::get_singleton()->instance_set_transform(paste_instance, xf);
 		return;
 	}
 
+	Vector3 center = 0.5 * Vector3(node->get_center_x(), node->get_center_y(), node->get_center_z());
+	Vector3 scale = (Vector3(1, 1, 1) + (paste_indicator.end - paste_indicator.begin)) * node->get_cell_size();
 	Transform xf;
-	xf.scale(Vector3(1, 1, 1) * (Vector3(1, 1, 1) + (selection.end - selection.begin)) * node->get_cell_size());
-	xf.origin = (selection.begin + (selection.current - selection.click)) * node->get_cell_size();
+	xf.scale(scale);
+	xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
 	Basis rot;
-	rot.set_orthogonal_index(selection.duplicate_rot);
+	rot.set_orthogonal_index(paste_indicator.orientation);
 	xf.basis = rot * xf.basis;
+	xf.translate((-center * node->get_cell_size()) / scale);
 
-	VisualServer::get_singleton()->instance_set_transform(duplicate_instance, node->get_global_transform() * xf);
-}
+	VisualServer::get_singleton()->instance_set_transform(paste_instance, node->get_global_transform() * xf);
 
-struct __Item {
-	Vector3 pos;
-	int rot;
-	int item;
-};
-void GridMapEditor::_duplicate_paste() {
+	for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
 
-	if (!selection.active)
-		return;
+		ClipboardItem &item = E->get();
 
-	int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS);
-	bool reselect = options->get_popup()->is_item_checked(idx);
+		xf = Transform();
+		xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
+		xf.basis = rot * xf.basis;
+		xf.translate(item.grid_offset * node->get_cell_size());
+
+		Basis item_rot;
+		item_rot.set_orthogonal_index(item.orientation);
+		xf.basis = item_rot * xf.basis;
+
+		VisualServer::get_singleton()->instance_set_transform(item.instance, node->get_global_transform() * xf);
+	}
+}
 
-	List<__Item> items;
+void GridMapEditor::_do_paste() {
+
+	int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS);
+	bool reselect = options->get_popup()->is_item_checked(idx);
 
 	Basis rot;
-	rot.set_orthogonal_index(selection.duplicate_rot);
+	rot.set_orthogonal_index(paste_indicator.orientation);
 
-	for (int i = selection.begin.x; i <= selection.end.x; i++) {
+	Vector3 ofs = paste_indicator.current - paste_indicator.click;
+	undo_redo->create_action(TTR("GridMap Paste Selection"));
 
-		for (int j = selection.begin.y; j <= selection.end.y; j++) {
+	for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
 
-			for (int k = selection.begin.z; k <= selection.end.z; k++) {
+		ClipboardItem &item = E->get();
 
-				int itm = node->get_cell_item(i, j, k);
-				if (itm == GridMap::INVALID_CELL_ITEM)
-					continue;
-				int orientation = node->get_cell_item_orientation(i, j, k);
-				__Item item;
-				Vector3 rel = Vector3(i, j, k) - selection.begin;
-				rel = rot.xform(rel);
-
-				Basis orm;
-				orm.set_orthogonal_index(orientation);
-				orm = rot * orm;
-
-				item.pos = selection.begin + rel;
-				item.item = itm;
-				item.rot = orm.get_orthogonal_index();
-				items.push_back(item);
-			}
-		}
-	}
+		Vector3 pos = rot.xform(item.grid_offset) + paste_indicator.begin + ofs;
 
-	Vector3 ofs = selection.current - selection.click;
-	if (items.size()) {
-		undo_redo->create_action(TTR("GridMap Duplicate Selection"));
-		for (List<__Item>::Element *E = items.front(); E; E = E->next()) {
-			__Item &it = E->get();
-			Vector3 pos = it.pos + ofs;
+		Basis orm;
+		orm.set_orthogonal_index(item.orientation);
+		orm = rot * orm;
 
-			undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, it.item, it.rot);
-			undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z));
-		}
-		undo_redo->commit_action();
+		undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, item.cell_item, orm.get_orthogonal_index());
+		undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z));
 	}
 
 	if (reselect) {
 
-		selection.begin += ofs;
-		selection.end += ofs;
-		selection.click = selection.begin;
-		selection.current = selection.end;
-		_validate_selection();
+		undo_redo->add_do_method(this, "_set_selection", true, paste_indicator.begin + ofs, paste_indicator.end + ofs);
+		undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end);
 	}
+
+	undo_redo->commit_action();
+
+	_clear_clipboard_data();
 }
 
 bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<InputEvent> &p_event) {
@@ -596,28 +667,31 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
 
 			if (mb->get_button_index() == BUTTON_LEFT) {
 
-				if (input_action == INPUT_DUPLICATE) {
-					//paste
-					_duplicate_paste();
+				if (input_action == INPUT_PASTE) {
+					_do_paste();
 					input_action = INPUT_NONE;
-					_update_duplicate_indicator();
+					_update_paste_indicator();
 				} else if (mb->get_shift()) {
 					input_action = INPUT_SELECT;
+					last_selection = selection;
 				} else if (mb->get_command()) {
-					input_action = INPUT_COPY;
+					input_action = INPUT_PICK;
 				} else {
 					input_action = INPUT_PAINT;
 					set_items.clear();
 				}
 			} else if (mb->get_button_index() == BUTTON_RIGHT) {
-				if (input_action == INPUT_DUPLICATE) {
+				if (input_action == INPUT_PASTE) {
+					_clear_clipboard_data();
 					input_action = INPUT_NONE;
-					_update_duplicate_indicator();
-				} else if (mb->get_shift()) {
+					_update_paste_indicator();
+					return true;
+				} else if (selection.active) {
+					_set_selection(false);
+					return true;
+				} else {
 					input_action = INPUT_ERASE;
 					set_items.clear();
-				} else {
-					return false;
 				}
 			} else {
 				return false;
@@ -650,13 +724,21 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
 				return set_items.size() > 0;
 			}
 
+			if (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_SELECT) {
+
+				undo_redo->create_action("GridMap Selection");
+				undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
+				undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
+				undo_redo->commit_action();
+			}
+
 			if (mb->get_button_index() == BUTTON_LEFT && input_action != INPUT_NONE) {
 
 				set_items.clear();
 				input_action = INPUT_NONE;
 				return true;
 			}
-			if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_DUPLICATE)) {
+			if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) {
 				input_action = INPUT_NONE;
 				return true;
 			}
@@ -670,6 +752,45 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu
 		return do_input_action(p_camera, mm->get_position(), false);
 	}
 
+	Ref<InputEventKey> k = p_event;
+
+	if (k.is_valid()) {
+		if (k->is_pressed()) {
+			if (k->get_scancode() == KEY_ESCAPE) {
+
+				if (input_action == INPUT_PASTE) {
+					_clear_clipboard_data();
+					input_action = INPUT_NONE;
+					_update_paste_indicator();
+					return true;
+				} else if (selection.active) {
+					_set_selection(false);
+					return true;
+				} else {
+					selected_palette = -1;
+					mesh_library_palette->unselect_all();
+					update_palette();
+					_update_cursor_instance();
+					return true;
+				}
+			}
+
+			if (k->get_shift() && selection.active && input_action != INPUT_PASTE) {
+
+				if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
+					selection.click[edit_axis]--;
+					_validate_selection();
+					return true;
+				}
+				if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
+					selection.click[edit_axis]++;
+					_validate_selection();
+					return true;
+				}
+			}
+		}
+	}
+
 	Ref<InputEventPanGesture> pan_gesture = p_event;
 	if (pan_gesture.is_valid()) {
 
@@ -818,7 +939,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
 	input_action = INPUT_NONE;
 	selection.active = false;
 	_update_selection_transform();
-	_update_duplicate_indicator();
+	_update_paste_indicator();
 
 	spatial_editor = Object::cast_to<SpatialEditorPlugin>(editor->get_editor_plugin_screen());
 
@@ -953,13 +1074,15 @@ void GridMapEditor::_notification(int p_what) {
 			}
 
 			selection_instance = VisualServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world()->get_scenario());
-			duplicate_instance = VisualServer::get_singleton()->instance_create2(duplicate_mesh, get_tree()->get_root()->get_world()->get_scenario());
+			paste_instance = VisualServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world()->get_scenario());
 
 			_update_selection_transform();
-			_update_duplicate_indicator();
+			_update_paste_indicator();
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
+			_clear_clipboard_data();
+
 			for (int i = 0; i < 3; i++) {
 
 				VS::get_singleton()->free(grid_instance[i]);
@@ -970,9 +1093,9 @@ void GridMapEditor::_notification(int p_what) {
 			}
 
 			VisualServer::get_singleton()->free(selection_instance);
-			VisualServer::get_singleton()->free(duplicate_instance);
+			VisualServer::get_singleton()->free(paste_instance);
 			selection_instance = RID();
-			duplicate_instance = RID();
+			paste_instance = RID();
 		} break;
 
 		case NOTIFICATION_PROCESS: {
@@ -1065,6 +1188,7 @@ void GridMapEditor::_bind_methods() {
 	ClassDB::bind_method("_configure", &GridMapEditor::_configure);
 	ClassDB::bind_method("_item_selected_cbk", &GridMapEditor::_item_selected_cbk);
 	ClassDB::bind_method("_floor_changed", &GridMapEditor::_floor_changed);
+	ClassDB::bind_method("_set_selection", &GridMapEditor::_set_selection);
 
 	ClassDB::bind_method(D_METHOD("_set_display_mode", "mode"), &GridMapEditor::_set_display_mode);
 }
@@ -1128,15 +1252,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
 	options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KEY_MASK_SHIFT + KEY_D);
 	options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, KEY_W);
 	options->get_popup()->add_separator();
-	options->get_popup()->add_check_item("Duplicate Selects", MENU_OPTION_DUPLICATE_SELECTS);
-	options->get_popup()->add_separator();
-	options->get_popup()->add_item(TTR("Create Area"), MENU_OPTION_SELECTION_MAKE_AREA, KEY_CONTROL + KEY_C);
-	options->get_popup()->add_item(TTR("Create Exterior Connector"), MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR);
-	options->get_popup()->add_item(TTR("Erase Area"), MENU_OPTION_REMOVE_AREA);
+	options->get_popup()->add_check_item("Paste Selects", MENU_OPTION_PASTE_SELECTS);
 	options->get_popup()->add_separator();
-	options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C);
-	options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X);
-	options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F);
+	options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_CTRL + KEY_C);
+	options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KEY_MASK_CTRL + KEY_X);
+	options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_DELETE);
+	options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_CTRL + KEY_F);
 
 	options->get_popup()->add_separator();
 	options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS);
@@ -1211,7 +1332,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
 	last_mouseover = Vector3(-1, -1, -1);
 
 	selection_mesh = VisualServer::get_singleton()->mesh_create();
-	duplicate_mesh = VisualServer::get_singleton()->mesh_create();
+	paste_mesh = VisualServer::get_singleton()->mesh_create();
 
 	{
 		//selection mesh create
@@ -1319,12 +1440,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
 		VisualServer::get_singleton()->mesh_surface_set_material(selection_mesh, 1, outer_mat->get_rid());
 
 		d[VS::ARRAY_VERTEX] = triangles;
-		VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_TRIANGLES, d);
-		VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 0, inner_mat->get_rid());
+		VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_TRIANGLES, d);
+		VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 0, inner_mat->get_rid());
 
 		d[VS::ARRAY_VERTEX] = lines;
-		VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_LINES, d);
-		VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 1, outer_mat->get_rid());
+		VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_LINES, d);
+		VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 1, outer_mat->get_rid());
 
 		for (int i = 0; i < 3; i++) {
 			d[VS::ARRAY_VERTEX] = square[i];
@@ -1341,6 +1462,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
 
 GridMapEditor::~GridMapEditor() {
 
+	_clear_clipboard_data();
+
 	for (int i = 0; i < 3; i++) {
 
 		if (grid[i].is_valid())
@@ -1359,9 +1482,9 @@ GridMapEditor::~GridMapEditor() {
 	if (selection_instance.is_valid())
 		VisualServer::get_singleton()->free(selection_instance);
 
-	VisualServer::get_singleton()->free(duplicate_mesh);
-	if (duplicate_instance.is_valid())
-		VisualServer::get_singleton()->free(duplicate_instance);
+	VisualServer::get_singleton()->free(paste_mesh);
+	if (paste_instance.is_valid())
+		VisualServer::get_singleton()->free(paste_instance);
 }
 
 void GridMapEditorPlugin::_notification(int p_what) {

+ 31 - 12
modules/gridmap/grid_map_editor_plugin.h

@@ -54,9 +54,9 @@ class GridMapEditor : public VBoxContainer {
 		INPUT_NONE,
 		INPUT_PAINT,
 		INPUT_ERASE,
-		INPUT_COPY,
+		INPUT_PICK,
 		INPUT_SELECT,
-		INPUT_DUPLICATE,
+		INPUT_PASTE,
 	};
 
 	enum ClipMode {
@@ -116,8 +116,17 @@ class GridMapEditor : public VBoxContainer {
 	RID selection_instance;
 	RID selection_level_mesh[3];
 	RID selection_level_instance[3];
-	RID duplicate_mesh;
-	RID duplicate_instance;
+	RID paste_mesh;
+	RID paste_instance;
+
+	struct ClipboardItem {
+		int cell_item;
+		Vector3 grid_offset;
+		int orientation;
+		RID instance;
+	};
+
+	List<ClipboardItem> clipboard_items;
 
 	Ref<SpatialMaterial> indicator_mat;
 	Ref<SpatialMaterial> inner_mat;
@@ -132,9 +141,19 @@ class GridMapEditor : public VBoxContainer {
 		Vector3 current;
 		Vector3 begin;
 		Vector3 end;
-		int duplicate_rot;
 		bool active;
 	} selection;
+	Selection last_selection;
+
+	struct PasteIndicator {
+
+		Vector3 click;
+		Vector3 current;
+		Vector3 begin;
+		Vector3 end;
+		int orientation;
+	};
+	PasteIndicator paste_indicator;
 
 	bool cursor_visible;
 	Transform cursor_transform;
@@ -148,7 +167,6 @@ class GridMapEditor : public VBoxContainer {
 
 	enum Menu {
 
-		MENU_OPTION_CONFIGURE,
 		MENU_OPTION_NEXT_LEVEL,
 		MENU_OPTION_PREV_LEVEL,
 		MENU_OPTION_LOCK_VIEW,
@@ -165,13 +183,11 @@ class GridMapEditor : public VBoxContainer {
 		MENU_OPTION_CURSOR_BACK_ROTATE_X,
 		MENU_OPTION_CURSOR_BACK_ROTATE_Z,
 		MENU_OPTION_CURSOR_CLEAR_ROTATION,
-		MENU_OPTION_DUPLICATE_SELECTS,
-		MENU_OPTION_SELECTION_MAKE_AREA,
-		MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
+		MENU_OPTION_PASTE_SELECTS,
 		MENU_OPTION_SELECTION_DUPLICATE,
+		MENU_OPTION_SELECTION_CUT,
 		MENU_OPTION_SELECTION_CLEAR,
 		MENU_OPTION_SELECTION_FILL,
-		MENU_OPTION_REMOVE_AREA,
 		MENU_OPTION_GRIDMAP_SETTINGS
 
 	};
@@ -200,10 +216,13 @@ class GridMapEditor : public VBoxContainer {
 
 	void _icon_size_changed(float p_value);
 
-	void _update_duplicate_indicator();
-	void _duplicate_paste();
+	void _clear_clipboard_data();
+	void _set_clipboard_data();
+	void _update_paste_indicator();
+	void _do_paste();
 	void _update_selection_transform();
 	void _validate_selection();
+	void _set_selection(bool p_active, const Vector3 p_begin = Vector3(), const Vector3 p_end = Vector3());
 
 	void _floor_changed(float p_value);