|
@@ -73,34 +73,21 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
|
|
|
|
|
|
// Show only the correct settings.
|
|
|
if (tool_buttons_group->get_pressed_button() == select_tool_button) {
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
|
|
|
+ transform_toolbar->show();
|
|
|
+ } else if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
|
|
|
tools_settings_vsep->show();
|
|
|
picker_button->show();
|
|
|
erase_button->show();
|
|
|
+ transform_toolbar->show();
|
|
|
tools_settings_vsep_2->show();
|
|
|
random_tile_toggle->show();
|
|
|
scatter_label->show();
|
|
|
scatter_spinbox->show();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
|
|
|
- tools_settings_vsep->show();
|
|
|
- picker_button->show();
|
|
|
- erase_button->show();
|
|
|
- tools_settings_vsep_2->show();
|
|
|
- random_tile_toggle->show();
|
|
|
- scatter_label->show();
|
|
|
- scatter_spinbox->show();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
|
|
|
- tools_settings_vsep->show();
|
|
|
- picker_button->show();
|
|
|
- erase_button->show();
|
|
|
- tools_settings_vsep_2->show();
|
|
|
- random_tile_toggle->show();
|
|
|
- scatter_label->show();
|
|
|
- scatter_spinbox->show();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
|
|
+ } else {
|
|
|
tools_settings_vsep->show();
|
|
|
picker_button->show();
|
|
|
erase_button->show();
|
|
|
+ transform_toolbar->show();
|
|
|
tools_settings_vsep_2->show();
|
|
|
bucket_contiguous_checkbox->show();
|
|
|
random_tile_toggle->show();
|
|
@@ -109,6 +96,31 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void TileMapEditorTilesPlugin::_update_transform_buttons() {
|
|
|
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
|
|
+ if (!tile_map) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Ref<TileSet> tile_set = tile_map->get_tileset();
|
|
|
+ if (tile_set.is_null() || selection_pattern.is_null()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE || selection_pattern->get_size() == Vector2i(1, 1)) {
|
|
|
+ transform_button_rotate_left->set_disabled(false);
|
|
|
+ transform_button_rotate_left->set_tooltip_text("");
|
|
|
+ transform_button_rotate_right->set_disabled(false);
|
|
|
+ transform_button_rotate_right->set_tooltip_text("");
|
|
|
+ } else {
|
|
|
+ const String tooltip_text = TTR("Can't rotate patterns when using non-square tile grid.");
|
|
|
+ transform_button_rotate_left->set_disabled(true);
|
|
|
+ transform_button_rotate_left->set_tooltip_text(tooltip_text);
|
|
|
+ transform_button_rotate_right->set_disabled(true);
|
|
|
+ transform_button_rotate_right->set_tooltip_text(tooltip_text);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
|
|
|
Vector<TileMapSubEditorPlugin::TabData> tabs;
|
|
|
tabs.push_back({ toolbar, tiles_bottom_panel });
|
|
@@ -480,6 +492,11 @@ void TileMapEditorTilesPlugin::_update_theme() {
|
|
|
erase_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser")));
|
|
|
random_tile_toggle->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator")));
|
|
|
|
|
|
+ transform_button_rotate_left->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateLeft"));
|
|
|
+ transform_button_rotate_right->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateRight"));
|
|
|
+ transform_button_flip_h->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorX"));
|
|
|
+ transform_button_flip_v->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorY"));
|
|
|
+
|
|
|
missing_atlas_texture_icon = tiles_bottom_panel->get_editor_theme_icon(SNAME("TileSet"));
|
|
|
_update_tile_set_sources_list();
|
|
|
}
|
|
@@ -573,8 +590,17 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
|
|
|
Ref<InputEventKey> k = p_event;
|
|
|
if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
|
|
|
for (BaseButton *b : viewport_shortcut_buttons) {
|
|
|
+ if (b->is_disabled()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
|
|
|
- b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
|
|
|
+ if (b->is_toggle_mode()) {
|
|
|
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
|
|
|
+ } else {
|
|
|
+ // Can't press a button without toggle mode, so just emit the signal directly.
|
|
|
+ b->emit_signal(SNAME("pressed"));
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -924,18 +950,18 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|
|
Rect2 dest_rect;
|
|
|
dest_rect.size = source_rect.size;
|
|
|
|
|
|
- bool transpose = tile_data->get_transpose();
|
|
|
+ bool transpose = tile_data->get_transpose() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
|
|
if (transpose) {
|
|
|
dest_rect.position = (tile_map->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
|
|
|
} else {
|
|
|
dest_rect.position = (tile_map->map_to_local(E.key) - dest_rect.size / 2 - tile_offset);
|
|
|
}
|
|
|
|
|
|
- if (tile_data->get_flip_h()) {
|
|
|
+ if (tile_data->get_flip_h() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
|
|
|
dest_rect.size.x = -dest_rect.size.x;
|
|
|
}
|
|
|
|
|
|
- if (tile_data->get_flip_v()) {
|
|
|
+ if (tile_data->get_flip_v() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
|
|
|
dest_rect.size.y = -dest_rect.size.y;
|
|
|
}
|
|
|
|
|
@@ -1475,6 +1501,94 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
|
|
|
drag_type = DRAG_TYPE_NONE;
|
|
|
}
|
|
|
|
|
|
+void TileMapEditorTilesPlugin::_apply_transform(int p_type) {
|
|
|
+ if (selection_pattern.is_null() || selection_pattern->is_empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Ref<TileMapPattern> transformed_pattern;
|
|
|
+ transformed_pattern.instantiate();
|
|
|
+ bool keep_shape = selection_pattern->get_size() == Vector2i(1, 1);
|
|
|
+
|
|
|
+ Vector2i size = selection_pattern->get_size();
|
|
|
+ for (int y = 0; y < size.y; y++) {
|
|
|
+ for (int x = 0; x < size.x; x++) {
|
|
|
+ Vector2i src_coords = Vector2i(x, y);
|
|
|
+ if (!selection_pattern->has_cell(src_coords)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector2i dst_coords;
|
|
|
+
|
|
|
+ if (keep_shape) {
|
|
|
+ dst_coords = src_coords;
|
|
|
+ } else if (p_type == TRANSFORM_ROTATE_LEFT) {
|
|
|
+ dst_coords = Vector2i(y, size.x - x - 1);
|
|
|
+ } else if (p_type == TRANSFORM_ROTATE_RIGHT) {
|
|
|
+ dst_coords = Vector2i(size.y - y - 1, x);
|
|
|
+ } else if (p_type == TRANSFORM_FLIP_H) {
|
|
|
+ dst_coords = Vector2i(size.x - x - 1, y);
|
|
|
+ } else if (p_type == TRANSFORM_FLIP_V) {
|
|
|
+ dst_coords = Vector2i(x, size.y - y - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ transformed_pattern->set_cell(dst_coords,
|
|
|
+ selection_pattern->get_cell_source_id(src_coords), selection_pattern->get_cell_atlas_coords(src_coords),
|
|
|
+ _get_transformed_alternative(selection_pattern->get_cell_alternative_tile(src_coords), p_type));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ selection_pattern = transformed_pattern;
|
|
|
+ CanvasItemEditor::get_singleton()->update_viewport();
|
|
|
+}
|
|
|
+
|
|
|
+int TileMapEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, int p_transform) {
|
|
|
+ bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
|
|
+ bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
|
|
+ bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
|
|
+
|
|
|
+ switch (p_transform) {
|
|
|
+ case TRANSFORM_ROTATE_LEFT:
|
|
|
+ case TRANSFORM_ROTATE_RIGHT: {
|
|
|
+ // A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
|
|
|
+ const LocalVector<bool> rotation_matrix = {
|
|
|
+ 0, 0, 0,
|
|
|
+ 0, 1, 1,
|
|
|
+ 1, 1, 0,
|
|
|
+ 1, 0, 1,
|
|
|
+ 1, 0, 0,
|
|
|
+ 0, 0, 1,
|
|
|
+ 0, 1, 0,
|
|
|
+ 1, 1, 1
|
|
|
+ };
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; i++) {
|
|
|
+ if (transform_flip_h == rotation_matrix[i * 3] && transform_flip_v == rotation_matrix[i * 3 + 1] && transform_transpose == rotation_matrix[i * 3 + 2]) {
|
|
|
+ if (p_transform == TRANSFORM_ROTATE_LEFT) {
|
|
|
+ i = i / 4 * 4 + (i + 1) % 4;
|
|
|
+ } else {
|
|
|
+ i = i / 4 * 4 + Math::posmod(i - 1, 4);
|
|
|
+ }
|
|
|
+ transform_flip_h = rotation_matrix[i * 3];
|
|
|
+ transform_flip_v = rotation_matrix[i * 3 + 1];
|
|
|
+ transform_transpose = rotation_matrix[i * 3 + 2];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+ case TRANSFORM_FLIP_H: {
|
|
|
+ transform_flip_h = !transform_flip_h;
|
|
|
+ } break;
|
|
|
+ case TRANSFORM_FLIP_V: {
|
|
|
+ transform_flip_v = !transform_flip_v;
|
|
|
+ } break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return TileSetAtlasSource::alternative_no_transform(p_alternative_id) |
|
|
|
+ int(transform_flip_h) * TileSetAtlasSource::TRANSFORM_FLIP_H |
|
|
|
+ int(transform_flip_v) * TileSetAtlasSource::TRANSFORM_FLIP_V |
|
|
|
+ int(transform_transpose) * TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
|
|
+}
|
|
|
+
|
|
|
void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
|
|
|
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
|
|
|
if (!tile_map) {
|
|
@@ -1589,6 +1703,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
|
|
|
coords_array.push_back(E);
|
|
|
}
|
|
|
selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
|
|
|
+ _update_transform_buttons();
|
|
|
}
|
|
|
|
|
|
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
|
|
@@ -1662,6 +1777,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_sele
|
|
|
vertical_offset += MAX(organized_size.y, 1);
|
|
|
}
|
|
|
CanvasItemEditor::get_singleton()->update_viewport();
|
|
|
+ _update_transform_buttons();
|
|
|
}
|
|
|
|
|
|
void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() {
|
|
@@ -1700,6 +1816,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
|
|
|
_update_source_display();
|
|
|
tile_atlas_control->queue_redraw();
|
|
|
alternative_tiles_control->queue_redraw();
|
|
|
+ _update_transform_buttons();
|
|
|
}
|
|
|
|
|
|
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
|
|
@@ -2161,6 +2278,39 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
|
|
|
tools_settings->add_child(erase_button);
|
|
|
viewport_shortcut_buttons.push_back(erase_button);
|
|
|
|
|
|
+ // Transform toolbar.
|
|
|
+ transform_toolbar = memnew(HBoxContainer);
|
|
|
+ tools_settings->add_child(transform_toolbar);
|
|
|
+ transform_toolbar->add_child(memnew(VSeparator));
|
|
|
+
|
|
|
+ transform_button_rotate_left = memnew(Button);
|
|
|
+ transform_button_rotate_left->set_flat(true);
|
|
|
+ transform_button_rotate_left->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_left", TTR("Rotate Tile Left"), Key::Z));
|
|
|
+ transform_toolbar->add_child(transform_button_rotate_left);
|
|
|
+ transform_button_rotate_left->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_LEFT));
|
|
|
+ viewport_shortcut_buttons.push_back(transform_button_rotate_left);
|
|
|
+
|
|
|
+ transform_button_rotate_right = memnew(Button);
|
|
|
+ transform_button_rotate_right->set_flat(true);
|
|
|
+ transform_button_rotate_right->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_right", TTR("Rotate Tile Right"), Key::X));
|
|
|
+ transform_toolbar->add_child(transform_button_rotate_right);
|
|
|
+ transform_button_rotate_right->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_RIGHT));
|
|
|
+ viewport_shortcut_buttons.push_back(transform_button_rotate_right);
|
|
|
+
|
|
|
+ transform_button_flip_h = memnew(Button);
|
|
|
+ transform_button_flip_h->set_flat(true);
|
|
|
+ transform_button_flip_h->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_horizontal", TTR("Flip Tile Horizontally"), Key::C));
|
|
|
+ transform_toolbar->add_child(transform_button_flip_h);
|
|
|
+ transform_button_flip_h->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_H));
|
|
|
+ viewport_shortcut_buttons.push_back(transform_button_flip_h);
|
|
|
+
|
|
|
+ transform_button_flip_v = memnew(Button);
|
|
|
+ transform_button_flip_v->set_flat(true);
|
|
|
+ transform_button_flip_v->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_vertical", TTR("Flip Tile Vertically"), Key::V));
|
|
|
+ transform_toolbar->add_child(transform_button_flip_v);
|
|
|
+ transform_button_flip_v->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_V));
|
|
|
+ viewport_shortcut_buttons.push_back(transform_button_flip_v);
|
|
|
+
|
|
|
// Separator 2.
|
|
|
tools_settings_vsep_2 = memnew(VSeparator);
|
|
|
tools_settings->add_child(tools_settings_vsep_2);
|
|
@@ -2352,25 +2502,11 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
|
|
|
}
|
|
|
|
|
|
// Show only the correct settings.
|
|
|
- if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
|
|
|
- tools_settings_vsep->show();
|
|
|
- picker_button->show();
|
|
|
- erase_button->show();
|
|
|
- tools_settings_vsep_2->hide();
|
|
|
- bucket_contiguous_checkbox->hide();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
|
|
|
+ if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
|
|
|
tools_settings_vsep->show();
|
|
|
picker_button->show();
|
|
|
erase_button->show();
|
|
|
- tools_settings_vsep_2->hide();
|
|
|
- bucket_contiguous_checkbox->hide();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
|
|
|
- tools_settings_vsep->show();
|
|
|
- picker_button->show();
|
|
|
- erase_button->show();
|
|
|
- tools_settings_vsep_2->hide();
|
|
|
- bucket_contiguous_checkbox->hide();
|
|
|
- } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
|
|
|
+ } else {
|
|
|
tools_settings_vsep->show();
|
|
|
picker_button->show();
|
|
|
erase_button->show();
|
|
@@ -3496,7 +3632,6 @@ TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
|
|
|
|
|
|
void TileMapEditor::_notification(int p_what) {
|
|
|
switch (p_what) {
|
|
|
- case NOTIFICATION_ENTER_TREE:
|
|
|
case NOTIFICATION_THEME_CHANGED: {
|
|
|
missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
|
|
|
warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
|