mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-25 08:22:15 +00:00
Create Graphics library. (#316)
* Fix BoundingRect unit tests. * Create `graphics` library.
This commit is contained in:
committed by
Dylan Wilson
parent
d1ffba93f2
commit
d0c621d0dc
@@ -0,0 +1,69 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines options for how two-dimensional geometric objects are submitted to a <see cref="GraphicsDevice" />.
|
||||
/// </summary>
|
||||
public enum Batch2DSortMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Each draw call invokes
|
||||
/// <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> immediately for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
Immediate,
|
||||
|
||||
/// <summary>
|
||||
/// Each draw call is added to the end of an array of draw commands. Draw commands will be merged, if possible, for
|
||||
/// batching. When <see cref="DynamicBatch2D.End" /> is called, the merged draw commands are processed on a first come,
|
||||
/// first
|
||||
/// serve, basis where each command invokes
|
||||
/// <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
Deferred,
|
||||
|
||||
/// <summary>
|
||||
/// Each draw call is added to the end of an array of draw commands. Draw commands will be merged, if possible, for
|
||||
/// batching as they are enqueued and after they are sorted by their <see cref="Texture2D" />s. When
|
||||
/// <see cref="DynamicBatch2D.End" /> is called, the sorting takes place and then the merged draw commands are
|
||||
/// processed where
|
||||
/// each command invokes <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
Texture,
|
||||
|
||||
/// <summary>
|
||||
/// Each draw call is added to the end of an array of draw commands. Draw commands will be merged, if possible, for
|
||||
/// batching as they are enqueued and after they are sorted by their depth values in ascending order. When
|
||||
/// <see cref="DynamicBatch2D.End" /> is called, the sorting takes place and then the merged draw commands are
|
||||
/// processed where
|
||||
/// each command invokes <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Depth values, by default, range from <code>0.0f</code> to <code>1.0f</code>, inclusively, where smaller values
|
||||
/// such as <code>0.0f</code> are considered in-front of larger values such as <code>1.0f</code>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
FrontToBack,
|
||||
|
||||
/// <summary>
|
||||
/// Each draw call is added to the end of an array of draw commands. Draw commands will be merged, if possible, for
|
||||
/// batching as they are enqueued and after they are sorted by their depth values in descending order. When
|
||||
/// <see cref="DynamicBatch2D.End" /> is called, the sorting takes place and then the merged draw commands are
|
||||
/// processed where
|
||||
/// each command invokes <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Depth values, by default, range from <code>0.0f</code> to <code>1.0f</code>, inclusively, where smaller values
|
||||
/// such as <code>0.0f</code> are considered in-front of larger values such as <code>1.0f</code>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
BackToFront
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
// why batching? see top answer: http://answers.unity3d.com/questions/14578/whats-the-best-way-to-reduce-draw-calls.html
|
||||
|
||||
/// <summary>
|
||||
/// Enables a group of geometric objects to be drawn using the same settings.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertexType">The type of vertex.</typeparam>
|
||||
/// <typeparam name="TBatchDrawCommandData">The type of data stored with each draw command.</typeparam>
|
||||
/// <seealso cref="IDisposable" />
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> when instantiating
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" /> for geometry which changes frame-to-frame. For geometry
|
||||
/// which does not change frame-to-frame, or changes infrequently between frames, use
|
||||
/// <see cref="StaticGeometryBuffer{TVertexType}" /> instead.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class Batch<TVertexType, TBatchDrawCommandData> : IDisposable
|
||||
where TVertexType : struct, IVertexType
|
||||
where TBatchDrawCommandData : struct, IBatchDrawCommandData<TBatchDrawCommandData>
|
||||
{
|
||||
internal const int DefaultMaximumBatchCommandsCount = 2048;
|
||||
|
||||
private BatchCommandDrawer<TVertexType, TBatchDrawCommandData> _commandDrawer;
|
||||
private BatchCommandQueue<TVertexType, TBatchDrawCommandData> _currentCommandQueue;
|
||||
private DeferredBatchCommandQueue<TVertexType, TBatchDrawCommandData> _deferredCommandQueue;
|
||||
private ImmediateBatchCommandQueue<TVertexType, TBatchDrawCommandData> _immediateCommandQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Batch{TVertexType,TBatchDrawCommandData}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="geometryBuffer">The geometry buffer.</param>
|
||||
/// <param name="maximumBatchCommandsCount">
|
||||
/// The maximum number of batch draw commands that can be deferred. The default value is <code>2048</code>.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="geometryBuffer" /> is null.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="maximumBatchCommandsCount" /> is less than or equal
|
||||
/// <code>0</code>.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> for geometry which changes
|
||||
/// frame-to-frame and <see cref="StaticGeometryBuffer{TVertexType}" /> for geoemtry which does not change
|
||||
/// frame-to-frame, or changes infrequently between frames.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected Batch(GeometryBuffer<TVertexType> geometryBuffer,
|
||||
int maximumBatchCommandsCount = DefaultMaximumBatchCommandsCount)
|
||||
{
|
||||
if (geometryBuffer == null)
|
||||
throw new ArgumentNullException(nameof(geometryBuffer));
|
||||
|
||||
if (maximumBatchCommandsCount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maximumBatchCommandsCount));
|
||||
|
||||
GeometryBuffer = geometryBuffer;
|
||||
GraphicsDevice = geometryBuffer.GraphicsDevice;
|
||||
_commandDrawer = new BatchCommandDrawer<TVertexType, TBatchDrawCommandData>(GraphicsDevice, GeometryBuffer);
|
||||
|
||||
_immediateCommandQueue = new ImmediateBatchCommandQueue<TVertexType, TBatchDrawCommandData>(GraphicsDevice,
|
||||
_commandDrawer);
|
||||
_deferredCommandQueue = new DeferredBatchCommandQueue<TVertexType, TBatchDrawCommandData>(GraphicsDevice,
|
||||
_commandDrawer,
|
||||
maximumBatchCommandsCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GeometryBuffer{TVertexType}" /> associated with this
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="GeometryBuffer{TVertexType}" /> associated with this
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" />.
|
||||
/// </value>
|
||||
protected GeometryBuffer<TVertexType> GeometryBuffer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GraphicsDevice" /> associated with this
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="GraphicsDevice" /> associated with this
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" />.
|
||||
/// </value>
|
||||
public GraphicsDevice GraphicsDevice { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether batching is currently in progress by being within a <see cref="Begin" /> 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; private set; }
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// ReSharper disable once InvertIf
|
||||
if (diposing)
|
||||
{
|
||||
GeometryBuffer?.Dispose();
|
||||
|
||||
_commandDrawer?.Dispose();
|
||||
_commandDrawer = null;
|
||||
|
||||
|
||||
_immediateCommandQueue.Dispose();
|
||||
_immediateCommandQueue = null;
|
||||
|
||||
_deferredCommandQueue.Dispose();
|
||||
_deferredCommandQueue = null;
|
||||
}
|
||||
}
|
||||
|
||||
[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>
|
||||
/// Starts a group of geometry for rendering with the specified <see cref="Effect" />, <see cref="PrimitiveType" />,
|
||||
/// and <see cref="BatchSortMode" />.
|
||||
/// </summary>
|
||||
/// <param name="effect">The <see cref="Effect" />.</param>
|
||||
/// <param name="primitiveType">The <see cref="PrimitiveType" />.</param>
|
||||
/// <param name="sortMode">The <see cref="BatchSortMode" />.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="effect" /> is null.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="sortMode" /> is not a valid <see cref="BatchSortMode" /> value or
|
||||
/// <paramref name="primitiveType" /> is not a valid <see cref="PrimitiveType" /> value.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <see cref="Begin" /> 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>
|
||||
protected void Begin(Effect effect, PrimitiveType primitiveType, BatchSortMode sortMode = BatchSortMode.Deferred)
|
||||
{
|
||||
if (effect == null)
|
||||
throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
if ((primitiveType < PrimitiveType.TriangleList) || (primitiveType > PrimitiveType.LineStrip))
|
||||
throw new ArgumentOutOfRangeException(nameof(primitiveType));
|
||||
|
||||
EnsureHasNotBegun();
|
||||
HasBegun = true;
|
||||
|
||||
if (sortMode != BatchSortMode.Immediate)
|
||||
{
|
||||
var deferredQueuer = _deferredCommandQueue;
|
||||
deferredQueuer.SortMode = sortMode;
|
||||
_currentCommandQueue = deferredQueuer;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentCommandQueue = _immediateCommandQueue;
|
||||
}
|
||||
|
||||
_currentCommandQueue.Begin(effect, primitiveType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends and submits the group of geometry to the <see cref="GraphicsDevice" /> for rendering.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// <see cref="End" /> cannot be invoked until <see cref="Begin" /> has been invoked.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method must be called after all enqueuing of draw calls.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected void End()
|
||||
{
|
||||
EnsureHasBegun();
|
||||
HasBegun = false;
|
||||
|
||||
_currentCommandQueue.End();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits the group of geometry to the <see cref="GraphicsDevice" /> for rendering without ending the group of
|
||||
/// geometry.
|
||||
/// </summary>
|
||||
protected void Flush()
|
||||
{
|
||||
_currentCommandQueue.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds geometry to the group of geometry for rendering using the specified primitive type, vertices, indices, data,
|
||||
/// and optional sort key.
|
||||
/// </summary>
|
||||
/// <param name="startIndex">The starting index from the <see cref="GeometryBuffer" /> to use.</param>
|
||||
/// <param name="primitiveCount">The number of primitives from the <see cref="GeometryBuffer" /> to use.</param>
|
||||
/// <param name="sortKey">The sort key.</param>
|
||||
/// <param name="itemData">The <see cref="TBatchDrawCommandData" />.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="Begin" /> must be called before enqueuing any draw calls. When all the geometry have been enqueued
|
||||
/// for drawing, call <see cref="End" />.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected void Draw(ushort startIndex, ushort primitiveCount, float sortKey, ref TBatchDrawCommandData itemData)
|
||||
{
|
||||
EnsureHasBegun();
|
||||
_currentCommandQueue.EnqueueDrawCommand(startIndex, primitiveCount, sortKey, ref itemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
internal sealed class BatchCommandDrawer<TVertexType, TCommandData> : IDisposable
|
||||
where TVertexType : struct, IVertexType where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
||||
{
|
||||
internal readonly GeometryBuffer<TVertexType> GeometryBuffer;
|
||||
internal readonly GraphicsDevice GraphicsDevice;
|
||||
internal Effect Effect;
|
||||
internal PrimitiveType PrimitiveType;
|
||||
|
||||
internal BatchCommandDrawer(GraphicsDevice graphicsDevice, GeometryBuffer<TVertexType> geometryBuffer)
|
||||
{
|
||||
GraphicsDevice = graphicsDevice;
|
||||
GeometryBuffer = geometryBuffer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool isDisposing)
|
||||
{
|
||||
if (!isDisposing)
|
||||
return;
|
||||
|
||||
GeometryBuffer.Dispose();
|
||||
}
|
||||
|
||||
internal void SelectBuffers()
|
||||
{
|
||||
GeometryBuffer.Flush();
|
||||
GraphicsDevice.SetVertexBuffer(GeometryBuffer.VertexBuffer);
|
||||
GraphicsDevice.Indices = GeometryBuffer.IndexBuffer;
|
||||
}
|
||||
|
||||
internal void Draw(ref BatchDrawCommand<TCommandData> command)
|
||||
{
|
||||
var graphicsDevice = GraphicsDevice;
|
||||
command.Data.ApplyTo(Effect);
|
||||
foreach (var pass in Effect.CurrentTechnique.Passes)
|
||||
{
|
||||
pass.Apply();
|
||||
graphicsDevice.DrawIndexedPrimitives(PrimitiveType, 0, command.StartIndex,
|
||||
command.PrimitiveCount);
|
||||
}
|
||||
command.Data.SetReferencesToNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
internal abstract class BatchCommandQueue<TVertexType, TCommandData> : IDisposable
|
||||
where TVertexType : struct, IVertexType where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
||||
{
|
||||
internal BatchCommandDrawer<TVertexType, TCommandData> CommandDrawer;
|
||||
internal GraphicsDevice GraphicsDevice;
|
||||
internal PrimitiveType PrimitiveType;
|
||||
|
||||
protected BatchCommandQueue(GraphicsDevice graphicsDevice,
|
||||
BatchCommandDrawer<TVertexType, TCommandData> commandDrawer)
|
||||
{
|
||||
GraphicsDevice = graphicsDevice;
|
||||
CommandDrawer = commandDrawer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal virtual void Begin(Effect effect, PrimitiveType primitiveType)
|
||||
{
|
||||
CommandDrawer.Effect = effect;
|
||||
CommandDrawer.PrimitiveType = primitiveType;
|
||||
PrimitiveType = primitiveType;
|
||||
}
|
||||
|
||||
protected internal abstract void Flush();
|
||||
|
||||
internal void End()
|
||||
{
|
||||
Flush();
|
||||
CommandDrawer.Effect = null;
|
||||
}
|
||||
|
||||
internal abstract void EnqueueDrawCommand(ushort startIndex, ushort primitiveCount, float sortKey,
|
||||
ref TCommandData data);
|
||||
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (!isDisposing)
|
||||
return;
|
||||
|
||||
// don't dispose the batch drawer here; it is a shared reference
|
||||
CommandDrawer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when enqueuing draw commands into a
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}" />
|
||||
/// results in an overflow.
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
public class BatchCommandQueueOverflowException : Exception
|
||||
{
|
||||
internal BatchCommandQueueOverflowException(int maximumCommandsCount)
|
||||
: base(
|
||||
$"The maximum number of batch commands ({maximumCommandsCount}) has been reached. Consider increasing the maximum, reducing the number of commands, or flushing the commands to the GraphicsDevice."
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct BatchDrawCommand<TCommandData> : IComparable<BatchDrawCommand<TCommandData>>
|
||||
where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
||||
{
|
||||
internal float SortKey;
|
||||
internal ushort StartIndex;
|
||||
internal ushort PrimitiveCount;
|
||||
internal TCommandData Data;
|
||||
|
||||
internal BatchDrawCommand(ushort startIndex, ushort primitiveCount, float sortKey, TCommandData data)
|
||||
{
|
||||
SortKey = sortKey;
|
||||
StartIndex = startIndex;
|
||||
PrimitiveCount = primitiveCount;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
internal bool CanMergeWith(float sortKey, ref TCommandData commandData)
|
||||
{
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
return (SortKey == sortKey) && Data.Equals(ref commandData);
|
||||
}
|
||||
|
||||
public int CompareTo(BatchDrawCommand<TCommandData> other)
|
||||
{
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
var result = SortKey.CompareTo(other.SortKey);
|
||||
if (result == 0)
|
||||
result = Data.CompareTo(other.Data);
|
||||
if (result == 0)
|
||||
result = StartIndex.CompareTo(other.StartIndex);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines options for how geometric objects are submitted to a <see cref="GraphicsDevice" />.
|
||||
/// </summary>
|
||||
public enum BatchSortMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Each <see cref="Batch{TVertexType,TBatchDrawCommandData}.Draw" /> call invokes
|
||||
/// <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> immediately for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
Immediate,
|
||||
|
||||
/// <summary>
|
||||
/// Each <see cref="Batch{TVertexType,TBatchDrawCommandData}.Draw" /> call is added to the end of an array of draw
|
||||
/// commands. Draw commands will be merged, if possible, for batching. When
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}.Flush" /> or
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}.End" /> is called, the merged draw commands will be processed
|
||||
/// on a first come, first serve, basis where each command invokes
|
||||
/// <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
Deferred,
|
||||
|
||||
/// <summary>
|
||||
/// Each <see cref="Batch{TVertexType,TBatchDrawCommandData}.Draw" /> call is added to the end of an array of draw
|
||||
/// commands. Draw commands will be merged, if possible, for batching as are they called and after they are sorted in
|
||||
/// ascending order by their sort keys. When
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}.Flush" /> or
|
||||
/// <see cref="Batch{TVertexType,TBatchDrawCommandData}.End" /> is called, the sorting takes place and then the merged
|
||||
/// draw commands are processed where each command invokes
|
||||
/// <see cref="GraphicsDevice.DrawIndexedPrimitives(PrimitiveType, int, int, int)" /> for each
|
||||
/// <see cref="EffectPass" />.
|
||||
/// </summary>
|
||||
DeferredSorted
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
internal sealed class DeferredBatchCommandQueue<TVertexType, TCommandData> :
|
||||
BatchCommandQueue<TVertexType, TCommandData>
|
||||
where TVertexType : struct, IVertexType where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
||||
{
|
||||
private readonly BatchDrawCommand<TCommandData>[] _commands;
|
||||
private readonly GeometryBuffer<TVertexType> _geometryBuffer;
|
||||
private readonly int _maximumCommandsCount;
|
||||
private readonly ushort[] _sortedIndices;
|
||||
private int _commandsCount;
|
||||
private BatchDrawCommand<TCommandData> _currentCommand;
|
||||
internal BatchSortMode SortMode;
|
||||
|
||||
internal DeferredBatchCommandQueue(GraphicsDevice graphicsDevice,
|
||||
BatchCommandDrawer<TVertexType, TCommandData> commandDrawer, int maximumCommandsCount)
|
||||
: base(graphicsDevice, commandDrawer)
|
||||
{
|
||||
_commands = new BatchDrawCommand<TCommandData>[maximumCommandsCount];
|
||||
_geometryBuffer = commandDrawer.GeometryBuffer;
|
||||
_sortedIndices = new ushort[_geometryBuffer._indices.Length];
|
||||
_maximumCommandsCount = maximumCommandsCount;
|
||||
}
|
||||
|
||||
protected internal override void Flush()
|
||||
{
|
||||
// quit early if there are no deferred commands to process
|
||||
if (_commandsCount < 0)
|
||||
return;
|
||||
|
||||
var vertexBuffer = _geometryBuffer.VertexBuffer;
|
||||
// upload the vertices to the GPU's memory from CPU's memory
|
||||
vertexBuffer.SetData(_geometryBuffer._vertices, 0, _geometryBuffer._vertexCount);
|
||||
// tell the graphics API we want to use that buffer of vertices for rendering
|
||||
GraphicsDevice.SetVertexBuffer(vertexBuffer);
|
||||
|
||||
// conditional code depending on deferred mode
|
||||
// ReSharper disable once SwitchStatementMissingSomeCases
|
||||
switch (SortMode)
|
||||
{
|
||||
case BatchSortMode.Deferred:
|
||||
FlushDeferred();
|
||||
break;
|
||||
case BatchSortMode.DeferredSorted:
|
||||
FlushDeferredSorted();
|
||||
break;
|
||||
}
|
||||
|
||||
// prevent a highly unlikely, but possible, memory leak where the current command would be holding onto some reference(s)
|
||||
_currentCommand.Data.SetReferencesToNull();
|
||||
|
||||
// clear the geometry buffer if it is dynamic
|
||||
if (_geometryBuffer.BufferType == GeometryBufferType.Dynamic)
|
||||
_geometryBuffer.Clear();
|
||||
|
||||
// reset the commands count
|
||||
// we don't clear the array of commands because we will just overwrite the values anyways
|
||||
_commandsCount = 0;
|
||||
}
|
||||
|
||||
private void FlushDeferred()
|
||||
{
|
||||
var indexBuffer = _geometryBuffer.IndexBuffer;
|
||||
// upload the indices to the GPU's memory from CPU's memory
|
||||
indexBuffer.SetData(_geometryBuffer._indices, 0, _geometryBuffer._indexCount);
|
||||
// tell the graphics API we want to use that buffer of indices for rendering
|
||||
GraphicsDevice.Indices = indexBuffer;
|
||||
|
||||
// ReSharper disable once ForCanBeConvertedToForeach
|
||||
for (var index = 0; index < _commandsCount; index++)
|
||||
CommandDrawer.Draw(ref _commands[index]);
|
||||
}
|
||||
|
||||
private void FlushDeferredSorted()
|
||||
{
|
||||
// sort the commands
|
||||
Array.Sort(_commands, 0, _commandsCount);
|
||||
|
||||
_currentCommand = _commands[0];
|
||||
ushort sortedIndiesCount = 0;
|
||||
var newCommandsCount = 1;
|
||||
|
||||
// change the indices used by the command to use the sorted indices array so the indices are sequential for the graphics API
|
||||
_commands[0].StartIndex = 0;
|
||||
// get the number of vertices used for the current command, each index represents a vertex
|
||||
var commandIndicesCount = PrimitiveType.GetVerticesCount(_currentCommand.PrimitiveCount);
|
||||
// copy the indices of the current command into our sorted indices array so the indices are sequential for the graphics API
|
||||
Array.Copy(_geometryBuffer._indices, _currentCommand.StartIndex, _sortedIndices, sortedIndiesCount,
|
||||
commandIndicesCount);
|
||||
sortedIndiesCount += (ushort) commandIndicesCount;
|
||||
|
||||
// iterate through sorted commands 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 11) where the draw calls are not so expensive
|
||||
for (var index = 1; index < _commandsCount; index++)
|
||||
{
|
||||
var command = _commands[index];
|
||||
|
||||
// get the number of vertices used for the command, each index represents a vertex
|
||||
commandIndicesCount = PrimitiveType.GetVerticesCount(command.PrimitiveCount);
|
||||
|
||||
if (_currentCommand.CanMergeWith(command.SortKey, ref command.Data))
|
||||
{
|
||||
// increase the number of primitives for the current command by the amount of the merged command
|
||||
_commands[newCommandsCount - 1].PrimitiveCount =
|
||||
_currentCommand.PrimitiveCount += command.PrimitiveCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// could not merge command
|
||||
newCommandsCount++;
|
||||
// change the indices used by the command to use the sorted indices array so the indices are sequential for the graphics API
|
||||
_commands[newCommandsCount - 1].StartIndex = sortedIndiesCount;
|
||||
// set the current command to this command so we can check if the next command can be merged
|
||||
_currentCommand = command;
|
||||
}
|
||||
|
||||
// copy the indices of the command into our sorted indices array so the merged indices are sequential for the graphics API
|
||||
Array.Copy(_geometryBuffer._indices, command.StartIndex, _sortedIndices, sortedIndiesCount,
|
||||
commandIndicesCount);
|
||||
sortedIndiesCount += (ushort) commandIndicesCount;
|
||||
}
|
||||
|
||||
var indexBuffer = _geometryBuffer.IndexBuffer;
|
||||
// upload the indices to the GPU's memory from CPU's memory
|
||||
indexBuffer.SetData(_sortedIndices, 0, sortedIndiesCount);
|
||||
// tell the graphics API we want to use that buffer of indices for rendering
|
||||
GraphicsDevice.Indices = indexBuffer;
|
||||
|
||||
// ReSharper disable once ForCanBeConvertedToForeach
|
||||
for (var index = 0; index < newCommandsCount; index++)
|
||||
CommandDrawer.Draw(ref _commands[index]);
|
||||
}
|
||||
|
||||
internal override void EnqueueDrawCommand(ushort startIndex, ushort primitiveCount, float sortKey,
|
||||
ref TCommandData data)
|
||||
{
|
||||
// merge draw commands if possible to reduce expensive draw calls to the graphics API
|
||||
// this might need to be changed for next-gen graphics API (Vulkan, Metal, DirectX 11) where the draw calls are not so expensive
|
||||
if (_currentCommand.CanMergeWith(sortKey, ref data))
|
||||
{
|
||||
// since indices are added in sequential fasion we can just increase the primitive count of the current command
|
||||
_commands[_commandsCount - 1].PrimitiveCount = _currentCommand.PrimitiveCount += primitiveCount;
|
||||
return;
|
||||
}
|
||||
|
||||
// overflow check
|
||||
if (_commandsCount >= _maximumCommandsCount)
|
||||
throw new BatchCommandQueueOverflowException(_maximumCommandsCount);
|
||||
|
||||
// could not merge draw command, initialize a new one
|
||||
_currentCommand = new BatchDrawCommand<TCommandData>(startIndex, primitiveCount, sortKey, data);
|
||||
|
||||
// append the command to the array
|
||||
_commands[_commandsCount++] = _currentCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the data for batch draw commands.
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommandData">The type of the batch draw command data.</typeparam>
|
||||
/// <seealso cref="Extended.IEquatableByRef{TBatchDrawCommandData}" />
|
||||
public interface IBatchDrawCommandData<TCommandData> : IEquatableByRef<TCommandData>, IComparable<TCommandData>
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies the parameters for the <see cref="IBatchDrawCommandData{TBatchDrawCommandData}" /> to the specified
|
||||
/// <see cref="Effect" />.
|
||||
/// </summary>
|
||||
/// <param name="effect">The effect.</param>
|
||||
void ApplyTo(Effect effect);
|
||||
|
||||
/// <summary>
|
||||
/// Sets all the associated references to <code>null</code>.
|
||||
/// </summary>
|
||||
void SetReferencesToNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Batching
|
||||
{
|
||||
internal sealed class ImmediateBatchCommandQueue<TVertexType, TCommandData> :
|
||||
BatchCommandQueue<TVertexType, TCommandData>
|
||||
where TVertexType : struct, IVertexType
|
||||
where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
||||
{
|
||||
public ImmediateBatchCommandQueue(GraphicsDevice graphicsDevice,
|
||||
BatchCommandDrawer<TVertexType, TCommandData> batchCommandDrawer)
|
||||
: base(graphicsDevice, batchCommandDrawer)
|
||||
{
|
||||
}
|
||||
|
||||
protected internal override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
internal override void EnqueueDrawCommand(ushort startIndex, ushort primitiveCount, float sortKey,
|
||||
ref TCommandData data)
|
||||
{
|
||||
CommandDrawer.SelectBuffers();
|
||||
var command = new BatchDrawCommand<TCommandData>(startIndex, primitiveCount, sortKey, data);
|
||||
CommandDrawer.Draw(ref command);
|
||||
CommandDrawer.GeometryBuffer.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MonoGame.Extended.BitmapFonts;
|
||||
using MonoGame.Extended.Graphics.Batching;
|
||||
using MonoGame.Extended.Graphics.Effects;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables a group of dynamic two-dimensional geometric objects be batched together for rendering, if possible, using
|
||||
/// the same settings.
|
||||
/// </summary>
|
||||
/// <seealso cref="Batch{TVertexType,TBatchDrawCommandData}" />
|
||||
public class DynamicBatch2D : Batch<VertexPositionColorTexture, DynamicBatch2D.DrawCommandData>
|
||||
{
|
||||
internal const int DefaultMaximumVerticesCount = 8192;
|
||||
internal const int DefaultMaximumIndicesCount = 12288;
|
||||
private readonly DefaultEffect2D _defaultEffect;
|
||||
private readonly Texture2D _pixelTexture;
|
||||
private BlendState _blendState;
|
||||
private DepthStencilState _depthStencilState;
|
||||
private Effect _effect;
|
||||
|
||||
private DrawCommandData _pixelTextureDrawContext;
|
||||
private Matrix? _projectionMatrix;
|
||||
private RasterizerState _rasterizerState;
|
||||
private SamplerState _samplerState;
|
||||
private Batch2DSortMode _sortMode;
|
||||
private Matrix _viewMatrix;
|
||||
private Matrix _worldMatrix;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicBatch2D" /> class.
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice">The graphics device.</param>
|
||||
/// <param name="maximumVerticesCount">The maximum number of vertices. The default value is <code>8192</code>.</param>
|
||||
/// <param name="maximumIndicesCount">The maximum number of indices. The default value is <code>12288</code>.</param>
|
||||
/// <param name="maximumBatchCommandsCount">
|
||||
/// The maximum number of draw calls that can be deferred before they have to be
|
||||
/// submitted to the <see cref="GraphicsDevice" />.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="graphicsDevice" /> is null.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="maximumBatchCommandsCount" /> 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 DynamicBatch2D(GraphicsDevice graphicsDevice, ushort maximumVerticesCount = DefaultMaximumVerticesCount,
|
||||
ushort maximumIndicesCount = DefaultMaximumIndicesCount,
|
||||
int maximumBatchCommandsCount = DefaultMaximumBatchCommandsCount)
|
||||
: base(
|
||||
new DynamicGeometryBuffer<VertexPositionColorTexture>(graphicsDevice, maximumVerticesCount,
|
||||
maximumIndicesCount), maximumBatchCommandsCount)
|
||||
{
|
||||
_defaultEffect = new DefaultEffect2D(graphicsDevice);
|
||||
|
||||
_pixelTexture = new Texture2D(graphicsDevice, 1, 1);
|
||||
_pixelTexture.SetData(new[]
|
||||
{
|
||||
Color.White
|
||||
});
|
||||
|
||||
_pixelTextureDrawContext = new DrawCommandData
|
||||
{
|
||||
Texture = _pixelTexture
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
|
||||
/// unmanaged resources.
|
||||
/// </param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_pixelTextureDrawContext.Texture = null;
|
||||
_pixelTexture?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a group of two-dimensional geometry for rendering with the specified <see cref="BatchSortMode" />,
|
||||
/// <see cref="Effect" /> and the optional chain of <see cref="Matrix" />es for transforming between world, view, and
|
||||
/// projection spaces.
|
||||
/// </summary>
|
||||
/// <param name="sortMode">The <see cref="Batch2DSortMode" />. Default value is <see cref="Batch2DSortMode.Deferred" />.</param>
|
||||
/// <param name="blendState">
|
||||
/// The <see cref="BlendState" />. Use <code>null</code> to use the default
|
||||
/// <see cref="BlendState.AlphaBlend" />.
|
||||
/// </param>
|
||||
/// <param name="samplerState">
|
||||
/// The <see cref="SamplerState" />. Use <code>null</code> to use the default
|
||||
/// <see cref="SamplerState.LinearClamp" />.
|
||||
/// </param>
|
||||
/// <param name="depthStencilState">
|
||||
/// The <see cref="DepthStencilState" />. Use <code>null</code> to use the default
|
||||
/// <see cref="DepthStencilState.None" />.
|
||||
/// </param>
|
||||
/// <param name="rasterizerState">
|
||||
/// The <see cref="RasterizerState" />. Use <code>null</code> to use the default
|
||||
/// <see cref="RasterizerState.CullCounterClockwise" />.
|
||||
/// </param>
|
||||
/// <param name="effect">
|
||||
/// The <see cref="Effect" />. Use <code>null</code> to use the default <see cref="DefaultEffect2D" />.
|
||||
/// </param>
|
||||
/// <param name="worldMatrix">
|
||||
/// The <see cref="Matrix" /> to transform vertices from a common model-space to world-space. Use <code>null</code> to
|
||||
/// use <see cref="Matrix.Identity" />.
|
||||
/// </param>
|
||||
/// <param name="viewMatrix">
|
||||
/// The <see cref="Matrix" /> to transform vertices from the world-space to view-space. Use <code>null</code> to
|
||||
/// use <see cref="Matrix.Identity" />.
|
||||
/// </param>
|
||||
/// <param name="projectionMatrix">
|
||||
/// The <see cref="Matrix" /> to transform vertices from the view-space to projection-space. Use <code>null</code> to
|
||||
/// use an orthographic projection (a 2D projection) with <code>(0,0)</code> as the top-left and <code>(x,y)</code> as
|
||||
/// the bottom-right of the viewport.
|
||||
/// </param>
|
||||
public void Begin(Batch2DSortMode sortMode = Batch2DSortMode.Deferred, BlendState blendState = null,
|
||||
SamplerState samplerState = null, DepthStencilState depthStencilState = null,
|
||||
RasterizerState rasterizerState = null, Effect effect = null,
|
||||
Matrix? worldMatrix = null,
|
||||
Matrix? viewMatrix = null, Matrix? projectionMatrix = null)
|
||||
{
|
||||
_blendState = blendState ?? BlendState.AlphaBlend;
|
||||
_samplerState = samplerState ?? SamplerState.LinearClamp;
|
||||
_depthStencilState = depthStencilState ?? DepthStencilState.None;
|
||||
_rasterizerState = rasterizerState ?? RasterizerState.CullCounterClockwise;
|
||||
_effect = effect ?? _defaultEffect;
|
||||
_worldMatrix = worldMatrix ?? Matrix.Identity;
|
||||
_viewMatrix = viewMatrix ?? Matrix.Identity;
|
||||
_projectionMatrix = projectionMatrix;
|
||||
|
||||
BatchSortMode batchSortMode;
|
||||
switch (sortMode)
|
||||
{
|
||||
case Batch2DSortMode.Texture:
|
||||
case Batch2DSortMode.FrontToBack:
|
||||
case Batch2DSortMode.BackToFront:
|
||||
batchSortMode = BatchSortMode.DeferredSorted;
|
||||
break;
|
||||
case Batch2DSortMode.Deferred:
|
||||
batchSortMode = BatchSortMode.Deferred;
|
||||
break;
|
||||
case Batch2DSortMode.Immediate:
|
||||
batchSortMode = BatchSortMode.Immediate;
|
||||
ApplyStates();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(sortMode), sortMode, null);
|
||||
}
|
||||
|
||||
_sortMode = sortMode;
|
||||
|
||||
Begin(_effect, PrimitiveType.TriangleList, batchSortMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends and submits the group of geometry to the
|
||||
/// <see cref="P:MonoGame.Extended.Graphics.Batching.Batch`2.GraphicsDevice" /> for rendering.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method must be called after all enqueuing of draw calls.
|
||||
/// </remarks>
|
||||
public new void End()
|
||||
{
|
||||
if (_sortMode != Batch2DSortMode.Immediate)
|
||||
ApplyStates();
|
||||
|
||||
base.End();
|
||||
}
|
||||
|
||||
private void ApplyStates()
|
||||
{
|
||||
var graphicsDevice = GraphicsDevice;
|
||||
graphicsDevice.BlendState = _blendState;
|
||||
graphicsDevice.SamplerStates[0] = _samplerState;
|
||||
graphicsDevice.DepthStencilState = _depthStencilState;
|
||||
graphicsDevice.RasterizerState = _rasterizerState;
|
||||
|
||||
var matrixChainEffect = _effect as IMatrixChainEffect;
|
||||
if (matrixChainEffect == null)
|
||||
return;
|
||||
|
||||
matrixChainEffect.SetWorld(ref _worldMatrix);
|
||||
matrixChainEffect.SetView(ref _viewMatrix);
|
||||
|
||||
if (_projectionMatrix.HasValue)
|
||||
{
|
||||
matrixChainEffect.Projection = _projectionMatrix.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var viewport = GraphicsDevice.Viewport;
|
||||
var projectionOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
|
||||
var projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, -1);
|
||||
Matrix.Multiply(ref projectionOffset, ref projection, out projection);
|
||||
matrixChainEffect.SetProjection(ref projection);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private float GetSortKey(float depth)
|
||||
{
|
||||
// the sort key acts as the primary sort key
|
||||
// the secondary sort key is always the texture
|
||||
|
||||
// ReSharper disable once SwitchStatementMissingSomeCases
|
||||
switch (_sortMode)
|
||||
{
|
||||
case Batch2DSortMode.FrontToBack:
|
||||
return depth;
|
||||
case Batch2DSortMode.BackToFront:
|
||||
return -depth;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite using a specified <see cref="Texture" />, transform <see cref="Matrix2D" /> and an optional
|
||||
/// source <see cref="Rectangle" />, <see cref="Color" />, origin <see cref="Vector2" />,
|
||||
/// <see cref="SpriteEffects" />, and depth <see cref="float" />.
|
||||
/// </summary>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="destinationRectangle">
|
||||
/// The destination <see cref="Rectangle" /> that specifies the world destination for
|
||||
/// drawing the sprite. If this rectangle is not the same size as the <paramref name="sourceRectangle" />, the sprite
|
||||
/// will be scaled to fit.
|
||||
/// </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="rotation">
|
||||
/// The angle <see cref="float" /> (in radians) to rotate the 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
[Obsolete("The Draw method is deprecated, please use the DrawSprite method instead.")]
|
||||
public void Draw(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle = null,
|
||||
Color? color = null, float rotation = 0f, Vector2? origin = null, SpriteEffects effects = SpriteEffects.None,
|
||||
float depth = 0)
|
||||
{
|
||||
DrawSprite(texture, destinationRectangle, sourceRectangle, color, rotation, origin, effects, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite using a specified <see cref="Texture" />, transform <see cref="Matrix2D" /> and an optional
|
||||
/// source <see cref="Rectangle" />, <see cref="Color" />, origin <see cref="Vector2" />,
|
||||
/// <see cref="SpriteEffects" />, and depth <see cref="float" />.
|
||||
/// </summary>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="destinationRectangle">
|
||||
/// The destination <see cref="Rectangle" /> that specifies the world destination for
|
||||
/// drawing the sprite. If this rectangle is not the same size as the <paramref name="sourceRectangle" />, the sprite
|
||||
/// will be scaled to fit.
|
||||
/// </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="rotation">
|
||||
/// The angle <see cref="float" /> (in radians) to rotate the 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawSprite(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle = null,
|
||||
Color? color = null, float rotation = 0f, Vector2? origin = null, SpriteEffects effects = SpriteEffects.None,
|
||||
float depth = 0)
|
||||
{
|
||||
var position = new Vector2(destinationRectangle.X, destinationRectangle.Y);
|
||||
var size = new Size(destinationRectangle.Width, destinationRectangle.Height);
|
||||
|
||||
Matrix2D transformMatrix;
|
||||
CalculateTransformMatrix(position, rotation, null, out transformMatrix);
|
||||
|
||||
var geometryBuffer = GeometryBuffer;
|
||||
var startVertex = geometryBuffer._vertexCount;
|
||||
var startIndex = geometryBuffer._indexCount;
|
||||
geometryBuffer.EnqueueSprite(startVertex, texture, ref transformMatrix, sourceRectangle, size, color, origin,
|
||||
effects, depth);
|
||||
var commandData = new DrawCommandData(texture);
|
||||
var sortKey = GetSortKey(depth);
|
||||
Draw(startIndex, 2, sortKey, ref commandData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite using a specified <see cref="Texture" />, transform <see cref="Matrix2D" /> and an optional
|
||||
/// source <see cref="Rectangle" />, <see cref="Color" />, origin <see cref="Vector2" />,
|
||||
/// <see cref="SpriteEffects" />, and depth <see cref="float" />.
|
||||
/// </summary>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="transformMatrix">The transform <see cref="Matrix2D" />.</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="origin">
|
||||
/// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Vector2.Zero" />.
|
||||
/// </param>
|
||||
/// <param name="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawSprite(Texture2D texture, ref Matrix2D transformMatrix, Rectangle? sourceRectangle = null,
|
||||
Color? color = null, Vector2? origin = null, SpriteEffects effects = SpriteEffects.None,
|
||||
float depth = 0)
|
||||
{
|
||||
var geometryBuffer = GeometryBuffer;
|
||||
var startVertex = geometryBuffer._vertexCount;
|
||||
var startIndex = geometryBuffer._indexCount;
|
||||
geometryBuffer.EnqueueSprite(startVertex, texture, ref transformMatrix, sourceRectangle, null, color, origin,
|
||||
effects, depth);
|
||||
var commandData = new DrawCommandData(texture);
|
||||
var sortKey = GetSortKey(depth);
|
||||
Draw(startIndex, 2, sortKey, ref commandData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite using a specified <see cref="Texture" />, position <see cref="Vector2" /> and an optional source
|
||||
/// <see cref="Rectangle" />, <see cref="Color" />, rotation <see cref="float" />, origin <see cref="Vector2" />, scale
|
||||
/// <see cref="Vector2" />, <see cref="SpriteEffects" />, and depth <see cref="float" />.
|
||||
/// </summary>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="position">The world position <see cref="Vector2" />.</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="rotation">
|
||||
/// The angle <see cref="float" /> (in radians) to rotate the 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawSprite(Texture2D texture, Vector2 position, Rectangle? sourceRectangle = null,
|
||||
Color? color = null, float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
|
||||
SpriteEffects effects = SpriteEffects.None,
|
||||
float depth = 0)
|
||||
{
|
||||
Matrix2D transformMatrix;
|
||||
CalculateTransformMatrix(position, rotation, scale, out transformMatrix);
|
||||
DrawSprite(texture, ref transformMatrix, sourceRectangle, color, origin, effects, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a sprite using a specified <see cref="Texture" />, position <see cref="Vector2" /> and an optional source
|
||||
/// <see cref="Rectangle" />, <see cref="Color" />, rotation <see cref="float" />, origin <see cref="Vector2" />, scale
|
||||
/// <see cref="Vector2" />, <see cref="SpriteEffects" />, and depth <see cref="float" />.
|
||||
/// </summary>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="position">The world position <see cref="Vector2" />.</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="rotation">
|
||||
/// The angle <see cref="float" /> (in radians) to rotate the 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
[Obsolete("The Draw method is deprecated, please use the DrawSprite method instead.")]
|
||||
public void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle = null,
|
||||
Color? color = null, float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
|
||||
SpriteEffects effects = SpriteEffects.None,
|
||||
float depth = 0)
|
||||
{
|
||||
DrawSprite(texture, position, sourceRectangle, color, rotation, origin, scale, effects, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
|
||||
/// <see cref="StringBuilder" />, transform <see cref="Matrix2D" /> and optional <see cref="Color" />, origin
|
||||
/// <see cref="Vector2" />, <see cref="SpriteEffects" />, 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="Matrix2D" />.</param>
|
||||
/// <param name="color">
|
||||
/// The <see cref="Color" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Color.White" />.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Vector2.Zero" />.
|
||||
/// </param>
|
||||
/// <param name="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code>.</param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix2D transformMatrix,
|
||||
Color? color = null,
|
||||
Vector2? origin = null, SpriteEffects effects = SpriteEffects.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);
|
||||
|
||||
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;
|
||||
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, bounds, color, origin, effects, depth);
|
||||
|
||||
offset.X += i != text.Length - 1
|
||||
? fontRegion.XAdvance + bitmapFont.LetterSpacing
|
||||
: fontRegion.XOffset + fontRegion.Width;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="SpriteEffects" />, 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawString(BitmapFont bitmapFont, StringBuilder text, Vector2 position, Color? color = null,
|
||||
float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
|
||||
SpriteEffects effects = SpriteEffects.None, float depth = 0f)
|
||||
{
|
||||
Matrix2D transformMatrix;
|
||||
CalculateTransformMatrix(position, rotation, scale, out transformMatrix);
|
||||
DrawString(bitmapFont, text, ref transformMatrix, color, origin, effects, depth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws unicode (UTF-16) characters as sprites using the specified <see cref="BitmapFont" />, text
|
||||
/// <see cref="string" />, transform <see cref="Matrix2D" /> and optional <see cref="Color" />, origin
|
||||
/// <see cref="Vector2" />, <see cref="SpriteEffects" />, 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="Matrix2D" />.</param>
|
||||
/// <param name="color">
|
||||
/// The <see cref="Color" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Color.White" />.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Vector2.Zero" />.
|
||||
/// </param>
|
||||
/// <param name="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawString(BitmapFont bitmapFont, string text, ref Matrix2D transformMatrix, Color? color = null,
|
||||
Vector2? origin = null, SpriteEffects effects = SpriteEffects.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);
|
||||
|
||||
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;
|
||||
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, bounds, color, origin, effects, depth);
|
||||
|
||||
offset.X += i != text.Length - 1
|
||||
? fontRegion.XAdvance + bitmapFont.LetterSpacing
|
||||
: fontRegion.XOffset + fontRegion.Width;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="SpriteEffects" />, 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="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth <see cref="float" />. The default value is <code>0f</code></param>
|
||||
/// <exception cref="InvalidOperationException">The <see cref="Begin" /> method has not been called.</exception>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="bitmapFont" /> is null or <paramref name="text" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{VertexPositionColorTexture}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{VertexPositionColorTexture}" /> is full.
|
||||
/// </exception>
|
||||
/// <exception cref="BatchCommandQueueOverflowException">The batch command queue is full.</exception>
|
||||
public void DrawString(BitmapFont bitmapFont, string text, Vector2 position, Color? color = null,
|
||||
float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
|
||||
SpriteEffects effects = SpriteEffects.None, float depth = 0f)
|
||||
{
|
||||
var matrix = Matrix2D.Identity;
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
var scaleMatrix = Matrix2D.CreateScale(scale.Value);
|
||||
Matrix2D.Multiply(ref matrix, ref scaleMatrix, out matrix);
|
||||
}
|
||||
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (rotation != 0f)
|
||||
{
|
||||
var rotationMatrix = Matrix2D.CreateRotationZ(-rotation);
|
||||
Matrix2D.Multiply(ref matrix, ref rotationMatrix, out matrix);
|
||||
}
|
||||
|
||||
var translationMatrix = Matrix2D.CreateTranslation(position);
|
||||
Matrix2D.Multiply(ref matrix, ref translationMatrix, out matrix);
|
||||
|
||||
DrawString(bitmapFont, text, ref matrix, color, origin, effects, depth);
|
||||
}
|
||||
|
||||
private static void CalculateTransformMatrix(Vector2 position, float rotation, Vector2? scale,
|
||||
out Matrix2D transformMatrix)
|
||||
{
|
||||
transformMatrix = Matrix2D.Identity;
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
var scaleMatrix = Matrix2D.CreateScale(scale.Value);
|
||||
Matrix2D.Multiply(ref transformMatrix, ref scaleMatrix, out transformMatrix);
|
||||
}
|
||||
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (rotation != 0f)
|
||||
{
|
||||
var rotationMatrix = Matrix2D.CreateRotationZ(-rotation);
|
||||
Matrix2D.Multiply(ref transformMatrix, ref rotationMatrix, out transformMatrix);
|
||||
}
|
||||
|
||||
var translationMatrix = Matrix2D.CreateTranslation(position);
|
||||
Matrix2D.Multiply(ref transformMatrix, ref translationMatrix, out transformMatrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a drawing context for two-dimensional geometric objects.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public struct DrawCommandData : IBatchDrawCommandData<DrawCommandData>
|
||||
{
|
||||
public Texture2D Texture;
|
||||
public int TextureKey;
|
||||
|
||||
internal DrawCommandData(Texture2D texture)
|
||||
{
|
||||
Texture = texture;
|
||||
TextureKey = RuntimeHelpers.GetHashCode(texture);
|
||||
}
|
||||
|
||||
public void ApplyTo(Effect effect)
|
||||
{
|
||||
var textureEffect = effect as ITexture2DEffect;
|
||||
if (textureEffect != null)
|
||||
textureEffect.Texture = Texture;
|
||||
}
|
||||
|
||||
public void SetReferencesToNull()
|
||||
{
|
||||
Texture = null;
|
||||
}
|
||||
|
||||
public bool Equals(ref DrawCommandData other)
|
||||
{
|
||||
return Texture == other.Texture;
|
||||
}
|
||||
|
||||
public int CompareTo(DrawCommandData other)
|
||||
{
|
||||
return TextureKey.CompareTo(other.TextureKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Effects
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="Effect" /> that allows 2D objects, within a 3D context, to be represented on a 2D monitor.
|
||||
/// </summary>
|
||||
/// <seealso cref="Effect" />
|
||||
/// <seealso cref="IMatrixChainEffect" />
|
||||
public class DefaultEffect2D : MatrixChainEffect, ITexture2DEffect
|
||||
{
|
||||
private Texture2D _texture;
|
||||
private bool _textureIsDirty;
|
||||
private EffectParameter _textureParameter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultEffect2D" /> class.
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice">The graphics device.</param>
|
||||
public DefaultEffect2D(GraphicsDevice graphicsDevice)
|
||||
: base(graphicsDevice, EffectResource.DefaultEffect2D.Bytecode)
|
||||
{
|
||||
CacheEffectParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultEffect2D" /> class.
|
||||
/// </summary>
|
||||
/// <param name="cloneSource">The clone source.</param>
|
||||
public DefaultEffect2D(Effect cloneSource)
|
||||
: base(cloneSource)
|
||||
{
|
||||
CacheEffectParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Texture2D" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="Texture2D" />.
|
||||
/// </value>
|
||||
public Texture2D Texture
|
||||
{
|
||||
get { return _texture; }
|
||||
set
|
||||
{
|
||||
_texture = value;
|
||||
_textureIsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheEffectParameters()
|
||||
{
|
||||
_textureParameter = Parameters["TextureSampler+Texture"];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes derived parameter values immediately before applying the effect.
|
||||
/// </summary>
|
||||
protected override bool OnApply()
|
||||
{
|
||||
var result = base.OnApply();
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
if (_textureIsDirty)
|
||||
{
|
||||
_textureParameter.SetValue(_texture);
|
||||
_textureIsDirty = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Effects.DefaultEffect2D" /> embedded into the MonoGame.Extended library.
|
||||
/// </summary>
|
||||
public static readonly EffectResource DefaultEffect2D =
|
||||
new EffectResource("MonoGame.Extended.Graphics.Effects.Resources.DefaultEffect2D.mgfxo");
|
||||
|
||||
private readonly string _resourceName;
|
||||
private volatile byte[] _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>
|
||||
public EffectResource(string resourceName)
|
||||
{
|
||||
_resourceName = resourceName;
|
||||
}
|
||||
|
||||
/// <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 assembly = typeof(EffectResource).GetTypeInfo().Assembly;
|
||||
var stream = assembly.GetManifestResourceStream(_resourceName);
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(memoryStream);
|
||||
_bytecode = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return _bytecode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics.Effects
|
||||
{
|
||||
/// <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
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the model-to-world <see cref="Matrix" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The model-to-world <see cref="Matrix" />.
|
||||
/// </value>
|
||||
Matrix World { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world-to-view <see cref="Matrix" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The world-to-view <see cref="Matrix" />.
|
||||
/// </value>
|
||||
Matrix View { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the view-to-projection <see cref="Matrix" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The view-to-projection <see cref="Matrix" />.
|
||||
/// </value>
|
||||
Matrix Projection { get; set; }
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
@@ -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 ITexture2DEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Texture2D" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="Texture2D" />.
|
||||
/// </value>
|
||||
Texture2D Texture { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
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="IMatrixChainEffect" />
|
||||
public abstract class MatrixChainEffect : Effect, IMatrixChainEffect
|
||||
{
|
||||
private Matrix _projection = Matrix.Identity;
|
||||
private Matrix _view = Matrix.Identity;
|
||||
private Matrix _world = Matrix.Identity;
|
||||
private bool _worldViewProjectionIsDirty;
|
||||
private EffectParameter _worldViewProjectionParameter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MatrixChainEffect" /> class.
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice">The graphics device.</param>
|
||||
/// <param name="effectCode">The effect code.</param>
|
||||
protected MatrixChainEffect(GraphicsDevice graphicsDevice, byte[] effectCode)
|
||||
: base(graphicsDevice, effectCode)
|
||||
{
|
||||
CacheEffectParameters();
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
CacheEffectParameters();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 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;
|
||||
_worldViewProjectionIsDirty = 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;
|
||||
_worldViewProjectionIsDirty = 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;
|
||||
_worldViewProjectionIsDirty = true;
|
||||
}
|
||||
|
||||
private void CacheEffectParameters()
|
||||
{
|
||||
_worldViewProjectionParameter = Parameters["WorldViewProjection"];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes derived parameter values immediately before applying the effect.
|
||||
/// </summary>
|
||||
protected override bool OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
if (_worldViewProjectionIsDirty)
|
||||
{
|
||||
_worldViewProjectionIsDirty = false;
|
||||
|
||||
Matrix worldViewProjection;
|
||||
Matrix.Multiply(ref _world, ref _view, out worldViewProjection);
|
||||
Matrix.Multiply(ref worldViewProjection, ref _projection, out worldViewProjection);
|
||||
_worldViewProjectionParameter.SetValue(worldViewProjection);
|
||||
}
|
||||
|
||||
// in MonoGame 3.6 this method won't return anything; see https://github.com/MonoGame/MonoGame/pull/5090
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// The matrix that transforms geometry in the vertex shader
|
||||
float4x4 WorldViewProjection;
|
||||
// The texture used for texture sampling in the pixel shader
|
||||
Texture2D<float4> Texture;
|
||||
// The sampler used for texture sampling in the pixel shader
|
||||
SamplerState TextureSampler;
|
||||
|
||||
// The information layout used as input to the vertex shader. The actual information is passed to the GPU from the CPU
|
||||
struct VertexShaderInput
|
||||
{
|
||||
float4 Position : POSITION0;
|
||||
float4 Color : COLOR0;
|
||||
float2 TexureCoordinate : TEXCOORD0;
|
||||
};
|
||||
|
||||
// The information layout used as output from the vertex shader and input to the pixel shader
|
||||
struct VertexShaderOutput
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float4 Color : COLOR0;
|
||||
float2 TexureCoordinate : TEXCOORD0;
|
||||
};
|
||||
|
||||
// The information layout used as output from the pixel shader. The actual information is what is displayed on screen
|
||||
struct PixelShaderOutput
|
||||
{
|
||||
float4 Color : COLOR0;
|
||||
};
|
||||
|
||||
// The vertex shader code that transforms render geometry. The input information is from the CPU
|
||||
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
|
||||
{
|
||||
VertexShaderOutput output;
|
||||
output.Position = mul(input.Position, WorldViewProjection);
|
||||
output.Color = input.Color;
|
||||
output.TexureCoordinate = input.TexureCoordinate;
|
||||
return output;
|
||||
}
|
||||
|
||||
// The pixel shader code that determines the final color of each pixel. The input information is the output of the vertex shader
|
||||
PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : SV_Target0
|
||||
{
|
||||
PixelShaderOutput output;
|
||||
output.Color = Texture.Sample(TextureSampler, input.TexureCoordinate) * input.Color;
|
||||
return output;
|
||||
}
|
||||
|
||||
// The technique; a collection of passes to be applied, each specifying the vertex and shader to use
|
||||
technique
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = compile vs_2_0 VertexShaderFunction();
|
||||
PixelShader = compile ps_2_0 PixelShaderFunction();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
:: Can only use 2MGFX.exe on Windows...
|
||||
SET PATH=%PATH%;C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools
|
||||
|
||||
2MGFX.exe %cd%\DefaultEffect2D.fx %cd%\DefaultEffect2D.mgfxo
|
||||
|
||||
pause
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
public class DynamicGeometryBuffer<TVertexType> : GeometryBuffer<TVertexType>
|
||||
where TVertexType : struct, IVertexType
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicGeometryBuffer{TVertexType}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice">The graphics device.</param>
|
||||
/// <param name="maximumVerticesCount">The maximum number of vertices.</param>
|
||||
/// <param name="maximumIndicesCount">The maximum number of indices.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="graphicsDevice" /> is null.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="maximumVerticesCount" /> is <code>0</code>, or, <paramref name="maximumVerticesCount" /> is
|
||||
/// <code>0</code>.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> for geometry which changes
|
||||
/// frame-to-frame and <see cref="StaticGeometryBuffer{TVertexType}" /> for geoemtry which does not change
|
||||
/// frame-to-frame, or changes infrequently between frames.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Memory will be allocated for the vertex and index array buffers in proportion to
|
||||
/// <paramref name="maximumVerticesCount" /> and <paramref name="maximumIndicesCount" /> respectively.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public DynamicGeometryBuffer(GraphicsDevice graphicsDevice, ushort maximumVerticesCount,
|
||||
ushort maximumIndicesCount)
|
||||
: base(graphicsDevice, GeometryBufferType.Dynamic, maximumVerticesCount, maximumIndicesCount)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
public class StaticGeometryBuffer<TVertexType> : GeometryBuffer<TVertexType>
|
||||
where TVertexType : struct, IVertexType
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GeometryBuffer{TVertexType}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="graphicsDevice">The graphics device.</param>
|
||||
/// <param name="maximumVerticesCount">The maximum number of vertices.</param>
|
||||
/// <param name="maximumIndicesCount">The maximum number of indices.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="graphicsDevice" /> is null.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// <paramref name="maximumVerticesCount" /> is <code>0</code>, or, <paramref name="maximumVerticesCount" /> is
|
||||
/// <code>0</code>.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> for geometry which changes
|
||||
/// frame-to-frame and <see cref="StaticGeometryBuffer{TVertexType}" /> for geoemtry which does not change
|
||||
/// frame-to-frame, or changes infrequently between frames.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Memory will be allocated for the vertex and index array buffers in proportion to
|
||||
/// <paramref name="maximumVerticesCount" /> and <paramref name="maximumIndicesCount" /> respectively.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public StaticGeometryBuffer(GraphicsDevice graphicsDevice, ushort maximumVerticesCount,
|
||||
ushort maximumIndicesCount)
|
||||
: base(graphicsDevice, GeometryBufferType.Static, maximumVerticesCount, maximumIndicesCount)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
internal enum GeometryBufferType
|
||||
{
|
||||
Static,
|
||||
Dynamic
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores geometry which is to be uploaded to a <see cref="GraphicsDevice" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertexType">The type of the vertex type.</typeparam>
|
||||
/// <seealso cref="IDisposable" />
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> for geometry which changes
|
||||
/// frame-to-frame and <see cref="StaticGeometryBuffer{TVertexType}" /> for geoemtry which does not change
|
||||
/// frame-to-frame, or changes infrequently between frames.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
||||
public abstract class GeometryBuffer<TVertexType> : IDisposable
|
||||
where TVertexType : struct, IVertexType
|
||||
{
|
||||
internal readonly GeometryBufferType BufferType;
|
||||
|
||||
internal GeometryBuffer(GraphicsDevice graphicsDevice, GeometryBufferType bufferType,
|
||||
ushort maximumVerticesCount, ushort maximumIndicesCount)
|
||||
{
|
||||
if (graphicsDevice == null)
|
||||
throw new ArgumentNullException(nameof(graphicsDevice));
|
||||
|
||||
if (maximumVerticesCount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maximumVerticesCount));
|
||||
|
||||
if (maximumIndicesCount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maximumIndicesCount));
|
||||
|
||||
if ((bufferType != GeometryBufferType.Static) && (bufferType != GeometryBufferType.Dynamic))
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferType));
|
||||
|
||||
GraphicsDevice = graphicsDevice;
|
||||
|
||||
_vertices = new TVertexType[maximumVerticesCount];
|
||||
_indices = new ushort[maximumIndicesCount];
|
||||
_vertexCount = 0;
|
||||
MaximumVerticesCount = maximumVerticesCount;
|
||||
MaximumIndicesCount = maximumIndicesCount;
|
||||
|
||||
BufferType = bufferType;
|
||||
|
||||
switch (bufferType)
|
||||
{
|
||||
case GeometryBufferType.Static:
|
||||
VertexBuffer = new VertexBuffer(graphicsDevice, typeof(TVertexType), maximumVerticesCount,
|
||||
BufferUsage.WriteOnly);
|
||||
IndexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort), maximumIndicesCount,
|
||||
BufferUsage.WriteOnly);
|
||||
break;
|
||||
case GeometryBufferType.Dynamic:
|
||||
VertexBuffer = new DynamicVertexBuffer(graphicsDevice, typeof(TVertexType), maximumVerticesCount,
|
||||
BufferUsage.WriteOnly);
|
||||
IndexBuffer = new DynamicIndexBuffer(graphicsDevice, typeof(ushort), maximumIndicesCount,
|
||||
BufferUsage.WriteOnly);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferType));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertices as read-only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The vertices as read-only.
|
||||
/// </value>
|
||||
public IReadOnlyList<TVertexType> Vertices => _vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the indices as read-only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The indices as read-only.
|
||||
/// </value>
|
||||
public IReadOnlyList<ushort> Indices => _indices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of buffered vertices.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The number of buffered vertices.
|
||||
/// </value>
|
||||
public ushort VertexCount => _vertexCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of buffered indices.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The number of buffered indices.
|
||||
/// </value>
|
||||
public ushort IndexCount => _indexCount;
|
||||
|
||||
public ushort MaximumVerticesCount { get; }
|
||||
public ushort MaximumIndicesCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex buffer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The vertex buffer.
|
||||
/// </value>
|
||||
public VertexBuffer VertexBuffer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index buffer.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The index buffer.
|
||||
/// </value>
|
||||
public IndexBuffer IndexBuffer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="GraphicsDevice" /> associated with this
|
||||
/// <see cref="GeometryBuffer{TVertexType}" />.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The <see cref="GraphicsDevice" /> associated with this
|
||||
/// <see cref="GeometryBuffer{TVertexType}" />.
|
||||
/// </value>
|
||||
public GraphicsDevice GraphicsDevice { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the buffered geometry to the <see cref="GraphicsDevice" />.
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
VertexBuffer.SetData(_vertices, 0, _vertexCount);
|
||||
IndexBuffer.SetData(_indices, 0, _indexCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified vertex to the buffer of geometry.
|
||||
/// </summary>
|
||||
/// <param name="vertex">The vertex.</param>
|
||||
public bool Enqueue(ref TVertexType vertex)
|
||||
{
|
||||
ThrowIfWouldOverflowVertices(1);
|
||||
|
||||
_vertices[_vertexCount++] = vertex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified vertex index to the buffer of geometry.
|
||||
/// </summary>
|
||||
/// <param name="index">The vertex index.</param>
|
||||
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
||||
public void Enqueue(ushort index)
|
||||
{
|
||||
ThrowIfWouldOverflowIndices(1);
|
||||
_indices[_indexCount++] = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to add the specified vertices to the buffer of geometry.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="sourceIndex">
|
||||
/// The value that represents the index in the <paramref name="vertices" /> at which adding
|
||||
/// begins.
|
||||
/// </param>
|
||||
/// <param name="length">The number of vertices to add.</param>
|
||||
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
||||
public void Enqueue(TVertexType[] vertices, int sourceIndex, ushort length)
|
||||
{
|
||||
ThrowIfWouldOverflowVertices(length);
|
||||
|
||||
Array.Copy(vertices, sourceIndex, _vertices, VertexCount, length);
|
||||
_vertexCount += length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds vertex indices to the buffer of geometry.
|
||||
/// </summary>
|
||||
/// <param name="indices">The indices.</param>
|
||||
/// <param name="sourceIndex">
|
||||
/// The value that represents the index in the <paramref name="indices" /> at which adding
|
||||
/// begins.
|
||||
/// </param>
|
||||
/// <param name="length">The number of indices to add.</param>
|
||||
/// <param name="indexOffset">The index offset.</param>
|
||||
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
||||
public unsafe void Enqueue(ushort[] indices, int sourceIndex, ushort length, ushort indexOffset)
|
||||
{
|
||||
ThrowIfWouldOverflowIndices(length);
|
||||
|
||||
var start = _indexCount;
|
||||
var end = _indexCount + length;
|
||||
|
||||
Array.Copy(indices, sourceIndex, _indices, IndexCount, length);
|
||||
|
||||
fixed (ushort* fixedPointer = _indices)
|
||||
{
|
||||
var pointer = fixedPointer + start;
|
||||
var endPointer = fixedPointer + end;
|
||||
|
||||
while (pointer < endPointer)
|
||||
{
|
||||
*pointer += indexOffset;
|
||||
++pointer;
|
||||
}
|
||||
}
|
||||
|
||||
_indexCount += length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds clockwise quadrilateral indices to the buffer of geometry.
|
||||
/// </summary>
|
||||
/// <param name="indexOffset">The index offset.</param>
|
||||
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
||||
public unsafe void EnqueueClockwiseQuadrilateralIndices(ushort indexOffset)
|
||||
{
|
||||
ThrowIfWouldOverflowIndices(6);
|
||||
|
||||
fixed (ushort* fixedPointer = _indices)
|
||||
{
|
||||
var pointer = fixedPointer + _indexCount;
|
||||
*(pointer + 0) = (ushort) (0 + indexOffset);
|
||||
*(pointer + 1) = (ushort) (1 + indexOffset);
|
||||
*(pointer + 2) = (ushort) (2 + indexOffset);
|
||||
*(pointer + 3) = (ushort) (1 + indexOffset);
|
||||
*(pointer + 4) = (ushort) (3 + indexOffset);
|
||||
*(pointer + 5) = (ushort) (2 + indexOffset);
|
||||
}
|
||||
_indexCount += 6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the buffered geometry count.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_vertexCount = 0;
|
||||
_indexCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
|
||||
/// unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
VertexBuffer.Dispose();
|
||||
IndexBuffer.Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ThrowIfWouldOverflowVertices(ushort verticesCountToAdd)
|
||||
{
|
||||
if (_vertexCount + verticesCountToAdd > MaximumVerticesCount)
|
||||
throw new GeometryBufferOverflowException<TVertexType>(this, verticesCountToAdd, 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ThrowIfWouldOverflowIndices(ushort indicesCountToAdd)
|
||||
{
|
||||
if (_indexCount + indicesCountToAdd > MaximumIndicesCount)
|
||||
throw new GeometryBufferOverflowException<TVertexType>(this, 0, indicesCountToAdd);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void ThrowIfWouldOverflow(ushort verticesCountToAdd, ushort indicesCountToAdd)
|
||||
{
|
||||
if ((_vertexCount + verticesCountToAdd > MaximumVerticesCount) ||
|
||||
(_indexCount + indicesCountToAdd > MaximumIndicesCount))
|
||||
throw new GeometryBufferOverflowException<TVertexType>(this, verticesCountToAdd, indicesCountToAdd);
|
||||
}
|
||||
|
||||
//TODO: Add TIndexType to allow for different type of indices: int, short, etc
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
internal readonly TVertexType[] _vertices;
|
||||
internal readonly ushort[] _indices;
|
||||
internal ushort _vertexCount;
|
||||
internal ushort _indexCount;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
public static class GeometryBuilderExtensionsVertexPositionColorTexture
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a sprite to the back of specified <see cref="GeometryBuffer{VertexPositionColorTexture}" />.
|
||||
/// </summary>
|
||||
/// <param name="geometryBuffer">The <see cref="GeometryBuffer{VertexPositionColorTexture}" />.</param>
|
||||
/// <param name="indexOffset">The vertex index offset applied to generated indices.</param>
|
||||
/// <param name="texture">The <see cref="Texture" />.</param>
|
||||
/// <param name="transformMatrix">The transform <see cref="Matrix2D" />.</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="size">
|
||||
/// The <see cref="Size" /> of the sprite. Use <code>null</code> to use the default size which is either
|
||||
/// the size of the <paramref name="sourceRectangle" /> if it is not <code>null</code>, or the texture size if
|
||||
/// <paramref name="sourceRectangle" /> is <code>null</code>.
|
||||
/// </param>
|
||||
/// <param name="color">The <see cref="Color" />. Use <code>null</code> to use the default <see cref="Color.White" />.</param>
|
||||
/// <param name="origin">
|
||||
/// The origin <see cref="Vector2" />. Use <code>null</code> to use the default
|
||||
/// <see cref="Vector2.Zero" />.
|
||||
/// </param>
|
||||
/// <param name="effects">The <see cref="SpriteEffects" />. The default value is <see cref="SpriteEffects.None" />.</param>
|
||||
/// <param name="depth">The depth. The default value is <code>0</code>.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="texture" /> is null.</exception>
|
||||
/// <exception cref="GeometryBufferOverflowException{TVertexType}">
|
||||
/// The underlying
|
||||
/// <see cref="GeometryBuffer{TVertexType}" /> is full.
|
||||
/// </exception>
|
||||
public static unsafe void EnqueueSprite(this GeometryBuffer<VertexPositionColorTexture> geometryBuffer,
|
||||
ushort indexOffset, Texture2D texture, ref Matrix2D transformMatrix, Rectangle? sourceRectangle = null,
|
||||
Size? size = null,
|
||||
Color? color = null, Vector2? origin = null, SpriteEffects effects = SpriteEffects.None, float depth = 0)
|
||||
{
|
||||
if (texture == null)
|
||||
throw new ArgumentNullException(nameof(texture));
|
||||
|
||||
geometryBuffer.ThrowIfWouldOverflow(4, 6);
|
||||
|
||||
var origin1 = origin ?? Vector2.Zero;
|
||||
Vector2 positionTopLeft, positionBottomRight, textureCoordinateTopLeft, textureCoordinateBottomRight;
|
||||
Size size1;
|
||||
|
||||
if (sourceRectangle == null)
|
||||
{
|
||||
size1 = size ?? new Size(texture.Width, texture.Height);
|
||||
textureCoordinateTopLeft.X = 0;
|
||||
textureCoordinateTopLeft.Y = 0;
|
||||
textureCoordinateBottomRight.X = 1;
|
||||
textureCoordinateBottomRight.Y = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var textureRegion = sourceRectangle.Value;
|
||||
size1 = size ?? new Size(textureRegion.Width, textureRegion.Height);
|
||||
textureCoordinateTopLeft.X = textureRegion.X/(float) texture.Width;
|
||||
textureCoordinateTopLeft.Y = textureRegion.Y/(float) texture.Height;
|
||||
textureCoordinateBottomRight.X = (textureRegion.X + textureRegion.Width)/(float) texture.Width;
|
||||
textureCoordinateBottomRight.Y = (textureRegion.Y + textureRegion.Height)/(float) texture.Height;
|
||||
}
|
||||
|
||||
positionTopLeft.X = -origin1.X;
|
||||
positionTopLeft.Y = -origin1.Y;
|
||||
positionBottomRight.X = -origin1.X + size1.Width;
|
||||
positionBottomRight.Y = -origin1.Y + size1.Height;
|
||||
|
||||
var spriteEffect = effects;
|
||||
if ((spriteEffect & SpriteEffects.FlipVertically) != 0)
|
||||
{
|
||||
var temp = textureCoordinateBottomRight.Y;
|
||||
textureCoordinateBottomRight.Y = textureCoordinateTopLeft.Y;
|
||||
textureCoordinateTopLeft.Y = temp;
|
||||
}
|
||||
|
||||
if ((spriteEffect & SpriteEffects.FlipHorizontally) != 0)
|
||||
{
|
||||
var temp = textureCoordinateBottomRight.X;
|
||||
textureCoordinateBottomRight.X = textureCoordinateTopLeft.X;
|
||||
textureCoordinateTopLeft.X = temp;
|
||||
}
|
||||
|
||||
var vertex = new VertexPositionColorTexture(new Vector3(Vector2.Zero, depth), color ?? Color.White,
|
||||
Vector2.Zero);
|
||||
|
||||
fixed (VertexPositionColorTexture* fixedPointer = geometryBuffer._vertices)
|
||||
{
|
||||
var pointer = fixedPointer + geometryBuffer._vertexCount;
|
||||
|
||||
// top-left
|
||||
var position = positionTopLeft;
|
||||
transformMatrix.Transform(position, out position);
|
||||
vertex.Position.X = position.X;
|
||||
vertex.Position.Y = position.Y;
|
||||
vertex.TextureCoordinate = textureCoordinateTopLeft;
|
||||
*(pointer + 0) = vertex;
|
||||
|
||||
// top-right
|
||||
position.X = positionBottomRight.X;
|
||||
position.Y = positionTopLeft.Y;
|
||||
transformMatrix.Transform(position, out position);
|
||||
vertex.Position.X = position.X;
|
||||
vertex.Position.Y = position.Y;
|
||||
vertex.TextureCoordinate.X = textureCoordinateBottomRight.X;
|
||||
vertex.TextureCoordinate.Y = textureCoordinateTopLeft.Y;
|
||||
*(pointer + 1) = vertex;
|
||||
|
||||
// bottom-left
|
||||
position.X = positionTopLeft.X;
|
||||
position.Y = positionBottomRight.Y;
|
||||
transformMatrix.Transform(position, out position);
|
||||
vertex.Position.X = position.X;
|
||||
vertex.Position.Y = position.Y;
|
||||
vertex.TextureCoordinate.X = textureCoordinateTopLeft.X;
|
||||
vertex.TextureCoordinate.Y = textureCoordinateBottomRight.Y;
|
||||
*(pointer + 2) = vertex;
|
||||
|
||||
// bottom-right
|
||||
position = positionBottomRight;
|
||||
transformMatrix.Transform(position, out position);
|
||||
vertex.Position.X = position.X;
|
||||
vertex.Position.Y = position.Y;
|
||||
vertex.TextureCoordinate = textureCoordinateBottomRight;
|
||||
*(pointer + 3) = vertex;
|
||||
}
|
||||
|
||||
geometryBuffer._vertexCount += 4;
|
||||
|
||||
fixed (ushort* fixedPointer = geometryBuffer._indices)
|
||||
{
|
||||
var pointer = fixedPointer + geometryBuffer._indexCount;
|
||||
*(pointer + 0) = (ushort) (0 + indexOffset);
|
||||
*(pointer + 1) = (ushort) (1 + indexOffset);
|
||||
*(pointer + 2) = (ushort) (2 + indexOffset);
|
||||
*(pointer + 3) = (ushort) (1 + indexOffset);
|
||||
*(pointer + 4) = (ushort) (3 + indexOffset);
|
||||
*(pointer + 5) = (ushort) (2 + indexOffset);
|
||||
}
|
||||
|
||||
geometryBuffer._indexCount += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace MonoGame.Extended.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when enqueuing vertices or indices into a <see cref="GeometryBuffer{TVertexType}" />
|
||||
/// results in an overflow.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertexType">The vertex type.</typeparam>
|
||||
/// <seealso cref="Exception" />
|
||||
public class GeometryBufferOverflowException<TVertexType> : Exception
|
||||
where TVertexType : struct, IVertexType
|
||||
{
|
||||
internal GeometryBufferOverflowException(GeometryBuffer<TVertexType> geometryBuffer, int verticesCountToAdd,
|
||||
int indicesCountToAdd)
|
||||
: base(
|
||||
$"The GeometryBuffer could not enqueue the requested {verticesCountToAdd} vertices or {indicesCountToAdd} indices since it is full full with {geometryBuffer._vertexCount} (out of {geometryBuffer.MaximumVerticesCount}) vertices and {geometryBuffer._indexCount} (out of {geometryBuffer.MaximumIndicesCount}) indices. Consider increasing the maximum number of vertices or indices when instantiating the GeometryBuffer, flushing the GeometryBuffer if it's dynamic, or using multiple GeometryBuffers to accommodate the requested enqueue."
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{9B3AB8A1-78AA-471A-AFD0-B10B932115BC}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MonoGame.Extended.Graphics</RootNamespace>
|
||||
<AssemblyName>MonoGame.Extended.Graphics</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TargetFrameworkProfile>Profile111</TargetFrameworkProfile>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
<None Include="Effects\Resources\compile.bat" />
|
||||
<EmbeddedResource Include="Effects\Resources\DefaultEffect2D.mgfxo" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Batch2DSortMode.cs" />
|
||||
<Compile Include="Batching\Batch.cs" />
|
||||
<Compile Include="Batching\BatchCommandDrawer.cs" />
|
||||
<Compile Include="Batching\BatchCommandQueue.cs" />
|
||||
<Compile Include="Batching\BatchCommandQueueOverflowException.cs" />
|
||||
<Compile Include="Batching\BatchDrawCommand.cs" />
|
||||
<Compile Include="Batching\BatchSortMode.cs" />
|
||||
<Compile Include="Batching\DeferredBatchCommandQueue.cs" />
|
||||
<Compile Include="Batching\IBatchDrawCommandData.cs" />
|
||||
<Compile Include="Batching\ImmediateBatchCommandQueue.cs" />
|
||||
<Compile Include="DynamicBatch2D.cs" />
|
||||
<Compile Include="Effects\DefaultEffect2D.cs" />
|
||||
<Compile Include="Effects\EffectResource.cs" />
|
||||
<Compile Include="Effects\IMatrixChainEffect.cs" />
|
||||
<Compile Include="Effects\ITexture2DEffect.cs" />
|
||||
<Compile Include="Effects\MatrixChainEffect.cs" />
|
||||
<Compile Include="GeometryBuffer.cs" />
|
||||
<Compile Include="GeometryBuffer.Dynamic.cs" />
|
||||
<Compile Include="GeometryBuffer.Static.cs" />
|
||||
<Compile Include="GeometryBufferExtensions.VertexPositionColorTexture.cs" />
|
||||
<Compile Include="GeometryBufferOverflowException.cs" />
|
||||
<Compile Include="PrimitiveTypeExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Effects\Resources\DefaultEffect2D.fx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MonoGame.Framework">
|
||||
<HintPath>..\..\Dependencies\MonoGame.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MonoGame.Extended\MonoGame.Extended.csproj">
|
||||
<Project>{41724c52-3d50-45bb-81eb-3c8a247eafd1}</Project>
|
||||
<Name>MonoGame.Extended</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MonoGame.Extended.Graphics")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MonoGame.Extended.Graphics")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user