summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapLayerModelBuilder.cs
blob: fbc99d68706778ba36280698e8d70d10434012a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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);
        }
    }
}