2021-10-17 23:20:05 +02:00
using System ;
using System.Collections.Generic ;
2021-11-13 16:42:50 +01:00
using System.Linq ;
2021-10-17 23:20:05 +02:00
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Graphics ;
using MLEM.Extensions ;
2021-11-29 21:24:08 +01:00
namespace MLEM.Graphics {
2021-10-17 23:20:05 +02:00
/// <summary>
2021-10-18 01:22:22 +02:00
/// A static sprite batch is a variation of <see cref="SpriteBatch"/> that keeps all batched items in a <see cref="VertexBuffer"/>, allowing for them to be drawn multiple times.
2021-11-12 18:35:10 +01:00
/// To add items to a static sprite batch, use <see cref="BeginBatch"/> to begin batching, <see cref="ClearBatch"/> to clear currently batched items, <c>Add</c> and its various overloads to add batch items, <see cref="Remove"/> to remove them again, and <see cref="EndBatch"/> to end batching.
2021-10-17 23:20:05 +02:00
/// To draw the batched items, call <see cref="Draw"/>.
/// </summary>
public class StaticSpriteBatch : IDisposable {
2021-10-19 23:12:56 +02:00
// this maximum is limited by indices being a short
private const int MaxBatchItems = short . MaxValue / 6 ;
private static readonly VertexPositionColorTexture [ ] Data = new VertexPositionColorTexture [ MaxBatchItems * 4 ] ;
2021-10-17 23:20:05 +02:00
/// <summary>
/// The amount of vertices that are currently batched
/// </summary>
2021-11-12 20:21:08 +01:00
public int Vertices = > this . items . Count * 4 ;
2021-10-17 23:20:05 +02:00
private readonly GraphicsDevice graphicsDevice ;
private readonly SpriteEffect spriteEffect ;
2021-10-18 01:22:22 +02:00
private readonly List < VertexBuffer > vertexBuffers = new List < VertexBuffer > ( ) ;
2021-11-12 20:21:08 +01:00
private readonly ISet < Item > items = new HashSet < Item > ( ) ;
2021-10-18 01:22:22 +02:00
private IndexBuffer indices ;
2021-10-17 23:20:05 +02:00
private Texture2D texture ;
private bool batching ;
private bool batchChanged ;
/// <summary>
/// Creates a new static sprite batch with the given <see cref="GraphicsDevice"/>
/// </summary>
/// <param name="graphicsDevice">The graphics device to use for rendering</param>
public StaticSpriteBatch ( GraphicsDevice graphicsDevice ) {
this . graphicsDevice = graphicsDevice ;
this . spriteEffect = new SpriteEffect ( graphicsDevice ) ;
}
/// <summary>
/// Begins batching.
/// Call this method before calling <c>Add</c> or any of its overloads.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if this batch is currently batching already</exception>
public void BeginBatch ( ) {
if ( this . batching )
throw new InvalidOperationException ( "Already batching" ) ;
this . batching = true ;
}
/// <summary>
/// Ends batching.
/// Call this method after calling <c>Add</c> or any of its overloads the desired number of times to add batched items.
/// </summary>
2021-11-13 16:42:50 +01:00
/// <param name="sortMode">The drawing order for sprite drawing. <see cref="SpriteSortMode.Deferred" /> by default. Note that <see cref="SpriteSortMode.Immediate"/> and <see cref="SpriteSortMode.Texture"/> are not supported.</param>
2021-10-17 23:20:05 +02:00
/// <exception cref="InvalidOperationException">Thrown if this method is called before <see cref="BeginBatch"/> was called</exception>
2021-11-13 16:42:50 +01:00
/// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="sortMode"/> is <see cref="SpriteSortMode.Immediate"/> or <see cref="SpriteSortMode.Texture"/>, which are not supported</exception>
public void EndBatch ( SpriteSortMode sortMode = SpriteSortMode . Deferred ) {
2021-10-17 23:20:05 +02:00
if ( ! this . batching )
throw new InvalidOperationException ( "Not batching" ) ;
2021-11-13 16:42:50 +01:00
if ( sortMode = = SpriteSortMode . Immediate | | sortMode = = SpriteSortMode . Texture )
throw new ArgumentOutOfRangeException ( nameof ( sortMode ) , "Cannot use sprite sort modes Immediate or Texture for static batching" ) ;
2021-10-17 23:20:05 +02:00
this . batching = false ;
2021-11-12 18:12:57 +01:00
// if we didn't add or remove any batch items, we don't have to recalculate anything
2021-10-17 23:20:05 +02:00
if ( ! this . batchChanged )
return ;
this . batchChanged = false ;
2021-10-18 01:22:22 +02:00
// ensure we have enough vertex buffers
2021-11-12 20:21:08 +01:00
var requiredBuffers = ( this . items . Count / ( float ) MaxBatchItems ) . Ceil ( ) ;
2021-10-18 01:22:22 +02:00
while ( this . vertexBuffers . Count < requiredBuffers )
2021-10-19 23:12:56 +02:00
this . vertexBuffers . Add ( new VertexBuffer ( this . graphicsDevice , VertexPositionColorTexture . VertexDeclaration , MaxBatchItems * 4 , BufferUsage . WriteOnly ) ) ;
2021-10-17 23:20:05 +02:00
2021-11-13 16:42:50 +01:00
// order items according to the sort mode
IEnumerable < Item > ordered = this . items ;
if ( sortMode = = SpriteSortMode . BackToFront ) {
ordered = ordered . OrderBy ( i = > - i . Depth ) ;
} else if ( sortMode = = SpriteSortMode . FrontToBack ) {
ordered = ordered . OrderBy ( i = > i . Depth ) ;
}
2021-10-18 01:22:22 +02:00
// fill vertex buffers
2021-11-12 20:21:08 +01:00
var dataIndex = 0 ;
2021-10-17 23:20:05 +02:00
var arrayIndex = 0 ;
2021-11-13 16:42:50 +01:00
foreach ( var item in ordered ) {
2021-11-12 20:21:08 +01:00
Data [ dataIndex + + ] = item . TopLeft ;
Data [ dataIndex + + ] = item . TopRight ;
Data [ dataIndex + + ] = item . BottomLeft ;
Data [ dataIndex + + ] = item . BottomRight ;
if ( dataIndex > = Data . Length ) {
this . vertexBuffers [ arrayIndex + + ] . SetData ( Data ) ;
dataIndex = 0 ;
}
2021-10-17 23:20:05 +02:00
}
2021-11-12 20:21:08 +01:00
if ( dataIndex > 0 )
this . vertexBuffers [ arrayIndex ] . SetData ( Data ) ;
2021-10-17 23:20:05 +02:00
// ensure we have enough indices
2021-11-12 20:21:08 +01:00
var maxItems = Math . Min ( this . items . Count , MaxBatchItems ) ;
2021-10-17 23:20:05 +02:00
// each item has 2 triangles which each have 3 indices
2021-10-18 01:22:22 +02:00
if ( this . indices = = null | | this . indices . IndexCount < 6 * maxItems ) {
var newIndices = new short [ 6 * maxItems ] ;
2021-10-17 23:20:05 +02:00
var index = 0 ;
for ( var item = 0 ; item < maxItems ; item + + ) {
// a square is made up of two triangles
// 0--1
// | /|
// |/ |
// 2--3
// top left triangle (0 -> 1 -> 2)
2021-10-18 01:22:22 +02:00
newIndices [ index + + ] = ( short ) ( item * 4 + 0 ) ;
newIndices [ index + + ] = ( short ) ( item * 4 + 1 ) ;
newIndices [ index + + ] = ( short ) ( item * 4 + 2 ) ;
2021-10-17 23:20:05 +02:00
// bottom right triangle (1 -> 3 -> 2)
2021-10-18 01:22:22 +02:00
newIndices [ index + + ] = ( short ) ( item * 4 + 1 ) ;
newIndices [ index + + ] = ( short ) ( item * 4 + 3 ) ;
newIndices [ index + + ] = ( short ) ( item * 4 + 2 ) ;
2021-10-17 23:20:05 +02:00
}
2021-10-30 13:48:52 +02:00
this . indices ? . Dispose ( ) ;
2021-10-18 01:22:22 +02:00
this . indices = new IndexBuffer ( this . graphicsDevice , IndexElementSize . SixteenBits , newIndices . Length , BufferUsage . WriteOnly ) ;
this . indices . SetData ( newIndices ) ;
2021-10-17 23:20:05 +02:00
}
}
/// <summary>
/// Draws this batch's content onto the <see cref="GraphicsDevice"/>'s current render target (or the back buffer) with the given settings.
/// Note that this method should not be called while a regular <see cref="SpriteBatch"/> is currently active.
/// </summary>
/// <param name="blendState">State of the blending. Uses <see cref="BlendState.AlphaBlend"/> if null.</param>
/// <param name="samplerState">State of the sampler. Uses <see cref="SamplerState.LinearClamp"/> if null.</param>
/// <param name="depthStencilState">State of the depth-stencil buffer. Uses <see cref="DepthStencilState.None"/> if null.</param>
/// <param name="rasterizerState">State of the rasterization. Uses <see cref="RasterizerState.CullCounterClockwise"/> if null.</param>
/// <param name="effect">A custom <see cref="Effect"/> to override the default sprite effect. Uses default sprite effect if null.</param>
/// <param name="transformMatrix">An optional matrix used to transform the sprite geometry. Uses <see cref="Matrix.Identity"/> if null.</param>
/// <exception cref="InvalidOperationException">Thrown if this batch is currently batching</exception>
public void Draw ( BlendState blendState = null , SamplerState samplerState = null , DepthStencilState depthStencilState = null , RasterizerState rasterizerState = null , Effect effect = null , Matrix ? transformMatrix = null ) {
if ( this . batching )
throw new InvalidOperationException ( "Cannot draw the batch while batching" ) ;
this . graphicsDevice . BlendState = blendState ? ? BlendState . AlphaBlend ;
this . graphicsDevice . SamplerStates [ 0 ] = samplerState ? ? SamplerState . LinearClamp ;
this . graphicsDevice . DepthStencilState = depthStencilState ? ? DepthStencilState . None ;
this . graphicsDevice . RasterizerState = rasterizerState ? ? RasterizerState . CullCounterClockwise ;
2021-10-18 01:22:22 +02:00
this . graphicsDevice . Indices = this . indices ;
2021-10-17 23:20:05 +02:00
this . spriteEffect . TransformMatrix = transformMatrix ;
this . spriteEffect . CurrentTechnique . Passes [ 0 ] . Apply ( ) ;
var totalIndex = 0 ;
2021-10-18 01:22:22 +02:00
foreach ( var buffer in this . vertexBuffers ) {
2021-11-12 20:21:08 +01:00
var tris = Math . Min ( this . items . Count * 4 - totalIndex , buffer . VertexCount ) / 4 * 2 ;
2021-10-19 17:20:00 +02:00
if ( tris < = 0 )
break ;
2021-10-18 01:22:22 +02:00
this . graphicsDevice . SetVertexBuffer ( buffer ) ;
2021-10-17 23:20:05 +02:00
if ( effect ! = null ) {
foreach ( var pass in effect . CurrentTechnique . Passes ) {
pass . Apply ( ) ;
this . graphicsDevice . Textures [ 0 ] = this . texture ;
2021-10-18 01:22:22 +02:00
this . graphicsDevice . DrawIndexedPrimitives ( PrimitiveType . TriangleList , 0 , 0 , tris ) ;
2021-10-17 23:20:05 +02:00
}
} else {
this . graphicsDevice . Textures [ 0 ] = this . texture ;
2021-10-18 01:22:22 +02:00
this . graphicsDevice . DrawIndexedPrimitives ( PrimitiveType . TriangleList , 0 , 0 , tris ) ;
2021-10-17 23:20:05 +02:00
}
2021-10-18 01:22:22 +02:00
totalIndex + = buffer . VertexCount ;
2021-10-17 23:20:05 +02:00
}
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="sourceRectangle">An optional region on the texture which will be rendered. If null - draws full texture.</param>
/// <param name="color">A color mask.</param>
/// <param name="rotation">A rotation of this sprite.</param>
/// <param name="origin">Center of the rotation. 0,0 by default.</param>
/// <param name="scale">A scaling of this sprite.</param>
/// <param name="effects">Modificators for drawing. Can be combined.</param>
2021-11-13 16:42:50 +01:00
/// <param name="layerDepth">A depth of the layer of this sprite.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Vector2 position , Rectangle ? sourceRectangle , Color color , float rotation , Vector2 origin , Vector2 scale , SpriteEffects effects , float layerDepth ) {
2021-10-17 23:20:05 +02:00
origin * = scale ;
Vector2 size , texTl , texBr ;
if ( sourceRectangle . HasValue ) {
var src = sourceRectangle . Value ;
size . X = src . Width * scale . X ;
size . Y = src . Height * scale . Y ;
texTl . X = src . X * ( 1F / texture . Width ) ;
texTl . Y = src . Y * ( 1F / texture . Height ) ;
texBr . X = ( src . X + src . Width ) * ( 1F / texture . Width ) ;
texBr . Y = ( src . Y + src . Height ) * ( 1F / texture . Height ) ;
} else {
size . X = texture . Width * scale . X ;
size . Y = texture . Height * scale . Y ;
texTl = Vector2 . Zero ;
texBr = Vector2 . One ;
}
if ( ( effects & SpriteEffects . FlipVertically ) ! = 0 )
( texBr . Y , texTl . Y ) = ( texTl . Y , texBr . Y ) ;
if ( ( effects & SpriteEffects . FlipHorizontally ) ! = 0 )
( texBr . X , texTl . X ) = ( texTl . X , texBr . X ) ;
if ( rotation = = 0 ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , position - origin , size , color , texTl , texBr , layerDepth ) ;
2021-10-17 23:20:05 +02:00
} else {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , position , - origin , size , ( float ) Math . Sin ( rotation ) , ( float ) Math . Cos ( rotation ) , color , texTl , texBr , layerDepth ) ;
2021-10-17 23:20:05 +02:00
}
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="sourceRectangle">An optional region on the texture which will be rendered. If null - draws full texture.</param>
/// <param name="color">A color mask.</param>
/// <param name="rotation">A rotation of this sprite.</param>
/// <param name="origin">Center of the rotation. 0,0 by default.</param>
/// <param name="scale">A scaling of this sprite.</param>
/// <param name="effects">Modificators for drawing. Can be combined.</param>
2021-11-13 16:42:50 +01:00
/// <param name="layerDepth">A depth of the layer of this sprite.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Vector2 position , Rectangle ? sourceRectangle , Color color , float rotation , Vector2 origin , float scale , SpriteEffects effects , float layerDepth ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , position , sourceRectangle , color , rotation , origin , new Vector2 ( scale ) , effects , layerDepth ) ;
2021-10-17 23:20:05 +02:00
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="destinationRectangle">The drawing bounds on screen.</param>
/// <param name="sourceRectangle">An optional region on the texture which will be rendered. If null - draws full texture.</param>
/// <param name="color">A color mask.</param>
/// <param name="rotation">A rotation of this sprite.</param>
/// <param name="origin">Center of the rotation. 0,0 by default.</param>
/// <param name="effects">Modificators for drawing. Can be combined.</param>
2021-11-13 16:42:50 +01:00
/// <param name="layerDepth">A depth of the layer of this sprite.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Rectangle destinationRectangle , Rectangle ? sourceRectangle , Color color , float rotation , Vector2 origin , SpriteEffects effects , float layerDepth ) {
2021-10-17 23:20:05 +02:00
Vector2 texTl , texBr ;
if ( sourceRectangle . HasValue ) {
var src = sourceRectangle . Value ;
texTl . X = src . X * ( 1F / texture . Width ) ;
texTl . Y = src . Y * ( 1F / texture . Height ) ;
texBr . X = ( src . X + src . Width ) * ( 1F / texture . Width ) ;
texBr . Y = ( src . Y + src . Height ) * ( 1F / texture . Height ) ;
origin . X = origin . X * destinationRectangle . Width * ( src . Width ! = 0 ? src . Width : 1F / texture . Width ) ;
origin . Y = origin . Y * destinationRectangle . Height * ( src . Height ! = 0 ? src . Height : 1F / texture . Height ) ;
} else {
texTl = Vector2 . Zero ;
texBr = Vector2 . One ;
origin . X = origin . X * destinationRectangle . Width * ( 1F / texture . Width ) ;
origin . Y = origin . Y * destinationRectangle . Height * ( 1F / texture . Height ) ;
}
if ( ( effects & SpriteEffects . FlipVertically ) ! = 0 )
( texBr . Y , texTl . Y ) = ( texTl . Y , texBr . Y ) ;
if ( ( effects & SpriteEffects . FlipHorizontally ) ! = 0 )
( texBr . X , texTl . X ) = ( texTl . X , texBr . X ) ;
if ( rotation = = 0 ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , destinationRectangle . Location . ToVector2 ( ) - origin , destinationRectangle . Size . ToVector2 ( ) , color , texTl , texBr , layerDepth ) ;
2021-10-17 23:20:05 +02:00
} else {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , destinationRectangle . Location . ToVector2 ( ) , - origin , destinationRectangle . Size . ToVector2 ( ) , ( float ) Math . Sin ( rotation ) , ( float ) Math . Cos ( rotation ) , color , texTl , texBr , layerDepth ) ;
2021-10-17 23:20:05 +02:00
}
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="sourceRectangle">An optional region on the texture which will be rendered. If null - draws full texture.</param>
/// <param name="color">A color mask.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Vector2 position , Rectangle ? sourceRectangle , Color color ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , position , sourceRectangle , color , 0 , Vector2 . Zero , 1 , SpriteEffects . None , 0 ) ;
2021-10-17 23:20:05 +02:00
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="destinationRectangle">The drawing bounds on screen.</param>
/// <param name="sourceRectangle">An optional region on the texture which will be rendered. If null - draws full texture.</param>
/// <param name="color">A color mask.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Rectangle destinationRectangle , Rectangle ? sourceRectangle , Color color ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , destinationRectangle , sourceRectangle , color , 0 , Vector2 . Zero , SpriteEffects . None , 0 ) ;
2021-10-17 23:20:05 +02:00
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="position">The drawing location on screen.</param>
/// <param name="color">A color mask.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Vector2 position , Color color ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , position , null , color ) ;
2021-10-17 23:20:05 +02:00
}
/// <summary>
/// Adds an item to this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="texture">A texture.</param>
/// <param name="destinationRectangle">The drawing bounds on screen.</param>
/// <param name="color">A color mask.</param>
2021-11-12 20:21:08 +01:00
/// <returns>The <see cref="Item"/> that was created from the added data</returns>
public Item Add ( Texture2D texture , Rectangle destinationRectangle , Color color ) {
2021-11-12 18:12:57 +01:00
return this . Add ( texture , destinationRectangle , null , color ) ;
}
/// <summary>
/// Removes the given item from this batch.
/// Note that this batch needs to currently be batching, meaning <see cref="BeginBatch"/> has to have been called previously.
/// </summary>
/// <param name="item">The item to remove</param>
/// <returns>Whether the item was successfully removed</returns>
/// <exception cref="InvalidOperationException">Thrown if this method is called before <see cref="BeginBatch"/> was called</exception>
2021-11-12 20:21:08 +01:00
public bool Remove ( Item item ) {
2021-11-12 18:12:57 +01:00
if ( ! this . batching )
throw new InvalidOperationException ( "Not batching" ) ;
2021-11-12 20:21:08 +01:00
if ( this . items . Remove ( item ) ) {
2021-11-12 18:12:57 +01:00
this . batchChanged = true ;
return true ;
}
return false ;
2021-10-17 23:20:05 +02:00
}
2021-11-12 18:35:10 +01:00
/// <summary>
/// Clears the batch, removing all currently batched vertices.
/// After this operation, <see cref="Vertices"/> will return 0.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if this method is called before <see cref="BeginBatch"/> was called</exception>
public void ClearBatch ( ) {
if ( ! this . batching )
throw new InvalidOperationException ( "Not batching" ) ;
2021-11-12 20:21:08 +01:00
this . items . Clear ( ) ;
2021-11-12 18:35:10 +01:00
this . texture = null ;
this . batchChanged = true ;
}
2021-11-22 19:25:18 +01:00
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
2021-10-17 23:20:05 +02:00
public void Dispose ( ) {
this . spriteEffect . Dispose ( ) ;
2021-10-30 13:48:52 +02:00
this . indices ? . Dispose ( ) ;
foreach ( var buffer in this . vertexBuffers )
buffer . Dispose ( ) ;
2021-10-17 23:20:05 +02:00
GC . SuppressFinalize ( this ) ;
}
2021-11-12 20:21:08 +01:00
private Item Add ( Texture2D texture , Vector2 pos , Vector2 offset , Vector2 size , float sin , float cos , Color color , Vector2 texTl , Vector2 texBr , float depth ) {
2021-11-13 16:42:50 +01:00
return this . Add ( texture , depth ,
2021-10-17 23:20:05 +02:00
new VertexPositionColorTexture ( new Vector3 ( pos . X + offset . X * cos - offset . Y * sin , pos . Y + offset . X * sin + offset . Y * cos , depth ) , color , texTl ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X + ( offset . X + size . X ) * cos - offset . Y * sin , pos . Y + ( offset . X + size . X ) + offset . Y * cos , depth ) , color , new Vector2 ( texBr . X , texTl . Y ) ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X + offset . X * cos - ( offset . Y + size . Y ) * sin , pos . Y + offset . X * sin + ( offset . Y + size . Y ) * cos , depth ) , color , new Vector2 ( texTl . X , texBr . Y ) ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X + ( offset . X + size . X ) * cos - ( offset . Y + size . Y ) * sin , pos . Y + ( offset . X + size . X ) * sin + ( offset . Y + size . Y ) * cos , depth ) , color , texBr ) ) ;
}
2021-11-12 20:21:08 +01:00
private Item Add ( Texture2D texture , Vector2 pos , Vector2 size , Color color , Vector2 texTl , Vector2 texBr , float depth ) {
2021-11-13 16:42:50 +01:00
return this . Add ( texture , depth ,
2021-10-17 23:20:05 +02:00
new VertexPositionColorTexture ( new Vector3 ( pos , depth ) , color , texTl ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X + size . X , pos . Y , depth ) , color , new Vector2 ( texBr . X , texTl . Y ) ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X , pos . Y + size . Y , depth ) , color , new Vector2 ( texTl . X , texBr . Y ) ) ,
new VertexPositionColorTexture ( new Vector3 ( pos . X + size . X , pos . Y + size . Y , depth ) , color , texBr ) ) ;
}
2021-11-13 16:42:50 +01:00
private Item Add ( Texture2D texture , float depth , VertexPositionColorTexture tl , VertexPositionColorTexture tr , VertexPositionColorTexture bl , VertexPositionColorTexture br ) {
2021-10-17 23:20:05 +02:00
if ( ! this . batching )
throw new InvalidOperationException ( "Not batching" ) ;
if ( this . texture ! = null & & this . texture ! = texture )
2021-11-13 16:42:50 +01:00
throw new ArgumentException ( "Cannot use multiple textures in one batch" , nameof ( texture ) ) ;
var item = new Item ( tl , tr , bl , br , depth ) ;
2021-11-12 20:21:08 +01:00
this . items . Add ( item ) ;
2021-10-17 23:20:05 +02:00
this . texture = texture ;
this . batchChanged = true ;
2021-11-12 20:21:08 +01:00
return item ;
2021-11-12 18:12:57 +01:00
}
/// <summary>
/// A struct that represents an item added to a <see cref="StaticSpriteBatch"/> using <c>Add</c> or any of its overloads.
2021-11-12 20:21:08 +01:00
/// An item returned after adding can be removed using <see cref="Remove"/>.
2021-11-12 18:12:57 +01:00
/// </summary>
2021-11-12 20:21:08 +01:00
public class Item {
internal readonly VertexPositionColorTexture TopLeft ;
internal readonly VertexPositionColorTexture TopRight ;
internal readonly VertexPositionColorTexture BottomLeft ;
internal readonly VertexPositionColorTexture BottomRight ;
2021-11-13 16:42:50 +01:00
internal readonly float Depth ;
2021-11-12 20:21:08 +01:00
2021-11-13 16:42:50 +01:00
internal Item ( VertexPositionColorTexture topLeft , VertexPositionColorTexture topRight , VertexPositionColorTexture bottomLeft , VertexPositionColorTexture bottomRight , float depth ) {
2021-11-12 20:21:08 +01:00
this . TopLeft = topLeft ;
this . TopRight = topRight ;
this . BottomLeft = bottomLeft ;
this . BottomRight = bottomRight ;
2021-11-13 16:42:50 +01:00
this . Depth = depth ;
2021-11-12 18:12:57 +01:00
}
2021-10-17 23:20:05 +02:00
}
}
}