using System; using System.Drawing; using System.Numerics; using SlatedGameToolkit.Framework.Graphics.Meshes; using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Textures; using SlatedGameToolkit.Framework.Graphics.Shaders; using SlatedGameToolkit.Framework.Graphics.Programs; using SlatedGameToolkit.Framework.Exceptions; namespace SlatedGameToolkit.Framework.Graphics.Render { public class Batch : IDisposable { private int projLoc, viewLoc, modelLoc; private Camera camera; private GLShaderProgram shaderProgram; private Texture texture; private bool disposed; private VertexArray vertexArray; private const int VERTEX_LENGTH = 9; private GLMultiDrawElements multiDrawElements; public bool Batching { get; private set; } private Matrix4x4 modelsMatrix; private float[] data; private uint[] indices; private uint[] lengths; private uint[] offsets; private uint dataIndex, indicesIndex, lengthsIndex; public Batch(Camera camera, GLShaderProgram shaderProgram, uint BatchVertexSize = 4096) { multiDrawElements = OpenGL.RetrieveGLDelegate("glMultiDrawElements"); this.camera = camera; this.shaderProgram = shaderProgram; indices = new uint[BatchVertexSize]; lengths = new uint[BatchVertexSize]; offsets = new uint[BatchVertexSize]; data = new float[BatchVertexSize * VERTEX_LENGTH]; vertexArray = new VertexArray(); GLGetAttribLocation attribLocation = OpenGL.RetrieveGLDelegate("glGetAttribLocation"); VertexAttributeDefinition[] definitions = new VertexAttributeDefinition[3]; definitions[0] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aPosition"), 3, 3 * sizeof(float), 0); definitions[1] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aColor"), 4, 4 * sizeof(float), 3 * sizeof(float)); definitions[2] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aTexCoord"), 2, 2 * sizeof(float), (4 + 3) * sizeof(float)); modelLoc = shaderProgram.GetUniformLocation("models"); viewLoc = shaderProgram.GetUniformLocation("view"); projLoc = shaderProgram.GetUniformLocation("projection"); vertexArray.defineVertexAttributes(definitions: definitions); } public virtual void Begin(Texture texture, Matrix4x4 modelsMatrix) { if (Batching) throw new InvalidOperationException("This batch is already started."); this.texture = texture ?? throw new ArgumentNullException("texture"); this.Batching = true; this.modelsMatrix = modelsMatrix; } public virtual void Dispose() { if (disposed) return; if (Batching) End(); disposed = true; vertexArray.Dispose(); } public virtual void Add(IMeshable meshable, Color color) { ValueTuple[] vertices = meshable.Vertices; uint[] indices = meshable.Elements; if (vertices.Length * VERTEX_LENGTH + dataIndex >= data.Length || indices.Length + indicesIndex >= indicesIndex) Flush(); for (int i = 0; i < vertices.Length; i++) { data[dataIndex] = vertices[i].Item1.X; dataIndex++; data[dataIndex] = vertices[i].Item1.Y; dataIndex++; data[dataIndex] = vertices[i].Item1.Z; dataIndex++; data[dataIndex] = (float) color.A / byte.MaxValue; dataIndex++; data[dataIndex] = (float) color.R / byte.MaxValue; dataIndex++; data[dataIndex] = (float) color.G / byte.MaxValue; dataIndex++; data[dataIndex] = (float) color.B / byte.MaxValue; dataIndex++; data[dataIndex] = vertices[i].Item2.X; dataIndex++; data[dataIndex] = vertices[i].Item2.Y; dataIndex++; uint elementsCount = (uint)indices.Length; Array.Copy(indices, indices, elementsCount); indicesIndex += elementsCount; lengths[lengthsIndex] = elementsCount; lengthsIndex ++; } } public virtual void End() { if (!Batching) throw new InvalidOperationException("This batch was never started."); this.Batching = false; Flush(); } protected virtual void Flush() { texture.Use(); vertexArray.Use(); vertexArray.BufferVertices(data); vertexArray.BufferIndices(indices); dataIndex = 0; indicesIndex = 0; lengthsIndex = 0; shaderProgram.SetUniformMatrix4x4(modelLoc, modelsMatrix); shaderProgram.SetUniformMatrix4x4(viewLoc, camera.ViewMatrix); shaderProgram.SetUniformMatrix4x4(projLoc, camera.ProjectionMatrix); multiDrawElements(GLEnum.GL_TRIANGLE_STRIP, lengths, GLEnum.GL_UNSIGNED_INT, offsets, lengths.Length); } } }