namespace TheLabs; using LearnOpenTK.Common; using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; using System; public class Mesh : IDisposable { private readonly int _vao; private readonly int _vbo; private readonly int _ebo; private readonly int _vertexCount; private readonly bool _useIndices; public enum VertexLayout { PosColor, PosTex, PosTexNormal, PosTexNormalTangent } public Mesh(float[] vertices, uint[] indices, VertexLayout layout) { _vertexCount = indices.Length; _useIndices = true; _vao = GL.GenVertexArray(); GL.BindVertexArray(_vao); _vbo = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw); _ebo = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ebo); GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw); SetupAttributes(layout); // Refactored into a helper method to avoid duplication GL.BindVertexArray(0); } public Mesh(float[] vertices, VertexLayout layout) { // Calculate vertex count based on the layout size int strideCount = 0; if (layout == VertexLayout.PosColor) strideCount = 7; else if (layout == VertexLayout.PosTex) strideCount = 5; else if (layout == VertexLayout.PosTexNormal) strideCount = 8; _vertexCount = vertices.Length / strideCount; _useIndices = false; _ebo = 0; _vao = GL.GenVertexArray(); GL.BindVertexArray(_vao); _vbo = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw); SetupAttributes(layout); GL.BindVertexArray(0); } // Helper method to set up pointers (shared by both constructors) private void SetupAttributes(VertexLayout layout) { if (layout == VertexLayout.PosColor) { // Stride = 7 floats (3 Pos + 4 Color) var stride = 14 * sizeof(float); // Location 0: Position GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0); GL.EnableVertexAttribArray(0); // Location 1: Color GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float)); GL.EnableVertexAttribArray(1); GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float)); GL.EnableVertexAttribArray(2); GL.VertexAttribPointer(3, 3, VertexAttribPointerType.Float, false, stride, 8 * sizeof(float)); GL.EnableVertexAttribArray(3); // Loc 4: Bitangent (Offset 11) <-- NEW GL.VertexAttribPointer(4, 3, VertexAttribPointerType.Float, false, stride, 11 * sizeof(float)); GL.EnableVertexAttribArray(4); } else if (layout == VertexLayout.PosTex) { // Stride = 5 floats (3 Pos + 2 Tex) var stride = 5 * sizeof(float); // Location 0: Position GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0); GL.EnableVertexAttribArray(0); // Location 2: TexCoord (I used 2 to match the Normal layout below, but 1 works too if you change shader) // Let's keep it at 1 for now to break less code, but for lighting we usually move Tex to 2. // For now, let's assume your shader uses Location 2 for Texture if using Normals. GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float)); GL.EnableVertexAttribArray(2); } else if (layout == VertexLayout.PosTexNormal) { // Stride = 8 floats (3 Pos + 3 Normal + 2 Tex) var stride = 14 * sizeof(float); // Location 0: Position GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0); GL.EnableVertexAttribArray(0); // Location 1: Normal (Offset 3 floats) GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float)); GL.EnableVertexAttribArray(1); // Location 2: TexCoord (Offset 6 floats: 3 pos + 3 normal) GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 6 * sizeof(float)); GL.EnableVertexAttribArray(2); } else if (layout == VertexLayout.PosTexNormalTangent) { // --- NEW LOGIC FOR CUBES (Stride 14) --- var stride = 14 * sizeof(float); // Loc 0: Position GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0); GL.EnableVertexAttribArray(0); // Loc 1: Normal GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float)); GL.EnableVertexAttribArray(1); // Loc 2: TexCoord GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 6 * sizeof(float)); GL.EnableVertexAttribArray(2); // Loc 3: Tangent GL.VertexAttribPointer(3, 3, VertexAttribPointerType.Float, false, stride, 8 * sizeof(float)); GL.EnableVertexAttribArray(3); // Loc 4: Bitangent GL.VertexAttribPointer(4, 3, VertexAttribPointerType.Float, false, stride, 11 * sizeof(float)); GL.EnableVertexAttribArray(4); } } public void Draw() { GL.BindVertexArray(_vao); if (_useIndices) { GL.DrawElements(PrimitiveType.Triangles, _vertexCount, DrawElementsType.UnsignedInt, 0); } else { GL.DrawArrays(PrimitiveType.TriangleFan, 0, _vertexCount); } } public void Dispose() { GL.DeleteBuffer(_vbo); GL.DeleteBuffer(_ebo); GL.DeleteVertexArray(_vao); } }