using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using RecrownedAthenaeum.Camera; using System; namespace RecrownedAthenaeum.Render { /// /// Renders rectangles using the . /// public class RectangleRenderer : IDisposable { /// /// The used. /// public readonly PrimitiveBatch primitiveBatch; private bool disposed; private bool began; private bool filling; /// /// Creates a rectangle renderer with the given . /// /// public RectangleRenderer(PrimitiveBatch primitiveBatch) { this.primitiveBatch = primitiveBatch; } /// /// Creates a rectangle renderer. /// /// The graphics device used to create the /// The camera containing the matrix to be used for the transformations. public RectangleRenderer(GraphicsDevice graphicsDevice, Camera2D camera) : this(new PrimitiveBatch(graphicsDevice, camera, 4)) { } /// /// Begins the render batch. /// /// Whether or not to fill the rectangle. public void Begin(bool filled) { filling = filled; if (disposed) throw new ObjectDisposedException(GetType().Name); if (began) throw new InvalidOperationException("Cannot begin twice."); primitiveBatch.Begin(filled ? PrimitiveType.TriangleStrip : PrimitiveType.LineStrip); began = true; } /// /// Ends the batch. /// public void End() { if (disposed) throw new ObjectDisposedException(GetType().Name); if (!began) throw new InvalidOperationException("Cannot end before beginning."); primitiveBatch.End(); began = false; } /// /// Draws a basic rectangle given bottom left and top right. /// /// X coordinate of bottom left. /// Y coordinate of bottom left. /// Width of rectangle. /// Height of rectangle. /// Color of all vertices of this rectangle. /// Rotation of rectangle. Default is 0 radians. public void DrawRectangle(int x, int y, int width, int height, Color color, double rotation = 0) { if (disposed) throw new ObjectDisposedException(GetType().Name); if (!began) throw new InvalidOperationException("Renderer must be started by calling Begin."); Vector2[] corners = new Vector2[4]; corners[0] = new Vector2(x, y); corners[1] = new Vector2(x + (float)Math.Cos(rotation) * width, y + (float)Math.Sin(rotation) * width); double topRightAngleFromOrig = Math.Atan(height / (double)width); float origDiagonalHypotenuse = (float)Math.Sqrt(width * width * height * height); corners[2] = new Vector2(x + (float)Math.Cos(topRightAngleFromOrig + rotation) * origDiagonalHypotenuse, y + (float)Math.Sin(topRightAngleFromOrig + rotation) * origDiagonalHypotenuse); corners[3] = new Vector2(x + (float)Math.Cos(rotation + Math.PI / 4f) * height, y + (float)Math.Sin(rotation) * height); if (filling) { primitiveBatch.AddVertex(corners[1], color); primitiveBatch.AddVertex(corners[2], color); primitiveBatch.AddVertex(corners[0], color); primitiveBatch.AddVertex(corners[3], color); } else { primitiveBatch.AddVertex(corners[0], color); primitiveBatch.AddVertex(corners[1], color); primitiveBatch.AddVertex(corners[2], color); primitiveBatch.AddVertex(corners[3], color); } } /// /// Attempts to dispose of this object. /// public void Dispose() { if (disposed) throw new ObjectDisposedException(typeof(PrimitiveBatch).Name); Dispose(true); GC.SuppressFinalize(this); } /// /// An overridable of disposable. /// /// Only true when called by user code dispose. Destructor calling this results in false. public virtual void Dispose(bool disposing) { disposed = true; if (!disposed && disposing) { disposed = true; if (primitiveBatch != null) { primitiveBatch.Dispose(); } } } /// /// Destructor. /// ~RectangleRenderer () { Dispose(false); } } }