summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers')
-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
10 files changed, 656 insertions, 0 deletions
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