Skip to content

Commit 47d9521

Browse files
committed
Merge pull request #107080 from kleonc/tile_map_transformed_tile_dest_rect_fix
Fix rotated/flipped tiles destination rect calculations
2 parents 2cf16fb + f397e4f commit 47d9521

File tree

4 files changed

+77
-90
lines changed

4 files changed

+77
-90
lines changed

editor/plugins/tiles/tile_map_layer_editor.cpp

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -968,33 +968,12 @@ void TileMapLayerEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p
968968
continue;
969969
}
970970

971-
// Compute the offset
972971
Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
973-
Vector2i tile_offset = tile_data->get_texture_origin();
974972

975973
// Compute the destination rectangle in the CanvasItem.
976974
Rect2 dest_rect;
977-
dest_rect.size = source_rect.size;
978-
979-
bool transpose = tile_data->get_transpose() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
980-
if (transpose) {
981-
dest_rect.position = (tile_set->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2);
982-
SWAP(tile_offset.x, tile_offset.y);
983-
} else {
984-
dest_rect.position = (tile_set->map_to_local(E.key) - dest_rect.size / 2);
985-
}
986-
987-
if (tile_data->get_flip_h() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
988-
dest_rect.size.x = -dest_rect.size.x;
989-
tile_offset.x = -tile_offset.x;
990-
}
991-
992-
if (tile_data->get_flip_v() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
993-
dest_rect.size.y = -dest_rect.size.y;
994-
tile_offset.y = -tile_offset.y;
995-
}
996-
997-
dest_rect.position -= tile_offset;
975+
bool transpose;
976+
TileMapLayer::compute_transformed_tile_dest_rect(dest_rect, transpose, tile_set->map_to_local(E.key), source_rect.size, tile_data, E.value.alternative_tile);
998977

999978
// Get the tile modulation.
1000979
Color modulate = tile_data->get_modulate() * edited_layer->get_modulate_in_tree() * edited_layer->get_self_modulate();
@@ -1501,14 +1480,13 @@ void TileMapLayerEditorTilesPlugin::_stop_dragging() {
15011480
drag_type = DRAG_TYPE_NONE;
15021481
}
15031482

1504-
void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
1483+
void TileMapLayerEditorTilesPlugin::_apply_transform(TileTransformType p_type) {
15051484
if (selection_pattern.is_null() || selection_pattern->is_empty()) {
15061485
return;
15071486
}
15081487

15091488
Ref<TileMapPattern> transformed_pattern;
15101489
transformed_pattern.instantiate();
1511-
bool keep_shape = selection_pattern->get_size() == Vector2i(1, 1);
15121490

15131491
Vector2i size = selection_pattern->get_size();
15141492
for (int y = 0; y < size.y; y++) {
@@ -1520,9 +1498,7 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
15201498

15211499
Vector2i dst_coords;
15221500

1523-
if (keep_shape) {
1524-
dst_coords = src_coords;
1525-
} else if (p_type == TRANSFORM_ROTATE_LEFT) {
1501+
if (p_type == TRANSFORM_ROTATE_LEFT) {
15261502
dst_coords = Vector2i(y, size.x - x - 1);
15271503
} else if (p_type == TRANSFORM_ROTATE_RIGHT) {
15281504
dst_coords = Vector2i(size.y - y - 1, x);
@@ -1541,46 +1517,28 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
15411517
CanvasItemEditor::get_singleton()->update_viewport();
15421518
}
15431519

1544-
int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, int p_transform) {
1520+
int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, TileTransformType p_transform) {
15451521
bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
15461522
bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
15471523
bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
15481524

15491525
switch (p_transform) {
1550-
case TRANSFORM_ROTATE_LEFT:
1551-
case TRANSFORM_ROTATE_RIGHT: {
1552-
// A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
1553-
const LocalVector<bool> rotation_matrix = {
1554-
// NOLINTBEGIN(modernize-use-bool-literals)
1555-
0, 0, 0,
1556-
0, 1, 1,
1557-
1, 1, 0,
1558-
1, 0, 1,
1559-
1, 0, 0,
1560-
0, 0, 1,
1561-
0, 1, 0,
1562-
1, 1, 1
1563-
// NOLINTEND(modernize-use-bool-literals)
1564-
};
1565-
1566-
for (int i = 0; i < 8; i++) {
1567-
if (transform_flip_h == rotation_matrix[i * 3] && transform_flip_v == rotation_matrix[i * 3 + 1] && transform_transpose == rotation_matrix[i * 3 + 2]) {
1568-
if (p_transform == TRANSFORM_ROTATE_LEFT) {
1569-
i = i / 4 * 4 + (i + 1) % 4;
1570-
} else {
1571-
i = i / 4 * 4 + Math::posmod(i - 1, 4);
1572-
}
1573-
transform_flip_h = rotation_matrix[i * 3];
1574-
transform_flip_v = rotation_matrix[i * 3 + 1];
1575-
transform_transpose = rotation_matrix[i * 3 + 2];
1576-
break;
1577-
}
1578-
}
1526+
case TRANSFORM_ROTATE_LEFT: { // (h, v, t) -> (v, !h, !t)
1527+
bool negated_flip_h = !transform_flip_h;
1528+
transform_flip_h = transform_flip_v;
1529+
transform_flip_v = negated_flip_h;
1530+
transform_transpose = !transform_transpose;
1531+
} break;
1532+
case TRANSFORM_ROTATE_RIGHT: { // (h, v, t) -> (!v, h, !t)
1533+
bool negated_flip_v = !transform_flip_v;
1534+
transform_flip_v = transform_flip_h;
1535+
transform_flip_h = negated_flip_v;
1536+
transform_transpose = !transform_transpose;
15791537
} break;
1580-
case TRANSFORM_FLIP_H: {
1538+
case TRANSFORM_FLIP_H: { // (h, v, t) -> (!h, v, t)
15811539
transform_flip_h = !transform_flip_h;
15821540
} break;
1583-
case TRANSFORM_FLIP_V: {
1541+
case TRANSFORM_FLIP_V: { // (h, v, t) -> (h, !v, t)
15841542
transform_flip_v = !transform_flip_v;
15851543
} break;
15861544
}

editor/plugins/tiles/tile_map_layer_editor.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
7474
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
7575

7676
public:
77-
enum {
77+
enum TileTransformType {
7878
TRANSFORM_ROTATE_LEFT,
7979
TRANSFORM_ROTATE_RIGHT,
8080
TRANSFORM_FLIP_H,
@@ -146,8 +146,8 @@ class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
146146
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
147147
void _stop_dragging();
148148

149-
void _apply_transform(int p_type);
150-
int _get_transformed_alternative(int p_alternative_id, int p_transform);
149+
void _apply_transform(TileTransformType p_type);
150+
int _get_transformed_alternative(int p_alternative_id, TileTransformType p_transform);
151151

152152
///// Selection system. /////
153153
RBSet<Vector2i> tile_map_selection;
@@ -423,3 +423,5 @@ class TileMapLayerEditor : public VBoxContainer {
423423
// Static functions.
424424
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
425425
};
426+
427+
VARIANT_ENUM_CAST(TileMapLayerEditorTilesPlugin::TileTransformType);

scene/2d/tile_map_layer.cpp

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,34 +2598,10 @@ void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const
25982598
// Get the tile modulation.
25992599
Color modulate = tile_data->get_modulate();
26002600

2601-
// Compute the offset.
2602-
Vector2 tile_offset = tile_data->get_texture_origin();
2603-
2604-
// Get destination rect.
2601+
// Compute the dest rect.
26052602
Rect2 dest_rect;
2606-
dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
2607-
dest_rect.size.x += FP_ADJUST;
2608-
dest_rect.size.y += FP_ADJUST;
2609-
2610-
bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
2611-
if (transpose) {
2612-
dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2);
2613-
SWAP(tile_offset.x, tile_offset.y);
2614-
} else {
2615-
dest_rect.position = (p_position - dest_rect.size / 2);
2616-
}
2617-
2618-
if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
2619-
dest_rect.size.x = -dest_rect.size.x;
2620-
tile_offset.x = -tile_offset.x;
2621-
}
2622-
2623-
if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
2624-
dest_rect.size.y = -dest_rect.size.y;
2625-
tile_offset.y = -tile_offset.y;
2626-
}
2627-
2628-
dest_rect.position -= tile_offset;
2603+
bool transpose;
2604+
compute_transformed_tile_dest_rect(dest_rect, transpose, p_position, atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size, tile_data, p_alternative_tile);
26292605

26302606
// Draw the tile.
26312607
if (p_frame >= 0) {
@@ -2657,6 +2633,56 @@ void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const
26572633
}
26582634
}
26592635

2636+
void TileMapLayer::compute_transformed_tile_dest_rect(Rect2 &r_dest_rect, bool &r_transpose, const Vector2 &p_position, const Vector2 &p_dest_rect_size, const TileData *p_tile_data, int p_alternative_tile) {
2637+
DEV_ASSERT(p_tile_data);
2638+
// Conceptually the order of transformations is (starting from the tile centered at the origin):
2639+
// - Per TileSet-tile transforms (transpose then flips).
2640+
// - Translation so texture origin is at the origin.
2641+
// - Per TileMapLayer-cell transforms (transpose then flips).
2642+
// - Translation to target position.
2643+
2644+
const bool tile_transpose = p_tile_data->get_transpose();
2645+
const bool tile_flip_h = p_tile_data->get_flip_h();
2646+
const bool tile_flip_v = p_tile_data->get_flip_v();
2647+
2648+
const Vector2 texture_origin = p_tile_data->get_texture_origin();
2649+
2650+
const bool cell_transpose = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
2651+
const bool cell_flip_h = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
2652+
const bool cell_flip_v = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
2653+
2654+
const bool final_transpose = tile_transpose != cell_transpose;
2655+
const bool final_flip_h = cell_flip_h != (cell_transpose ? tile_flip_v : tile_flip_h);
2656+
const bool final_flip_v = cell_flip_v != (cell_transpose ? tile_flip_h : tile_flip_v);
2657+
2658+
// Rect draw commands swap the size based on the passed transpose, so the size is left non-tranposed here.
2659+
// Position calculations need to use transposed size though.
2660+
Rect2 dest_rect;
2661+
dest_rect.size = p_dest_rect_size;
2662+
dest_rect.size.x += FP_ADJUST;
2663+
dest_rect.size.y += FP_ADJUST;
2664+
Vector2 transposed_size = final_transpose ? Vector2(dest_rect.size.y, dest_rect.size.x) : dest_rect.size;
2665+
if (final_flip_h) {
2666+
dest_rect.size.x = -dest_rect.size.x;
2667+
}
2668+
if (final_flip_v) {
2669+
dest_rect.size.y = -dest_rect.size.y;
2670+
}
2671+
2672+
dest_rect.position = -0.5f * transposed_size;
2673+
dest_rect.position -= cell_transpose ? Vector2(texture_origin.y, texture_origin.x) : texture_origin;
2674+
if (cell_flip_h) {
2675+
dest_rect.position.x = -(dest_rect.position.x + transposed_size.x);
2676+
}
2677+
if (cell_flip_v) {
2678+
dest_rect.position.y = -(dest_rect.position.y + transposed_size.y);
2679+
}
2680+
dest_rect.position += p_position;
2681+
2682+
r_dest_rect = dest_rect;
2683+
r_transpose = final_transpose;
2684+
}
2685+
26602686
void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) {
26612687
// Set the current cell tile (using integer position).
26622688
Vector2i pk(p_coords);

scene/2d/tile_map_layer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ class TileMapLayer : public Node2D {
527527
// Not exposed to users.
528528
TileMapCell get_cell(const Vector2i &p_coords) const;
529529

530+
static void compute_transformed_tile_dest_rect(Rect2 &r_dest_rect, bool &r_transpose, const Vector2 &p_position, const Vector2 &p_dest_rect_size, const TileData *p_tile_data, int p_alternative_tile);
530531
static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, const TileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0);
531532

532533
////////////// Exposed functions //////////////

0 commit comments

Comments
 (0)