summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Tiled/Renderers/TiledMapRenderer.cs
blob: 66babadfe53882142579df50dbec8d6a3d7d069c (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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;
        }
    }
}