diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index abb349e1d84..0087366daac 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -968,33 +968,12 @@ void TileMapLayerEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p continue; } - // Compute the offset 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. Rect2 dest_rect; - dest_rect.size = source_rect.size; - - 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; + 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); // Get the tile modulation. 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; } -void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) { +void TileMapLayerEditorTilesPlugin::_apply_transform(TileTransformType p_type) { if (selection_pattern.is_null() || selection_pattern->is_empty()) { return; } Ref 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++) { @@ -1520,9 +1498,7 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) { Vector2i dst_coords; - if (keep_shape) { - dst_coords = src_coords; - } else if (p_type == TRANSFORM_ROTATE_LEFT) { + 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); @@ -1541,46 +1517,28 @@ void TileMapLayerEditorTilesPlugin::_apply_transform(int p_type) { 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_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 rotation_matrix = { - // NOLINTBEGIN(modernize-use-bool-literals) - 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; - } - } + case TRANSFORM_ROTATE_LEFT: { // (h, v, t) -> (v, !h, !t) + bool negated_flip_h = !transform_flip_h; + transform_flip_h = transform_flip_v; + transform_flip_v = negated_flip_h; + transform_transpose = !transform_transpose; } 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; } break; - case TRANSFORM_FLIP_V: { + case TRANSFORM_FLIP_V: { // (h, v, t) -> (h, !v, t) transform_flip_v = !transform_flip_v; } break; } diff --git a/editor/plugins/tiles/tile_map_layer_editor.h b/editor/plugins/tiles/tile_map_layer_editor.h index 4ce45325f46..af78f4577ed 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.h +++ b/editor/plugins/tiles/tile_map_layer_editor.h @@ -74,7 +74,7 @@ class TileMapLayerEditorTilesPlugin : public TileMapLayerSubEditorPlugin { GDCLASS(TileMapLayerEditorTilesPlugin, TileMapLayerSubEditorPlugin); public: - enum { + enum TileTransformType { TRANSFORM_ROTATE_LEFT, TRANSFORM_ROTATE_RIGHT, TRANSFORM_FLIP_H, @@ -146,8 +146,8 @@ private: HashMap _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase); void _stop_dragging(); - void _apply_transform(int p_type); - int _get_transformed_alternative(int p_alternative_id, int p_transform); + void _apply_transform(TileTransformType p_type); + int _get_transformed_alternative(int p_alternative_id, TileTransformType p_transform); ///// Selection system. ///// RBSet tile_map_selection; @@ -423,3 +423,5 @@ public: // Static functions. static Vector get_line(const TileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell); }; + +VARIANT_ENUM_CAST(TileMapLayerEditorTilesPlugin::TileTransformType); diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index 371d4749b4b..92432c926f0 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -2598,34 +2598,10 @@ void TileMapLayer::draw_tile(RID p_canvas_item, const Vector2 &p_position, const // Get the tile modulation. Color modulate = tile_data->get_modulate(); - // Compute the offset. - Vector2 tile_offset = tile_data->get_texture_origin(); - - // Get destination rect. + // Compute the dest rect. Rect2 dest_rect; - dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; - dest_rect.size.x += FP_ADJUST; - 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; + bool transpose; + 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); // Draw the tile. 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) { // Set the current cell tile (using integer position). Vector2i pk(p_coords); diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 40a9c84f62e..7bca54d5cbe 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -527,6 +527,7 @@ public: // Not exposed to users. 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 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 //////////////