diff --git a/MLEM/Misc/StaticSpriteBatch.cs b/MLEM/Misc/StaticSpriteBatch.cs index 39cc795..2ff2e5d 100644 --- a/MLEM/Misc/StaticSpriteBatch.cs +++ b/MLEM/Misc/StaticSpriteBatch.cs @@ -9,6 +9,7 @@ namespace MLEM.Misc { /// A static sprite batch is a variation of that keeps all batched items in a , allowing for them to be drawn multiple times. /// To add items to a static sprite batch, use to begin batching, to clear currently batched items, Add and its various overloads to add batch items, to remove them again, and to end batching. /// To draw the batched items, call . + /// Unlike a , items added to a static sprite batch will be drawn in an arbitrary order. If depth sorting is desired, the 's should be modified to include depth. /// public class StaticSpriteBatch : IDisposable { @@ -19,13 +20,13 @@ namespace MLEM.Misc { /// /// The amount of vertices that are currently batched /// - public int Vertices => this.vertices.Count; + public int Vertices => this.items.Count * 4; private readonly GraphicsDevice graphicsDevice; private readonly SpriteEffect spriteEffect; private readonly List vertexBuffers = new List(); - private readonly List vertices = new List(); + private readonly ISet items = new HashSet(); private IndexBuffer indices; private Texture2D texture; private bool batching; @@ -67,22 +68,28 @@ namespace MLEM.Misc { this.batchChanged = false; // ensure we have enough vertex buffers - var requiredBuffers = (this.vertices.Count / (MaxBatchItems * 4F)).Ceil(); + 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)); // fill vertex buffers + var dataIndex = 0; var arrayIndex = 0; - var totalIndex = 0; - while (totalIndex < this.vertices.Count) { - var now = Math.Min(this.vertices.Count - totalIndex, Data.Length); - this.vertices.CopyTo(totalIndex, Data, 0, now); - this.vertexBuffers[arrayIndex++].SetData(Data); - totalIndex += now; + foreach (var item in this.items) { + 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; + } } + if (dataIndex > 0) + this.vertexBuffers[arrayIndex].SetData(Data); // ensure we have enough indices - var maxItems = Math.Min(this.vertices.Count / 4, MaxBatchItems); + var maxItems = Math.Min(this.items.Count, MaxBatchItems); // each item has 2 triangles which each have 3 indices if (this.indices == null || this.indices.IndexCount < 6 * maxItems) { var newIndices = new short[6 * maxItems]; @@ -134,7 +141,7 @@ namespace MLEM.Misc { var totalIndex = 0; foreach (var buffer in this.vertexBuffers) { - var tris = Math.Min(this.vertices.Count - totalIndex, buffer.VertexCount) / 4 * 2; + var tris = Math.Min(this.items.Count * 4 - totalIndex, buffer.VertexCount) / 4 * 2; if (tris <= 0) break; this.graphicsDevice.SetVertexBuffer(buffer); @@ -165,8 +172,8 @@ namespace MLEM.Misc { /// A scaling of this sprite. /// Modificators for drawing. Can be combined. /// A depth of the layer of this sprite. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { + /// The that was created from the added data + public Item Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { origin *= scale; Vector2 size, texTl, texBr; @@ -210,8 +217,8 @@ namespace MLEM.Misc { /// A scaling of this sprite. /// Modificators for drawing. Can be combined. /// A depth of the layer of this sprite. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { + /// The that was created from the added data + public Item Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { return this.Add(texture, position, sourceRectangle, color, rotation, origin, new Vector2(scale), effects, layerDepth); } @@ -227,8 +234,8 @@ namespace MLEM.Misc { /// Center of the rotation. 0,0 by default. /// Modificators for drawing. Can be combined. /// A depth of the layer of this sprite. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) { + /// The that was created from the added data + public Item Add(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) { Vector2 texTl, texBr; if (sourceRectangle.HasValue) { var src = sourceRectangle.Value; @@ -265,8 +272,8 @@ namespace MLEM.Misc { /// The drawing location on screen. /// An optional region on the texture which will be rendered. If null - draws full texture. /// A color mask. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color) { + /// The that was created from the added data + public Item Add(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color) { return this.Add(texture, position, sourceRectangle, color, 0, Vector2.Zero, 1, SpriteEffects.None, 0); } @@ -278,8 +285,8 @@ namespace MLEM.Misc { /// The drawing bounds on screen. /// An optional region on the texture which will be rendered. If null - draws full texture. /// A color mask. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color) { + /// The that was created from the added data + public Item Add(Texture2D texture, Rectangle destinationRectangle, Rectangle? sourceRectangle, Color color) { return this.Add(texture, destinationRectangle, sourceRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0); } @@ -290,8 +297,8 @@ namespace MLEM.Misc { /// A texture. /// The drawing location on screen. /// A color mask. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Vector2 position, Color color) { + /// The that was created from the added data + public Item Add(Texture2D texture, Vector2 position, Color color) { return this.Add(texture, position, null, color); } @@ -302,8 +309,8 @@ namespace MLEM.Misc { /// A texture. /// The drawing bounds on screen. /// A color mask. - /// The that was created from the added data - public ItemInfo Add(Texture2D texture, Rectangle destinationRectangle, Color color) { + /// The that was created from the added data + public Item Add(Texture2D texture, Rectangle destinationRectangle, Color color) { return this.Add(texture, destinationRectangle, null, color); } @@ -314,12 +321,10 @@ namespace MLEM.Misc { /// The item to remove /// Whether the item was successfully removed /// Thrown if this method is called before was called - public bool Remove(ItemInfo item) { + public bool Remove(Item item) { if (!this.batching) throw new InvalidOperationException("Not batching"); - var firstIndex = this.vertices.IndexOf(item.First); - if (firstIndex >= 0 && firstIndex + item.Count <= this.vertices.Count) { - this.vertices.RemoveRange(firstIndex, item.Count); + if (this.items.Remove(item)) { this.batchChanged = true; return true; } @@ -334,7 +339,7 @@ namespace MLEM.Misc { public void ClearBatch() { if (!this.batching) throw new InvalidOperationException("Not batching"); - this.vertices.Clear(); + this.items.Clear(); this.texture = null; this.batchChanged = true; } @@ -348,7 +353,7 @@ namespace MLEM.Misc { GC.SuppressFinalize(this); } - private ItemInfo Add(Texture2D texture, Vector2 pos, Vector2 offset, Vector2 size, float sin, float cos, Color color, Vector2 texTl, Vector2 texBr, float depth) { + private Item Add(Texture2D texture, Vector2 pos, Vector2 offset, Vector2 size, float sin, float cos, Color color, Vector2 texTl, Vector2 texBr, float depth) { return this.Add(texture, 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)), @@ -356,7 +361,7 @@ namespace MLEM.Misc { 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)); } - private ItemInfo Add(Texture2D texture, Vector2 pos, Vector2 size, Color color, Vector2 texTl, Vector2 texBr, float depth) { + private Item Add(Texture2D texture, Vector2 pos, Vector2 size, Color color, Vector2 texTl, Vector2 texBr, float depth) { return this.Add(texture, 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)), @@ -364,32 +369,34 @@ namespace MLEM.Misc { new VertexPositionColorTexture(new Vector3(pos.X + size.X, pos.Y + size.Y, depth), color, texBr)); } - private ItemInfo Add(Texture2D texture, VertexPositionColorTexture tl, VertexPositionColorTexture tr, VertexPositionColorTexture bl, VertexPositionColorTexture br) { + private Item Add(Texture2D texture, 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"); + var item = new Item(tl, tr, bl, br); + this.items.Add(item); this.texture = texture; - this.vertices.Add(tl); - this.vertices.Add(tr); - this.vertices.Add(bl); - this.vertices.Add(br); this.batchChanged = true; - return new ItemInfo(tl, 4); + return item; } /// /// A struct that represents an item added to a using Add or any of its overloads. - /// A returned can be removed using . + /// An item returned after adding can be removed using . /// - public readonly struct ItemInfo { + public class Item { - internal readonly VertexPositionColorTexture First; - internal readonly int Count; + internal readonly VertexPositionColorTexture TopLeft; + internal readonly VertexPositionColorTexture TopRight; + internal readonly VertexPositionColorTexture BottomLeft; + internal readonly VertexPositionColorTexture BottomRight; - internal ItemInfo(VertexPositionColorTexture first, int count) { - this.First = first; - this.Count = count; + internal Item(VertexPositionColorTexture topLeft, VertexPositionColorTexture topRight, VertexPositionColorTexture bottomLeft, VertexPositionColorTexture bottomRight) { + this.TopLeft = topLeft; + this.TopRight = topRight; + this.BottomLeft = bottomLeft; + this.BottomRight = bottomRight; } }