diff options
author | chai <215380520@qq.com> | 2024-06-03 10:15:45 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2024-06-03 10:15:45 +0800 |
commit | acea7b2e728787a0d83bbf83c8c1f042d2c32e7e (patch) | |
tree | 0bfec05c1ca2d71be2c337bcd110a0421f19318b /Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics | |
parent | 88febcb02bf127d961c6471d9e846c0e1315f5c3 (diff) |
+ plugins project
Diffstat (limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics')
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 Binary files differnew file mode 100644 index 0000000..b83b6ed --- /dev/null +++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.dx11.mgfxo 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 Binary files differnew file mode 100644 index 0000000..4f51d2a --- /dev/null +++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Effects/Resources/DefaultEffect.ogl.mgfxo 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 |