소스 검색

Fix rotated/flipped tiles destination rect calculations

kleonc 3 달 전
부모
커밋
f397e4f752
4개의 변경된 파일77개의 추가작업 그리고 90개의 파일을 삭제
  1. 18 60
      editor/plugins/tiles/tile_map_layer_editor.cpp
  2. 5 3
      editor/plugins/tiles/tile_map_layer_editor.h
  3. 53 27
      scene/2d/tile_map_layer.cpp
  4. 1 0
      scene/2d/tile_map_layer.h

+ 18 - 60
editor/plugins/tiles/tile_map_layer_editor.cpp

@@ -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 = 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.
 							// 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) {
-				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);
 				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_RIGHT: {
-			// A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
-			const LocalVector<bool> 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_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;
 		} break;
-		case TRANSFORM_FLIP_H: {
+		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;
 	}
 	}

+ 5 - 3
editor/plugins/tiles/tile_map_layer_editor.h

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

+ 53 - 27
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.
 		// Get the tile modulation.
 		Color modulate = tile_data->get_modulate();
 		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;
 		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.
 		// 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);

+ 1 - 0
scene/2d/tile_map_layer.h

@@ -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 //////////////