|
|
@@ -379,13 +379,27 @@ public sealed class TilemapRenderer : IDisposable
|
|
|
/// <param name="flipFlags">The flip transformation flags.</param>
|
|
|
/// <param name="texture">The texture to calculate UVs from.</param>
|
|
|
/// <returns>An array of 4 UV coordinates: [top-left, top-right, bottom-left, bottom-right].</returns>
|
|
|
+ /// <remarks>
|
|
|
+ /// UV coordinates are inset by 0.5 texels to prevent texture bleeding and visible seams
|
|
|
+ /// between tiles. This is a common technique to avoid sampling pixels outside the intended
|
|
|
+ /// tile boundary when texture filtering is applied.
|
|
|
+ /// </remarks>
|
|
|
private Vector2[] CalculateTextureCoordinates(Rectangle sourceRect, TilemapTileFlipFlags flipFlags, Texture2D texture)
|
|
|
{
|
|
|
- // Normalize coordinates to 0-1 range
|
|
|
- float left = (float)sourceRect.Left / texture.Width;
|
|
|
- float right = (float)sourceRect.Right / texture.Width;
|
|
|
- float top = (float)sourceRect.Top / texture.Height;
|
|
|
- float bottom = (float)sourceRect.Bottom / texture.Height;
|
|
|
+ // Calculate texel size for UV inset
|
|
|
+ float texelWidth = 1.0f / texture.Width;
|
|
|
+ float texelHeight = 1.0f / texture.Height;
|
|
|
+
|
|
|
+ // Inset by 0.5 texels to prevent sampling outside tile boundaries
|
|
|
+ // This eliminates visible seams between tiles
|
|
|
+ float insetU = texelWidth * 0.5f;
|
|
|
+ float insetV = texelHeight * 0.5f;
|
|
|
+
|
|
|
+ // Normalize coordinates to 0-1 range with inset applied
|
|
|
+ float left = (sourceRect.Left + 0.5f) / texture.Width;
|
|
|
+ float right = (sourceRect.Right - 0.5f) / texture.Width;
|
|
|
+ float top = (sourceRect.Top + 0.5f) / texture.Height;
|
|
|
+ float bottom = (sourceRect.Bottom - 0.5f) / texture.Height;
|
|
|
|
|
|
// Apply horizontal flip
|
|
|
if ((flipFlags & TilemapTileFlipFlags.FlipHorizontally) != 0)
|
|
|
@@ -687,6 +701,9 @@ public sealed class TilemapRenderer : IDisposable
|
|
|
// Save GraphicsDevice state for SpriteBatch mixing
|
|
|
SaveGraphicsDeviceState();
|
|
|
|
|
|
+ // Configure sampler state to prevent tile seams
|
|
|
+ _graphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
|
|
|
+
|
|
|
// Set up matrices
|
|
|
_viewMatrix = camera.GetViewMatrix();
|
|
|
_projectionMatrix = Matrix.CreateOrthographicOffCenter(
|
|
|
@@ -924,6 +941,9 @@ public sealed class TilemapRenderer : IDisposable
|
|
|
if (!_isWorldMode)
|
|
|
throw new InvalidOperationException("Not in world mode. Use LoadWorld() first, or use Draw() for single tilemap.");
|
|
|
|
|
|
+ // Configure sampler state to prevent tile seams
|
|
|
+ _graphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
|
|
|
+
|
|
|
// Set up matrices
|
|
|
_viewMatrix = camera.GetViewMatrix();
|
|
|
_projectionMatrix = Matrix.CreateOrthographicOffCenter(
|