GridMap editor fixes and improvements

This change fixes a few outstanding issues and greatly improves the usability
of the GridMap editor through the following changes:

- Copied mesh now gets displayed during pasting (also renamed the related
  identifiers accordingly)
- Duplication/paste indicator now gets rotated around the correct pivot point
  (duplication worked properly before, but the indicator was shown misplaced
  when rotated)
- Selected mesh library item cursor is no longer shown during selection and
  duplication/pasting
- Back rotate X/Y/Z is now working during duplication/pasting
- Added true cut operation thanks to now having a proper clipboard (clear
  operation got remapped to the DEL key)
- Got rid of some weird workarounds in the duplication code
- Fill and clear operations now correctly make the selection marker inactive
  as this was broken partly due to the workarounds mentioned above
  (duplication continues to keep the selection marker active to allow
  subsequent duplications)
- Clear current selection on RMB, but treat selection as an action so previous
  selection can be restored on undo
- Separated selection and paste indicator data as it's prone to error and
  confusion and it's anyway needed now that selection is treated as an action
- Added support for cancelling paste, selection, and even unselect the
  currently selected mesh library item with the ESC key (previously there
  wasn't a way to unselect)
- Changed the key binding of fill/clear/duplicate operations to use Ctrl as a
  modifier
- Changed erase to use RMB instead of Shift+RMB (free look is available
  through Shift+F anyway, so no need to occupy RMB for it during gridmap
  editing)
- Removed unused area, external connector, and configure menu items (there's
  also the non-functional clip mode menu items, but I'm not sure whether there
  are any plans with that, I suppose it's meant to be an editor aid)
- Renamed INPUT_COPY to INPUT_PICK to better reflect its purpose
- Added support for using Shift+Q and Shift+E to select multiple floors/planes
  without actually changing the current floor/plane as it happens when using
  e.g. the mouse wheel

Fixes #25373 and #15883
This commit is contained in:
Daniel Rakos 2019-01-28 11:09:36 +01:00
parent c991379b81
commit 07e2a86fc4
2 changed files with 285 additions and 143 deletions

View File

@ -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,49 +523,26 @@ 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::_update_duplicate_indicator() {
void GridMapEditor::_clear_clipboard_data() {
if (!selection.active || input_action != INPUT_DUPLICATE) {
for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
Transform xf;
xf.basis.set_zero();
VisualServer::get_singleton()->instance_set_transform(duplicate_instance, xf);
return;
VisualServer::get_singleton()->free(E->get().instance);
}
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();
Basis rot;
rot.set_orthogonal_index(selection.duplicate_rot);
xf.basis = rot * xf.basis;
VisualServer::get_singleton()->instance_set_transform(duplicate_instance, node->get_global_transform() * xf);
clipboard_items.clear();
}
struct __Item {
Vector3 pos;
int rot;
int item;
};
void GridMapEditor::_duplicate_paste() {
void GridMapEditor::_set_clipboard_data() {
if (!selection.active)
return;
_clear_clipboard_data();
int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS);
bool reselect = options->get_popup()->is_item_checked(idx);
List<__Item> items;
Basis rot;
rot.set_orthogonal_index(selection.duplicate_rot);
Ref<MeshLibrary> meshLibrary = node->get_mesh_library();
for (int i = selection.begin.x; i <= selection.end.x; i++) {
@ -532,44 +553,94 @@ void GridMapEditor::_duplicate_paste() {
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;
Ref<Mesh> mesh = meshLibrary->get_item_mesh(itm);
item.pos = selection.begin + rel;
item.item = itm;
item.rot = orm.get_orthogonal_index();
items.push_back(item);
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);
}
}
}
}
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;
void GridMapEditor::_update_paste_indicator() {
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();
if (input_action != INPUT_PASTE) {
Transform xf;
xf.basis.set_zero();
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(scale);
xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
Basis 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(paste_instance, node->get_global_transform() * xf);
for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
ClipboardItem &item = E->get();
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);
}
}
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(paste_indicator.orientation);
Vector3 ofs = paste_indicator.current - paste_indicator.click;
undo_redo->create_action(TTR("GridMap Paste Selection"));
for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
ClipboardItem &item = E->get();
Vector3 pos = rot.xform(item.grid_offset) + paste_indicator.begin + 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, 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_check_item("Paste Selects", MENU_OPTION_PASTE_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_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) {

View File

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