using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using RecrownedAthenaeum.Camera; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RecrownedAthenaeum.Render { public class PrimitiveBatch : IDisposable { List vertices; int maxVertices; int bufferPosition; BasicEffect basicEffect; PrimitiveType primitiveType; int verticesPerPrimitive; GraphicsDevice graphicsDevice; bool began; bool isDisposed; /// /// Creates a batch used to draw primitives. /// /// The current graphics device being used. /// The current camera being used. /// The amount of vertices every batch can hold before flushing. Default is 450. Should be changed to be the most optimal number if possible. Especially if using strip primitive types. public PrimitiveBatch(GraphicsDevice graphicsDevice, Camera2D camera, int verticesPerBatch = 500) { this.graphicsDevice = graphicsDevice ?? throw new ArgumentNullException("Graphics device can't be null."); maxVertices = verticesPerBatch; basicEffect = new BasicEffect(graphicsDevice); basicEffect.VertexColorEnabled = true; basicEffect.View = camera.Matrix; vertices = new List(verticesPerBatch); } /// /// Starts the batch. Batch cannot be started twice. /// /// The type of primitive this batch would be drawing. public void Begin(PrimitiveType primitiveType) { if (began) throw new InvalidOperationException("Begin is being called twice before being ended."); this.primitiveType = primitiveType; verticesPerPrimitive = 0; switch (primitiveType) { case PrimitiveType.LineList: verticesPerPrimitive = 2; break; case PrimitiveType.TriangleList: verticesPerPrimitive = 3; break; default: verticesPerPrimitive = 1; break; } basicEffect.CurrentTechnique.Passes[0].Apply(); began = true; } /// /// Ends the batch. Begin needs to be called before end. /// public void End() { if (!began) throw new InvalidOperationException("Begin must be called before ending."); Flush(); } /// /// Adds a vertex position for the primitive being drawn. The batch needs to have beens started before this. /// /// The vector that represents the vertex. /// The color of that vertex. public void AddVertex(Vector2 vertex, Color color) { if (!began) throw new InvalidOperationException("Begin needs to be called before adding vertex."); if (primitiveType != PrimitiveType.LineStrip && primitiveType != PrimitiveType.TriangleStrip && bufferPosition + verticesPerPrimitive >= maxVertices && (bufferPosition % maxVertices == 0)) { Flush(); } vertices[bufferPosition] = new VertexPositionColor(new Vector3(vertex, 0), color); bufferPosition++; } /// /// Flushes the batch. Automatically called if required if using or . Otherwise, manual flushing is required. /// public void Flush() { if (!began) throw new InvalidOperationException("Begin needs to be called before flushing."); if (bufferPosition == 0) return; graphicsDevice.DrawUserPrimitives(primitiveType, vertices.ToArray(), 0, bufferPosition / verticesPerPrimitive); bufferPosition = 0; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Dispose(bool disposing) { if (disposing && !isDisposed) { basicEffect.Dispose(); isDisposed = true; } } } }