diff --git a/Diamond/Buffers/VertexDataAttribute.cs b/Diamond/Buffers/VertexDataAttribute.cs index a12c109..dd45cef 100644 --- a/Diamond/Buffers/VertexDataAttribute.cs +++ b/Diamond/Buffers/VertexDataAttribute.cs @@ -2,11 +2,22 @@ namespace Diamond.Buffers { + /// + /// Marks a struct as vertex data that can be sent to a shader attribute + /// [AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] public sealed class VertexDataAttribute : Attribute { + /// + /// The pointer divisor for this type. A value of 0 indicates per-vertex, a value of 1+ + /// indicates every n instances + /// public int Divisor { get; set; } = 0; + /// + /// Mark a struct with information about how to iterate over vertex data of this type. + /// All fields of this struct must have [VertexPointer] + /// public VertexDataAttribute() { } diff --git a/Diamond/Buffers/VertexDataInfo.cs b/Diamond/Buffers/VertexDataInfo.cs new file mode 100644 index 0000000..8e7fd7d --- /dev/null +++ b/Diamond/Buffers/VertexDataInfo.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.InteropServices; +using Diamond.Shaders; +using OpenTK.Graphics.OpenGL4; + +namespace Diamond.Buffers +{ + /// + /// Get vertex pointer information about a struct to infer how to point shader attributes to it + /// + public class VertexDataInfo + { + /// + /// All shader attributes supported by this type + /// + public IReadOnlyCollection Pointers { get; } + + /// + /// The size of this type in bytes + /// + public int Stride { get; } + + /// + /// The pointer divisor for this type. A value of 0 indicates per-vertex, a value of 1+ + /// indicates every n instances + /// + public readonly int Divisor; + + /// + /// Create an info class with pre-computed values + /// + /// The type's supported attributes + /// The type's stride + /// The type's pointer divisor + private VertexDataInfo(IList pointers, int stride, int divisor) + { + Pointers = new ReadOnlyCollection(pointers); + Stride = stride; + Divisor = divisor; + } + + /// + /// Enable the attributes associated with this type on Program.Current + /// + public void EnableVertexPointers() + { + if (Program.Current == null) + throw new InvalidOperationException("Cant render a mesh with no active shader."); + + foreach (var attr in Pointers) + { + var loc = Program.Current.AttributeLocation(attr.Name); + if (!loc.HasValue) + continue; + GL.EnableVertexAttribArray((int) loc); + GL.VertexAttribDivisor((int) loc, Divisor); + } + } + + /// + /// Disable the attributes associated with this type on Program.Current + /// + public void DisableVertexPointers() + { + if (Program.Current == null) + throw new InvalidOperationException("Cant render a mesh with no active shader."); + + foreach (var attr in Pointers) + { + var loc = Program.Current.AttributeLocation(attr.Name); + if (!loc.HasValue) + continue; + GL.DisableVertexAttribArray((int) loc); + } + } + + /// + /// A cache of already computed information to prevent redundant reflection calls + /// + private static readonly Dictionary attribCache = + new Dictionary(); + + /// + /// Get the VertexDataInfo for a particular type + /// + /// The type to analyse + /// The VertexDataInfo for the type, or null if the type is not supported + public static VertexDataInfo GetInfo() where T : struct + { + if (attribCache.ContainsKey(typeof(T))) return attribCache[typeof(T)]; + + var vertexDataAttributes = typeof(T).GetCustomAttributes(typeof(VertexDataAttribute), false); + + // the type must have [VertexData] + if (vertexDataAttributes.Length != 1) + return null; + + 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); + + // all fields must have [VertexPointer] + if (attrs.Length == 0) + return null; + + 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/Buffers/VertexPointerAttribute.cs b/Diamond/Buffers/VertexPointerAttribute.cs index 53b7dd3..04ce453 100644 --- a/Diamond/Buffers/VertexPointerAttribute.cs +++ b/Diamond/Buffers/VertexPointerAttribute.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Runtime.InteropServices; -using Diamond.Shaders; using OpenTK.Graphics.OpenGL4; namespace Diamond.Buffers @@ -40,91 +36,19 @@ namespace Diamond.Buffers /// The offset of this attribute within each element /// Corresponds to the offset parameter to glVertexAttribPointer /// - public int Offset { get; set; } = 0; + // todo this, and other values, should be moved into a different type for use with VertexDataInfo. + // this class should just mark fields with their use - other types should manage binding those fields + public int Offset { get; internal set; } = 0; + /// + /// Mark a field with information about how to point a shader attribute to it + /// + /// The name of the attribute to point to this + /// The number of elements to read from this field public VertexPointerAttribute(string name, int size) { Name = name; 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 InvalidOperationException("Cant render a mesh with no active shader."); - - foreach (var attr in Pointers) - { - var loc = Program.Current.AttributeLocation(attr.Name); - if (!loc.HasValue) - continue; - GL.EnableVertexAttribArray((int) loc); - GL.VertexAttribDivisor((int) loc, Divisor); - } - } - - public void DisableVertexPointers() - { - if (Program.Current == null) - throw new InvalidOperationException("Cant render a mesh with no active shader."); - - foreach (var attr in Pointers) - { - var loc = Program.Current.AttributeLocation(attr.Name); - if (!loc.HasValue) - continue; - GL.DisableVertexAttribArray((int) loc); - } - } - - private static readonly 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) - return null; - - 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/Diamond.csproj b/Diamond/Diamond.csproj index a02a020..cfa8083 100644 --- a/Diamond/Diamond.csproj +++ b/Diamond/Diamond.csproj @@ -52,6 +52,7 @@ +