summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics')
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs387
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs450
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/DefaultEffect.cs203
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/EffectResource.cs119
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/ITextureEffect.cs18
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/MatrixChainEffect.cs155
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.dx11.mgfxobin0 -> 4829 bytes
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.fx72
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.ogl.mgfxobin0 -> 4833 bytes
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Macros.fxh60
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/RebuildEffects.bat15
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Structures.fxh51
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/FlipFlags.cs13
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder.cs24
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs119
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IBatchDrawCallInfo.cs19
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IMatrixChainEffect.cs30
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj28
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/PrimitiveTypeExtensions.cs42
-rw-r--r--Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/RenderTarget2DExtensions.cs41
20 files changed, 1846 insertions, 0 deletions
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs
new file mode 100644
index 0000000..3cb5704
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs
@@ -0,0 +1,387 @@
+using System;
+using System.Runtime.CompilerServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ /// <summary>
+ /// Minimizes draw calls to a <see cref="GraphicsDevice" /> by sorting them and attempting to merge them together
+ /// before submitting them.
+ /// </summary>
+ /// <typeparam name="TDrawCallInfo">The type of the information for a draw call.</typeparam>
+ /// <seealso cref="IDisposable" />
+ public abstract class Batcher<TDrawCallInfo> : IDisposable
+ where TDrawCallInfo : struct, IBatchDrawCallInfo<TDrawCallInfo>, IComparable<TDrawCallInfo>
+ {
+ internal const int DefaultBatchMaximumDrawCallsCount = 2048;
+ private BlendState _blendState;
+ private SamplerState _samplerState;
+ private DepthStencilState _depthStencilState;
+ private RasterizerState _rasterizerState;
+ private readonly Effect _defaultEffect;
+ private Effect _currentEffect;
+ private Matrix? _viewMatrix;
+ private Matrix? _projectionMatrix;
+
+ /// <summary>
+ /// The array of <see cref="TDrawCallInfo" /> structs currently enqueued.
+ /// </summary>
+ protected TDrawCallInfo[] DrawCalls;
+
+ /// <summary>
+ /// The number of <see cref="TDrawCallInfo" /> structs currently enqueued.
+ /// </summary>
+ protected int EnqueuedDrawCallCount;
+
+ /// <summary>
+ /// Gets the <see cref="GraphicsDevice" /> associated with this <see cref="Batcher{TDrawCallInfo}" />.
+ /// </summary>
+ /// <value>
+ /// The <see cref="GraphicsDevice" /> associated with this <see cref="Batcher{TDrawCallInfo}" />.
+ /// </value>
+ public GraphicsDevice GraphicsDevice { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether batching is currently in progress by being within a <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair block of code.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if batching has begun; otherwise, <c>false</c>.
+ /// </value>
+ public bool HasBegun { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Batcher{TDrawCallInfo}" /> class.
+ /// </summary>
+ /// <param name="graphicsDevice">The graphics device.</param>
+ /// <param name="defaultEffect">The default effect.</param>
+ /// <param name="maximumDrawCallsCount">
+ /// The maximum number of <see cref="TDrawCallInfo" /> structs that can be enqueued before a
+ /// <see cref="Batcher{TDrawCallInfo}.Flush" />
+ /// is required. The default value is <code>2048</code>.
+ /// </param>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="graphicsDevice" /> is
+ /// null.
+ /// </exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="maximumDrawCallsCount" /> is less than or equal
+ /// <code>0</code>.
+ /// </exception>
+ protected Batcher(GraphicsDevice graphicsDevice, Effect defaultEffect,
+ int maximumDrawCallsCount = DefaultBatchMaximumDrawCallsCount)
+ {
+ if (graphicsDevice == null)
+ throw new ArgumentNullException(nameof(graphicsDevice));
+
+ if (maximumDrawCallsCount <= 0)
+ throw new ArgumentOutOfRangeException(nameof(maximumDrawCallsCount));
+
+ GraphicsDevice = graphicsDevice;
+ _defaultEffect = defaultEffect;
+ DrawCalls = new TDrawCallInfo[maximumDrawCallsCount];
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="diposing">
+ /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
+ /// unmanaged resources.
+ /// </param>
+ protected virtual void Dispose(bool diposing)
+ {
+ if (!diposing)
+ return;
+ _defaultEffect.Dispose();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected void EnsureHasBegun([CallerMemberName] string callerMemberName = null)
+ {
+ if (!HasBegun)
+ throw new InvalidOperationException(
+ $"The {nameof(Begin)} method must be called before the {callerMemberName} method can be called.");
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected void EnsureHasNotBegun([CallerMemberName] string callerMemberName = null)
+ {
+ if (HasBegun)
+ throw new InvalidOperationException(
+ $"The {nameof(End)} method must be called before the {callerMemberName} method can be called.");
+ }
+
+ /// <summary>
+ /// Begins the batch operation using an optional <see cref="BlendState" />, <see cref="SamplerState" />,
+ /// <see cref="DepthStencilState" />, <see cref="RasterizerState" />, <see cref="Effect" />, world-to-view
+ /// <see cref="Matrix" />, or view-to-projection <see cref="Matrix" />.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The default objects for <paramref name="blendState" />, <paramref name="samplerState" />,
+ /// <paramref name="depthStencilState" />, and <paramref name="rasterizerState" /> are
+ /// <see cref="BlendState.AlphaBlend" />, <see cref="SamplerState.LinearClamp" />,
+ /// <see cref="DepthStencilState.None" /> and <see cref="RasterizerState.CullCounterClockwise" /> respectively.
+ /// Passing
+ /// <code>null</code> for any of the previously mentioned parameters result in using their default object.
+ /// </para>
+ /// </remarks>
+ /// <param name="blendState">The <see cref="BlendState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" />, <see cref="End" /> pair.</param>
+ /// <param name="samplerState">
+ /// The texture <see cref="SamplerState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="depthStencilState">
+ /// The <see cref="DepthStencilState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="rasterizerState">
+ /// The <see cref="RasterizerState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="effect">The <see cref="Effect" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and <see cref="End" /> pair.</param>
+ /// <param name="viewMatrix">
+ /// The world-to-view transformation matrix to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="projectionMatrix">
+ /// The view-to-projection transformation matrix to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <exception cref="InvalidOperationException">
+ /// <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> cannot be invoked again until <see cref="End" /> has been invoked.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// This method must be called before any enqueuing of draw calls. When all the geometry have been enqueued for
+ /// drawing, call <see cref="End" />.
+ /// </para>
+ /// </remarks>
+ public virtual void Begin(Matrix? viewMatrix = null, Matrix? projectionMatrix = null, BlendState blendState = null, SamplerState samplerState = null,
+ DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null)
+ {
+ var viewMatrix1 = viewMatrix ?? Matrix.Identity;
+ var projectionMatrix1 = projectionMatrix ?? Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, -1);
+ Begin(ref viewMatrix1, ref projectionMatrix1, blendState, samplerState, depthStencilState, rasterizerState, effect);
+ }
+
+ /// <summary>
+ /// Begins the batch operation using an optional <see cref="BlendState" />, <see cref="SamplerState" />,
+ /// <see cref="DepthStencilState" />, <see cref="RasterizerState" />, <see cref="Effect" />, world-to-view
+ /// <see cref="Matrix" />, or view-to-projection <see cref="Matrix" />.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The default objects for <paramref name="blendState" />, <paramref name="samplerState" />,
+ /// <paramref name="depthStencilState" />, and <paramref name="rasterizerState" /> are
+ /// <see cref="BlendState.AlphaBlend" />, <see cref="SamplerState.LinearClamp" />,
+ /// <see cref="DepthStencilState.None" /> and <see cref="RasterizerState.CullCounterClockwise" /> respectively.
+ /// Passing
+ /// <code>null</code> for any of the previously mentioned parameters result in using their default object.
+ /// </para>
+ /// </remarks>
+ /// <param name="blendState">The <see cref="BlendState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" />, <see cref="End" /> pair.</param>
+ /// <param name="samplerState">
+ /// The texture <see cref="SamplerState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="depthStencilState">
+ /// The <see cref="DepthStencilState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="rasterizerState">
+ /// The <see cref="RasterizerState" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="effect">The <see cref="Effect" /> to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and <see cref="End" /> pair.</param>
+ /// <param name="viewMatrix">
+ /// The world-to-view transformation matrix to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <param name="projectionMatrix">
+ /// The view-to-projection transformation matrix to use for the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and
+ /// <see cref="End" /> pair.
+ /// </param>
+ /// <exception cref="InvalidOperationException">
+ /// <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> cannot be invoked again until <see cref="End" /> has been invoked.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// This method must be called before any enqueuing of draw calls. When all the geometry have been enqueued for
+ /// drawing, call <see cref="End" />.
+ /// </para>
+ /// </remarks>
+ public virtual void Begin(ref Matrix viewMatrix, ref Matrix projectionMatrix, BlendState blendState = null, SamplerState samplerState = null,
+ DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null)
+ {
+ EnsureHasNotBegun();
+ HasBegun = true;
+
+ // Store the states to be applied on End()
+ // This ensures that two or more batchers will not affect each other
+ _blendState = blendState ?? BlendState.AlphaBlend;
+ _samplerState = samplerState ?? SamplerState.PointClamp;
+ _depthStencilState = depthStencilState ?? DepthStencilState.None;
+ _rasterizerState = rasterizerState ?? RasterizerState.CullCounterClockwise;
+ _currentEffect = effect ?? _defaultEffect;
+ _projectionMatrix = projectionMatrix;
+ _viewMatrix = viewMatrix;
+ }
+
+ /// <summary>
+ /// Flushes the batched geometry to the <see cref="GraphicsDevice" /> and restores it's state to how it was before
+ /// <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> was called.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// <see cref="End" /> cannot be invoked until <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> has been invoked.
+ /// </exception>
+ /// <remarks>
+ /// <para>
+ /// This method must be called after all enqueuing of draw calls.
+ /// </para>
+ /// </remarks>
+ public void End()
+ {
+ EnsureHasBegun();
+ Flush();
+ HasBegun = false;
+ }
+
+ /// <summary>
+ /// Sorts then submits the (sorted) enqueued draw calls to the <see cref="GraphicsDevice" /> for
+ /// rendering without ending the <see cref="Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> and <see cref="End" /> pair.
+ /// </summary>
+ protected void Flush()
+ {
+ if (EnqueuedDrawCallCount == 0)
+ return;
+ SortDrawCallsAndBindBuffers();
+ ApplyStates();
+ SubmitDrawCalls();
+ RestoreStates();
+ }
+
+ /// <summary>
+ /// Sorts the enqueued draw calls and binds any used <see cref="VertexBuffer" /> or <see cref="IndexBuffer" /> to the <see cref="GraphicsDevice" />.
+ /// </summary>
+ protected abstract void SortDrawCallsAndBindBuffers();
+
+ private void ApplyStates()
+ {
+ var oldBlendState = GraphicsDevice.BlendState;
+ var oldSamplerState = GraphicsDevice.SamplerStates[0];
+ var oldDepthStencilState = GraphicsDevice.DepthStencilState;
+ var oldRasterizerState = GraphicsDevice.RasterizerState;
+
+ GraphicsDevice.BlendState = _blendState;
+
+ GraphicsDevice.SamplerStates[0] = _samplerState;
+ GraphicsDevice.DepthStencilState = _depthStencilState;
+ GraphicsDevice.RasterizerState = _rasterizerState;
+
+ _blendState = oldBlendState;
+ _samplerState = oldSamplerState;
+ _depthStencilState = oldDepthStencilState;
+ _rasterizerState = oldRasterizerState;
+
+ var viewMatrix = _viewMatrix ?? Matrix.Identity;
+ var projectionMatrix = _projectionMatrix ??
+ Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width,
+ GraphicsDevice.Viewport.Height, 0, 0f, -1f);
+
+ var matrixChainEffect = _currentEffect as IMatrixChainEffect;
+ if (matrixChainEffect != null)
+ {
+ matrixChainEffect.World = Matrix.Identity;
+ matrixChainEffect.SetView(ref viewMatrix);
+ matrixChainEffect.SetProjection(ref projectionMatrix);
+ }
+ else
+ {
+ var effectMatrices = _currentEffect as IEffectMatrices;
+ if (effectMatrices == null)
+ return;
+ effectMatrices.World = Matrix.Identity;
+ effectMatrices.View = viewMatrix;
+ effectMatrices.Projection = projectionMatrix;
+ }
+ }
+
+ private void RestoreStates()
+ {
+ GraphicsDevice.BlendState = _blendState;
+ GraphicsDevice.SamplerStates[0] = _samplerState;
+ GraphicsDevice.DepthStencilState = _depthStencilState;
+ GraphicsDevice.RasterizerState = _rasterizerState;
+ }
+
+ /// <summary>
+ /// Enqueues draw call information.
+ /// </summary>
+ /// <param name="drawCall">The draw call information.</param>
+ /// <remarks>
+ /// <para>
+ /// If possible, the <paramref name="drawCall" /> is merged with the last enqueued draw call information instead of
+ /// being
+ /// enqueued.
+ /// </para>
+ /// <para>
+ /// If the enqueue buffer is full, a <see cref="Flush" /> is invoked and then afterwards
+ /// <paramref name="drawCall" /> is enqueued.
+ /// </para>
+ /// </remarks>
+ protected void Enqueue(ref TDrawCallInfo drawCall)
+ {
+ if (EnqueuedDrawCallCount > 0 && drawCall.TryMerge(ref DrawCalls[EnqueuedDrawCallCount - 1]))
+ return;
+ if (EnqueuedDrawCallCount >= DrawCalls.Length)
+ Flush();
+ DrawCalls[EnqueuedDrawCallCount++] = drawCall;
+ }
+
+ /* It might be better to have derived classes just implement the for loop instead of having this virtual method call...
+ * However, if the derived class is only going to override this method once and the code is short, which should both be
+ * true, then maybe we can get away with this virtual method call by having it inlined. So tell the JIT or AOT compiler
+ * we would like it be so. This does NOT guarantee the compiler will respect our wishes.
+ */
+
+ /// <summary>
+ /// Submits a draw operation to the <see cref="GraphicsDevice" /> using the specified <see cref="TDrawCallInfo"/>.
+ /// </summary>
+ /// <param name="drawCall">The draw call information.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected abstract void InvokeDrawCall(ref TDrawCallInfo drawCall);
+
+ private void SubmitDrawCalls()
+ {
+ if (EnqueuedDrawCallCount == 0)
+ return;
+
+ for (var i = 0; i < EnqueuedDrawCallCount; i++)
+ {
+ DrawCalls[i].SetState(_currentEffect);
+
+ foreach (var pass in _currentEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+ InvokeDrawCall(ref DrawCalls[i]);
+ }
+ }
+
+ Array.Clear(DrawCalls, 0, EnqueuedDrawCallCount);
+ EnqueuedDrawCallCount = 0;
+ }
+ }
+}
+ \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs
new file mode 100644
index 0000000..e7ec533
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs
@@ -0,0 +1,450 @@
+using System;
+using System.Text;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using MonoGame.Extended.BitmapFonts;
+using MonoGame.Extended.Graphics.Effects;
+using MonoGame.Extended.Graphics.Geometry;
+
+namespace MonoGame.Extended.Graphics
+{
+ /// <summary>
+ /// A general purpose <see cref="Batcher{TDrawCallInfo}" /> for two-dimensional geometry that change
+ /// frequently between frames such as sprites and shapes.
+ /// </summary>
+ /// <seealso cref="IDisposable" />
+ /// <remarks>
+ /// <para>For drawing user interfaces, consider using <see cref="UIBatcher(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> instead because it supports scissor rectangles.</para>
+ /// </remarks>
+ public sealed class Batcher2D : Batcher<Batcher2D.DrawCallInfo>
+ {
+
+ internal const int DefaultMaximumVerticesCount = 8192;
+ internal const int DefaultMaximumIndicesCount = 12288;
+
+ private readonly VertexBuffer _vertexBuffer;
+ private readonly IndexBuffer _indexBuffer;
+ private readonly VertexPositionColorTexture[] _vertices;
+ private int _vertexCount;
+ private readonly ushort[] _indices;
+ private int _indexCount;
+ private readonly ushort[] _sortedIndices;
+ private readonly GeometryBuilder2D _geometryBuilder;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Batcher2D" /> class.
+ /// </summary>
+ /// <param name="graphicsDevice">The graphics device.</param>
+ /// <param name="maximumVerticesCount">
+ /// The maximum number of vertices that can be enqueued before a
+ /// <see cref="Batcher{TDrawCallInfo}.Flush" /> is required. The default value is <code>8192</code>.
+ /// </param>
+ /// <param name="maximumIndicesCount">
+ /// The maximum number of indices that can be enqueued before a
+ /// <see cref="Batcher{TDrawCallInfo}.Flush" /> is required. The default value is <code>12288</code>.
+ /// </param>
+ /// <param name="maximumDrawCallsCount">
+ /// The maximum number of <see cref="DrawCallInfo" /> structs that can be enqueued before a
+ /// <see cref="Batcher{TDrawCallInfo}.Flush" /> is required. The default value is <code>2048</code>.
+ /// </param>
+ /// <exception cref="ArgumentNullException"><paramref name="graphicsDevice" />.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="maximumDrawCallsCount" /> is less than or equal
+ /// <code>0</code>, or <paramref name="maximumVerticesCount" /> is less than or equal to <code>0</code>, or,
+ /// <paramref name="maximumVerticesCount" /> is less than or equal to <code>0</code>.
+ /// </exception>
+ public Batcher2D(GraphicsDevice graphicsDevice,
+ ushort maximumVerticesCount = DefaultMaximumVerticesCount,
+ ushort maximumIndicesCount = DefaultMaximumIndicesCount,
+ int maximumDrawCallsCount = DefaultBatchMaximumDrawCallsCount)
+ : base(
+ graphicsDevice,
+ new DefaultEffect(graphicsDevice)
+ {
+ TextureEnabled = true,
+ VertexColorEnabled = true
+ }, maximumDrawCallsCount)
+
+ {
+ _vertices = new VertexPositionColorTexture[maximumVerticesCount];
+ _vertexBuffer = new DynamicVertexBuffer(graphicsDevice, VertexPositionColorTexture.VertexDeclaration, maximumVerticesCount, BufferUsage.WriteOnly);
+
+ _indices = new ushort[maximumIndicesCount];
+ _sortedIndices = new ushort[maximumIndicesCount];
+ _indexBuffer = new DynamicIndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, maximumIndicesCount, BufferUsage.WriteOnly);
+ _geometryBuilder = new GeometryBuilder2D(4, 6);
+ }
+
+ protected override void SortDrawCallsAndBindBuffers()
+ {
+ // Upload the vertices to the GPU and then select that vertex stream for drawing
+ _vertexBuffer.SetData(_vertices, 0, _vertexCount);
+ GraphicsDevice.SetVertexBuffer(_vertexBuffer);
+
+ Array.Sort(DrawCalls, 0, EnqueuedDrawCallCount);
+ BuildSortedIndices();
+
+ // Upload the indices to the GPU and then select that index stream for drawing
+ _indexBuffer.SetData(_sortedIndices, 0, _indexCount);
+ GraphicsDevice.Indices = _indexBuffer;
+
+ _indexCount = 0;
+ _vertexCount = 0;
+ }
+
+ private void BuildSortedIndices()
+ {
+ var newDrawCallsCount = 0;
+ DrawCalls[0].StartIndex = 0;
+ var currentDrawCall = DrawCalls[0];
+ DrawCalls[newDrawCallsCount++] = DrawCalls[0];
+
+ var drawCallIndexCount = currentDrawCall.PrimitiveCount * 3;
+ Array.Copy(_indices, currentDrawCall.StartIndex, _sortedIndices, 0, drawCallIndexCount);
+ var sortedIndexCount = drawCallIndexCount;
+
+ // iterate through sorted draw calls checking if any can now be merged to reduce expensive draw calls to the graphics API
+ // this might need to be changed for next-gen graphics API (Vulkan, Metal, DirectX 12) where the draw calls are not so expensive
+ for (var i = 1; i < EnqueuedDrawCallCount; i++)
+ {
+ currentDrawCall = DrawCalls[i];
+ drawCallIndexCount = currentDrawCall.PrimitiveCount * 3;
+ Array.Copy(_indices, currentDrawCall.StartIndex, _sortedIndices, sortedIndexCount, drawCallIndexCount);
+ sortedIndexCount += drawCallIndexCount;
+
+ if (currentDrawCall.TryMerge(ref DrawCalls[newDrawCallsCount - 1]))
+ continue;
+
+ currentDrawCall.StartIndex = sortedIndexCount;
+ DrawCalls[newDrawCallsCount++] = currentDrawCall;
+ }
+
+ EnqueuedDrawCallCount = newDrawCallsCount;
+ }
+
+ /// <summary>
+ /// Submits a draw operation to the <see cref="GraphicsDevice" /> using the specified <see cref="DrawCallInfo"/>.
+ /// </summary>
+ /// <param name="drawCall">The draw call information.</param>
+ protected override void InvokeDrawCall(ref DrawCallInfo drawCall)
+ {
+ GraphicsDevice.DrawIndexedPrimitives(drawCall.PrimitiveType, 0, drawCall.StartIndex, drawCall.PrimitiveCount);
+ }
+
+ /// <summary>
+ /// Draws a sprite using a specified <see cref="Texture" />, transform <see cref="Matrix2" />, source
+ /// <see cref="Rectangle" />, and an optional
+ /// <see cref="Color" />, origin <see cref="Vector2" />, <see cref="FlipFlags" />, and depth <see cref="float" />.
+ /// </summary>
+ /// <param name="texture">The <see cref="Texture" />.</param>
+ /// <param name="transformMatrix">The transform <see cref="Matrix2" />.</param>
+ /// <param name="sourceRectangle">
+ /// The texture region <see cref="Rectangle" /> of the <paramref name="texture" />. Use
+ /// <code>null</code> to use the entire <see cref="Texture2D" />.
+ /// </param>
+ /// <param name="color">The <see cref="Color" />. Use <code>null</code> to use the default <see cref="Color.White" />.</param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0</code>.</param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
+ public void DrawSprite(Texture2D texture, ref Matrix2 transformMatrix, ref Rectangle sourceRectangle,
+ Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0)
+ {
+ _geometryBuilder.BuildSprite(_vertexCount, ref transformMatrix, texture, ref sourceRectangle, color, flags, depth);
+ EnqueueBuiltGeometry(texture, depth);
+ }
+
+ /// <summary>
+ /// Draws a <see cref="Texture" /> using the specified transform <see cref="Matrix2" /> and an optional
+ /// <see cref="Color" />, origin <see cref="Vector2" />, <see cref="FlipFlags" />, and depth <see cref="float" />.
+ /// </summary>
+ /// <param name="texture">The <see cref="Texture" />.</param>
+ /// <param name="transformMatrix">The transform <see cref="Matrix2" />.</param>
+ /// <param name="color">The <see cref="Color" />. Use <code>null</code> to use the default <see cref="Color.White" />.</param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0</code>.</param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
+ public void DrawTexture(Texture2D texture, ref Matrix2 transformMatrix, Color? color = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0)
+ {
+ var rectangle = default(Rectangle);
+ _geometryBuilder.BuildSprite(_vertexCount, ref transformMatrix, texture, ref rectangle, color, flags, depth);
+ EnqueueBuiltGeometry(texture, depth);
+ }
+
+ private void EnqueueBuiltGeometry(Texture2D texture, float depth)
+ {
+ if ((_vertexCount + _geometryBuilder.VertexCount > _vertices.Length) ||
+ (_indexCount + _geometryBuilder.IndexCount > _indices.Length))
+ Flush();
+ var drawCall = new DrawCallInfo(texture, _geometryBuilder.PrimitiveType, _indexCount,
+ _geometryBuilder.PrimitivesCount, depth);
+ Array.Copy(_geometryBuilder.Vertices, 0, _vertices, _vertexCount, _geometryBuilder.VertexCount);
+ _vertexCount += _geometryBuilder.VertexCount;
+ Array.Copy(_geometryBuilder.Indices, 0, _indices, _indexCount, _geometryBuilder.IndexCount);
+ _indexCount += _geometryBuilder.IndexCount;
+ Enqueue(ref drawCall);
+ }
+
+ /// <summary>
+ /// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
+ /// <see cref="StringBuilder" />, transform <see cref="Matrix2" /> and optional <see cref="Color" />, origin
+ /// <see cref="Vector2" />, <see cref="FlipFlags" />, and depth <see cref="float" />.
+ /// </summary>
+ /// <param name="bitmapFont">The <see cref="BitmapFont" />.</param>
+ /// <param name="text">The text <see cref="StringBuilder" />.</param>
+ /// <param name="transformMatrix">The transform <see cref="Matrix2" />.</param>
+ /// <param name="color">
+ /// The <see cref="Color" />. Use <code>null</code> to use the default
+ /// <see cref="Color.White" />.
+ /// </param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code>.</param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
+ public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix2 transformMatrix,
+ Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ EnsureHasBegun();
+
+ if (bitmapFont == null)
+ throw new ArgumentNullException(nameof(bitmapFont));
+
+ if (text == null)
+ throw new ArgumentNullException(nameof(text));
+
+ var lineSpacing = bitmapFont.LineHeight;
+ var offset = new Vector2(0, 0);
+
+ BitmapFontRegion lastGlyph = null;
+ for (var i = 0; i < text.Length;)
+ {
+ int character;
+ if (char.IsLowSurrogate(text[i]))
+ {
+ character = char.ConvertToUtf32(text[i - 1], text[i]);
+ i += 2;
+ }
+ else if (char.IsHighSurrogate(text[i]))
+ {
+ character = char.ConvertToUtf32(text[i], text[i - 1]);
+ i += 2;
+ }
+ else
+ {
+ character = text[i];
+ i += 1;
+ }
+
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (character)
+ {
+ case '\r':
+ continue;
+ case '\n':
+ offset.X = 0;
+ offset.Y += lineSpacing;
+ lastGlyph = null;
+ continue;
+ }
+
+ var fontRegion = bitmapFont.GetCharacterRegion(character);
+ if (fontRegion == null)
+ continue;
+
+ var transform1Matrix = transformMatrix;
+ transform1Matrix.M31 += offset.X + fontRegion.XOffset;
+ transform1Matrix.M32 += offset.Y + fontRegion.YOffset;
+
+ var textureRegion = fontRegion.TextureRegion;
+ var bounds = textureRegion.Bounds;
+ DrawSprite(textureRegion.Texture, ref transform1Matrix, ref bounds, color, flags, depth);
+
+ var advance = fontRegion.XAdvance + bitmapFont.LetterSpacing;
+ if (BitmapFont.UseKernings && lastGlyph != null)
+ {
+ int amount;
+ if (lastGlyph.Kernings.TryGetValue(character, out amount))
+ {
+ advance += amount;
+ }
+ }
+
+ offset.X += i != text.Length - 1
+ ? advance
+ : fontRegion.XOffset + fontRegion.Width;
+
+ lastGlyph = fontRegion;
+ }
+ }
+
+ /// <summary>
+ /// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
+ /// <see cref="StringBuilder" />, position <see cref="Vector2" /> and optional <see cref="Color" />, rotation
+ /// <see cref="float" />, origin <see cref="Vector2" />, scale <see cref="Vector2" /> <see cref="FlipFlags" />, and
+ /// depth <see cref="float" />.
+ /// </summary>
+ /// <param name="bitmapFont">The <see cref="BitmapFont" />.</param>
+ /// <param name="text">The text <see cref="string" />.</param>
+ /// <param name="position">The position <see cref="Vector2" />.</param>
+ /// <param name="color">
+ /// The <see cref="Color" />. Use <code>null</code> to use the default
+ /// <see cref="Color.White" />.
+ /// </param>
+ /// <param name="rotation">
+ /// The angle <see cref="float" /> (in radians) to rotate each sprite about its <paramref name="origin" />. The default
+ /// value is <code>0f</code>.
+ /// </param>
+ /// <param name="origin">
+ /// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
+ /// <see cref="Vector2.Zero" />.
+ /// </param>
+ /// <param name="scale">
+ /// The scale <see cref="Vector2" />. Use <code>null</code> to use the default
+ /// <see cref="Vector2.One" />.
+ /// </param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
+ public void DrawString(BitmapFont bitmapFont, StringBuilder text, Vector2 position, Color? color = null,
+ float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ Matrix2 transformMatrix;
+ Matrix2.CreateFrom(position, rotation, scale, origin, out transformMatrix);
+ DrawString(bitmapFont, text, ref transformMatrix, color, flags, depth);
+ }
+
+ /// <summary>
+ /// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
+ /// <see cref="string" />, transform <see cref="Matrix2" /> and optional <see cref="Color" />, origin
+ /// <see cref="Vector2" />, <see cref="FlipFlags" />, and depth <see cref="float" />.
+ /// </summary>
+ /// <param name="bitmapFont">The <see cref="BitmapFont" />.</param>
+ /// <param name="text">The text <see cref="string" />.</param>
+ /// <param name="transformMatrix">The transform <see cref="Matrix2" />.</param>
+ /// <param name="color">
+ /// The <see cref="Color" />. Use <code>null</code> to use the default
+ /// <see cref="Color.White" />.
+ /// </param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
+ public void DrawString(BitmapFont bitmapFont, string text, ref Matrix2 transformMatrix, Color? color = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ EnsureHasBegun();
+
+ if (bitmapFont == null)
+ throw new ArgumentNullException(nameof(bitmapFont));
+
+ if (text == null)
+ throw new ArgumentNullException(nameof(text));
+
+ var glyphs = bitmapFont.GetGlyphs(text);
+ foreach (var glyph in glyphs)
+ {
+ var transform1Matrix = transformMatrix;
+ transform1Matrix.M31 += glyph.Position.X;
+ transform1Matrix.M32 += glyph.Position.Y;
+
+ var texture = glyph.FontRegion.TextureRegion.Texture;
+ var bounds = texture.Bounds;
+ DrawSprite(texture, ref transform1Matrix, ref bounds, color, flags, depth);
+ }
+ }
+
+ /// <summary>
+ /// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
+ /// <see cref="string" />, position <see cref="Vector2" /> and optional <see cref="Color" />, rotation
+ /// <see cref="float" />, origin <see cref="Vector2" />, scale <see cref="Vector2" /> <see cref="FlipFlags" />, and
+ /// depth <see cref="float" />.
+ /// </summary>
+ /// <param name="bitmapFont">The <see cref="BitmapFont" />.</param>
+ /// <param name="text">The text <see cref="string" />.</param>
+ /// <param name="position">The position <see cref="Vector2" />.</param>
+ /// <param name="color">
+ /// The <see cref="Color" />. Use <code>null</code> to use the default
+ /// <see cref="Color.White" />.
+ /// </param>
+ /// <param name="rotation">
+ /// The angle <see cref="float" /> (in radians) to rotate each sprite about its <paramref name="origin" />. The default
+ /// value is <code>0f</code>.
+ /// </param>
+ /// <param name="origin">
+ /// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
+ /// <see cref="Vector2.Zero" />.
+ /// </param>
+ /// <param name="scale">
+ /// The scale <see cref="Vector2" />. Use <code>null</code> to use the default
+ /// <see cref="Vector2.One" />.
+ /// </param>
+ /// <param name="flags">The <see cref="FlipFlags" />. The default value is <see cref="FlipFlags.None" />.</param>
+ /// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
+ /// <exception cref="InvalidOperationException">The <see cref="Batcher{TDrawCallInfo}.Begin(ref Matrix, ref Matrix, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect)" /> method has not been called.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
+ public void DrawString(BitmapFont bitmapFont, string text, Vector2 position, Color? color = null,
+ float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ Matrix2 matrix;
+ Matrix2.CreateFrom(position, rotation, scale, origin, out matrix);
+ DrawString(bitmapFont, text, ref matrix, color, flags, depth);
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public struct DrawCallInfo : IBatchDrawCallInfo<DrawCallInfo>, IComparable<DrawCallInfo>
+ {
+ internal readonly PrimitiveType PrimitiveType;
+ internal int StartIndex;
+ internal int PrimitiveCount;
+ internal readonly Texture2D Texture;
+ internal readonly uint TextureKey;
+ internal readonly uint DepthKey;
+
+ internal unsafe DrawCallInfo(Texture2D texture, PrimitiveType primitiveType, int startIndex, int primitiveCount, float depth)
+ {
+ PrimitiveType = primitiveType;
+ StartIndex = startIndex;
+ PrimitiveCount = primitiveCount;
+ Texture = texture;
+ TextureKey = (uint)RuntimeHelpers.GetHashCode(texture);
+ DepthKey = *(uint*)&depth;
+ }
+
+ public void SetState(Effect effect)
+ {
+ var textureEffect = effect as ITextureEffect;
+ if (textureEffect != null)
+ textureEffect.Texture = Texture;
+ }
+
+ public bool TryMerge(ref DrawCallInfo drawCall)
+ {
+ if (PrimitiveType != drawCall.PrimitiveType || TextureKey != drawCall.TextureKey ||
+ DepthKey != drawCall.DepthKey)
+ return false;
+ drawCall.PrimitiveCount += PrimitiveCount;
+ return true;
+ }
+
+ [SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField")]
+ public int CompareTo(DrawCallInfo other)
+ {
+ var result = TextureKey.CompareTo(other.TextureKey);;
+ if (result != 0)
+ return result;
+ result = DepthKey.CompareTo(other.DepthKey);
+ return result != 0 ? result : ((byte)PrimitiveType).CompareTo((byte)other.PrimitiveType);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/DefaultEffect.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/DefaultEffect.cs
new file mode 100644
index 0000000..df71e47
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/DefaultEffect.cs
@@ -0,0 +1,203 @@
+using System.Collections.Specialized;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Effects
+{
+ /// <summary>
+ /// An <see cref="Effect" /> that allows objects, within a 3D context, to be represented on a 2D monitor.
+ /// </summary>
+ /// <seealso cref="MatrixChainEffect" />
+ /// <seealso cref="ITextureEffect" />
+ public class DefaultEffect : MatrixChainEffect, ITextureEffect
+ {
+ /// <summary>
+ /// The bitmask for use with <see cref="MatrixChainEffect.Flags" /> indicating wether <see cref="Texture" /> has
+ /// changed in the last frame.
+ /// </summary>
+ protected static int DirtyTextureBitMask = BitVector32.CreateMask(UseDefaultProjectionBitMask);
+
+ /// <summary>
+ /// The bitmask for use with <see cref="MatrixChainEffect.Flags" /> indicating wether the underlying vertex shader and
+ /// fragment (pixel) shaders have changed to one of the pre-defined shaders in the last frame.
+ /// </summary>
+ protected static int DirtyShaderIndexBitMask = BitVector32.CreateMask(DirtyTextureBitMask);
+
+ /// <summary>
+ /// The bitmask for use with <see cref="MatrixChainEffect.Flags" /> indicating wether the material color has changed in
+ /// the last frame.
+ /// </summary>
+ public static int DirtyMaterialColorBitMask = BitVector32.CreateMask(DirtyShaderIndexBitMask);
+
+ private Texture2D _texture;
+ private EffectParameter _textureParameter;
+
+ private float _alpha = 1;
+ private Color _diffuseColor = Color.White;
+ private EffectParameter _diffuseColorParameter;
+
+ private bool _textureEnabled;
+ private bool _vertexColorEnabled;
+
+ /// <summary>
+ /// Gets or sets the material <see cref="Texture2D" />.
+ /// </summary>
+ /// <value>
+ /// The material <see cref="Texture2D" />.
+ /// </value>
+ public Texture2D Texture
+ {
+ get { return _texture; }
+ set
+ {
+ _texture = value;
+ Flags[DirtyTextureBitMask] = true;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the material color alpha.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The alpha channel uses the premultiplied (associated) representation. This means that the RGB components of a
+ /// color represent
+ /// the color of the object of pixel, adjusted for its opacity by multiplication of <see cref="Alpha" />.
+ /// </para>
+ /// </remarks>
+ public float Alpha
+ {
+ get { return _alpha; }
+
+ set
+ {
+ _alpha = value;
+ Flags[DirtyMaterialColorBitMask] = true;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets whether texturing is enabled.
+ /// </summary>
+ public bool TextureEnabled
+ {
+ get { return _textureEnabled; }
+
+ set
+ {
+ if (_textureEnabled == value)
+ return;
+ _textureEnabled = value;
+ Flags[DirtyShaderIndexBitMask] = true;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets whether vertex color is enabled.
+ /// </summary>
+ public bool VertexColorEnabled
+ {
+ get { return _vertexColorEnabled; }
+
+ set
+ {
+ if (_vertexColorEnabled == value)
+ return;
+ _vertexColorEnabled = value;
+ Flags[DirtyShaderIndexBitMask] = true;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultEffect" /> class.
+ /// </summary>
+ /// <param name="graphicsDevice">The graphics device.</param>
+ public DefaultEffect(GraphicsDevice graphicsDevice)
+ : base(graphicsDevice, EffectResource.DefaultEffect.Bytecode)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultEffect" /> class.
+ /// </summary>
+ /// <param name="graphicsDevice">The graphics device.</param>
+ /// <param name="byteCode">The byte code of the shader program.</param>
+ public DefaultEffect(GraphicsDevice graphicsDevice, byte[] byteCode)
+ : base(graphicsDevice, byteCode)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultEffect" /> class.
+ /// </summary>
+ /// <param name="cloneSource">The clone source.</param>
+ public DefaultEffect(Effect cloneSource)
+ : base(cloneSource)
+ {
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ Flags[DirtyMaterialColorBitMask] = true;
+ _textureParameter = Parameters["Texture"];
+ _diffuseColorParameter = Parameters["DiffuseColor"];
+ }
+
+ /// <summary>
+ /// Computes derived parameter values immediately before applying the effect.
+ /// </summary>
+ protected override void OnApply()
+ {
+ base.OnApply();
+
+ if (Flags[DirtyTextureBitMask])
+ {
+ _textureParameter.SetValue(_texture);
+ Flags[DirtyTextureBitMask] = false;
+ }
+
+ // ReSharper disable once InvertIf
+ if (Flags[DirtyMaterialColorBitMask])
+ {
+ UpdateMaterialColor();
+ Flags[DirtyMaterialColorBitMask] = false;
+ }
+
+ // ReSharper disable once InvertIf
+ if (Flags[DirtyShaderIndexBitMask])
+ {
+ var shaderIndex = 0;
+
+ if (_textureEnabled)
+ shaderIndex += 1;
+
+ if (_vertexColorEnabled)
+ shaderIndex += 2;
+
+ Flags[DirtyShaderIndexBitMask] = false;
+ CurrentTechnique = Techniques[shaderIndex];
+ }
+ }
+
+ /// <summary>
+ /// Updates the material color parameters associated with this <see cref="DefaultEffect" />.
+ /// </summary>
+ protected virtual void UpdateMaterialColor()
+ {
+ var diffuseColorVector3 = _diffuseColor.ToVector3();
+
+ var diffuseColorVector4 = new Vector4()
+ {
+ X = diffuseColorVector3.X * Alpha,
+ Y = diffuseColorVector3.Y * Alpha,
+ Z = diffuseColorVector3.Z * Alpha,
+ W = Alpha,
+ };
+
+ _diffuseColorParameter.SetValue(diffuseColorVector4);
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/EffectResource.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/EffectResource.cs
new file mode 100644
index 0000000..43bd535
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/EffectResource.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Effects
+{
+ /// <summary>
+ /// Reperesents the bytecode of an <see cref="Effect" /> that is encapsulated inside a compiled assembly.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Files that are encapsulated inside a compiled assembly are commonly known as Manifiest or embedded resources.
+ /// Since embedded resources are added to the assembly at compiled time, they can not be accidentally deleted or
+ /// misplaced. However, if the file needs to be changed, the assembly will need to be re-compiled with the changed
+ /// file.
+ /// </para>
+ /// <para>
+ /// To add an embedded resource file to an assembly, first add it to the project and then change the Build Action
+ /// in the Properties of the file to <code>Embedded Resource</code>. The next time the project is compiled, the
+ /// compiler will add the file to the assembly as an embedded resource. The compiler adds namespace(s) to the
+ /// embedded resource so it matches with the path of where the file was added to the project.
+ /// </para>
+ /// </remarks>
+ public class EffectResource
+ {
+ private static EffectResource _defaultEffect;
+ private static string _shaderExtension;
+
+ /// <summary>
+ /// Gets the <see cref="Effects.DefaultEffect" /> embedded into the MonoGame.Extended.Graphics library.
+ /// </summary>
+ public static EffectResource DefaultEffect => _defaultEffect ?? (_defaultEffect = new EffectResource($"MonoGame.Extended.Graphics.Effects.Resources.DefaultEffect.{_shaderExtension}.mgfxo"));
+
+ static EffectResource()
+ {
+ DetermineShaderExtension();
+ }
+
+ private static void DetermineShaderExtension()
+ {
+ // use reflection to figure out if Shader.Profile is OpenGL (0) or DirectX (1),
+ // may need to be changed / fixed for future shader profiles
+
+ var assembly = typeof(Game).GetTypeInfo().Assembly;
+ Debug.Assert(assembly != null);
+
+ var shaderType = assembly.GetType("Microsoft.Xna.Framework.Graphics.Shader");
+ Debug.Assert(shaderType != null);
+ var shaderTypeInfo = shaderType.GetTypeInfo();
+ Debug.Assert(shaderTypeInfo != null);
+
+ // https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Graphics/Shader/Shader.cs#L47
+ var profileProperty = shaderTypeInfo.GetDeclaredProperty("Profile");
+ var value = (int)profileProperty.GetValue(null);
+
+ switch (value)
+ {
+ case 0:
+ // OpenGL
+ _shaderExtension = "ogl";
+ break;
+ case 1:
+ // DirectX
+ _shaderExtension = "dx11";
+ break;
+ default:
+ throw new InvalidOperationException("Unknown shader profile.");
+ }
+ }
+
+ private readonly string _resourceName;
+ private volatile byte[] _bytecode;
+ private readonly Assembly _assembly;
+
+ /// <summary>
+ /// Gets the bytecode of the <see cref="Effect" /> file.
+ /// </summary>
+ /// <value>
+ /// The bytecode of the <see cref="Effect" /> file.
+ /// </value>
+ public byte[] Bytecode
+ {
+ get
+ {
+ if (_bytecode != null)
+ return _bytecode;
+
+ lock (this)
+ {
+ if (_bytecode != null)
+ return _bytecode;
+
+ var stream = _assembly.GetManifestResourceStream(_resourceName);
+ using (var memoryStream = new MemoryStream())
+ {
+ stream.CopyTo(memoryStream);
+ _bytecode = memoryStream.ToArray();
+ }
+ }
+
+ return _bytecode;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EffectResource" /> class.
+ /// </summary>
+ /// <param name="resourceName">The name of the embedded resource. This must include the namespace(s).</param>
+ /// <param name="assembly">The assembly which the embedded resource is apart of.</param>
+ public EffectResource(string resourceName, Assembly assembly = null)
+ {
+ _resourceName = resourceName;
+ _assembly = assembly ?? typeof(EffectResource).GetTypeInfo().Assembly;
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/ITextureEffect.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/ITextureEffect.cs
new file mode 100644
index 0000000..ea2ef0b
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/ITextureEffect.cs
@@ -0,0 +1,18 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Effects
+{
+ /// <summary>
+ /// Defines an <see cref="Effect" /> that uses a <see cref="Texture2D" />.
+ /// </summary>
+ public interface ITextureEffect
+ {
+ /// <summary>
+ /// Gets or sets the <see cref="Texture2D" />.
+ /// </summary>
+ /// <value>
+ /// The <see cref="Texture2D" />.
+ /// </value>
+ Texture2D Texture { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/MatrixChainEffect.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/MatrixChainEffect.cs
new file mode 100644
index 0000000..046479f
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/MatrixChainEffect.cs
@@ -0,0 +1,155 @@
+using System.Collections.Specialized;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Effects
+{
+ /// <summary>
+ /// An <see cref="Effect" /> that uses the standard chain of matrix transformations to represent a 3D object on a 2D
+ /// monitor.
+ /// </summary>
+ /// <seealso cref="Effect" />
+ /// <seealso cref="IEffectMatrices" />
+ public abstract class MatrixChainEffect : Effect, IMatrixChainEffect
+ {
+ /// <summary>
+ /// The bitmask for use with <see cref="Flags"/> indicating wether <see cref="World"/>, <see cref="View"/>, or <see cref="Projection"/> has changed in the last frame.
+ /// </summary>
+ protected static int DirtyWorldViewProjectionBitMask = BitVector32.CreateMask();
+
+ /// <summary>
+ /// The bitmask for use with <see cref="Flags"/> indicating wether to use a default projection matrix or a custom projection matrix.
+ /// </summary>
+ protected static int UseDefaultProjectionBitMask = BitVector32.CreateMask(DirtyWorldViewProjectionBitMask);
+
+ /// <summary>
+ /// The dirty flags associated with this <see cref="MatrixChainEffect"/>.
+ /// </summary>
+ protected BitVector32 Flags;
+
+ private Matrix _projection = Matrix.Identity;
+ private Matrix _view = Matrix.Identity;
+ private Matrix _world = Matrix.Identity;
+ private EffectParameter _matrixParameter;
+
+ /// <summary>
+ /// Gets or sets the model-to-world <see cref="Matrix" />.
+ /// </summary>
+ /// <value>
+ /// The model-to-world <see cref="Matrix" />.
+ /// </value>
+ public Matrix World
+ {
+ get { return _world; }
+ set { SetWorld(ref value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the world-to-view <see cref="Matrix" />.
+ /// </summary>
+ /// <value>
+ /// The world-to-view <see cref="Matrix" />.
+ /// </value>
+ public Matrix View
+ {
+ get { return _view; }
+ set { SetView(ref value); }
+ }
+
+ /// <summary>
+ /// Gets or sets the view-to-projection <see cref="Matrix" />.
+ /// </summary>
+ /// <value>
+ /// The view-to-projection <see cref="Matrix" />.
+ /// </value>
+ public Matrix Projection
+ {
+ get { return _projection; }
+ set { SetProjection(ref value); }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MatrixChainEffect" /> class.
+ /// </summary>
+ /// <param name="graphicsDevice">The graphics device.</param>
+ /// <param name="byteCode">The effect code.</param>
+ protected MatrixChainEffect(GraphicsDevice graphicsDevice, byte[] byteCode)
+ : base(graphicsDevice, byteCode)
+ {
+ Initialize();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MatrixChainEffect" /> class.
+ /// </summary>
+ /// <param name="cloneSource">The clone source.</param>
+ protected MatrixChainEffect(Effect cloneSource)
+ : base(cloneSource)
+ {
+ Initialize();
+ }
+
+ private void Initialize()
+ {
+ Flags[UseDefaultProjectionBitMask] = true;
+
+ _matrixParameter = Parameters["WorldViewProjection"];
+ }
+
+ /// <summary>
+ /// Sets the model-to-world <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="world">The model-to-world <see cref="Matrix" />.</param>
+ public void SetWorld(ref Matrix world)
+ {
+ _world = world;
+ Flags[DirtyWorldViewProjectionBitMask] = true;
+ }
+
+ /// <summary>
+ /// Sets the world-to-view <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="view">The world-to-view <see cref="Matrix" />.</param>
+ public void SetView(ref Matrix view)
+ {
+ _view = view;
+ Flags[DirtyWorldViewProjectionBitMask] = true;
+ }
+
+ /// <summary>
+ /// Sets the view-to-projection <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="projection">The view-to-projection <see cref="Matrix" />.</param>
+ public void SetProjection(ref Matrix projection)
+ {
+ _projection = projection;
+ Flags[DirtyWorldViewProjectionBitMask] = true;
+ Flags[UseDefaultProjectionBitMask] = false;
+ }
+
+ /// <summary>
+ /// Computes derived parameter values immediately before applying the effect.
+ /// </summary>
+ protected override void OnApply()
+ {
+ base.OnApply();
+
+ // ReSharper disable once InvertIf
+ if (Flags[DirtyWorldViewProjectionBitMask] || Flags[UseDefaultProjectionBitMask])
+ {
+ if (Flags[UseDefaultProjectionBitMask])
+ {
+ var viewport = GraphicsDevice.Viewport;
+ _projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, -1);
+ }
+
+ Matrix worldViewProjection;
+ Matrix.Multiply(ref _world, ref _view, out worldViewProjection);
+ Matrix.Multiply(ref worldViewProjection, ref _projection, out worldViewProjection);
+ _matrixParameter.SetValue(worldViewProjection);
+
+ Flags[DirtyWorldViewProjectionBitMask] = false;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.dx11.mgfxo b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.dx11.mgfxo
new file mode 100644
index 0000000..b83b6ed
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.dx11.mgfxo
Binary files differ
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.fx b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.fx
new file mode 100644
index 0000000..30a772d
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.fx
@@ -0,0 +1,72 @@
+#include "Macros.fxh"
+#include "Structures.fxh"
+
+DECLARE_TEXTURE(Texture, 0);
+
+BEGIN_CONSTANTS
+
+float4 DiffuseColor = float4(1, 1, 1, 1);
+
+MATRIX_CONSTANTS
+
+float4x4 WorldViewProjection _vs(c0) _cb(c0);
+
+END_CONSTANTS
+
+VertexShaderOutputPosition VertexShaderFunctionPosition(VertexShaderInputPosition input)
+{
+ VertexShaderOutputPosition output;
+ output.Position = mul(input.Position, WorldViewProjection);
+ return output;
+}
+
+float4 PixelShaderFunctionPosition(VertexShaderOutputPosition input) : SV_Target0
+{
+ return DiffuseColor;
+}
+
+VertexShaderOutputPositionTexture VertexShaderFunctionPositionTexture(VertexShaderInputPositionTexture input)
+{
+ VertexShaderOutputPositionTexture output;
+ output.Position = mul(input.Position, WorldViewProjection);
+ output.TextureCoordinate = input.TextureCoordinate;
+ return output;
+}
+
+float4 PixelShaderFunctionPositionTexture(VertexShaderOutputPositionTexture input) : SV_Target0
+{
+ return SAMPLE_TEXTURE(Texture, input.TextureCoordinate) * DiffuseColor;
+}
+
+VertexShaderOutputPositionColor VertexShaderFunctionPositionColor(VertexShaderInputPositionColor input)
+{
+ VertexShaderOutputPositionColor output;
+ output.Position = mul(input.Position, WorldViewProjection);
+ output.Color = input.Color;
+ return output;
+}
+
+float4 PixelShaderFunctionPositionColor(VertexShaderOutputPositionColor input) : SV_Target0
+{
+ return input.Color * DiffuseColor;
+}
+
+VertexShaderOutputPositionColorTexture VertexShaderFunctionPositionColorTexture(VertexShaderInputPositionColorTexture input)
+{
+ VertexShaderOutputPositionColorTexture output;
+ output.Position = mul(input.Position, WorldViewProjection);
+ output.Color = input.Color;
+ output.TextureCoordinate = input.TextureCoordinate;
+ return output;
+}
+
+float4 PixelShaderFunctionPositionColorTexture(VertexShaderOutputPositionColorTexture input) : SV_Target0
+{
+ float4 textureColor = SAMPLE_TEXTURE(Texture, input.TextureCoordinate);
+ return textureColor * input.Color * DiffuseColor;
+}
+
+TECHNIQUE(Position, VertexShaderFunctionPosition, PixelShaderFunctionPosition);
+TECHNIQUE(PositionTexture, VertexShaderFunctionPositionTexture, PixelShaderFunctionPositionTexture);
+TECHNIQUE(PositionColor, VertexShaderFunctionPositionColor, PixelShaderFunctionPositionColor);
+TECHNIQUE(PositionColorTexture, VertexShaderFunctionPositionColorTexture, PixelShaderFunctionPositionColorTexture); \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.ogl.mgfxo b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.ogl.mgfxo
new file mode 100644
index 0000000..4f51d2a
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.ogl.mgfxo
Binary files differ
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Macros.fxh b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Macros.fxh
new file mode 100644
index 0000000..c253efe
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Macros.fxh
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------------
+// Macros.fxh
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+#if SM4
+
+// Macros for targetting shader model 4.0 (DX11)
+
+#define TECHNIQUE(name, vsname, psname ) \
+ technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
+
+#define BEGIN_CONSTANTS cbuffer Parameters : register(b0) {
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS };
+
+#define _vs(r)
+#define _ps(r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, index) \
+ Texture2D<float4> Name : register(t##index); \
+ sampler Name##Sampler : register(s##index)
+
+#define DECLARE_CUBEMAP(Name, index) \
+ TextureCube<float4> Name : register(t##index); \
+ sampler Name##Sampler : register(s##index)
+
+#define SAMPLE_TEXTURE(Name, texCoord) Name.Sample(Name##Sampler, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord) Name.Sample(Name##Sampler, texCoord)
+
+
+#else
+
+// Macros for targetting shader model 2.0 (DX9)
+
+#define TECHNIQUE(name, vsname, psname ) \
+ technique name { pass { VertexShader = compile vs_2_0 vsname (); PixelShader = compile ps_2_0 psname(); } }
+
+#define BEGIN_CONSTANTS
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS
+
+#define _vs(r) : register(vs, r)
+#define _ps(r) : register(ps, r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, index) \
+ sampler2D Name : register(s##index);
+
+#define DECLARE_CUBEMAP(Name, index) \
+ samplerCUBE Name : register(s##index);
+
+#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name, texCoord)
+
+
+#endif \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/RebuildEffects.bat b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/RebuildEffects.bat
new file mode 100644
index 0000000..4fc7f21
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/RebuildEffects.bat
@@ -0,0 +1,15 @@
+setlocal
+
+SET TWOMGFX="mgfxc"
+
+@for /f %%f IN ('dir /b *.fx') do (
+
+ call %TWOMGFX% %%~nf.fx %%~nf.ogl.mgfxo /Profile:OpenGL
+
+ call %TWOMGFX% %%~nf.fx %%~nf.dx11.mgfxo /Profile:DirectX_11
+
+)
+
+endlocal
+
+pause \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Structures.fxh b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Structures.fxh
new file mode 100644
index 0000000..d3af6a0
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/Structures.fxh
@@ -0,0 +1,51 @@
+// Vertex shader input structures.
+
+struct VertexShaderInputPosition
+{
+ float4 Position : SV_Position;
+};
+
+struct VertexShaderInputPositionColor
+{
+ float4 Position : SV_Position;
+ float4 Color : COLOR;
+};
+
+struct VertexShaderInputPositionTexture
+{
+ float4 Position : SV_Position;
+ float2 TextureCoordinate : TEXCOORD0;
+};
+
+struct VertexShaderInputPositionColorTexture
+{
+ float4 Position : SV_Position;
+ float4 Color : COLOR;
+ float2 TextureCoordinate : TEXCOORD0;
+};
+
+// Vertex shader output structures.
+
+struct VertexShaderOutputPosition
+{
+ float4 Position : SV_Position;
+};
+
+struct VertexShaderOutputPositionColor
+{
+ float4 Position : SV_Position;
+ float4 Color : COLOR0;
+};
+
+struct VertexShaderOutputPositionTexture
+{
+ float4 Position : SV_Position;
+ float2 TextureCoordinate : TEXCOORD0;
+};
+
+struct VertexShaderOutputPositionColorTexture
+{
+ float4 Position : SV_Position;
+ float4 Color : COLOR0;
+ float2 TextureCoordinate : TEXCOORD0;
+};
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/FlipFlags.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/FlipFlags.cs
new file mode 100644
index 0000000..6c19887
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/FlipFlags.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace MonoGame.Extended.Graphics
+{
+ [Flags]
+ public enum FlipFlags : byte
+ {
+ None = 0,
+ FlipDiagonally = 1 << 0,
+ FlipVertically = 1 << 1,
+ FlipHorizontally = 1 << 2
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder.cs
new file mode 100644
index 0000000..dad4f15
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder.cs
@@ -0,0 +1,24 @@
+using System;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Geometry
+{
+ public abstract class GeometryBuilder<TVertexType, TIndexType>
+ where TVertexType : struct, IVertexType
+ where TIndexType : struct
+ {
+ public PrimitiveType PrimitiveType { get; protected set; }
+ public int VertexCount { get; protected set; }
+ public int IndexCount { get; protected set; }
+ public int PrimitivesCount { get; protected set; }
+
+ public TVertexType[] Vertices { get; }
+ public TIndexType[] Indices { get; }
+
+ protected GeometryBuilder(int maximumVerticesCount, int maximumIndicesCount)
+ {
+ Vertices = new TVertexType[maximumVerticesCount];
+ Indices = new TIndexType[maximumIndicesCount];
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs
new file mode 100644
index 0000000..c113fbe
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Geometry/GeometryBuilder2D.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Runtime.CompilerServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics.Geometry
+{
+ public class GeometryBuilder2D : GeometryBuilder<VertexPositionColorTexture, ushort>
+ {
+ public GeometryBuilder2D(int maximumVerticesCount, int maximumIndicesCount)
+ : base(maximumVerticesCount, maximumIndicesCount)
+ {
+ }
+
+ public void BuildSprite(int indexOffset, ref Matrix2 transformMatrix, Texture2D texture,
+ ref Rectangle sourceRectangle,
+ Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0)
+ {
+ if (texture == null)
+ throw new ArgumentNullException(nameof(texture));
+
+ var texelLeft = 0f;
+ var texelTop = 0f;
+ var texelRight = 1f;
+ var texelBottom = 1f;
+
+ if (sourceRectangle.Width > 0)
+ {
+ texelLeft = (float)sourceRectangle.X / texture.Width;
+ texelTop = (float)sourceRectangle.Y / texture.Height;
+ texelRight = (sourceRectangle.X + sourceRectangle.Width) / (float)texture.Width;
+ texelBottom = (sourceRectangle.Y + sourceRectangle.Height) / (float)texture.Height;
+ }
+ else
+ {
+ sourceRectangle.Width = texture.Width;
+ sourceRectangle.Height = texture.Height;
+ }
+
+ var color1 = color ?? Color.White;
+
+ var vertices = Vertices;
+
+ transformMatrix.Transform(0, 0, ref vertices[0].Position);
+ vertices[0].Position.Z = depth;
+ vertices[0].Color = color1;
+ vertices[0].TextureCoordinate.X = texelLeft;
+ vertices[0].TextureCoordinate.Y = texelTop;
+
+ transformMatrix.Transform(sourceRectangle.Width, 0, ref vertices[1].Position);
+ vertices[1].Position.Z = depth;
+ vertices[1].Color = color1;
+ vertices[1].TextureCoordinate.X = texelRight;
+ vertices[1].TextureCoordinate.Y = texelTop;
+
+ transformMatrix.Transform(0, sourceRectangle.Height, ref vertices[2].Position);
+ vertices[2].Position.Z = depth;
+ vertices[2].Color = color1;
+ vertices[2].TextureCoordinate.X = texelLeft;
+ vertices[2].TextureCoordinate.Y = texelBottom;
+
+ transformMatrix.Transform(sourceRectangle.Width, sourceRectangle.Height, ref vertices[3].Position);
+ vertices[3].Position.Z = depth;
+ vertices[3].Color = color1;
+ vertices[3].TextureCoordinate.X = texelRight;
+ vertices[3].TextureCoordinate.Y = texelBottom;
+
+ var flipDiagonally = (flags & FlipFlags.FlipDiagonally) != 0;
+ var flipHorizontally = (flags & FlipFlags.FlipHorizontally) != 0;
+ var flipVertically = (flags & FlipFlags.FlipVertically) != 0;
+
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertices[1].TextureCoordinate.X, ref vertices[2].TextureCoordinate.X);
+ FloatHelper.Swap(ref vertices[1].TextureCoordinate.Y, ref vertices[2].TextureCoordinate.Y);
+ }
+
+ if (flipHorizontally)
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertices[0].TextureCoordinate.Y, ref vertices[1].TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertices[2].TextureCoordinate.Y, ref vertices[3].TextureCoordinate.Y);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertices[0].TextureCoordinate.X, ref vertices[1].TextureCoordinate.X);
+ FloatHelper.Swap(ref vertices[2].TextureCoordinate.X, ref vertices[3].TextureCoordinate.X);
+ }
+
+ if (flipVertically)
+ if (flipDiagonally)
+ {
+ FloatHelper.Swap(ref vertices[0].TextureCoordinate.X, ref vertices[2].TextureCoordinate.X);
+ FloatHelper.Swap(ref vertices[1].TextureCoordinate.X, ref vertices[3].TextureCoordinate.X);
+ }
+ else
+ {
+ FloatHelper.Swap(ref vertices[0].TextureCoordinate.Y, ref vertices[2].TextureCoordinate.Y);
+ FloatHelper.Swap(ref vertices[1].TextureCoordinate.Y, ref vertices[3].TextureCoordinate.Y);
+ }
+
+ VertexCount = 4;
+ AddQuadrilateralIndices(indexOffset);
+ IndexCount = 6;
+ PrimitivesCount = 2;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void AddQuadrilateralIndices(int indexOffset)
+ {
+ Indices[0] = (ushort)(0 + indexOffset);
+ Indices[1] = (ushort)(1 + indexOffset);
+ Indices[2] = (ushort)(2 + indexOffset);
+ Indices[3] = (ushort)(1 + indexOffset);
+ Indices[4] = (ushort)(3 + indexOffset);
+ Indices[5] = (ushort)(2 + indexOffset);
+ }
+ }
+}
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IBatchDrawCallInfo.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IBatchDrawCallInfo.cs
new file mode 100644
index 0000000..67c4755
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IBatchDrawCallInfo.cs
@@ -0,0 +1,19 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ /// <summary>
+ /// Defines the for a deferred draw call when batching.
+ /// </summary>
+ public interface IBatchDrawCallInfo<TDrawCallInfo> where TDrawCallInfo : IBatchDrawCallInfo<TDrawCallInfo>
+ {
+ /// <summary>
+ /// Applies any state from the <see cref="IBatchDrawCallInfo{TDrawCallInfo}" /> to the
+ /// <see cref="Effect" /> or <see cref="Effect.GraphicsDevice"/>.
+ /// </summary>
+ /// <param name="effect">The effect.</param>
+ void SetState(Effect effect);
+
+ bool TryMerge(ref TDrawCallInfo drawCall);
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IMatrixChainEffect.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IMatrixChainEffect.cs
new file mode 100644
index 0000000..da8ccb9
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/IMatrixChainEffect.cs
@@ -0,0 +1,30 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ /// <summary>
+ /// Defines an <see cref="Effect" /> that uses the standard chain of matrix transformations to represent a 3D object on
+ /// a 2D monitor.
+ /// </summary>
+ public interface IMatrixChainEffect : IEffectMatrices
+ {
+ /// <summary>
+ /// Sets the model-to-world <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="world">The model-to-world <see cref="Matrix" />.</param>
+ void SetWorld(ref Matrix world);
+
+ /// <summary>
+ /// Sets the world-to-view <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="view">The world-to-view <see cref="Matrix" />.</param>
+ void SetView(ref Matrix view);
+
+ /// <summary>
+ /// Sets the view-to-projection <see cref="Matrix" />.
+ /// </summary>
+ /// <param name="projection">The view-to-projection <see cref="Matrix" />.</param>
+ void SetProjection(ref Matrix projection);
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj
new file mode 100644
index 0000000..fd25850
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/MonoGame.Extended.Graphics.csproj
@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>Graphics makes MonoGame more awesome.</Description>
+ <PackageTags>monogame graphics batcher effects</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <None Remove="Effects\Resources\DefaultEffect.dx11.mgfxo" />
+ <None Remove="Effects\Resources\DefaultEffect.fx" />
+ <None Remove="Effects\Resources\DefaultEffect.ogl.mgfxo" />
+ <None Remove="Effects\Resources\Macros.fxh" />
+ <None Remove="Effects\Resources\Structures.fxh" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <EmbeddedResource Include="Effects\Resources\DefaultEffect.dx11.mgfxo" />
+ <EmbeddedResource Include="Effects\Resources\DefaultEffect.fx" />
+ <EmbeddedResource Include="Effects\Resources\DefaultEffect.ogl.mgfxo" />
+ <EmbeddedResource Include="Effects\Resources\Macros.fxh" />
+ <EmbeddedResource Include="Effects\Resources\Structures.fxh" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\MonoGame.Extended\MonoGame.Extended.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/PrimitiveTypeExtensions.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/PrimitiveTypeExtensions.cs
new file mode 100644
index 0000000..68a39ef
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/PrimitiveTypeExtensions.cs
@@ -0,0 +1,42 @@
+using System;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ public static class PrimitiveTypeExtensions
+ {
+ internal static int GetPrimitivesCount(this PrimitiveType primitiveType, int verticesCount)
+ {
+ switch (primitiveType)
+ {
+ case PrimitiveType.LineStrip:
+ return verticesCount - 1;
+ case PrimitiveType.LineList:
+ return verticesCount/2;
+ case PrimitiveType.TriangleStrip:
+ return verticesCount - 2;
+ case PrimitiveType.TriangleList:
+ return verticesCount/3;
+ default:
+ throw new ArgumentException("Invalid primitive type.");
+ }
+ }
+
+ internal static int GetVerticesCount(this PrimitiveType primitiveType, int primitivesCount)
+ {
+ switch (primitiveType)
+ {
+ case PrimitiveType.LineStrip:
+ return primitivesCount + 1;
+ case PrimitiveType.LineList:
+ return primitivesCount*2;
+ case PrimitiveType.TriangleStrip:
+ return primitivesCount + 2;
+ case PrimitiveType.TriangleList:
+ return primitivesCount*3;
+ default:
+ throw new ArgumentException("Invalid primitive type.");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/RenderTarget2DExtensions.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/RenderTarget2DExtensions.cs
new file mode 100644
index 0000000..f090603
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/RenderTarget2DExtensions.cs
@@ -0,0 +1,41 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ public static class RenderTarget2DExtensions
+ {
+ public static IDisposable BeginDraw(this RenderTarget2D renderTarget, GraphicsDevice graphicsDevice,
+ Color backgroundColor)
+ {
+ return new RenderTargetOperation(renderTarget, graphicsDevice, backgroundColor);
+ }
+
+ private class RenderTargetOperation : IDisposable
+ {
+ private readonly GraphicsDevice _graphicsDevice;
+ private readonly RenderTargetUsage _previousRenderTargetUsage;
+ private readonly Viewport _viewport;
+
+ public RenderTargetOperation(RenderTarget2D renderTarget, GraphicsDevice graphicsDevice,
+ Color backgroundColor)
+ {
+ _graphicsDevice = graphicsDevice;
+ _viewport = _graphicsDevice.Viewport;
+ _previousRenderTargetUsage = _graphicsDevice.PresentationParameters.RenderTargetUsage;
+
+ _graphicsDevice.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents;
+ _graphicsDevice.SetRenderTarget(renderTarget);
+ _graphicsDevice.Clear(backgroundColor);
+ }
+
+ public void Dispose()
+ {
+ _graphicsDevice.SetRenderTarget(null);
+ _graphicsDevice.PresentationParameters.RenderTargetUsage = _previousRenderTargetUsage;
+ _graphicsDevice.Viewport = _viewport;
+ }
+ }
+ }
+} \ No newline at end of file