summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-06-03 10:15:45 +0800
committerchai <215380520@qq.com>2024-06-03 10:15:45 +0800
commitacea7b2e728787a0d83bbf83c8c1f042d2c32e7e (patch)
tree0bfec05c1ca2d71be2c337bcd110a0421f19318b /Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled
parent88febcb02bf127d961c6471d9e846c0e1315f5c3 (diff)
+ plugins project
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled')
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/ContentReaderExtensions.cs20
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj13
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModel.cs32
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModelBuilder.cs28
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapEffect.cs37
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModel.cs38
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModelBuilder.cs125
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModel.cs41
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs126
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapRenderer.cs191
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModel.cs22
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModelBuilder.cs16
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs75
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapEllipseContent.cs6
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapGroupLayerContent.cs20
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageContent.cs44
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageLayerContent.cs30
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs61
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerModelContent.cs139
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectContent.cs78
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectDrawOrderContent.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectLayerContent.cs28
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectTemplateContent.cs17
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapOrientationContent.cs12
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolygonContent.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolylineContent.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs28
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerAxisContent.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerIndexContent.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileContent.cs9
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileDrawOrderContent.cs12
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerContent.cs30
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataChunkContent.cs26
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataContent.cs33
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileOffsetContent.cs17
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs76
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetGridContent.cs16
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileAnimationFrameContent.cs18
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileContent.cs40
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMap.cs139
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapEllipseObject.cs17
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs17
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapHelper.cs51
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapImageLayer.cs18
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayer.cs26
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayerType.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObject.cs35
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectDrawOrder.cs8
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs20
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectType.cs11
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapOrientation.cs9
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolygonObject.cs15
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolylineObject.cs15
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapProperties.cs14
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs32
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapReader.cs229
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapRectangleObject.cs12
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTile.cs28
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileDrawOrder.cs10
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileFlipFlags.cs14
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileLayer.cs66
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileObject.cs18
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileset.cs70
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs46
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs150
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs36
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTileAnimationFrame.cs144
67 files changed, 2814 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/ContentReaderExtensions.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/ContentReaderExtensions.cs
new file mode 100644
index 0000000..86f1b6b
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/ContentReaderExtensions.cs
@@ -0,0 +1,20 @@
+using Microsoft.Xna.Framework.Content;
+
+namespace MonoGame.Extended.Tiled
+{
+ public static class ContentReaderExtensions
+ {
+ public static void ReadTiledMapProperties(this ContentReader reader, TiledMapProperties properties)
+ {
+ var count = reader.ReadInt32();
+
+ for (var i = 0; i < count; i++)
+ {
+ var key = reader.ReadString();
+ var value = new TiledMapPropertyValue(reader.ReadString());
+ ReadTiledMapProperties(reader, value.Properties);
+ properties[key] = value;
+ }
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj
new file mode 100644
index 0000000..a52cc65
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/MonoGame.Extended.Tiled.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>Support for Tiled maps to make MonoGame more awesome. See http://www.mapeditor.org</Description>
+ <PackageTags>monogame tiled maps orthographic isometric</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MonoGame.Extended.Graphics\MonoGame.Extended.Graphics.csproj" />
+ <ProjectReference Include="..\MonoGame.Extended\MonoGame.Extended.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModel.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModel.cs
new file mode 100644
index 0000000..791819a
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModel.cs
@@ -0,0 +1,32 @@
+using Microsoft.Xna.Framework.Graphics;
+using System;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public sealed class TiledMapAnimatedLayerModel : TiledMapLayerModel
+ {
+ public TiledMapAnimatedLayerModel(GraphicsDevice graphicsDevice, Texture2D texture, VertexPositionTexture[] vertices, ushort[] indices, TiledMapTilesetAnimatedTile[] animatedTilesetTiles, TiledMapTileFlipFlags[] animatedTilesetTileFlipFlags)
+ : base(graphicsDevice, texture, vertices, indices)
+ {
+ Vertices = vertices;
+ AnimatedTilesetTiles = animatedTilesetTiles;
+ _animatedTilesetFlipFlags = animatedTilesetTileFlipFlags;
+ }
+
+ public VertexPositionTexture[] Vertices { get; }
+ public TiledMapTilesetAnimatedTile[] AnimatedTilesetTiles { get; }
+ private readonly TiledMapTileFlipFlags[] _animatedTilesetFlipFlags;
+
+ public ReadOnlySpan<TiledMapTileFlipFlags> AnimatedTilesetFlipFlags => _animatedTilesetFlipFlags;
+
+ protected override VertexBuffer CreateVertexBuffer(GraphicsDevice graphicsDevice, int vertexCount)
+ {
+ return new DynamicVertexBuffer(graphicsDevice, VertexPositionTexture.VertexDeclaration, vertexCount, BufferUsage.WriteOnly);
+ }
+
+ protected override IndexBuffer CreateIndexBuffer(GraphicsDevice graphicsDevice, int indexCount)
+ {
+ return new DynamicIndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, indexCount, BufferUsage.WriteOnly); ;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModelBuilder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModelBuilder.cs
new file mode 100644
index 0000000..7afcc21
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapAnimatedLayerModelBuilder.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public class TiledMapAnimatedLayerModelBuilder : TiledMapLayerModelBuilder<TiledMapAnimatedLayerModel>
+ {
+ public TiledMapAnimatedLayerModelBuilder()
+ {
+ AnimatedTilesetTiles = new List<TiledMapTilesetAnimatedTile>();
+ AnimatedTilesetFlipFlags = new List<TiledMapTileFlipFlags>();
+ }
+
+ public List<TiledMapTilesetAnimatedTile> AnimatedTilesetTiles { get; }
+ public List<TiledMapTileFlipFlags> AnimatedTilesetFlipFlags { get; }
+
+ protected override void ClearBuffers()
+ {
+ AnimatedTilesetTiles.Clear();
+ AnimatedTilesetFlipFlags.Clear();
+ }
+
+ protected override TiledMapAnimatedLayerModel CreateModel(GraphicsDevice graphicsDevice, Texture2D texture)
+ {
+ return new TiledMapAnimatedLayerModel(graphicsDevice, texture, Vertices.ToArray(), Indices.ToArray(), AnimatedTilesetTiles.ToArray(), AnimatedTilesetFlipFlags.ToArray());
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapEffect.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapEffect.cs
new file mode 100644
index 0000000..b24e2e9
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapEffect.cs
@@ -0,0 +1,37 @@
+using Microsoft.Xna.Framework.Graphics;
+using MonoGame.Extended.Graphics.Effects;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public interface ITiledMapEffect : IEffectMatrices, ITextureEffect
+ {
+ float Alpha { get; set; }
+ }
+
+ public class TiledMapEffect : DefaultEffect, ITiledMapEffect
+ {
+ public TiledMapEffect(GraphicsDevice graphicsDevice)
+ : base(graphicsDevice)
+ {
+ Initialize();
+ }
+
+ public TiledMapEffect(GraphicsDevice graphicsDevice, byte[] byteCode)
+ : base(graphicsDevice, byteCode)
+ {
+ Initialize();
+ }
+
+ public TiledMapEffect(Effect cloneSource)
+ : base(cloneSource)
+ {
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ VertexColorEnabled = false;
+ TextureEnabled = true;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModel.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModel.cs
new file mode 100644
index 0000000..f837e1a
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModel.cs
@@ -0,0 +1,38 @@
+using System;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public abstract class TiledMapLayerModel : IDisposable
+ {
+ protected TiledMapLayerModel(GraphicsDevice graphicsDevice, Texture2D texture, VertexPositionTexture[] vertices, ushort[] indices)
+ {
+ Texture = texture;
+
+ // ReSharper disable once VirtualMemberCallInConstructor
+ VertexBuffer = CreateVertexBuffer(graphicsDevice, vertices.Length);
+ VertexBuffer.SetData(vertices, 0, vertices.Length);
+
+ // ReSharper disable once VirtualMemberCallInConstructor
+ IndexBuffer = CreateIndexBuffer(graphicsDevice, indices.Length);
+ IndexBuffer.SetData(indices, 0, indices.Length);
+
+ TriangleCount = indices.Length / 3;
+ }
+
+ public void Dispose()
+ {
+ IndexBuffer.Dispose();
+ VertexBuffer.Dispose();
+ }
+
+ public Texture2D Texture { get; }
+ public VertexBuffer VertexBuffer { get; }
+ public IndexBuffer IndexBuffer { get; }
+ public int TriangleCount { get; }
+
+ protected abstract VertexBuffer CreateVertexBuffer(GraphicsDevice graphicsDevice, int vertexCount);
+ protected abstract IndexBuffer CreateIndexBuffer(GraphicsDevice graphicsDevice, int indexCount);
+
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModelBuilder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModelBuilder.cs
new file mode 100644
index 0000000..fbc99d6
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModelBuilder.cs
@@ -0,0 +1,125 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public abstract class TiledMapLayerModelBuilder<T>
+ {
+ protected TiledMapLayerModelBuilder()
+ {
+ Indices = new List<ushort>();
+ Vertices = new List<VertexPositionTexture>();
+ }
+
+ public List<ushort> Indices { get; }
+ public List<VertexPositionTexture> Vertices { get; }
+ public bool IsFull => Vertices.Count + TiledMapHelper.VerticesPerTile >= TiledMapHelper.MaximumVerticesPerModel;
+ public bool IsBuildable => Vertices.Any();
+
+ protected abstract void ClearBuffers();
+ protected abstract T CreateModel(GraphicsDevice graphicsDevice, Texture2D texture);
+
+ public T Build(GraphicsDevice graphicsDevice, Texture2D texture)
+ {
+ var model = CreateModel(graphicsDevice, texture);
+ Vertices.Clear();
+ Indices.Clear();
+ ClearBuffers();
+ return model;
+ }
+
+ public void AddSprite(Texture2D texture, Point2 position, Rectangle sourceRectangle, TiledMapTileFlipFlags flipFlags)
+ {
+ Indices.AddRange(CreateTileIndices(Vertices.Count));
+ Debug.Assert(Indices.Count <= TiledMapHelper.MaximumIndicesPerModel);
+
+ Vertices.AddRange(CreateVertices(texture, position, sourceRectangle, flipFlags));
+ Debug.Assert(Vertices.Count <= TiledMapHelper.MaximumVerticesPerModel);
+ }
+
+ private static IEnumerable<VertexPositionTexture> CreateVertices(Texture2D texture, Vector2 position, Rectangle sourceRectangle, TiledMapTileFlipFlags flags = TiledMapTileFlipFlags.None)
+ {
+ var reciprocalWidth = 1f / texture.Width;
+ var reciprocalHeight = 1f / texture.Height;
+ var texelLeft = sourceRectangle.X * reciprocalWidth;
+ var texelTop = sourceRectangle.Y * reciprocalHeight;
+ var texelRight = (sourceRectangle.X + sourceRectangle.Width) * reciprocalWidth;
+ var texelBottom = (sourceRectangle.Y + sourceRectangle.Height) * reciprocalHeight;
+
+ VertexPositionTexture vertexTopLeft, vertexTopRight, vertexBottomLeft, vertexBottomRight;
+
+ vertexTopLeft.Position = new Vector3(position, 0);
+ vertexTopRight.Position = new Vector3(position + new Vector2(sourceRectangle.Width, 0), 0);
+ vertexBottomLeft.Position = new Vector3(position + new Vector2(0, sourceRectangle.Height), 0);
+ vertexBottomRight.Position = new Vector3(position + new Vector2(sourceRectangle.Width, sourceRectangle.Height), 0);
+
+ vertexTopLeft.TextureCoordinate.Y = texelTop;
+ vertexTopLeft.TextureCoordinate.X = texelLeft;
+
+ vertexTopRight.TextureCoordinate.Y = texelTop;
+ vertexTopRight.TextureCoordinate.X = texelRight;
+
+ vertexBottomLeft.TextureCoordinate.Y = texelBottom;
+ vertexBottomLeft.TextureCoordinate.X = texelLeft;
+
+ vertexBottomRight.TextureCoordinate.Y = texelBottom;
+ vertexBottomRight.TextureCoordinate.X = texelRight;
+
+ var flipDiagonally = (flags & TiledMapTileFlipFlags.FlipDiagonally) != 0;
+ var flipHorizontally = (flags & TiledMapTileFlipFlags.FlipHorizontally) != 0;
+ var flipVertically = (flags & TiledMapTileFlipFlags.FlipVertically) != 0;
+
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.X, ref vertexBottomLeft.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.Y, ref vertexBottomLeft.TextureCoordinate.Y);
+ }
+
+ if (flipHorizontally)
+ {
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.Y, ref vertexTopRight.TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertexBottomLeft.TextureCoordinate.Y, ref vertexBottomRight.TextureCoordinate.Y);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.X, ref vertexTopRight.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexBottomLeft.TextureCoordinate.X, ref vertexBottomRight.TextureCoordinate.X);
+ }
+ }
+
+ if (flipVertically)
+ {
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.X, ref vertexBottomLeft.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.X, ref vertexBottomRight.TextureCoordinate.X);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.Y, ref vertexBottomLeft.TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.Y, ref vertexBottomRight.TextureCoordinate.Y);
+ }
+ }
+
+ yield return vertexTopLeft;
+ yield return vertexTopRight;
+ yield return vertexBottomLeft;
+ yield return vertexBottomRight;
+ }
+
+ private static IEnumerable<ushort> CreateTileIndices(int indexOffset)
+ {
+ yield return (ushort)(0 + indexOffset);
+ yield return (ushort)(1 + indexOffset);
+ yield return (ushort)(2 + indexOffset);
+ yield return (ushort)(1 + indexOffset);
+ yield return (ushort)(3 + indexOffset);
+ yield return (ushort)(2 + indexOffset);
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModel.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModel.cs
new file mode 100644
index 0000000..52e56bc
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModel.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public class TiledMapModel : IDisposable
+ {
+ private readonly TiledMap _map;
+ private readonly Dictionary<TiledMapTileset, List<TiledMapTilesetAnimatedTile>> _animatedTilesByTileset;
+
+ public TiledMapModel(TiledMap map, Dictionary<TiledMapLayer, TiledMapLayerModel[]> layersOfLayerModels)
+ {
+ _map = map;
+ LayersOfLayerModels = layersOfLayerModels;
+ _animatedTilesByTileset = _map.Tilesets
+ .ToDictionary(i => i, i => i.Tiles.OfType<TiledMapTilesetAnimatedTile>()
+ .ToList());
+ }
+
+ public void Dispose()
+ {
+ foreach (var layerModel in LayersOfLayerModels)
+ foreach (var model in layerModel.Value)
+ model.Dispose();
+ }
+
+ public ReadOnlyCollection<TiledMapTileset> Tilesets => _map.Tilesets;
+ public ReadOnlyCollection<TiledMapLayer> Layers => _map.Layers;
+
+ // each layer has many models
+ public Dictionary<TiledMapLayer, TiledMapLayerModel[]> LayersOfLayerModels { get; }
+
+ public IEnumerable<TiledMapTilesetAnimatedTile> GetAnimatedTiles(int tilesetIndex)
+ {
+ var tileset = _map.Tilesets[tilesetIndex];
+ return _animatedTilesByTileset[tileset];
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs
new file mode 100644
index 0000000..b96925e
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapModelBuilder.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public class TiledMapModelBuilder
+ {
+ private readonly GraphicsDevice _graphicsDevice;
+
+ public TiledMapModelBuilder(GraphicsDevice graphicsDevice)
+ {
+ _graphicsDevice = graphicsDevice;
+ }
+
+ private IEnumerable<TiledMapLayerModel> CreateLayerModels(TiledMap map, TiledMapLayer layer)
+ {
+ switch(layer)
+ {
+ case TiledMapTileLayer tileLayer:
+ return CreateTileLayerModels(map, tileLayer);
+ case TiledMapImageLayer imageLayer:
+ return CreateImageLayerModels(imageLayer);
+ default:
+ return new List<TiledMapLayerModel>();
+ }
+
+ }
+
+ private IEnumerable<TiledMapLayerModel> CreateImageLayerModels(TiledMapImageLayer imageLayer)
+ {
+ var modelBuilder = new TiledMapStaticLayerModelBuilder();
+ modelBuilder.AddSprite(imageLayer.Image, imageLayer.Position, imageLayer.Image.Bounds, TiledMapTileFlipFlags.None);
+ yield return modelBuilder.Build(_graphicsDevice, imageLayer.Image);
+ }
+
+ private IEnumerable<TiledMapLayerModel> CreateTileLayerModels(TiledMap map, TiledMapTileLayer tileLayer)
+ {
+ var layerModels = new List<TiledMapLayerModel>();
+ var staticLayerBuilder = new TiledMapStaticLayerModelBuilder();
+ var animatedLayerBuilder = new TiledMapAnimatedLayerModelBuilder();
+
+ foreach (var tileset in map.Tilesets)
+ {
+ var firstGlobalIdentifier = map.GetTilesetFirstGlobalIdentifier(tileset);
+ var lastGlobalIdentifier = tileset.TileCount + firstGlobalIdentifier - 1;
+ var texture = tileset.Texture;
+
+ foreach (var tile in tileLayer.Tiles.Where(t => firstGlobalIdentifier <= t.GlobalIdentifier && t.GlobalIdentifier <= lastGlobalIdentifier))
+ {
+ var tileGid = tile.GlobalIdentifier;
+ var localTileIdentifier = tileGid - firstGlobalIdentifier;
+ var position = GetTilePosition(map, tile);
+ var sourceRectangle = tileset.GetTileRegion(localTileIdentifier);
+ var flipFlags = tile.Flags;
+
+ // animated tiles
+ var tilesetTile = tileset.Tiles.FirstOrDefault(x => x.LocalTileIdentifier == localTileIdentifier);
+ if (tilesetTile?.Texture is not null)
+ {
+ position.Y += map.TileHeight - sourceRectangle.Height;
+ texture = tilesetTile.Texture;
+ }
+
+ if (tilesetTile is TiledMapTilesetAnimatedTile animatedTilesetTile)
+ {
+ animatedLayerBuilder.AddSprite(texture, position, sourceRectangle, flipFlags);
+ animatedTilesetTile.CreateTextureRotations(tileset, flipFlags);
+ animatedLayerBuilder.AnimatedTilesetTiles.Add(animatedTilesetTile);
+ animatedLayerBuilder.AnimatedTilesetFlipFlags.Add(flipFlags);
+
+ if (animatedLayerBuilder.IsFull)
+ layerModels.Add(animatedLayerBuilder.Build(_graphicsDevice, texture));
+ }
+ else
+ {
+ staticLayerBuilder.AddSprite(texture, position, sourceRectangle, flipFlags);
+
+ if (staticLayerBuilder.IsFull)
+ layerModels.Add(staticLayerBuilder.Build(_graphicsDevice, texture));
+ }
+ }
+
+ if (staticLayerBuilder.IsBuildable)
+ layerModels.Add(staticLayerBuilder.Build(_graphicsDevice, texture));
+
+ if (animatedLayerBuilder.IsBuildable)
+ layerModels.Add(animatedLayerBuilder.Build(_graphicsDevice, texture));
+ }
+
+ return layerModels;
+ }
+
+ public TiledMapModel Build(TiledMap map)
+ {
+ var dictionary = new Dictionary<TiledMapLayer, TiledMapLayerModel[]>();
+ foreach (var layer in map.Layers)
+ BuildLayer(map, layer, dictionary);
+
+ return new TiledMapModel(map, dictionary);
+ }
+
+ private void BuildLayer(TiledMap map, TiledMapLayer layer, Dictionary<TiledMapLayer, TiledMapLayerModel[]> dictionary)
+ {
+ if (layer is TiledMapGroupLayer groupLayer)
+ foreach (var subLayer in groupLayer.Layers)
+ BuildLayer(map, subLayer, dictionary);
+ else
+ dictionary.Add(layer, CreateLayerModels(map, layer).ToArray());
+ }
+
+ private static Point2 GetTilePosition(TiledMap map, TiledMapTile mapTile)
+ {
+ switch (map.Orientation)
+ {
+ case TiledMapOrientation.Orthogonal:
+ return TiledMapHelper.GetOrthogonalPosition(mapTile.X, mapTile.Y, map.TileWidth, map.TileHeight);
+ case TiledMapOrientation.Isometric:
+ return TiledMapHelper.GetIsometricPosition(mapTile.X, mapTile.Y, map.TileWidth, map.TileHeight);
+ default:
+ throw new NotSupportedException($"{map.Orientation} Tiled Maps are not yet implemented.");
+ }
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapRenderer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapRenderer.cs
new file mode 100644
index 0000000..66babad
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapRenderer.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public class TiledMapRenderer : IDisposable
+ {
+ private readonly TiledMapModelBuilder _mapModelBuilder;
+ private readonly TiledMapEffect _defaultEffect;
+ private readonly GraphicsDevice _graphicsDevice;
+ private TiledMapModel _mapModel;
+ private Matrix _worldMatrix = Matrix.Identity;
+
+ public TiledMapRenderer(GraphicsDevice graphicsDevice, TiledMap map = null)
+ {
+ if (graphicsDevice == null) throw new ArgumentNullException(nameof(graphicsDevice));
+
+ _graphicsDevice = graphicsDevice;
+ _defaultEffect = new TiledMapEffect(graphicsDevice);
+ _mapModelBuilder = new TiledMapModelBuilder(graphicsDevice);
+
+ if(map != null)
+ LoadMap(map);
+ }
+
+ public void Dispose()
+ {
+ _mapModel?.Dispose();
+ _defaultEffect.Dispose();
+ }
+
+ public void LoadMap(TiledMap map)
+ {
+ _mapModel?.Dispose();
+ _mapModel = map != null ? _mapModelBuilder.Build(map) : null;
+ }
+
+ public void Update(GameTime gameTime)
+ {
+ if(_mapModel == null)
+ return;
+
+ for (var tilesetIndex = 0; tilesetIndex < _mapModel.Tilesets.Count; tilesetIndex++)
+ {
+ foreach (var animatedTilesetTile in _mapModel.GetAnimatedTiles(tilesetIndex))
+ animatedTilesetTile.Update(gameTime);
+ }
+
+ foreach(var layer in _mapModel.LayersOfLayerModels)
+ UpdateAnimatedLayerModels(layer.Value.OfType<TiledMapAnimatedLayerModel>());
+ }
+
+ private static unsafe void UpdateAnimatedLayerModels(IEnumerable<TiledMapAnimatedLayerModel> animatedLayerModels)
+ {
+ foreach (var animatedModel in animatedLayerModels)
+ {
+ // update the texture coordinates for each animated tile
+ fixed (VertexPositionTexture* fixedVerticesPointer = animatedModel.Vertices)
+ {
+ var verticesPointer = fixedVerticesPointer;
+ for (int i = 0; i < animatedModel.AnimatedTilesetTiles.Length; i++)
+ {
+ var currentFrameTextureCoordinates = animatedModel.AnimatedTilesetTiles[i].CurrentAnimationFrame.GetTextureCoordinates(animatedModel.AnimatedTilesetFlipFlags[i]);
+
+ // ReSharper disable ArrangeRedundantParentheses
+ (*verticesPointer++).TextureCoordinate = currentFrameTextureCoordinates[0];
+ (*verticesPointer++).TextureCoordinate = currentFrameTextureCoordinates[1];
+ (*verticesPointer++).TextureCoordinate = currentFrameTextureCoordinates[2];
+ (*verticesPointer++).TextureCoordinate = currentFrameTextureCoordinates[3];
+ // ReSharper restore ArrangeRedundantParentheses
+ }
+ }
+
+ // copy (upload) the updated vertices to the GPU's memory
+ animatedModel.VertexBuffer.SetData(animatedModel.Vertices, 0, animatedModel.Vertices.Length);
+ }
+ }
+
+ public void Draw(Matrix? viewMatrix = null, Matrix? projectionMatrix = null, Effect effect = null, float depth = 0.0f)
+ {
+ var viewMatrix1 = viewMatrix ?? Matrix.Identity;
+ var projectionMatrix1 = projectionMatrix ?? Matrix.CreateOrthographicOffCenter(0, _graphicsDevice.Viewport.Width, _graphicsDevice.Viewport.Height, 0, 0, -1);
+
+ Draw(ref viewMatrix1, ref projectionMatrix1, effect, depth);
+ }
+
+ public void Draw(ref Matrix viewMatrix, ref Matrix projectionMatrix, Effect effect = null, float depth = 0.0f)
+ {
+ if (_mapModel == null)
+ return;
+
+ for (var index = 0; index < _mapModel.Layers.Count; index++)
+ Draw(index, ref viewMatrix, ref projectionMatrix, effect, depth);
+ }
+
+ public void Draw(TiledMapLayer layer, Matrix? viewMatrix = null, Matrix? projectionMatrix = null, Effect effect = null, float depth = 0.0f)
+ {
+ var viewMatrix1 = viewMatrix ?? Matrix.Identity;
+ var projectionMatrix1 = projectionMatrix ?? Matrix.CreateOrthographicOffCenter(0, _graphicsDevice.Viewport.Width, _graphicsDevice.Viewport.Height, 0, 0, -1);
+
+ Draw(layer, ref viewMatrix1, ref projectionMatrix1, effect, depth);
+ }
+
+ public void Draw(int layerIndex, Matrix? viewMatrix = null, Matrix? projectionMatrix = null, Effect effect = null, float depth = 0.0f)
+ {
+ var viewMatrix1 = viewMatrix ?? Matrix.Identity;
+ var projectionMatrix1 = projectionMatrix ?? Matrix.CreateOrthographicOffCenter(0, _graphicsDevice.Viewport.Width, _graphicsDevice.Viewport.Height, 0, 0, -1);
+
+ Draw(layerIndex, ref viewMatrix1, ref projectionMatrix1, effect, depth);
+ }
+
+ public void Draw(int layerIndex, ref Matrix viewMatrix, ref Matrix projectionMatrix, Effect effect = null, float depth = 0.0f)
+ {
+ var layer = _mapModel.Layers[layerIndex];
+
+ Draw(layer, ref viewMatrix, ref projectionMatrix, effect, depth);
+ }
+
+ public void Draw(TiledMapLayer layer, ref Matrix viewMatrix, ref Matrix projectionMatrix, Effect effect = null, float depth = 0.0f)
+ {
+ if (_mapModel == null)
+ return;
+
+ if (!layer.IsVisible)
+ return;
+
+ if (layer is TiledMapObjectLayer)
+ return;
+
+ Draw(layer, Vector2.Zero, Vector2.One, ref viewMatrix, ref projectionMatrix, effect, depth);
+ }
+
+ private void Draw(TiledMapLayer layer, Vector2 parentOffset, Vector2 parentParallaxFactor, ref Matrix viewMatrix, ref Matrix projectionMatrix, Effect effect, float depth)
+ {
+ var offset = parentOffset + layer.Offset;
+ var parallaxFactor = parentParallaxFactor * layer.ParallaxFactor;
+
+ if (layer is TiledMapGroupLayer groupLayer)
+ {
+ foreach (var subLayer in groupLayer.Layers)
+ Draw(subLayer, offset, parallaxFactor, ref viewMatrix, ref projectionMatrix, effect, depth);
+ }
+ else
+ {
+ _worldMatrix.Translation = new Vector3(offset, depth);
+
+ var effect1 = effect ?? _defaultEffect;
+ var tiledMapEffect = effect1 as ITiledMapEffect;
+ if (tiledMapEffect == null)
+ return;
+
+ // model-to-world transform
+ tiledMapEffect.World = _worldMatrix;
+ tiledMapEffect.View = parallaxFactor == Vector2.One ? viewMatrix : IncludeParallax(viewMatrix, parallaxFactor);
+ tiledMapEffect.Projection = projectionMatrix;
+
+ foreach (var layerModel in _mapModel.LayersOfLayerModels[layer])
+ {
+ // desired alpha
+ tiledMapEffect.Alpha = layer.Opacity;
+
+ // desired texture
+ tiledMapEffect.Texture = layerModel.Texture;
+
+ // bind the vertex and index buffer
+ _graphicsDevice.SetVertexBuffer(layerModel.VertexBuffer);
+ _graphicsDevice.Indices = layerModel.IndexBuffer;
+
+ // for each pass in our effect
+ foreach (var pass in effect1.CurrentTechnique.Passes)
+ {
+ // apply the pass, effectively choosing which vertex shader and fragment (pixel) shader to use
+ pass.Apply();
+
+ // draw the geometry from the vertex buffer / index buffer
+ _graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, layerModel.TriangleCount);
+ }
+ }
+ }
+ }
+
+ private Matrix IncludeParallax(Matrix viewMatrix, Vector2 parallaxFactor)
+ {
+ viewMatrix.Translation *=new Vector3(parallaxFactor, 1f);
+ return viewMatrix;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModel.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModel.cs
new file mode 100644
index 0000000..34bf683
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModel.cs
@@ -0,0 +1,22 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public sealed class TiledMapStaticLayerModel : TiledMapLayerModel
+ {
+ public TiledMapStaticLayerModel(GraphicsDevice graphicsDevice, Texture2D texture, VertexPositionTexture[] vertices, ushort[] indices)
+ : base(graphicsDevice, texture, vertices, indices)
+ {
+ }
+
+ protected override VertexBuffer CreateVertexBuffer(GraphicsDevice graphicsDevice, int vertexCount)
+ {
+ return new VertexBuffer(graphicsDevice, VertexPositionTexture.VertexDeclaration, vertexCount, BufferUsage.WriteOnly);
+ }
+
+ protected override IndexBuffer CreateIndexBuffer(GraphicsDevice graphicsDevice, int indexCount)
+ {
+ return new IndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, indexCount, BufferUsage.WriteOnly); ;
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModelBuilder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModelBuilder.cs
new file mode 100644
index 0000000..8c7d49b
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapStaticLayerModelBuilder.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Renderers
+{
+ public class TiledMapStaticLayerModelBuilder : TiledMapLayerModelBuilder<TiledMapStaticLayerModel>
+ {
+ protected override void ClearBuffers()
+ {
+ }
+
+ protected override TiledMapStaticLayerModel CreateModel(GraphicsDevice graphicsDevice, Texture2D texture)
+ {
+ return new TiledMapStaticLayerModel(graphicsDevice, texture, Vertices.ToArray(), Indices.ToArray());
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs
new file mode 100644
index 0000000..30ecbfd
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapContent.cs
@@ -0,0 +1,75 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ [XmlRoot(ElementName = "map")]
+ public class TiledMapContent
+ {
+ public TiledMapContent()
+ {
+ Properties = new List<TiledMapPropertyContent>();
+ Tilesets = new List<TiledMapTilesetContent>();
+ Layers = new List<TiledMapLayerContent>();
+ }
+
+ [XmlIgnore]
+ public string Name { get; set; }
+
+ [XmlIgnore]
+ public string FilePath { get; set; }
+
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
+ [XmlAttribute(AttributeName = "version")]
+ public string Version { get; set; }
+
+ [XmlAttribute(AttributeName = "orientation")]
+ public TiledMapOrientationContent Orientation { get; set; }
+
+ [XmlAttribute(AttributeName = "renderorder")]
+ public TiledMapTileDrawOrderContent RenderOrder { get; set; }
+
+ [XmlAttribute(AttributeName = "backgroundcolor")]
+ public string BackgroundColor { get; set; }
+
+ [XmlAttribute(AttributeName = "width")]
+ public int Width { get; set; }
+
+ [XmlAttribute(AttributeName = "height")]
+ public int Height { get; set; }
+
+ [XmlAttribute(AttributeName = "tilewidth")]
+ public int TileWidth { get; set; }
+
+ [XmlAttribute(AttributeName = "tileheight")]
+ public int TileHeight { get; set; }
+
+ [XmlAttribute(AttributeName = "hexsidelength")]
+ public int HexSideLength { get; set; }
+
+ [XmlAttribute(AttributeName = "staggeraxis")]
+ public TiledMapStaggerAxisContent StaggerAxis { get; set; }
+
+ [XmlAttribute(AttributeName = "staggerindex")]
+ public TiledMapStaggerIndexContent StaggerIndex { get; set; }
+
+ [XmlElement(ElementName = "tileset")]
+ public List<TiledMapTilesetContent> Tilesets { get; set; }
+
+ [XmlElement(ElementName = "layer", Type = typeof(TiledMapTileLayerContent))]
+ [XmlElement(ElementName = "imagelayer", Type = typeof(TiledMapImageLayerContent))]
+ [XmlElement(ElementName = "objectgroup", Type = typeof(TiledMapObjectLayerContent))]
+ [XmlElement(ElementName = "group", Type = typeof(TiledMapGroupLayerContent))]
+ public List<TiledMapLayerContent> Layers { get; set; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapEllipseContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapEllipseContent.cs
new file mode 100644
index 0000000..78f7bdd
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapEllipseContent.cs
@@ -0,0 +1,6 @@
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapEllipseContent
+ {
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapGroupLayerContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapGroupLayerContent.cs
new file mode 100644
index 0000000..a72b276
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapGroupLayerContent.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapGroupLayerContent : TiledMapLayerContent
+ {
+ protected TiledMapGroupLayerContent()
+ : base(TiledMapLayerType.GroupLayer)
+ {
+ }
+
+ [XmlElement(ElementName = "layer", Type = typeof(TiledMapTileLayerContent))]
+ [XmlElement(ElementName = "imagelayer", Type = typeof(TiledMapImageLayerContent))]
+ [XmlElement(ElementName = "objectgroup", Type = typeof(TiledMapObjectLayerContent))]
+ [XmlElement(ElementName = "group", Type = typeof(TiledMapGroupLayerContent))]
+ public List<TiledMapLayerContent> Layers { get; set; }
+
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageContent.cs
new file mode 100644
index 0000000..714da37
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageContent.cs
@@ -0,0 +1,44 @@
+using System.Xml.Serialization;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapImageContent
+ {
+ //[XmlIgnore]
+ //public Texture2DContent Content { get; set; }
+
+ //[XmlIgnore]
+ //public ExternalReference<Texture2DContent> ContentRef { get; set; }
+
+ [XmlAttribute(AttributeName = "source")]
+ public string Source { get; set; }
+
+ [XmlAttribute(AttributeName = "width")]
+ public int Width { get; set; }
+
+ [XmlAttribute(AttributeName = "height")]
+ public int Height { get; set; }
+
+ [XmlAttribute(AttributeName = "format")]
+ public string Format { get; set; }
+
+ [XmlAttribute(AttributeName = "trans")]
+ public string RawTransparentColor { get; set; } = string.Empty;
+
+ [XmlIgnore]
+ public Color TransparentColor
+ {
+ get => RawTransparentColor == string.Empty ? Color.Transparent : ColorHelper.FromHex(RawTransparentColor);
+ set => RawTransparentColor = ColorHelper.ToHex(value);
+ }
+
+ [XmlElement(ElementName = "data")]
+ public TiledMapTileLayerDataContent Data { get; set; }
+
+ public override string ToString()
+ {
+ return Source;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageLayerContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageLayerContent.cs
new file mode 100644
index 0000000..f347d1e
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapImageLayerContent.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapImageLayerContent : TiledMapLayerContent
+ {
+ [XmlAttribute(AttributeName = "x")]
+ public int X { get; set; }
+
+ [XmlAttribute(AttributeName = "y")]
+ public int Y { get; set; }
+
+ [XmlElement(ElementName = "image")]
+ public TiledMapImageContent Image { get; set; }
+
+ public TiledMapImageLayerContent()
+ : base(TiledMapLayerType.ImageLayer)
+ {
+ Opacity = 1.0f;
+ Visible = true;
+ Properties = new List<TiledMapPropertyContent>();
+ }
+
+ public override string ToString()
+ {
+ return $"{Name}: {Image}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs
new file mode 100644
index 0000000..01d8a9d
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerContent.cs
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ [XmlInclude(typeof(TiledMapTileLayerContent))]
+ [XmlInclude(typeof(TiledMapImageLayerContent))]
+ [XmlInclude(typeof(TiledMapObjectLayerContent))]
+ public abstract class TiledMapLayerContent
+ {
+ protected TiledMapLayerContent(TiledMapLayerType layerType)
+ {
+ LayerType = layerType;
+ Opacity = 1.0f;
+ ParallaxX = 1.0f;
+ ParallaxY = 1.0f;
+ Visible = true;
+ Properties = new List<TiledMapPropertyContent>();
+ }
+
+ [XmlAttribute(AttributeName = "name")]
+ public string Name { get; set; }
+
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
+ [XmlAttribute(AttributeName = "opacity")]
+ public float Opacity { get; set; }
+
+ [XmlAttribute(AttributeName = "visible")]
+ public bool Visible { get; set; }
+
+ [XmlAttribute(AttributeName = "offsetx")]
+ public float OffsetX { get; set; }
+
+ [XmlAttribute(AttributeName = "offsety")]
+ public float OffsetY { get; set; }
+
+ [XmlAttribute(AttributeName = "parallaxx")]
+ public float ParallaxX { get; set; }
+
+ [XmlAttribute(AttributeName = "parallaxy")]
+ public float ParallaxY { get; set; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+
+ [XmlIgnore]
+ public TiledMapLayerType LayerType { get; }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerModelContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerModelContent.cs
new file mode 100644
index 0000000..4c2a761
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapLayerModelContent.cs
@@ -0,0 +1,139 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapLayerModelContent
+ {
+ private readonly List<VertexPositionTexture> _vertices;
+ private readonly List<ushort> _indices;
+
+ public string LayerName { get; }
+ public ReadOnlyCollection<VertexPositionTexture> Vertices { get; }
+ public ReadOnlyCollection<ushort> Indices { get; }
+ public Size2 ImageSize { get; }
+ public string TextureAssetName { get; }
+
+ public TiledMapLayerModelContent(string layerName, TiledMapImageContent image)
+ {
+ LayerName = layerName;
+ _vertices = new List<VertexPositionTexture>();
+ Vertices = new ReadOnlyCollection<VertexPositionTexture>(_vertices);
+ _indices = new List<ushort>();
+ Indices = new ReadOnlyCollection<ushort>(_indices);
+ ImageSize = new Size2(image.Width, image.Height);
+ TextureAssetName = Path.ChangeExtension(image.Source, null);
+ }
+
+ public TiledMapLayerModelContent(string layerName, TiledMapTilesetContent tileset)
+ : this(layerName, tileset.Image)
+ {
+ }
+
+ public void AddTileVertices(Point2 position, Rectangle? sourceRectangle = null, TiledMapTileFlipFlags flags = TiledMapTileFlipFlags.None)
+ {
+ float texelLeft, texelTop, texelRight, texelBottom;
+ var sourceRectangle1 = sourceRectangle ?? new Rectangle(0, 0, (int)ImageSize.Width, (int)ImageSize.Height);
+
+ if (sourceRectangle.HasValue)
+ {
+ var reciprocalWidth = 1f / ImageSize.Width;
+ var reciprocalHeight = 1f / ImageSize.Height;
+ texelLeft = sourceRectangle1.X * reciprocalWidth;
+ texelTop = sourceRectangle1.Y * reciprocalHeight;
+ texelRight = (sourceRectangle1.X + sourceRectangle1.Width) * reciprocalWidth;
+ texelBottom = (sourceRectangle1.Y + sourceRectangle1.Height) * reciprocalHeight;
+ }
+ else
+ {
+ texelLeft = 0;
+ texelTop = 0;
+ texelBottom = 1;
+ texelRight = 1;
+ }
+
+ VertexPositionTexture vertexTopLeft, vertexTopRight, vertexBottomLeft, vertexBottomRight;
+
+ vertexTopLeft.Position = new Vector3(position, 0);
+ vertexTopRight.Position = new Vector3(position + new Vector2(sourceRectangle1.Width, 0), 0);
+ vertexBottomLeft.Position = new Vector3(position + new Vector2(0, sourceRectangle1.Height), 0);
+ vertexBottomRight.Position =
+ new Vector3(position + new Vector2(sourceRectangle1.Width, sourceRectangle1.Height), 0);
+
+ vertexTopLeft.TextureCoordinate.Y = texelTop;
+ vertexTopLeft.TextureCoordinate.X = texelLeft;
+
+ vertexTopRight.TextureCoordinate.Y = texelTop;
+ vertexTopRight.TextureCoordinate.X = texelRight;
+
+ vertexBottomLeft.TextureCoordinate.Y = texelBottom;
+ vertexBottomLeft.TextureCoordinate.X = texelLeft;
+
+ vertexBottomRight.TextureCoordinate.Y = texelBottom;
+ vertexBottomRight.TextureCoordinate.X = texelRight;
+
+ var flipDiagonally = (flags & TiledMapTileFlipFlags.FlipDiagonally) != 0;
+ var flipHorizontally = (flags & TiledMapTileFlipFlags.FlipHorizontally) != 0;
+ var flipVertically = (flags & TiledMapTileFlipFlags.FlipVertically) != 0;
+
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.X, ref vertexBottomLeft.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.Y, ref vertexBottomLeft.TextureCoordinate.Y);
+ }
+
+ if (flipHorizontally)
+ {
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.Y, ref vertexTopRight.TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertexBottomLeft.TextureCoordinate.Y, ref vertexBottomRight.TextureCoordinate.Y);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.X, ref vertexTopRight.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexBottomLeft.TextureCoordinate.X, ref vertexBottomRight.TextureCoordinate.X);
+ }
+ }
+
+ if (flipVertically)
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.X, ref vertexBottomLeft.TextureCoordinate.X);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.X, ref vertexBottomRight.TextureCoordinate.X);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertexTopLeft.TextureCoordinate.Y, ref vertexBottomLeft.TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertexTopRight.TextureCoordinate.Y, ref vertexBottomRight.TextureCoordinate.Y);
+ }
+
+ _vertices.Add(vertexTopLeft);
+ _vertices.Add(vertexTopRight);
+ _vertices.Add(vertexBottomLeft);
+ _vertices.Add(vertexBottomRight);
+
+ Debug.Assert(Vertices.Count <= TiledMapHelper.MaximumVerticesPerModel);
+ }
+
+ public void AddTileIndices()
+ {
+ var indexOffset = Vertices.Count;
+
+ Debug.Assert(3 + indexOffset <= TiledMapHelper.MaximumVerticesPerModel);
+
+ _indices.Add((ushort)(0 + indexOffset));
+ _indices.Add((ushort)(1 + indexOffset));
+ _indices.Add((ushort)(2 + indexOffset));
+ _indices.Add((ushort)(1 + indexOffset));
+ _indices.Add((ushort)(3 + indexOffset));
+ _indices.Add((ushort)(2 + indexOffset));
+
+ Debug.Assert(Indices.Count <= TiledMapHelper.MaximumIndicesPerModel);
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectContent.cs
new file mode 100644
index 0000000..76e2ba2
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectContent.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ // This content class is going to be a lot more complex than the others we use.
+ // Objects can reference a template file which has starting values for the
+ // object. The value in the object file overrides any value specified in the
+ // template. All values have to be able to store a null value so we know if the
+ // XML parser actually found a value for the property and not just a default
+ // value. Default values are used when the object and any templates don't
+ // specify a value.
+ public class TiledMapObjectContent
+ {
+ // TODO: HACK These shouldn't be public
+ public uint? _globalIdentifier;
+ public int? _identifier;
+ public float? _height;
+ public float? _rotation;
+ public bool? _visible;
+ public float? _width;
+ public float? _x;
+ public float? _y;
+
+ [XmlAttribute(DataType = "int", AttributeName = "id")]
+ public int Identifier { get => _identifier ?? 0; set => _identifier = value; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "name")]
+ public string Name { get; set; }
+
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
+ [XmlAttribute(DataType = "float", AttributeName = "x")]
+ public float X { get => _x ?? 0; set => _x = value; }
+
+ [XmlAttribute(DataType = "float", AttributeName = "y")]
+ public float Y { get => _y ?? 0; set => _y = value; }
+
+ [XmlAttribute(DataType = "float", AttributeName = "width")]
+ public float Width { get => _width ?? 0; set => _width = value; }
+
+ [XmlAttribute(DataType = "float", AttributeName = "height")]
+ public float Height { get => _height ?? 0; set => _height = value; }
+
+ [XmlAttribute(DataType = "float", AttributeName = "rotation")]
+ public float Rotation { get => _rotation ?? 0; set => _rotation = value; }
+
+ [XmlAttribute(DataType = "boolean", AttributeName = "visible")]
+ public bool Visible { get => _visible ?? true; set => _visible = value; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+
+ [XmlAttribute(DataType = "unsignedInt", AttributeName = "gid")]
+ public uint GlobalIdentifier { get => _globalIdentifier??0; set => _globalIdentifier = value; }
+
+ [XmlElement(ElementName = "ellipse")]
+ public TiledMapEllipseContent Ellipse { get; set; }
+
+ [XmlElement(ElementName = "polygon")]
+ public TiledMapPolygonContent Polygon { get; set; }
+
+ [XmlElement(ElementName = "polyline")]
+ public TiledMapPolylineContent Polyline { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "template")]
+ public string TemplateSource { get; set; }
+
+
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectDrawOrderContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectDrawOrderContent.cs
new file mode 100644
index 0000000..f441375
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectDrawOrderContent.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public enum TiledMapObjectDrawOrderContent : byte
+ {
+ [XmlEnum(Name = "topdown")] TopDown,
+ [XmlEnum(Name = "index")] Manual
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectLayerContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectLayerContent.cs
new file mode 100644
index 0000000..30669b5
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectLayerContent.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapObjectLayerContent : TiledMapLayerContent
+ {
+ public TiledMapObjectLayerContent()
+ : base(TiledMapLayerType.ObjectLayer)
+ {
+ Objects = new List<TiledMapObjectContent>();
+ }
+
+ [XmlAttribute(AttributeName = "color")]
+ public string Color { get; set; }
+
+ [XmlElement(ElementName = "object")]
+ public List<TiledMapObjectContent> Objects { get; set; }
+
+ [XmlAttribute(AttributeName = "draworder")]
+ public TiledMapObjectDrawOrderContent DrawOrder { get; set; }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectTemplateContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectTemplateContent.cs
new file mode 100644
index 0000000..096f528
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapObjectTemplateContent.cs
@@ -0,0 +1,17 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ [XmlRoot(ElementName = "template")]
+ public class TiledMapObjectTemplateContent
+ {
+ [XmlElement(ElementName = "tileset")]
+ public TiledMapTilesetContent Tileset { get; set; }
+
+ //[XmlIgnore]
+ //public ExternalReference<TiledMapTilesetContent> TilesetReference { get; set; }
+
+ [XmlElement(ElementName = "object")]
+ public TiledMapObjectContent Object { get; set; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapOrientationContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapOrientationContent.cs
new file mode 100644
index 0000000..af83824
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapOrientationContent.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public enum TiledMapOrientationContent : byte
+ {
+ [XmlEnum(Name = "orthogonal")] Orthogonal,
+ [XmlEnum(Name = "isometric")] Isometric,
+ [XmlEnum(Name = "staggered")] Staggered,
+ [XmlEnum(Name = "hexagonal")] Hexagonal
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolygonContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolygonContent.cs
new file mode 100644
index 0000000..e4a3d5e
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolygonContent.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapPolygonContent
+ {
+ [XmlAttribute(AttributeName = "points")]
+ public string Points { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolylineContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolylineContent.cs
new file mode 100644
index 0000000..ba15f59
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPolylineContent.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapPolylineContent
+ {
+ [XmlAttribute(AttributeName = "points")]
+ public string Points { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs
new file mode 100644
index 0000000..10cdfc1
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapPropertyContent.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapPropertyContent
+ {
+ [XmlAttribute(AttributeName = "name")]
+ public string Name { get; set; }
+
+ [XmlAttribute(AttributeName = "value")]
+ public string ValueAttribute { get; set; }
+
+ [XmlText]
+ public string ValueBody { get; set; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+
+ public string Value => ValueAttribute ?? ValueBody;
+
+ public override string ToString()
+ {
+ return $"{Name}: {Value}";
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerAxisContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerAxisContent.cs
new file mode 100644
index 0000000..7a073f5
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerAxisContent.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public enum TiledMapStaggerAxisContent : byte
+ {
+ [XmlEnum("x")]X,
+ [XmlEnum("y")]Y
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerIndexContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerIndexContent.cs
new file mode 100644
index 0000000..834c3a6
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapStaggerIndexContent.cs
@@ -0,0 +1,10 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public enum TiledMapStaggerIndexContent : byte
+ {
+ [XmlEnum("even")]Even,
+ [XmlEnum("odd")]Odd
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileContent.cs
new file mode 100644
index 0000000..fcd7a69
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileContent.cs
@@ -0,0 +1,9 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public struct TiledMapTileContent
+ {
+ [XmlAttribute(AttributeName = "gid")] public uint GlobalIdentifier;
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileDrawOrderContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileDrawOrderContent.cs
new file mode 100644
index 0000000..4762afd
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileDrawOrderContent.cs
@@ -0,0 +1,12 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public enum TiledMapTileDrawOrderContent : byte
+ {
+ [XmlEnum(Name = "right-down")] RightDown,
+ [XmlEnum(Name = "right-up")] RightUp,
+ [XmlEnum(Name = "left-down")] LeftDown,
+ [XmlEnum(Name = "left-up")] LeftUp
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerContent.cs
new file mode 100644
index 0000000..49f5ccf
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerContent.cs
@@ -0,0 +1,30 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTileLayerContent : TiledMapLayerContent
+ {
+ public TiledMapTileLayerContent()
+ : base(TiledMapLayerType.TileLayer)
+ {
+ }
+
+ [XmlAttribute(AttributeName = "x")]
+ public int X { get; set; }
+
+ [XmlAttribute(AttributeName = "y")]
+ public int Y { get; set; }
+
+ [XmlAttribute(AttributeName = "width")]
+ public int Width { get; set; }
+
+ [XmlAttribute(AttributeName = "height")]
+ public int Height { get; set; }
+
+ [XmlElement(ElementName = "data")]
+ public TiledMapTileLayerDataContent Data { get; set; }
+
+ [XmlIgnore]
+ public TiledMapTile[] Tiles { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataChunkContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataChunkContent.cs
new file mode 100644
index 0000000..0ec10d9
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataChunkContent.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTileLayerDataChunkContent
+ {
+ [XmlAttribute(AttributeName = "x")]
+ public int X { get; set; }
+
+ [XmlAttribute(AttributeName = "y")]
+ public int Y { get; set; }
+
+ [XmlAttribute(AttributeName = "width")]
+ public int Width { get; set; }
+
+ [XmlAttribute(AttributeName = "height")]
+ public int Height { get; set; }
+
+ [XmlElement(ElementName = "tile")]
+ public List<TiledMapTileContent> Tiles { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataContent.cs
new file mode 100644
index 0000000..c19a02e
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileLayerDataContent.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTileLayerDataContent
+ {
+ public TiledMapTileLayerDataContent()
+ {
+ Tiles = new List<TiledMapTileContent>();
+ }
+
+ [XmlAttribute(AttributeName = "encoding")]
+ public string Encoding { get; set; }
+
+ [XmlAttribute(AttributeName = "compression")]
+ public string Compression { get; set; }
+
+ [XmlElement(ElementName = "tile")]
+ public List<TiledMapTileContent> Tiles { get; set; }
+
+ [XmlElement(ElementName = "chunk")]
+ public List<TiledMapTileLayerDataChunkContent> Chunks { get; set; }
+
+ [XmlText]
+ public string Value { get; set; }
+
+ public override string ToString()
+ {
+ return $"{Encoding} {Compression}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileOffsetContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileOffsetContent.cs
new file mode 100644
index 0000000..7d2af87
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTileOffsetContent.cs
@@ -0,0 +1,17 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ [XmlRoot(ElementName = "tileoffset")]
+ public class TiledMapTileOffsetContent
+ {
+ [XmlAttribute(AttributeName = "x")] public int X;
+
+ [XmlAttribute(AttributeName = "y")] public int Y;
+
+ public override string ToString()
+ {
+ return $"{X}, {Y}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs
new file mode 100644
index 0000000..fa04c31
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetContent.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ [XmlRoot(ElementName = "tileset")]
+ public class TiledMapTilesetContent
+ {
+ public TiledMapTilesetContent()
+ {
+ TileOffset = new TiledMapTileOffsetContent();
+ Tiles = new List<TiledMapTilesetTileContent>();
+ Properties = new List<TiledMapPropertyContent>();
+ }
+
+ [XmlAttribute(AttributeName = "firstgid")]
+ public int FirstGlobalIdentifier { get; set; }
+
+ [XmlAttribute(AttributeName = "source")]
+ public string Source { get; set; }
+
+ [XmlAttribute(AttributeName = "name")]
+ public string Name { get; set; }
+
+ // Deprecated as of Tiled 1.9.0 (replaced by "class" attribute)
+ [XmlAttribute(DataType = "string", AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlAttribute(DataType = "string", AttributeName = "class")]
+ public string Class { get; set; }
+
+ [XmlAttribute(AttributeName = "tilewidth")]
+ public int TileWidth { get; set; }
+
+ [XmlAttribute(AttributeName = "tileheight")]
+ public int TileHeight { get; set; }
+
+ [XmlAttribute(AttributeName = "spacing")]
+ public int Spacing { get; set; }
+
+ [XmlAttribute(AttributeName = "margin")]
+ public int Margin { get; set; }
+
+ [XmlAttribute(AttributeName = "columns")]
+ public int Columns { get; set; }
+
+ [XmlAttribute(AttributeName = "tilecount")]
+ public int TileCount { get; set; }
+
+ [XmlElement(ElementName = "tileoffset")]
+ public TiledMapTileOffsetContent TileOffset { get; set; }
+
+ [XmlElement(ElementName = "grid")]
+ public TiledMapTilesetGridContent Grid { get; set; }
+
+ [XmlElement(ElementName = "tile")]
+ public List<TiledMapTilesetTileContent> Tiles { get; set; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+
+ [XmlElement(ElementName = "image")]
+ public TiledMapImageContent Image { get; set; }
+
+ public bool ContainsGlobalIdentifier(int globalIdentifier)
+ {
+ return globalIdentifier >= FirstGlobalIdentifier && globalIdentifier < FirstGlobalIdentifier + TileCount;
+ }
+
+ public override string ToString()
+ {
+ return $"{Name}: {Image}";
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetGridContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetGridContent.cs
new file mode 100644
index 0000000..a9071b8
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetGridContent.cs
@@ -0,0 +1,16 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTilesetGridContent
+ {
+ [XmlAttribute(AttributeName = "orientation")]
+ public TiledMapOrientationContent Orientation { get; set; }
+
+ [XmlAttribute(AttributeName = "width")]
+ public int Width { get; set; }
+
+ [XmlAttribute(AttributeName = "height")]
+ public int Height { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileAnimationFrameContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileAnimationFrameContent.cs
new file mode 100644
index 0000000..5a17137
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileAnimationFrameContent.cs
@@ -0,0 +1,18 @@
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTilesetTileAnimationFrameContent
+ {
+ [XmlAttribute(AttributeName = "tileid")]
+ public int TileIdentifier { get; set; }
+
+ [XmlAttribute(AttributeName = "duration")]
+ public int Duration { get; set; }
+
+ public override string ToString()
+ {
+ return $"TileID: {TileIdentifier}, Duration: {Duration}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileContent.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileContent.cs
new file mode 100644
index 0000000..2a82197
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Serialization/TiledMapTilesetTileContent.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace MonoGame.Extended.Tiled.Serialization
+{
+ public class TiledMapTilesetTileContent
+ {
+ public TiledMapTilesetTileContent()
+ {
+ Properties = new List<TiledMapPropertyContent>();
+ Type = string.Empty;
+ }
+
+ [XmlAttribute(AttributeName = "id")]
+ public int LocalIdentifier { get; set; }
+
+ [XmlAttribute(AttributeName = "type")]
+ public string Type { get; set; }
+
+ [XmlElement(ElementName = "image")]
+ public TiledMapImageContent Image { get; set; }
+
+ [XmlArray("objectgroup")]
+ [XmlArrayItem("object")]
+ public List<TiledMapObjectContent> Objects { get; set; }
+
+ [XmlArray("animation")]
+ [XmlArrayItem("frame")]
+ public List<TiledMapTilesetTileAnimationFrameContent> Frames { get; set; }
+
+ [XmlArray("properties")]
+ [XmlArrayItem("property")]
+ public List<TiledMapPropertyContent> Properties { get; set; }
+
+ public override string ToString()
+ {
+ return LocalIdentifier.ToString();
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMap.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMap.cs
new file mode 100644
index 0000000..b3486bd
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMap.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMap
+ {
+ private readonly List<TiledMapImageLayer> _imageLayers = new List<TiledMapImageLayer>();
+ private readonly List<TiledMapLayer> _layers = new List<TiledMapLayer>();
+ private readonly Dictionary<string, TiledMapLayer> _layersByName = new Dictionary<string, TiledMapLayer>();
+ private readonly List<TiledMapObjectLayer> _objectLayers = new List<TiledMapObjectLayer>();
+ private readonly List<TiledMapTileLayer> _tileLayers = new List<TiledMapTileLayer>();
+ private readonly List<TiledMapTileset> _tilesets = new List<TiledMapTileset>();
+ private readonly List<Tuple<TiledMapTileset, int>> _firstGlobalIdentifiers = new List<Tuple<TiledMapTileset, int>>();
+
+ public string Name { get; }
+ public string Type { get; }
+ public int Width { get; }
+ public int Height { get; }
+ public int TileWidth { get; }
+ public int TileHeight { get; }
+ public TiledMapTileDrawOrder RenderOrder { get; }
+ public TiledMapOrientation Orientation { get; }
+ public TiledMapProperties Properties { get; }
+ public ReadOnlyCollection<TiledMapTileset> Tilesets { get; }
+ public ReadOnlyCollection<TiledMapLayer> Layers { get; }
+ public ReadOnlyCollection<TiledMapImageLayer> ImageLayers { get; }
+ public ReadOnlyCollection<TiledMapTileLayer> TileLayers { get; }
+ public ReadOnlyCollection<TiledMapObjectLayer> ObjectLayers { get; }
+
+ public Color? BackgroundColor { get; set; }
+ public int WidthInPixels => Width * TileWidth;
+ public int HeightInPixels => Height * TileHeight;
+
+ private TiledMap()
+ {
+ Layers = new ReadOnlyCollection<TiledMapLayer>(_layers);
+ ImageLayers = new ReadOnlyCollection<TiledMapImageLayer>(_imageLayers);
+ TileLayers = new ReadOnlyCollection<TiledMapTileLayer>(_tileLayers);
+ ObjectLayers = new ReadOnlyCollection<TiledMapObjectLayer>(_objectLayers);
+ Tilesets = new ReadOnlyCollection<TiledMapTileset>(_tilesets);
+ Properties = new TiledMapProperties();
+ }
+
+ public TiledMap(string name, string type, int width, int height, int tileWidth, int tileHeight, TiledMapTileDrawOrder renderOrder, TiledMapOrientation orientation, Color? backgroundColor = null)
+ : this()
+ {
+ Name = name;
+ Type = type;
+ Width = width;
+ Height = height;
+ TileWidth = tileWidth;
+ TileHeight = tileHeight;
+ RenderOrder = renderOrder;
+ Orientation = orientation;
+ BackgroundColor = backgroundColor;
+ }
+
+ public void AddTileset(TiledMapTileset tileset, int firstGlobalIdentifier)
+ {
+ _tilesets.Add(tileset);
+ _firstGlobalIdentifiers.Add(new Tuple<TiledMapTileset, int>(tileset, firstGlobalIdentifier));
+ }
+
+ public void AddLayer(TiledMapLayer layer)
+ => AddLayer(layer, true);
+
+ private void AddLayer(TiledMapLayer layer, bool root)
+ {
+ if (root) _layers.Add(layer);
+
+ if (_layersByName.ContainsKey(layer.Name))
+ throw new ArgumentException($"The TiledMap '{Name}' contains two or more layers named '{layer.Name}'. Please ensure all layers have unique names.");
+
+ _layersByName.Add(layer.Name, layer);
+
+ switch(layer)
+ {
+ case TiledMapImageLayer imageLayer:
+ _imageLayers.Add(imageLayer);
+ break;
+ case TiledMapTileLayer tileLayer:
+ _tileLayers.Add(tileLayer);
+ break;
+ case TiledMapObjectLayer objectLayer:
+ _objectLayers.Add(objectLayer);
+ break;
+ case TiledMapGroupLayer groupLayer:
+ foreach (var subLayer in groupLayer.Layers)
+ AddLayer(subLayer, false);
+ break;
+ }
+ }
+
+ public TiledMapLayer GetLayer(string layerName)
+ {
+ TiledMapLayer layer;
+ _layersByName.TryGetValue(layerName, out layer);
+ return layer;
+ }
+
+ public T GetLayer<T>(string layerName)
+ where T : TiledMapLayer
+ {
+ return GetLayer(layerName) as T;
+ }
+
+ public TiledMapTileset GetTilesetByTileGlobalIdentifier(int tileIdentifier)
+ {
+ foreach (var tileset in _firstGlobalIdentifiers)
+ {
+ if (tileIdentifier >= tileset.Item2 && tileIdentifier < tileset.Item2 + tileset.Item1.TileCount)
+ return tileset.Item1;
+ }
+
+ return null;
+ }
+
+ public int GetTilesetFirstGlobalIdentifier(TiledMapTileset tileset)
+ {
+ return _firstGlobalIdentifiers.FirstOrDefault(t => t.Item1 == tileset).Item2;
+ }
+
+ private static int CountLayers(TiledMapLayer layer)
+ {
+ var value = 0;
+ if (layer is TiledMapGroupLayer groupLayer)
+ foreach (var subLayer in groupLayer.Layers)
+ value += CountLayers(subLayer);
+ else
+ value = 1;
+
+ return value;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapEllipseObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapEllipseObject.cs
new file mode 100644
index 0000000..9140d62
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapEllipseObject.cs
@@ -0,0 +1,17 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMapEllipseObject : TiledMapObject
+ {
+ public TiledMapEllipseObject(int identifier, string name, Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ : base(identifier, name, size, position, rotation, opacity, isVisible, type)
+ {
+ Radius = new Vector2(size.Width / 2.0f, size.Height / 2.0f);
+ Center = new Vector2(position.X + Radius.X, position.Y);
+ }
+
+ public Vector2 Center { get; }
+ public Vector2 Radius { get; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs
new file mode 100644
index 0000000..48d43d1
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapGroupLayer.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapGroupLayer : TiledMapLayer
+ {
+ public List<TiledMapLayer> Layers { get; }
+ public TiledMapGroupLayer(string name, string type, List<TiledMapLayer> layers, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
+ {
+ Layers = layers;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapHelper.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapHelper.cs
new file mode 100644
index 0000000..43edf67
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapHelper.cs
@@ -0,0 +1,51 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public static class TiledMapHelper
+ {
+ // 4 vertices per tile
+ public const int VerticesPerTile = 4;
+ // 2 triangles per tile (mesh), with each triangle indexing 3 out of 4 vertices, so 6 vertices
+ public const int IndicesPerTile = 6;
+ // by using ushort type for indices we are limited to indexing vertices from 0 to 65535
+ // this limits us on how many vertices can fit inside a single vertex buffer (65536 vertices)
+ public const int MaximumVerticesPerModel = ushort.MaxValue + 1;
+ // and thus, we know how many tiles we can fit inside a vertex or index buffer (16384 tiles)
+ public const int MaximumTilesPerGeometryContent = MaximumVerticesPerModel / VerticesPerTile;
+ // and thus, we also know the maximum number of indices we can fit inside a single index buffer (98304 indices)
+ public const int MaximumIndicesPerModel = MaximumTilesPerGeometryContent * IndicesPerTile;
+ // these optimal maximum numbers of course are not considering texture bindings which would practically lower the actual number of tiles per vertex / index buffer
+ // thus, the reason why it is a good to have ONE giant tileset (at least per layer)
+
+ internal static Rectangle GetTileSourceRectangle(int localTileIdentifier, int tileWidth, int tileHeight, int columns, int margin, int spacing)
+ {
+ var x = margin + localTileIdentifier % columns * (tileWidth + spacing);
+ var y = margin + localTileIdentifier / columns * (tileHeight + spacing);
+ return new Rectangle(x, y, tileWidth, tileHeight);
+ }
+
+ internal static Point2 GetOrthogonalPosition(int tileX, int tileY, int tileWidth, int tileHeight)
+ {
+ var x = tileX * tileWidth;
+ var y = tileY * tileHeight;
+ return new Vector2(x, y);
+ }
+
+ internal static Point2 GetIsometricPosition(int tileX, int tileY, int tileWidth, int tileHeight)
+ {
+ // You can think of an isometric Tiled map as a regular orthogonal map that is rotated -45 degrees
+ // i.e.: the origin (0, 0) is the top tile of the diamond grid;
+ // (mapWidth, 0) is the far right tile of the diamond grid
+ // (0, mapHeight) is the far left tile of the diamond grid
+ // (mapWidth, mapHeight) is the bottom tile of the diamond grid
+
+ var halfTileWidth = tileWidth * 0.5f;
+ var halfTileHeight = tileHeight * 0.5f;
+ // -1 because we want the top the tile-diamond (top-center) to be the origin in tile space
+ var x = (tileX - tileY - 1) * halfTileWidth;
+ var y = (tileX + tileY) * halfTileHeight;
+ return new Vector2(x, y);
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapImageLayer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapImageLayer.cs
new file mode 100644
index 0000000..33bb700
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapImageLayer.cs
@@ -0,0 +1,18 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapImageLayer : TiledMapLayer, IMovable
+ {
+ public TiledMapImageLayer(string name, string type, Texture2D image, Vector2? position = null, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
+ {
+ Image = image;
+ Position = position ?? Vector2.Zero;
+ }
+
+ public Texture2D Image { get; }
+ public Vector2 Position { get; set; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayer.cs
new file mode 100644
index 0000000..6226dd1
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayer.cs
@@ -0,0 +1,26 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public abstract class TiledMapLayer
+ {
+ public string Name { get; }
+ public string Type { get; }
+ public bool IsVisible { get; set; }
+ public float Opacity { get; set; }
+ public Vector2 Offset { get; set; }
+ public Vector2 ParallaxFactor { get; set; }
+ public TiledMapProperties Properties { get; }
+
+ protected TiledMapLayer(string name, string type, Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
+ {
+ Name = name;
+ Type = type;
+ Offset = offset ?? Vector2.Zero;
+ ParallaxFactor = parallaxFactor ?? Vector2.One;
+ Opacity = opacity;
+ IsVisible = isVisible;
+ Properties = new TiledMapProperties();
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayerType.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayerType.cs
new file mode 100644
index 0000000..8af5a2e
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapLayerType.cs
@@ -0,0 +1,10 @@
+namespace MonoGame.Extended.Tiled
+{
+ public enum TiledMapLayerType : byte
+ {
+ ImageLayer = 0,
+ TileLayer = 1,
+ ObjectLayer = 2,
+ GroupLayer = 3
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObject.cs
new file mode 100644
index 0000000..1e25084
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObject.cs
@@ -0,0 +1,35 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public abstract class TiledMapObject
+ {
+ protected TiledMapObject(int identifier, string name, Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ {
+ Identifier = identifier;
+ Name = name;
+ IsVisible = isVisible;
+ Rotation = rotation;
+ Position = position;
+ Size = size;
+ Opacity = opacity;
+ Type = type;
+ Properties = new TiledMapProperties();
+ }
+
+ public int Identifier { get; }
+ public string Name { get; set; }
+ public string Type { get; set; }
+ public bool IsVisible { get; set; }
+ public float Opacity { get; set; }
+ public float Rotation { get; set; }
+ public Vector2 Position { get; }
+ public Size2 Size { get; set; }
+ public TiledMapProperties Properties { get; }
+
+ public override string ToString()
+ {
+ return $"{Identifier}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectDrawOrder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectDrawOrder.cs
new file mode 100644
index 0000000..1668f66
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectDrawOrder.cs
@@ -0,0 +1,8 @@
+namespace MonoGame.Extended.Tiled
+{
+ public enum TiledMapObjectDrawOrder : byte
+ {
+ TopDown,
+ Index,
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs
new file mode 100644
index 0000000..b39b8d7
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectLayer.cs
@@ -0,0 +1,20 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapObjectLayer : TiledMapLayer
+ {
+ public TiledMapObjectLayer(string name, string type, TiledMapObject[] objects, Color? color = null, TiledMapObjectDrawOrder drawOrder = TiledMapObjectDrawOrder.TopDown,
+ Vector2? offset = null, Vector2? parallaxFactor = null, float opacity = 1.0f, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
+ {
+ Color = color;
+ DrawOrder = drawOrder;
+ Objects = objects;
+ }
+
+ public Color? Color { get; }
+ public TiledMapObjectDrawOrder DrawOrder { get; }
+ public TiledMapObject[] Objects { get; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectType.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectType.cs
new file mode 100644
index 0000000..305500a
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapObjectType.cs
@@ -0,0 +1,11 @@
+namespace MonoGame.Extended.Tiled
+{
+ public enum TiledMapObjectType : byte
+ {
+ Rectangle,
+ Ellipse,
+ Polygon,
+ Polyline,
+ Tile
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapOrientation.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapOrientation.cs
new file mode 100644
index 0000000..798c1c8
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapOrientation.cs
@@ -0,0 +1,9 @@
+namespace MonoGame.Extended.Tiled
+{
+ public enum TiledMapOrientation
+ {
+ Orthogonal,
+ Isometric,
+ Staggered
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolygonObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolygonObject.cs
new file mode 100644
index 0000000..2df343f
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolygonObject.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMapPolygonObject : TiledMapObject
+ {
+ public TiledMapPolygonObject(int identifier, string name, Point2[] points, Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ : base(identifier, name, size, position, rotation, opacity, isVisible, type)
+ {
+ Points = points;
+ }
+
+ public Point2[] Points { get; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolylineObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolylineObject.cs
new file mode 100644
index 0000000..de50dbc
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPolylineObject.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMapPolylineObject : TiledMapObject
+ {
+ public TiledMapPolylineObject(int identifier, string name, Point2[] points, Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ : base(identifier, name, size, position, rotation, opacity, isVisible, type)
+ {
+ Points = points;
+ }
+
+ public Point2[] Points { get; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapProperties.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapProperties.cs
new file mode 100644
index 0000000..651ae5d
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapProperties.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapProperties : Dictionary<string, TiledMapPropertyValue>
+ {
+ public bool TryGetValue(string key, out string value)
+ {
+ bool result = TryGetValue(key, out TiledMapPropertyValue tmpVal);
+ value = result ? null : tmpVal.Value;
+ return result;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs
new file mode 100644
index 0000000..d7a0893
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapPropertyValue.cs
@@ -0,0 +1,32 @@
+namespace MonoGame.Extended.Tiled;
+
+public class TiledMapPropertyValue
+{
+ public string Value { get; }
+
+ public TiledMapProperties Properties;
+
+ public TiledMapPropertyValue()
+ {
+ Value = string.Empty;
+ Properties = new();
+ }
+
+ public TiledMapPropertyValue(string value)
+ {
+ Value = value;
+ Properties = new();
+ }
+
+ public TiledMapPropertyValue(TiledMapProperties properties)
+ {
+ Value = string.Empty;
+ Properties = properties;
+ }
+
+ public override string ToString() => Value;
+
+ //public static implicit operator TiledMapPropertyValue(string value) => new(value);
+ public static implicit operator string(TiledMapPropertyValue value) => value.Value;
+ public static implicit operator TiledMapProperties(TiledMapPropertyValue value) => value.Properties;
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapReader.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapReader.cs
new file mode 100644
index 0000000..bd49655
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapReader.cs
@@ -0,0 +1,229 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using MonoGame.Extended.Content;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapReader : ContentTypeReader<TiledMap>
+ {
+ protected override TiledMap Read(ContentReader reader, TiledMap existingInstance)
+ {
+ if (existingInstance != null)
+ return existingInstance;
+
+ var map = ReadTiledMap(reader);
+ reader.ReadTiledMapProperties(map.Properties);
+ ReadTilesets(reader, map);
+ ReadLayers(reader, map);
+ return map;
+ }
+
+ private static TiledMap ReadTiledMap(ContentReader reader)
+ {
+ var name = reader.AssetName;
+ var type = reader.ReadString();
+ var width = reader.ReadInt32();
+ var height = reader.ReadInt32();
+ var tileWidth = reader.ReadInt32();
+ var tileHeight = reader.ReadInt32();
+ var backgroundColor = reader.ReadColor();
+ var renderOrder = (TiledMapTileDrawOrder)reader.ReadByte();
+ var orientation = (TiledMapOrientation)reader.ReadByte();
+
+ return new TiledMap(name, type, width, height, tileWidth, tileHeight, renderOrder, orientation, backgroundColor);
+ }
+
+ private static void ReadTilesets(ContentReader reader, TiledMap map)
+ {
+ var tilesetCount = reader.ReadInt32();
+
+ for (var i = 0; i < tilesetCount; i++)
+ {
+ var firstGlobalIdentifier = reader.ReadInt32();
+ var tileset = ReadTileset(reader, map);
+ map.AddTileset(tileset, firstGlobalIdentifier);
+ }
+ }
+
+ private static TiledMapTileset ReadTileset(ContentReader reader, TiledMap map)
+ {
+ var external = reader.ReadBoolean();
+ var tileset = external ? reader.ReadExternalReference<TiledMapTileset>() : TiledMapTilesetReader.ReadTileset(reader);
+
+ return tileset;
+ }
+
+ private static void ReadLayers(ContentReader reader, TiledMap map)
+ {
+ foreach (var layer in ReadGroup(reader, map))
+ map.AddLayer(layer);
+ }
+ private static List<TiledMapLayer> ReadGroup(ContentReader reader, TiledMap map)
+ {
+ var layerCount = reader.ReadInt32();
+ var value = new List<TiledMapLayer>(layerCount);
+
+ for (var i = 0; i < layerCount; i++)
+ value.Add(ReadLayer(reader, map));
+
+ return value;
+ }
+
+ private static TiledMapLayer ReadLayer(ContentReader reader, TiledMap map)
+ {
+ var layerType = (TiledMapLayerType)reader.ReadByte();
+ var name = reader.ReadString();
+ var type = reader.ReadString();
+ var isVisible = reader.ReadBoolean();
+ var opacity = reader.ReadSingle();
+ var offsetX = reader.ReadSingle();
+ var offsetY = reader.ReadSingle();
+ var offset = new Vector2(offsetX, offsetY);
+ var parallaxX = reader.ReadSingle();
+ var parallaxY = reader.ReadSingle();
+ var parallaxFactor = new Vector2(parallaxX, parallaxY);
+ var properties = new TiledMapProperties();
+
+ reader.ReadTiledMapProperties(properties);
+
+ TiledMapLayer layer;
+
+ switch (layerType)
+ {
+ case TiledMapLayerType.ImageLayer:
+ layer = ReadImageLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible);
+ break;
+ case TiledMapLayerType.TileLayer:
+ layer = ReadTileLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible, map);
+ break;
+ case TiledMapLayerType.ObjectLayer:
+ layer = ReadObjectLayer(reader, name, type, offset, parallaxFactor, opacity, isVisible, map);
+ break;
+ case TiledMapLayerType.GroupLayer:
+ layer = new TiledMapGroupLayer(name, type, ReadGroup(reader, map), offset, parallaxFactor, opacity, isVisible);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ foreach (var property in properties)
+ layer.Properties.Add(property.Key, property.Value);
+
+ return layer;
+ }
+
+ private static TiledMapLayer ReadObjectLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
+ {
+ var color = reader.ReadColor();
+ var drawOrder = (TiledMapObjectDrawOrder)reader.ReadByte();
+ var objectCount = reader.ReadInt32();
+ var objects = new TiledMapObject[objectCount];
+
+ for (var i = 0; i < objectCount; i++)
+ objects[i] = ReadTiledMapObject(reader, map);
+
+ return new TiledMapObjectLayer(name, type, objects, color, drawOrder, offset, parallaxFactor, opacity, isVisible);
+ }
+
+ private static TiledMapObject ReadTiledMapObject(ContentReader reader, TiledMap map)
+ {
+ var objectType = (TiledMapObjectType)reader.ReadByte();
+ var identifier = reader.ReadInt32();
+ var name = reader.ReadString();
+ var type = reader.ReadString();
+ var position = new Vector2(reader.ReadSingle(), reader.ReadSingle());
+ var width = reader.ReadSingle();
+ var height = reader.ReadSingle();
+ var size = new Size2(width, height);
+ var rotation = reader.ReadSingle();
+ var isVisible = reader.ReadBoolean();
+ var properties = new TiledMapProperties();
+ const float opacity = 1.0f;
+
+ reader.ReadTiledMapProperties(properties);
+
+ TiledMapObject mapObject;
+
+ switch (objectType)
+ {
+ case TiledMapObjectType.Rectangle:
+ mapObject = new TiledMapRectangleObject(identifier, name, size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Tile:
+ var globalTileIdentifierWithFlags = reader.ReadUInt32();
+ var tile = new TiledMapTile(globalTileIdentifierWithFlags, (ushort)position.X, (ushort)position.Y);
+ var tileset = map.GetTilesetByTileGlobalIdentifier(tile.GlobalIdentifier);
+ var localTileIdentifier = tile.GlobalIdentifier - map.GetTilesetFirstGlobalIdentifier(tileset);
+ var tilesetTile = tileset.Tiles.FirstOrDefault(x => x.LocalTileIdentifier == localTileIdentifier);
+ mapObject = new TiledMapTileObject(identifier, name, tileset, tilesetTile, size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Ellipse:
+ mapObject = new TiledMapEllipseObject(identifier, name, size, position, rotation, opacity, isVisible);
+ break;
+ case TiledMapObjectType.Polygon:
+ mapObject = new TiledMapPolygonObject(identifier, name, ReadPoints(reader), size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Polyline:
+ mapObject = new TiledMapPolylineObject(identifier, name, ReadPoints(reader), size, position, rotation, opacity, isVisible, type);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ foreach (var property in properties)
+ mapObject.Properties.Add(property.Key, property.Value);
+
+ return mapObject;
+ }
+
+ private static Point2[] ReadPoints(ContentReader reader)
+ {
+ var pointCount = reader.ReadInt32();
+ var points = new Point2[pointCount];
+
+ for (var i = 0; i < pointCount; i++)
+ {
+ var x = reader.ReadSingle();
+ var y = reader.ReadSingle();
+ points[i] = new Point2(x, y);
+ }
+
+ return points;
+ }
+
+ private static TiledMapImageLayer ReadImageLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible)
+ {
+ var texture = reader.ReadExternalReference<Texture2D>();
+ var x = reader.ReadSingle();
+ var y = reader.ReadSingle();
+ var position = new Vector2(x, y);
+ return new TiledMapImageLayer(name, type, texture, position, offset, parallaxFactor, opacity, isVisible);
+ }
+
+ private static TiledMapTileLayer ReadTileLayer(ContentReader reader, string name, string type, Vector2 offset, Vector2 parallaxFactor, float opacity, bool isVisible, TiledMap map)
+ {
+ var width = reader.ReadInt32();
+ var height = reader.ReadInt32();
+ var tileWidth = map.TileWidth;
+ var tileHeight = map.TileHeight;
+
+ var tileCount = reader.ReadInt32();
+ var layer = new TiledMapTileLayer(name, type, width, height, tileWidth, tileHeight, offset, parallaxFactor, opacity, isVisible);
+
+ for (var i = 0; i < tileCount; i++)
+ {
+ var globalTileIdentifierWithFlags = reader.ReadUInt32();
+ var x = reader.ReadUInt16();
+ var y = reader.ReadUInt16();
+
+ layer.Tiles[x + y * width] = new TiledMapTile(globalTileIdentifierWithFlags, x, y);
+ }
+
+ return layer;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapRectangleObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapRectangleObject.cs
new file mode 100644
index 0000000..de4c7ec
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapRectangleObject.cs
@@ -0,0 +1,12 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMapRectangleObject : TiledMapObject
+ {
+ public TiledMapRectangleObject(int identifier, string name, Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ : base(identifier, name, size, position, rotation, opacity, isVisible, type)
+ {
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTile.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTile.cs
new file mode 100644
index 0000000..d3147a4
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTile.cs
@@ -0,0 +1,28 @@
+namespace MonoGame.Extended.Tiled
+{
+ public struct TiledMapTile
+ {
+ public readonly ushort X;
+ public readonly ushort Y;
+ public readonly uint GlobalTileIdentifierWithFlags;
+
+ public int GlobalIdentifier => (int)(GlobalTileIdentifierWithFlags & ~(uint)TiledMapTileFlipFlags.All);
+ public bool IsFlippedHorizontally => (GlobalTileIdentifierWithFlags & (uint)TiledMapTileFlipFlags.FlipHorizontally) != 0;
+ public bool IsFlippedVertically => (GlobalTileIdentifierWithFlags & (uint)TiledMapTileFlipFlags.FlipVertically) != 0;
+ public bool IsFlippedDiagonally => (GlobalTileIdentifierWithFlags & (uint)TiledMapTileFlipFlags.FlipDiagonally) != 0;
+ public bool IsBlank => GlobalIdentifier == 0;
+ public TiledMapTileFlipFlags Flags => (TiledMapTileFlipFlags)(GlobalTileIdentifierWithFlags & (uint)TiledMapTileFlipFlags.All);
+
+ public TiledMapTile(uint globalTileIdentifierWithFlags, ushort x, ushort y)
+ {
+ GlobalTileIdentifierWithFlags = globalTileIdentifierWithFlags;
+ X = x;
+ Y = y;
+ }
+
+ public override string ToString()
+ {
+ return $"GlobalIdentifier: {GlobalIdentifier}, Flags: {Flags}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileDrawOrder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileDrawOrder.cs
new file mode 100644
index 0000000..a40b596
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileDrawOrder.cs
@@ -0,0 +1,10 @@
+namespace MonoGame.Extended.Tiled
+{
+ public enum TiledMapTileDrawOrder : byte
+ {
+ RightDown,
+ RightUp,
+ LeftDown,
+ LeftUp
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileFlipFlags.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileFlipFlags.cs
new file mode 100644
index 0000000..f883bad
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileFlipFlags.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace MonoGame.Extended.Tiled
+{
+ [Flags]
+ public enum TiledMapTileFlipFlags : uint
+ {
+ None = 0,
+ FlipDiagonally = 0x20000000,
+ FlipVertically = 0x40000000,
+ FlipHorizontally = 0x80000000,
+ All = FlipDiagonally | FlipVertically | FlipHorizontally
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileLayer.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileLayer.cs
new file mode 100644
index 0000000..5d07f06
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileLayer.cs
@@ -0,0 +1,66 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapTileLayer : TiledMapLayer
+ {
+ public TiledMapTileLayer(string name, string type, int width, int height, int tileWidth, int tileHeight, Vector2? offset = null,
+ Vector2? parallaxFactor = null, float opacity = 1, bool isVisible = true)
+ : base(name, type, offset, parallaxFactor, opacity, isVisible)
+ {
+ Width = width;
+ Height = height;
+ TileWidth = tileWidth;
+ TileHeight = tileHeight;
+ Tiles = new TiledMapTile[Width * Height];
+ }
+
+ public int Width { get; }
+ public int Height { get; }
+ public int TileWidth { get; }
+ public int TileHeight { get; }
+ public TiledMapTile[] Tiles { get; }
+
+ public int GetTileIndex(ushort x, ushort y)
+ {
+ return x + y * Width;
+ }
+
+ public bool TryGetTile(ushort x, ushort y, out TiledMapTile? tile)
+ {
+ if (x >= Width)
+ {
+ tile = null;
+ return false;
+ }
+ var index = GetTileIndex(x, y);
+
+ if (index < 0 || index >= Tiles.Length)
+ {
+ tile = null;
+ return false;
+ }
+
+ tile = Tiles[index];
+ return true;
+ }
+
+ public TiledMapTile GetTile(ushort x, ushort y)
+ {
+ var index = GetTileIndex(x, y);
+ return Tiles[index];
+ }
+
+ public void SetTile(ushort x, ushort y, uint globalIdentifier)
+ {
+ var index = GetTileIndex(x, y);
+ Tiles[index] = new TiledMapTile(globalIdentifier, x, y);
+ }
+
+ public void RemoveTile(ushort x, ushort y)
+ {
+ SetTile(x, y, 0);
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileObject.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileObject.cs
new file mode 100644
index 0000000..b4e8679
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileObject.cs
@@ -0,0 +1,18 @@
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public sealed class TiledMapTileObject : TiledMapObject
+ {
+ public TiledMapTileObject(int identifier, string name, TiledMapTileset tileset, TiledMapTilesetTile tile,
+ Size2 size, Vector2 position, float rotation = 0, float opacity = 1, bool isVisible = true, string type = null)
+ : base(identifier, name, size, position, rotation, opacity, isVisible, type)
+ {
+ Tileset = tileset;
+ Tile = tile;
+ }
+
+ public TiledMapTilesetTile Tile { get; }
+ public TiledMapTileset Tileset { get; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileset.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileset.cs
new file mode 100644
index 0000000..ed14e15
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTileset.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using MonoGame.Extended.TextureAtlases;
+
+namespace MonoGame.Extended.Tiled
+{
+ public interface ITileset
+ {
+ int ActualWidth { get; }
+ int Columns { get; }
+ int ActualHeight { get; }
+ int Rows { get; }
+ int TileWidth { get; }
+ int TileHeight { get; }
+ Texture2D Texture { get; }
+ TextureRegion2D GetRegion(int column, int row);
+ }
+
+ public class TiledMapTileset : ITileset
+ {
+ public TiledMapTileset(Texture2D texture, string type, int tileWidth, int tileHeight, int tileCount, int spacing, int margin, int columns)
+ {
+ Texture = texture;
+ Type = type;
+ TileWidth = tileWidth;
+ TileHeight = tileHeight;
+ TileCount = tileCount;
+ Spacing = spacing;
+ Margin = margin;
+ Columns = columns;
+ Properties = new TiledMapProperties();
+ Tiles = new List<TiledMapTilesetTile>();
+ }
+
+ public string Name => Texture.Name;
+ public Texture2D Texture { get; }
+
+ public TextureRegion2D GetRegion(int column, int row)
+ {
+ var x = Margin + column * (TileWidth + Spacing);
+ var y = Margin + row * (TileHeight + Spacing);
+ return new TextureRegion2D(Texture, x, y, TileWidth, TileHeight);
+ }
+
+ public string Type { get; }
+ public int TileWidth { get; }
+ public int TileHeight { get; }
+ public int Spacing { get; }
+ public int Margin { get; }
+ public int TileCount { get; }
+ public int Columns { get; }
+ public List<TiledMapTilesetTile> Tiles { get; }
+ public TiledMapProperties Properties { get; }
+
+ public int Rows => (int)Math.Ceiling((double) TileCount / Columns);
+ public int ActualWidth => TileWidth * Columns;
+ public int ActualHeight => TileHeight * Rows;
+
+ public Rectangle GetTileRegion(int localTileIdentifier)
+ {
+ return Texture is not null
+ ? TiledMapHelper.GetTileSourceRectangle(localTileIdentifier, TileWidth, TileHeight, Columns, Margin,
+ Spacing)
+ : Tiles.FirstOrDefault(x => x.LocalTileIdentifier == localTileIdentifier).Texture.Bounds;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs
new file mode 100644
index 0000000..1bbb770
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetAnimatedTile.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.ObjectModel;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapTilesetAnimatedTile : TiledMapTilesetTile
+ {
+ private TimeSpan _timer = TimeSpan.Zero;
+ private int _frameIndex;
+
+ public ReadOnlyCollection<TiledMapTilesetTileAnimationFrame> AnimationFrames { get; }
+ public TiledMapTilesetTileAnimationFrame CurrentAnimationFrame { get; private set; }
+
+ public TiledMapTilesetAnimatedTile(int localTileIdentifier,
+ TiledMapTilesetTileAnimationFrame[] frames, string type = null, TiledMapObject[] objects = null, Texture2D texture = null)
+ : base(localTileIdentifier, type, objects, texture)
+ {
+ if (frames.Length == 0) throw new InvalidOperationException("There must be at least one tileset animation frame");
+
+ AnimationFrames = new ReadOnlyCollection<TiledMapTilesetTileAnimationFrame>(frames);
+ CurrentAnimationFrame = AnimationFrames[0];
+ }
+
+ public void CreateTextureRotations(TiledMapTileset tileset, TiledMapTileFlipFlags flipFlags)
+ {
+ for (int i = 0; i < AnimationFrames.Count; i++)
+ {
+ AnimationFrames[i].CreateTextureRotations(tileset, flipFlags);
+ }
+ }
+
+ public void Update(GameTime gameTime)
+ {
+ _timer += gameTime.ElapsedGameTime;
+
+ if (_timer <= CurrentAnimationFrame.Duration)
+ return;
+
+ _timer -= CurrentAnimationFrame.Duration;
+ _frameIndex = (_frameIndex + 1) % AnimationFrames.Count;
+ CurrentAnimationFrame = AnimationFrames[_frameIndex];
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs
new file mode 100644
index 0000000..646f835
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetReader.cs
@@ -0,0 +1,150 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using System;
+
+namespace MonoGame.Extended.Tiled
+{
+ public class TiledMapTilesetReader : ContentTypeReader<TiledMapTileset>
+ {
+ protected override TiledMapTileset Read(ContentReader reader, TiledMapTileset existingInstance)
+ {
+ if (existingInstance != null)
+ return existingInstance;
+
+ return ReadTileset(reader);
+ }
+
+ public static TiledMapTileset ReadTileset(ContentReader reader)
+ {
+ var texture = reader.ReadExternalReference<Texture2D>();
+ var @class = reader.ReadString();
+ var tileWidth = reader.ReadInt32();
+ var tileHeight = reader.ReadInt32();
+ var tileCount = reader.ReadInt32();
+ var spacing = reader.ReadInt32();
+ var margin = reader.ReadInt32();
+ var columns = reader.ReadInt32();
+ var explicitTileCount = reader.ReadInt32();
+
+ var tileset = new TiledMapTileset(texture, @class, tileWidth, tileHeight, tileCount, spacing, margin, columns);
+
+ for (var tileIndex = 0; tileIndex < explicitTileCount; tileIndex++)
+ ReadTile(reader, tileset);
+
+ reader.ReadTiledMapProperties(tileset.Properties);
+ return tileset;
+ }
+
+ private static void ReadTile(ContentReader reader, TiledMapTileset tileset)
+ {
+ var texture = reader.ReadExternalReference<Texture2D>();
+
+ var localTileIdentifier = reader.ReadInt32();
+ var type = reader.ReadString();
+ var animationFramesCount = reader.ReadInt32();
+ var objectCount = reader.ReadInt32();
+ var objects = new TiledMapObject[objectCount];
+
+ for (var i = 0; i < objectCount; i++)
+ objects[i] = ReadTiledMapObject(reader, tileset);
+
+ var tilesetTile = animationFramesCount <= 0
+ ? new TiledMapTilesetTile(localTileIdentifier, type, objects, texture)
+ : new TiledMapTilesetAnimatedTile(localTileIdentifier,
+ ReadTiledMapTilesetAnimationFrames(reader, tileset, animationFramesCount), type, objects, texture);
+
+ reader.ReadTiledMapProperties(tilesetTile.Properties);
+ tileset.Tiles.Add(tilesetTile);
+ }
+
+ private static TiledMapTilesetTileAnimationFrame[] ReadTiledMapTilesetAnimationFrames(ContentReader reader, TiledMapTileset tileset, int animationFramesCount)
+ {
+ var animationFrames = new TiledMapTilesetTileAnimationFrame[animationFramesCount];
+
+ for (var i = 0; i < animationFramesCount; i++)
+ {
+ var localTileIdentifierForFrame = reader.ReadInt32();
+ var frameDurationInMilliseconds = reader.ReadInt32();
+ var tileSetTileFrame = new TiledMapTilesetTileAnimationFrame(tileset, localTileIdentifierForFrame, frameDurationInMilliseconds);
+ animationFrames[i] = tileSetTileFrame;
+ }
+
+ return animationFrames;
+ }
+
+ private static TiledMapTilesetTile ReadTiledMapTilesetTile(ContentReader reader, TiledMapTileset tileset, Func<TiledMapObject[], TiledMapTilesetTile> createTile)
+ {
+ var texture = reader.ReadExternalReference<Texture2D>();
+ var objectCount = reader.ReadInt32();
+ var objects = new TiledMapObject[objectCount];
+
+ for (var i = 0; i < objectCount; i++)
+ objects[i] = ReadTiledMapObject(reader, tileset);
+
+ return createTile(objects);
+ }
+
+ private static TiledMapObject ReadTiledMapObject(ContentReader reader, TiledMapTileset tileset)
+ {
+ var objectType = (TiledMapObjectType)reader.ReadByte();
+ var identifier = reader.ReadInt32();
+ var name = reader.ReadString();
+ var type = reader.ReadString();
+ var position = new Vector2(reader.ReadSingle(), reader.ReadSingle());
+ var width = reader.ReadSingle();
+ var height = reader.ReadSingle();
+ var size = new Size2(width, height);
+ var rotation = reader.ReadSingle();
+ var isVisible = reader.ReadBoolean();
+ var properties = new TiledMapProperties();
+ const float opacity = 1.0f;
+
+ reader.ReadTiledMapProperties(properties);
+
+ TiledMapObject mapObject;
+
+ switch (objectType)
+ {
+ case TiledMapObjectType.Rectangle:
+ mapObject = new TiledMapRectangleObject(identifier, name, size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Tile:
+ reader.ReadUInt32(); // Tile objects within TiledMapTilesetTiles currently ignore the gid and behave like rectangle objects.
+ mapObject = new TiledMapRectangleObject(identifier, name, size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Ellipse:
+ mapObject = new TiledMapEllipseObject(identifier, name, size, position, rotation, opacity, isVisible);
+ break;
+ case TiledMapObjectType.Polygon:
+ mapObject = new TiledMapPolygonObject(identifier, name, ReadPoints(reader), size, position, rotation, opacity, isVisible, type);
+ break;
+ case TiledMapObjectType.Polyline:
+ mapObject = new TiledMapPolylineObject(identifier, name, ReadPoints(reader), size, position, rotation, opacity, isVisible, type);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ foreach (var property in properties)
+ mapObject.Properties.Add(property.Key, property.Value);
+
+ return mapObject;
+ }
+
+ private static Point2[] ReadPoints(ContentReader reader)
+ {
+ var pointCount = reader.ReadInt32();
+ var points = new Point2[pointCount];
+
+ for (var i = 0; i < pointCount; i++)
+ {
+ var x = reader.ReadSingle();
+ var y = reader.ReadSingle();
+ points[i] = new Point2(x, y);
+ }
+
+ return points;
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs
new file mode 100644
index 0000000..e378aa3
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTile.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Tiled
+{
+ [DebuggerDisplay("{LocalTileIdentifier}: Type: {Type}, Properties: {Properties.Count}, Objects: {Objects.Count}")]
+ public class TiledMapTilesetTile
+ {
+ // For remove libraries
+ public TiledMapTilesetTile(int localTileIdentifier, string type = null,
+ TiledMapObject[] objects = null)
+ {
+ LocalTileIdentifier = localTileIdentifier;
+ Type = type;
+ Objects = objects != null ? new List<TiledMapObject>(objects) : new List<TiledMapObject>();
+ Properties = new TiledMapProperties();
+ }
+
+ public TiledMapTilesetTile(int localTileIdentifier, string type = null,
+ TiledMapObject[] objects = null, Texture2D texture = null)
+ {
+ Texture = texture;
+ LocalTileIdentifier = localTileIdentifier;
+ Type = type;
+ Objects = objects != null ? new List<TiledMapObject>(objects) : new List<TiledMapObject>();
+ Properties = new TiledMapProperties();
+ }
+
+ public int LocalTileIdentifier { get; }
+ public string Type { get; }
+ public TiledMapProperties Properties { get; }
+ public List<TiledMapObject> Objects { get; }
+ public Texture2D Texture { get; }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTileAnimationFrame.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTileAnimationFrame.cs
new file mode 100644
index 0000000..9dbca80
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/TiledMapTilesetTileAnimationFrame.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace MonoGame.Extended.Tiled
+{
+ public struct TiledMapTilesetTileAnimationFrame
+ {
+ public readonly int LocalTileIdentifier;
+ public readonly TimeSpan Duration;
+ public readonly Vector2[] TextureCoordinates;
+ private readonly Dictionary<TiledMapTileFlipFlags, Vector2[]> _flipDictionary = new Dictionary<TiledMapTileFlipFlags, Vector2[]>();
+
+ internal TiledMapTilesetTileAnimationFrame(TiledMapTileset tileset, int localTileIdentifier, int durationInMilliseconds)
+ {
+ LocalTileIdentifier = localTileIdentifier;
+ Duration = new TimeSpan(0, 0, 0, 0, durationInMilliseconds);
+ TextureCoordinates = new Vector2[4];
+ CreateTextureCoordinates(tileset);
+ }
+
+ public Vector2[] GetTextureCoordinates(TiledMapTileFlipFlags flipFlags)
+ {
+ if (!_flipDictionary.TryGetValue(flipFlags, out Vector2[] flippedTextureCoordiantes))
+ {
+ return TextureCoordinates;
+ }
+ else
+ {
+ return flippedTextureCoordiantes;
+ }
+ }
+
+ public void CreateTextureRotations(TiledMapTileset tileset, TiledMapTileFlipFlags flipFlags)
+ {
+ if (!_flipDictionary.ContainsKey(flipFlags))
+ {
+ if (flipFlags == TiledMapTileFlipFlags.None)
+ {
+ _flipDictionary.Add(flipFlags, TextureCoordinates);
+ }
+ else
+ {
+ _flipDictionary.Add(flipFlags, TransformTextureCoordinates(tileset, flipFlags));
+ }
+ }
+ }
+
+ public Vector2[] TransformTextureCoordinates(TiledMapTileset tileset, TiledMapTileFlipFlags flipFlags)
+ {
+ var sourceRectangle = tileset.GetTileRegion(LocalTileIdentifier);
+ var texture = tileset.Texture;
+ var texelLeft = (float)sourceRectangle.X / texture.Width;
+ var texelTop = (float)sourceRectangle.Y / texture.Height;
+ var texelRight = (sourceRectangle.X + sourceRectangle.Width) / (float)texture.Width;
+ var texelBottom = (sourceRectangle.Y + sourceRectangle.Height) / (float)texture.Height;
+
+ var flipDiagonally = (flipFlags & TiledMapTileFlipFlags.FlipDiagonally) != 0;
+ var flipHorizontally = (flipFlags & TiledMapTileFlipFlags.FlipHorizontally) != 0;
+ var flipVertically = (flipFlags & TiledMapTileFlipFlags.FlipVertically) != 0;
+ var transform = new Vector2[4];
+
+ transform[0].X = texelLeft;
+ transform[0].Y = texelTop;
+
+ transform[1].X = texelRight;
+ transform[1].Y = texelTop;
+
+ transform[2].X = texelLeft;
+ transform[2].Y = texelBottom;
+
+ transform[3].X = texelRight;
+ transform[3].Y = texelBottom;
+
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref transform[1].X, ref transform[2].X);
+ FloatHelper.Swap(ref transform[1].Y, ref transform[2].Y);
+ }
+
+ if (flipHorizontally)
+ {
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref transform[0].Y, ref transform[1].Y);
+ FloatHelper.Swap(ref transform[2].Y, ref transform[3].Y);
+ }
+ else
+ {
+ FloatHelper.Swap(ref transform[0].X, ref transform[1].X);
+ FloatHelper.Swap(ref transform[2].X, ref transform[3].X);
+ }
+ }
+
+ if (flipVertically)
+ {
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref transform[0].X, ref transform[2].X);
+ FloatHelper.Swap(ref transform[1].X, ref transform[3].X);
+ }
+ else
+ {
+ FloatHelper.Swap(ref transform[0].Y, ref transform[2].Y);
+ FloatHelper.Swap(ref transform[1].Y, ref transform[3].Y);
+ }
+ }
+
+ transform[0] = transform[0];
+ transform[1] = transform[1];
+ transform[2] = transform[2];
+ transform[3] = transform[3];
+
+ return transform;
+ }
+
+ private void CreateTextureCoordinates(TiledMapTileset tileset)
+ {
+ var sourceRectangle = tileset.GetTileRegion(LocalTileIdentifier);
+ var texture = tileset.Texture;
+ var texelLeft = (float)sourceRectangle.X / texture.Width;
+ var texelTop = (float)sourceRectangle.Y / texture.Height;
+ var texelRight = (sourceRectangle.X + sourceRectangle.Width) / (float)texture.Width;
+ var texelBottom = (sourceRectangle.Y + sourceRectangle.Height) / (float)texture.Height;
+
+ TextureCoordinates[0].X = texelLeft;
+ TextureCoordinates[0].Y = texelTop;
+
+ TextureCoordinates[1].X = texelRight;
+ TextureCoordinates[1].Y = texelTop;
+
+ TextureCoordinates[2].X = texelLeft;
+ TextureCoordinates[2].Y = texelBottom;
+
+ TextureCoordinates[3].X = texelRight;
+ TextureCoordinates[3].Y = texelBottom;
+ }
+
+ public override string ToString()
+ {
+ return $"{LocalTileIdentifier}:{Duration}";
+ }
+ }
+}