From 73e70c3a603e2b80fa756c1d618172a63d86a426 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Mon, 27 Feb 2017 02:22:54 -0500 Subject: [PATCH] Added Mesh class, removed attrib pointer management from Program, shifted to mesh. MUST use Program.UseDefault instead of GL.Use(0) to prevent crash, but meshes can't render with it anyway. --- Diamond/Buffers/GLBuffer.cs | 4 +- Diamond/Buffers/SubArray.cs | 76 ++++++++++--------- Diamond/Buffers/VertexDataAttribute.cs | 2 + Diamond/Buffers/VertexPointerAttribute.cs | 91 +++++++++++++++++++++-- Diamond/Shaders/Program.cs | 61 ++++++--------- hexworld/HexRender.cs | 48 +++++------- hexworld/Mesh.cs | 78 +++++++++++++++++++ hexworld/Tile.cs | 4 +- hexworld/hexworld.csproj | 1 + 9 files changed, 252 insertions(+), 113 deletions(-) create mode 100644 hexworld/Mesh.cs diff --git a/Diamond/Buffers/GLBuffer.cs b/Diamond/Buffers/GLBuffer.cs index dfad2e8..d0de5d1 100644 --- a/Diamond/Buffers/GLBuffer.cs +++ b/Diamond/Buffers/GLBuffer.cs @@ -8,7 +8,7 @@ using OpenTK.Graphics.OpenGL4; namespace Diamond.Buffers { - public class GLBuffer : GLObject + public class GLBuffer : GLObject where T : struct { public readonly BufferTarget Target; public readonly BufferUsageHint Usage; @@ -27,7 +27,7 @@ namespace Diamond.Buffers protected override void Delete() => GL.DeleteBuffer(Id); - public void Data(T[] data) where T : struct + public void Data(T[] data) { var size = Marshal.SizeOf(); Bind(); diff --git a/Diamond/Buffers/SubArray.cs b/Diamond/Buffers/SubArray.cs index 351bf00..05228b8 100644 --- a/Diamond/Buffers/SubArray.cs +++ b/Diamond/Buffers/SubArray.cs @@ -45,42 +45,6 @@ namespace Diamond.Buffers yield return Array[Offset + i]; } - public static T1[] Join(params SubArray[] subArrays) => Join((IEnumerable>) subArrays); - - public static T1[] Join(IEnumerable> subArrays) - { - HashSet uniqueArrays = new HashSet(); - foreach (var subArray in subArrays) - { - uniqueArrays.Add(subArray.Array); - } - - if (uniqueArrays.Count == 0) return new T1[0]; - if (uniqueArrays.Count == 1) return uniqueArrays.ToArray()[0]; - - var length = 0; - var offsets = new Dictionary(); - foreach (var uniqueArray in uniqueArrays) - { - offsets[uniqueArray] = length; - length += uniqueArray.Length; - } - - var array = new T1[length]; - foreach (var uniqueArray in uniqueArrays) - { - System.Array.ConstrainedCopy(uniqueArray, 0, array, offsets[uniqueArray], uniqueArray.Length); - } - - foreach (var subArray in subArrays) - { - subArray.Offset = offsets[subArray.Array]; - subArray.Array = array; - } - - return array; - } - public T[] ToArray() { var arr = new T[Length]; @@ -96,4 +60,44 @@ namespace Diamond.Buffers return $"[{string.Join(", ", this)}]"; } } + + public static class SubArray + { + public static T[] Join(params SubArray[] subArrays) => Join((IEnumerable>)subArrays); + + public static T[] Join(IEnumerable> subArrays) + { + HashSet uniqueArrays = new HashSet(); + foreach (var subArray in subArrays) + { + uniqueArrays.Add(subArray.Array); + } + + if (uniqueArrays.Count == 0) return new T[0]; + if (uniqueArrays.Count == 1) return uniqueArrays.ToArray()[0]; + + var length = 0; + var offsets = new Dictionary(); + foreach (var uniqueArray in uniqueArrays) + { + offsets[uniqueArray] = length; + length += uniqueArray.Length; + } + + var array = new T[length]; + foreach (var uniqueArray in uniqueArrays) + { + System.Array.ConstrainedCopy(uniqueArray, 0, array, offsets[uniqueArray], uniqueArray.Length); + } + + foreach (var subArray in subArrays) + { + subArray.Offset = offsets[subArray.Array]; + subArray.Array = array; + } + + return array; + } + + } } \ No newline at end of file diff --git a/Diamond/Buffers/VertexDataAttribute.cs b/Diamond/Buffers/VertexDataAttribute.cs index 98cb70f..de0d010 100644 --- a/Diamond/Buffers/VertexDataAttribute.cs +++ b/Diamond/Buffers/VertexDataAttribute.cs @@ -9,6 +9,8 @@ namespace Diamond.Buffers [AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] public sealed class VertexDataAttribute : Attribute { + public int Divisor { get; set; } = 0; + public VertexDataAttribute() { } diff --git a/Diamond/Buffers/VertexPointerAttribute.cs b/Diamond/Buffers/VertexPointerAttribute.cs index f588634..4b56d7e 100644 --- a/Diamond/Buffers/VertexPointerAttribute.cs +++ b/Diamond/Buffers/VertexPointerAttribute.cs @@ -1,4 +1,8 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using Diamond.Shaders; using OpenTK.Graphics.OpenGL4; namespace Diamond.Buffers @@ -32,12 +36,6 @@ namespace Diamond.Buffers /// public bool Normalized { get; set; } = false; - /// - /// The divisor to Instanced draw calls - /// Corresponds to the divisor parameter to glVertexAttribDivisor - /// - public int Divisor { get; set; } = 0; - /// /// The offset of this attribute within each element /// Corresponds to the offset parameter to glVertexAttribPointer @@ -50,4 +48,85 @@ namespace Diamond.Buffers Size = size; } } + + public class VertexDataInfo + { + public readonly IReadOnlyCollection Pointers; + public readonly int Stride; + public readonly int Divisor; + + private VertexDataInfo(IList pointers, int stride, int divisor) + { + Pointers = new ReadOnlyCollection(pointers); + Stride = stride; + Divisor = divisor; + } + + public void EnableVertexPointers() + { + if (Program.Current == null) + { + throw new Exception("Cant render a mesh with no active shader."); // todo make an exception here. + } + + foreach (var attr in Pointers) + { + if (!Program.Current.TryGetAttribute(attr.Name, out int loc)) continue; + GL.EnableVertexAttribArray(loc); + GL.VertexAttribDivisor(loc, Divisor); + } + } + + public void DisableVertexPointers() + { + if (Program.Current == null) + { + throw new Exception("Cant render a mesh with no active shader."); // todo make an exception here. + } + + foreach (var attr in Pointers) + { + if (!Program.Current.TryGetAttribute(attr.Name, out int loc)) continue; + GL.DisableVertexAttribArray(loc); + } + } + + private static Dictionary attribCache = + new Dictionary(); + + public static VertexDataInfo GetInfo() where T : struct + { + if (attribCache.ContainsKey(typeof(T))) return attribCache[typeof(T)]; + + var vertexDataAttributes = typeof(T).GetCustomAttributes(typeof(VertexDataAttribute), false); + + if (vertexDataAttributes.Length != 1) + { + throw new Exception("Can't use type {typeof(T)} as mesh data."); // todo make an exception here. + } + + var vertdataattrib = (VertexDataAttribute) vertexDataAttributes[0]; + var divisor = vertdataattrib.Divisor; + + + var attribList = new List(); + var stride = Marshal.SizeOf(); + + foreach (var fieldInfo in typeof(T).GetFields()) + { + var attrs = fieldInfo.GetCustomAttributes(typeof(VertexPointerAttribute), false); + if (attrs.Length == 0) continue; + + var offset = (int) Marshal.OffsetOf(fieldInfo.Name); + foreach (var attr in attrs) + { + var vpa = (VertexPointerAttribute) attr; + vpa.Offset = offset; + attribList.Add(vpa); + } + } + + return new VertexDataInfo(attribList, stride, divisor); + } + } } \ No newline at end of file diff --git a/Diamond/Shaders/Program.cs b/Diamond/Shaders/Program.cs index 7298774..92f0831 100644 --- a/Diamond/Shaders/Program.cs +++ b/Diamond/Shaders/Program.cs @@ -9,6 +9,8 @@ namespace Diamond.Shaders { public class Program : GLObject { + public static Program Current { get; private set; } + private Dictionary uniforms = new Dictionary(); private Dictionary attributes = new Dictionary(); @@ -84,57 +86,42 @@ namespace Diamond.Shaders return id; } - public void SetAttribPointers(GLBuffer buff, Type vertexType) + public void SetAttribPointers(GLBuffer buff) where T : struct { - if (vertexType.GetCustomAttributes(typeof(VertexDataAttribute), false).Length == 0) - { - throw new ShaderException($"Cannot attach buffer {buff} to program {this}" + - $" with vertex {vertexType} because it has no" + - $" VertexData attribute."); - } - - var attribList = new List(); - var Stride = Marshal.SizeOf(vertexType); - - foreach (var fieldInfo in vertexType.GetFields()) - { - var attrs = fieldInfo.GetCustomAttributes(typeof(VertexPointerAttribute), false); - if (attrs.Length == 0) continue; - - var offset = (int) Marshal.OffsetOf(vertexType, fieldInfo.Name); - foreach (var attr in attrs) - { - var vpa = (VertexPointerAttribute) attr; - vpa.Offset = offset; - attribList.Add(vpa); - } - } + var vdi = VertexDataInfo.GetInfo(); buff.Bind(); - foreach (var attr in attribList) + foreach (var attr in vdi.Pointers) { if (!TryGetAttribute(attr.Name, out int loc)) continue; - GL.VertexAttribPointer(loc, attr.Size, attr.Type, attr.Normalized, Stride, attr.Offset); - GL.VertexAttribDivisor(loc, attr.Divisor); + GL.VertexAttribPointer(loc, attr.Size, attr.Type, attr.Normalized, vdi.Stride, attr.Offset); } } - public void EnableAllAttribArrays() +// public void EnableAllAttribArrays() +// { +// foreach (var loc in attributes.Values) +// GL.EnableVertexAttribArray(loc); +// } +// +// public void DisableAllAttribArrays() +// { +// foreach (var loc in attributes.Values) +// GL.DisableVertexAttribArray(loc); +// } + + public void Use() { - foreach (var loc in attributes.Values) - GL.EnableVertexAttribArray(loc); + GL.UseProgram(Id); + Current = this; } - public void DisableAllAttribArrays() + public static void UseDefault() { - foreach (var loc in attributes.Values) - GL.DisableVertexAttribArray(loc); + GL.UseProgram(0); + Current = null; } - public void Use() => GL.UseProgram(Id); - - public static void UseDefault() => GL.UseProgram(0); - public static Program FromShaders(params Shader[] shaders) { var p = new Program(); diff --git a/hexworld/HexRender.cs b/hexworld/HexRender.cs index fd74eda..0709a42 100644 --- a/hexworld/HexRender.cs +++ b/hexworld/HexRender.cs @@ -24,8 +24,8 @@ namespace hexworld private Texture _stone; private Texture _gray; - private GLBuffer _tileGLBuffer; - private GLBuffer _vertexGLBuffer; + private GLBuffer _tileGLBuffer; + private GLBuffer _vertexGLBuffer; protected override void OnClosed(EventArgs e) { @@ -33,7 +33,7 @@ namespace hexworld _pgm.Dispose(); -// _tileGLBuffer.Dispose(); + _tileGLBuffer.Dispose(); _vertexGLBuffer.Dispose(); _grass.Dispose(); @@ -50,9 +50,9 @@ namespace hexworld private SubArray _stoneTiles; private SubArray _grayTiles; - private SubArray _cubeVertices; - private SubArray _panelVertices; - private SubArray _sidesVertices; + private Mesh _cubeMesh; + private Mesh _panelMesh; + private Mesh _sidesMesh; private Tile[] _allTiles; private Vertex[] _allVertices; @@ -100,12 +100,9 @@ namespace hexworld } } - _cubeVertices = new SubArray( - JsonConvert.DeserializeObject(File.ReadAllText(@"res\data_vert_cubes.json"))); - _panelVertices = new SubArray( - JsonConvert.DeserializeObject(File.ReadAllText(@"res\data_vert_panels.json"))); - _sidesVertices = new SubArray( - JsonConvert.DeserializeObject(File.ReadAllText(@"res\data_vert_sides.json"))); + _cubeMesh = Mesh.FromJson(File.ReadAllText(@"res\data_vert_cubes.json")); + _panelMesh = Mesh.FromJson(File.ReadAllText(@"res\data_vert_panels.json")); + _sidesMesh = Mesh.FromJson(File.ReadAllText(@"res\data_vert_sides.json")); _grassTiles = new SubArray( JsonConvert.DeserializeObject(File.ReadAllText(@"res\data_tile_grass.json"))); @@ -114,17 +111,17 @@ namespace hexworld _grayTiles = new SubArray( JsonConvert.DeserializeObject(File.ReadAllText(@"res\data_tile_gray.json"))); - _allTiles = SubArray.Join(_stoneTiles, _grassTiles, _grayTiles); - _allVertices = SubArray.Join(_panelVertices, _cubeVertices, _sidesVertices); + _allTiles = SubArray.Join(_stoneTiles, _grassTiles, _grayTiles); + _allVertices = Mesh.Join(_panelMesh, _cubeMesh, _sidesMesh); - _tileGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw); + _tileGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw); _tileGLBuffer.Data(_allTiles); - _vertexGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw); + _vertexGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw); _vertexGLBuffer.Data(_allVertices); - _pgm.SetAttribPointers(_tileGLBuffer, typeof(Tile)); - _pgm.SetAttribPointers(_vertexGLBuffer, typeof(Vertex)); + _pgm.SetAttribPointers(_tileGLBuffer); + _pgm.SetAttribPointers(_vertexGLBuffer); _grass = Texture.FromBitmap(new Bitmap(@"res\grass.png")); _stone = Texture.FromBitmap(new Bitmap(@"res\stone.png")); @@ -172,7 +169,6 @@ namespace hexworld GL.CullFace(CullFaceMode.Back); _pgm.Use(); - _pgm.EnableAllAttribArrays(); _grass.Bind(0); _stone.Bind(1); @@ -182,27 +178,19 @@ namespace hexworld GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view); GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj); - GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles, - _cubeVertices.Offset, _cubeVertices.Length, - _grassTiles.Length, _grassTiles.Offset); + _cubeMesh.DrawInstanced(_grassTiles); GL.Uniform1(_pgm.GetUniform("tex"), 1); GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view); GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj); - GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles, - _panelVertices.Offset, _panelVertices.Length, - _stoneTiles.Length, _stoneTiles.Offset); + _panelMesh.DrawInstanced(_stoneTiles); GL.Uniform1(_pgm.GetUniform("tex"), 2); GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view); GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj); - GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles, - _sidesVertices.Offset, _sidesVertices.Length, - _grayTiles.Length, _grayTiles.Offset); - - _pgm.DisableAllAttribArrays(); + _sidesMesh.DrawInstanced(_grayTiles); SwapBuffers(); } diff --git a/hexworld/Mesh.cs b/hexworld/Mesh.cs new file mode 100644 index 0000000..5459da9 --- /dev/null +++ b/hexworld/Mesh.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using System.Threading.Tasks; +using Diamond.Buffers; +using Diamond.Shaders; +using Newtonsoft.Json; +using OpenTK.Graphics.OpenGL4; + +namespace hexworld +{ + public class Mesh where T : struct + { + public SubArray Vertices; + public PrimitiveType Primitive; + + private static VertexDataInfo tVdi; + private static List attribs; + + static Mesh() + { + tVdi = VertexDataInfo.GetInfo(); + } + + public Mesh(T[] vertices, PrimitiveType primitive = PrimitiveType.Triangles) + : this(new SubArray(vertices), primitive) + { + } + + public Mesh(SubArray vertices, PrimitiveType primitive = PrimitiveType.Triangles) + { + Vertices = vertices; + Primitive = primitive; + } + + public void Draw() + { + tVdi.EnableVertexPointers(); + + GL.DrawArrays(Primitive, Vertices.Offset, Vertices.Length); + + tVdi.DisableVertexPointers(); + } + + public void DrawInstanced(SubArray instanceArray) where TI : struct + { + var tiVdi = VertexDataInfo.GetInfo(); + + tVdi.EnableVertexPointers(); + tiVdi.EnableVertexPointers(); + + GL.DrawArraysInstancedBaseInstance(Primitive, Vertices.Offset, Vertices.Length, instanceArray.Length, + instanceArray.Offset); + + tVdi.DisableVertexPointers(); + tiVdi.DisableVertexPointers(); + } + } + + public static class Mesh + { + public static Mesh FromJson(string json) where T : struct + { + var vertices = JsonConvert.DeserializeObject(json); + return new Mesh(vertices); + } + + + public static T[] Join(params Mesh[] meshes) where T : struct => Join((IEnumerable>) meshes); + + public static T[] Join(IEnumerable> meshes) where T : struct + { + return SubArray.Join(meshes.Select(x => x.Vertices)); + } + } +} \ No newline at end of file diff --git a/hexworld/Tile.cs b/hexworld/Tile.cs index 28cd83a..e8f2619 100644 --- a/hexworld/Tile.cs +++ b/hexworld/Tile.cs @@ -4,11 +4,11 @@ using OpenTK; namespace hexworld { - [VertexData] + [VertexData(Divisor = 1)] public struct Tile { [JsonProperty("pos")] - [VertexPointer("glbpos", 3, Divisor = 1)] + [VertexPointer("glbpos", 3)] public Vector3 Position; public Tile(Vector3 position) diff --git a/hexworld/hexworld.csproj b/hexworld/hexworld.csproj index 9bf43e8..2d33d93 100644 --- a/hexworld/hexworld.csproj +++ b/hexworld/hexworld.csproj @@ -51,6 +51,7 @@ +