Merge pull request #107080 from kleonc/tile_map_transformed_tile_dest_rect_fix
Fix rotated/flipped tiles destination rect calculations
This commit is contained in:
@ -968,33 +968,12 @@ void TileMapLayerEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the offset
|
|
||||||
Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
|
Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
|
||||||
Vector2i tile_offset = tile_data->get_texture_origin();
|
|
||||||
|
|
||||||
// Compute the destination rectangle in the CanvasItem.
|
// Compute the destination rectangle in the CanvasItem.
|
||||||
Rect2 dest_rect;
|
Rect2 dest_rect;
|
||||||
dest_rect.size = source_rect.size;
|
bool transpose;
|
||||||
|
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);
|
||||||
bool transpose = tile_data->get_transpose() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
|
||||||
if (transpose) {
|
|
||||||
dest_rect.position = (tile_set->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2);
|
|
||||||
SWAP(tile_offset.x, tile_offset.y);
|
|
||||||
} else {
|
|
||||||
dest_rect.position = (tile_set->map_to_local(E.key) - dest_rect.size / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile_data->get_flip_h() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
|
|
||||||
dest_rect.size.x = -dest_rect.size.x;
|
|
||||||
tile_offset.x = -tile_offset.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile_data->get_flip_v() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
|
|
||||||
dest_rect.size.y = -dest_rect.size.y;
|
|
||||||
tile_offset.y = -tile_offset.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest_rect.position -= tile_offset;
|
|
||||||
|
|
||||||
// Get the tile modulation.
|
// Get the tile modulation.
|
||||||
Color modulate = tile_data->get_modulate() * edited_layer->get_modulate_in_tree() * edited_layer->get_self_modulate();
|
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() {
|
|||||||
drag_type = DRAG_TYPE_NONE;
|
drag_type = DRAG_TYPE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
|
void TileMapLayerEditorTilesPlugin::_apply_transform(TileTransformType p_type) {
|
||||||
if (selection_pattern.is_null() || selection_pattern->is_empty()) {
|
if (selection_pattern.is_null() || selection_pattern->is_empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<TileMapPattern> transformed_pattern;
|
Ref<TileMapPattern> transformed_pattern;
|
||||||
transformed_pattern.instantiate();
|
transformed_pattern.instantiate();
|
||||||
bool keep_shape = selection_pattern->get_size() == Vector2i(1, 1);
|
|
||||||
|
|
||||||
Vector2i size = selection_pattern->get_size();
|
Vector2i size = selection_pattern->get_size();
|
||||||
for (int y = 0; y < size.y; y++) {
|
for (int y = 0; y < size.y; y++) {
|
||||||
@ -1520,9 +1498,7 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
|
|||||||
|
|
||||||
Vector2i dst_coords;
|
Vector2i dst_coords;
|
||||||
|
|
||||||
if (keep_shape) {
|
if (p_type == TRANSFORM_ROTATE_LEFT) {
|
||||||
dst_coords = src_coords;
|
|
||||||
} else if (p_type == TRANSFORM_ROTATE_LEFT) {
|
|
||||||
dst_coords = Vector2i(y, size.x - x - 1);
|
dst_coords = Vector2i(y, size.x - x - 1);
|
||||||
} else if (p_type == TRANSFORM_ROTATE_RIGHT) {
|
} else if (p_type == TRANSFORM_ROTATE_RIGHT) {
|
||||||
dst_coords = Vector2i(size.y - y - 1, x);
|
dst_coords = Vector2i(size.y - y - 1, x);
|
||||||
@ -1541,46 +1517,28 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) {
|
|||||||
CanvasItemEditor::get_singleton()->update_viewport();
|
CanvasItemEditor::get_singleton()->update_viewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, int p_transform) {
|
int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, TileTransformType p_transform) {
|
||||||
bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
||||||
bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
||||||
bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
||||||
|
|
||||||
switch (p_transform) {
|
switch (p_transform) {
|
||||||
case TRANSFORM_ROTATE_LEFT:
|
case TRANSFORM_ROTATE_LEFT: { // (h, v, t) -> (v, !h, !t)
|
||||||
case TRANSFORM_ROTATE_RIGHT: {
|
bool negated_flip_h = !transform_flip_h;
|
||||||
// A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
|
transform_flip_h = transform_flip_v;
|
||||||
const LocalVector<bool> rotation_matrix = {
|
transform_flip_v = negated_flip_h;
|
||||||
// NOLINTBEGIN(modernize-use-bool-literals)
|
transform_transpose = !transform_transpose;
|
||||||
0, 0, 0,
|
|
||||||
0, 1, 1,
|
|
||||||
1, 1, 0,
|
|
||||||
1, 0, 1,
|
|
||||||
1, 0, 0,
|
|
||||||
0, 0, 1,
|
|
||||||
0, 1, 0,
|
|
||||||
1, 1, 1
|
|
||||||
// NOLINTEND(modernize-use-bool-literals)
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
} break;
|
||||||
case TRANSFORM_FLIP_H: {
|
case TRANSFORM_ROTATE_RIGHT: { // (h, v, t) -> (!v, h, !t)
|
||||||
|
bool negated_flip_v = !transform_flip_v;
|
||||||
|
transform_flip_v = transform_flip_h;
|
||||||
|
transform_flip_h = negated_flip_v;
|
||||||
|
transform_transpose = !transform_transpose;
|
||||||
|
} break;
|
||||||
|
case TRANSFORM_FLIP_H: { // (h, v, t) -> (!h, v, t)
|
||||||
transform_flip_h = !transform_flip_h;
|
transform_flip_h = !transform_flip_h;
|
||||||
} break;
|
} break;
|
||||||
case TRANSFORM_FLIP_V: {
|
case TRANSFORM_FLIP_V: { // (h, v, t) -> (h, !v, t)
|
||||||
transform_flip_v = !transform_flip_v;
|
transform_flip_v = !transform_flip_v;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin {
|
|||||||
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
|
GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum {
|
enum TileTransformType {
|
||||||
TRANSFORM_ROTATE_LEFT,
|
TRANSFORM_ROTATE_LEFT,
|
||||||
TRANSFORM_ROTATE_RIGHT,
|
TRANSFORM_ROTATE_RIGHT,
|
||||||
TRANSFORM_FLIP_H,
|
TRANSFORM_FLIP_H,
|
||||||
@ -146,8 +146,8 @@ private:
|
|||||||
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
|
||||||
void _stop_dragging();
|
void _stop_dragging();
|
||||||
|
|
||||||
void _apply_transform(int p_type);
|
void _apply_transform(TileTransformType p_type);
|
||||||
int _get_transformed_alternative(int p_alternative_id, int p_transform);
|
int _get_transformed_alternative(int p_alternative_id, TileTransformType p_transform);
|
||||||
|
|
||||||
///// Selection system. /////
|
///// Selection system. /////
|
||||||
RBSet<Vector2i> tile_map_selection;
|
RBSet<Vector2i> tile_map_selection;
|
||||||
@ -423,3 +423,5 @@ public:
|
|||||||
// Static functions.
|
// Static functions.
|
||||||
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
|
static Vector<Vector2i> get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VARIANT_ENUM_CAST(TileMapLayerEditorTilesPlugin::TileTransformType);
|
||||||
|
|||||||
@ -2598,34 +2598,10 @@ void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const
|
|||||||
// Get the tile modulation.
|
// Get the tile modulation.
|
||||||
Color modulate = tile_data->get_modulate();
|
Color modulate = tile_data->get_modulate();
|
||||||
|
|
||||||
// Compute the offset.
|
// Compute the dest rect.
|
||||||
Vector2 tile_offset = tile_data->get_texture_origin();
|
|
||||||
|
|
||||||
// Get destination rect.
|
|
||||||
Rect2 dest_rect;
|
Rect2 dest_rect;
|
||||||
dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size;
|
bool transpose;
|
||||||
dest_rect.size.x += FP_ADJUST;
|
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);
|
||||||
dest_rect.size.y += FP_ADJUST;
|
|
||||||
|
|
||||||
bool transpose = tile_data->get_transpose() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
|
||||||
if (transpose) {
|
|
||||||
dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2);
|
|
||||||
SWAP(tile_offset.x, tile_offset.y);
|
|
||||||
} else {
|
|
||||||
dest_rect.position = (p_position - dest_rect.size / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile_data->get_flip_h() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
|
|
||||||
dest_rect.size.x = -dest_rect.size.x;
|
|
||||||
tile_offset.x = -tile_offset.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tile_data->get_flip_v() ^ bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
|
|
||||||
dest_rect.size.y = -dest_rect.size.y;
|
|
||||||
tile_offset.y = -tile_offset.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest_rect.position -= tile_offset;
|
|
||||||
|
|
||||||
// Draw the tile.
|
// Draw the tile.
|
||||||
if (p_frame >= 0) {
|
if (p_frame >= 0) {
|
||||||
@ -2657,6 +2633,56 @@ void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
DEV_ASSERT(p_tile_data);
|
||||||
|
// Conceptually the order of transformations is (starting from the tile centered at the origin):
|
||||||
|
// - Per TileSet-tile transforms (transpose then flips).
|
||||||
|
// - Translation so texture origin is at the origin.
|
||||||
|
// - Per TileMapLayer-cell transforms (transpose then flips).
|
||||||
|
// - Translation to target position.
|
||||||
|
|
||||||
|
const bool tile_transpose = p_tile_data->get_transpose();
|
||||||
|
const bool tile_flip_h = p_tile_data->get_flip_h();
|
||||||
|
const bool tile_flip_v = p_tile_data->get_flip_v();
|
||||||
|
|
||||||
|
const Vector2 texture_origin = p_tile_data->get_texture_origin();
|
||||||
|
|
||||||
|
const bool cell_transpose = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
||||||
|
const bool cell_flip_h = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
|
||||||
|
const bool cell_flip_v = bool(p_alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
|
||||||
|
|
||||||
|
const bool final_transpose = tile_transpose != cell_transpose;
|
||||||
|
const bool final_flip_h = cell_flip_h != (cell_transpose ? tile_flip_v : tile_flip_h);
|
||||||
|
const bool final_flip_v = cell_flip_v != (cell_transpose ? tile_flip_h : tile_flip_v);
|
||||||
|
|
||||||
|
// Rect draw commands swap the size based on the passed transpose, so the size is left non-tranposed here.
|
||||||
|
// Position calculations need to use transposed size though.
|
||||||
|
Rect2 dest_rect;
|
||||||
|
dest_rect.size = p_dest_rect_size;
|
||||||
|
dest_rect.size.x += FP_ADJUST;
|
||||||
|
dest_rect.size.y += FP_ADJUST;
|
||||||
|
Vector2 transposed_size = final_transpose ? Vector2(dest_rect.size.y, dest_rect.size.x) : dest_rect.size;
|
||||||
|
if (final_flip_h) {
|
||||||
|
dest_rect.size.x = -dest_rect.size.x;
|
||||||
|
}
|
||||||
|
if (final_flip_v) {
|
||||||
|
dest_rect.size.y = -dest_rect.size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_rect.position = -0.5f * transposed_size;
|
||||||
|
dest_rect.position -= cell_transpose ? Vector2(texture_origin.y, texture_origin.x) : texture_origin;
|
||||||
|
if (cell_flip_h) {
|
||||||
|
dest_rect.position.x = -(dest_rect.position.x + transposed_size.x);
|
||||||
|
}
|
||||||
|
if (cell_flip_v) {
|
||||||
|
dest_rect.position.y = -(dest_rect.position.y + transposed_size.y);
|
||||||
|
}
|
||||||
|
dest_rect.position += p_position;
|
||||||
|
|
||||||
|
r_dest_rect = dest_rect;
|
||||||
|
r_transpose = final_transpose;
|
||||||
|
}
|
||||||
|
|
||||||
void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) {
|
void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) {
|
||||||
// Set the current cell tile (using integer position).
|
// Set the current cell tile (using integer position).
|
||||||
Vector2i pk(p_coords);
|
Vector2i pk(p_coords);
|
||||||
|
|||||||
@ -527,6 +527,7 @@ public:
|
|||||||
// Not exposed to users.
|
// Not exposed to users.
|
||||||
TileMapCell get_cell(const Vector2i &p_coords) const;
|
TileMapCell get_cell(const Vector2i &p_coords) const;
|
||||||
|
|
||||||
|
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);
|
||||||
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);
|
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);
|
||||||
|
|
||||||
////////////// Exposed functions //////////////
|
////////////// Exposed functions //////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user