diff --git a/CHANGELOG.md b/CHANGELOG.md
index a42aa18..f13e47f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,9 +20,7 @@ Additions
Improvements
- Allow comparing Keybind and Combination based on the amount of modifiers they have
-
-Fixes
-- Fixed StaticSpriteBatch not resetting its texture when all items are removed
+- Allow using multiple textures in a StaticSpriteBatch
### MLEM.Ui
Additions
diff --git a/MLEM/Graphics/StaticSpriteBatch.cs b/MLEM/Graphics/StaticSpriteBatch.cs
index 933d71f..76da56a 100644
--- a/MLEM/Graphics/StaticSpriteBatch.cs
+++ b/MLEM/Graphics/StaticSpriteBatch.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
-using MLEM.Extensions;
namespace MLEM.Graphics {
///
@@ -18,17 +17,31 @@ namespace MLEM.Graphics {
private static readonly VertexPositionColorTexture[] Data = new VertexPositionColorTexture[MaxBatchItems * 4];
///
- /// The amount of vertices that are currently batched
+ /// The amount of vertices that are currently batched.
///
public int Vertices => this.items.Count * 4;
+ ///
+ /// The amount of vertex buffers that this static sprite batch has.
+ /// To see the amount of buffers that are actually in use, see .
+ ///
+ public int Buffers => this.vertexBuffers.Count;
+ ///
+ /// The amount of textures that this static sprite batch is currently using.
+ ///
+ public int Textures => this.textures.Distinct().Count();
+ ///
+ /// The amount of vertex buffers that are currently filled in this static sprite batch.
+ /// To see the amount of buffers that are available, see .
+ ///
+ public int FilledBuffers { get; private set; }
private readonly GraphicsDevice graphicsDevice;
private readonly SpriteEffect spriteEffect;
private readonly List vertexBuffers = new List();
+ private readonly List textures = new List();
private readonly ISet- items = new HashSet
- ();
private IndexBuffer indices;
- private Texture2D texture;
private bool batching;
private bool batchChanged;
@@ -56,49 +69,55 @@ namespace MLEM.Graphics {
/// Ends batching.
/// Call this method after calling Add or any of its overloads the desired number of times to add batched items.
///
- /// The drawing order for sprite drawing. by default. Note that and are not supported.
- /// Thrown if this method is called before was called
- /// Thrown if the is or , which are not supported
- public void EndBatch(SpriteSortMode sortMode = SpriteSortMode.Deferred) {
+ /// The drawing order for sprite drawing. by default, since it is the best in terms of rendering performance. Note that is not supported.
+ /// Thrown if this method is called before was called.
+ /// Thrown if the is , which is not supported.
+ public void EndBatch(SpriteSortMode sortMode = SpriteSortMode.Texture) {
if (!this.batching)
throw new InvalidOperationException("Not batching");
- if (sortMode == SpriteSortMode.Immediate || sortMode == SpriteSortMode.Texture)
- throw new ArgumentOutOfRangeException(nameof(sortMode), "Cannot use sprite sort modes Immediate or Texture for static batching");
+ if (sortMode == SpriteSortMode.Immediate)
+ throw new ArgumentOutOfRangeException(nameof(sortMode), "Cannot use sprite sort mode Immediate for static batching");
this.batching = false;
// if we didn't add or remove any batch items, we don't have to recalculate anything
if (!this.batchChanged)
return;
this.batchChanged = false;
-
- // ensure we have enough vertex buffers
- var requiredBuffers = (this.items.Count / (float) MaxBatchItems).Ceil();
- while (this.vertexBuffers.Count < requiredBuffers)
- this.vertexBuffers.Add(new VertexBuffer(this.graphicsDevice, VertexPositionColorTexture.VertexDeclaration, MaxBatchItems * 4, BufferUsage.WriteOnly));
+ this.FilledBuffers = 0;
+ this.textures.Clear();
// order items according to the sort mode
IEnumerable- ordered = this.items;
- if (sortMode == SpriteSortMode.BackToFront) {
- ordered = ordered.OrderBy(i => -i.Depth);
- } else if (sortMode == SpriteSortMode.FrontToBack) {
- ordered = ordered.OrderBy(i => i.Depth);
+ switch (sortMode) {
+ case SpriteSortMode.Texture:
+ // SortingKey is internal, but this will do for batching the same texture together
+ ordered = ordered.OrderBy(i => i.Texture.GetHashCode());
+ break;
+ case SpriteSortMode.BackToFront:
+ ordered = ordered.OrderBy(i => -i.Depth);
+ break;
+ case SpriteSortMode.FrontToBack:
+ ordered = ordered.OrderBy(i => i.Depth);
+ break;
}
// fill vertex buffers
var dataIndex = 0;
- var arrayIndex = 0;
+ Texture2D texture = null;
foreach (var item in ordered) {
+ // if the texture changes, we also have to start a new buffer!
+ if (dataIndex > 0 && (item.Texture != texture || dataIndex >= Data.Length)) {
+ this.FillBuffer(this.FilledBuffers++, texture, Data);
+ dataIndex = 0;
+ }
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;
- }
+ texture = item.Texture;
}
if (dataIndex > 0)
- this.vertexBuffers[arrayIndex].SetData(Data);
+ this.FillBuffer(this.FilledBuffers++, texture, Data);
// ensure we have enough indices
var maxItems = Math.Min(this.items.Count, MaxBatchItems);
@@ -152,21 +171,23 @@ namespace MLEM.Graphics {
this.spriteEffect.CurrentTechnique.Passes[0].Apply();
var totalIndex = 0;
- foreach (var buffer in this.vertexBuffers) {
+ for (var i = 0; i < this.FilledBuffers; i++) {
+ var buffer = this.vertexBuffers[i];
+ var texture = this.textures[i];
var tris = Math.Min(this.items.Count * 4 - totalIndex, buffer.VertexCount) / 4 * 2;
- if (tris <= 0)
- break;
+
this.graphicsDevice.SetVertexBuffer(buffer);
if (effect != null) {
foreach (var pass in effect.CurrentTechnique.Passes) {
pass.Apply();
- this.graphicsDevice.Textures[0] = this.texture;
+ this.graphicsDevice.Textures[0] = texture;
this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, tris);
}
} else {
- this.graphicsDevice.Textures[0] = this.texture;
+ this.graphicsDevice.Textures[0] = texture;
this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, tris);
}
+
totalIndex += buffer.VertexCount;
}
}
@@ -337,8 +358,6 @@ namespace MLEM.Graphics {
if (!this.batching)
throw new InvalidOperationException("Not batching");
if (this.items.Remove(item)) {
- if (this.items.Count <= 0)
- this.texture = null;
this.batchChanged = true;
return true;
}
@@ -354,7 +373,8 @@ namespace MLEM.Graphics {
if (!this.batching)
throw new InvalidOperationException("Not batching");
this.items.Clear();
- this.texture = null;
+ this.textures.Clear();
+ this.FilledBuffers = 0;
this.batchChanged = true;
}
@@ -406,33 +426,39 @@ namespace MLEM.Graphics {
private Item Add(Texture2D texture, float depth, VertexPositionColorTexture tl, VertexPositionColorTexture tr, VertexPositionColorTexture bl, VertexPositionColorTexture br) {
if (!this.batching)
throw new InvalidOperationException("Not batching");
- if (this.texture != null && this.texture != texture)
- throw new ArgumentException("Cannot use multiple textures in one batch", nameof(texture));
- var item = new Item(tl, tr, bl, br, depth);
+ var item = new Item(texture, depth, tl, tr, bl, br);
this.items.Add(item);
- this.texture = texture;
this.batchChanged = true;
return item;
}
+ private void FillBuffer(int index, Texture2D texture, VertexPositionColorTexture[] data) {
+ if (this.vertexBuffers.Count <= index)
+ this.vertexBuffers.Add(new VertexBuffer(this.graphicsDevice, VertexPositionColorTexture.VertexDeclaration, MaxBatchItems * 4, BufferUsage.WriteOnly));
+ this.vertexBuffers[index].SetData(data);
+ this.textures.Insert(index, texture);
+ }
+
///
/// A struct that represents an item added to a using Add or any of its overloads.
/// An item returned after adding can be removed using .
///
public class Item {
+ internal readonly Texture2D Texture;
+ internal readonly float Depth;
internal readonly VertexPositionColorTexture TopLeft;
internal readonly VertexPositionColorTexture TopRight;
internal readonly VertexPositionColorTexture BottomLeft;
internal readonly VertexPositionColorTexture BottomRight;
- internal readonly float Depth;
- internal Item(VertexPositionColorTexture topLeft, VertexPositionColorTexture topRight, VertexPositionColorTexture bottomLeft, VertexPositionColorTexture bottomRight, float depth) {
+ internal Item(Texture2D texture, float depth, VertexPositionColorTexture topLeft, VertexPositionColorTexture topRight, VertexPositionColorTexture bottomLeft, VertexPositionColorTexture bottomRight) {
+ this.Texture = texture;
+ this.Depth = depth;
this.TopLeft = topLeft;
this.TopRight = topRight;
this.BottomLeft = bottomLeft;
this.BottomRight = bottomRight;
- this.Depth = depth;
}
}