diff --git a/Diamond/Attributes/VertexAttribAttribute.cs b/Diamond/Attributes/VertexAttribAttribute.cs
new file mode 100644
index 0000000..697608c
--- /dev/null
+++ b/Diamond/Attributes/VertexAttribAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+using OpenTK.Graphics.OpenGL4;
+
+namespace Diamond.Attributes
+{
+ ///
+ /// Use this field as a source for vertex attribute data
+ ///
+ [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
+ public sealed class VertexAttribAttribute : Attribute
+ {
+ ///
+ /// The attribute ID to send this value
+ ///
+ public int Attribute { get; }
+
+ ///
+ /// The number of elements for this attribute
+ ///
+ public int Size { get; }
+
+ ///
+ /// Whether this attribute should be normalized
+ ///
+ public bool Normalized { get; set; } = false;
+
+ ///
+ /// The eleemnt type for this attribute
+ ///
+ public VertexAttribPointerType Type { get; set; } = VertexAttribPointerType.Float;
+
+ ///
+ /// Mark a field as a source for a vertex attribute
+ ///
+ /// The attribute ID for this value
+ /// The number of elements for this attribute
+ public VertexAttribAttribute(int attribute, int size)
+ {
+ Attribute = attribute;
+ Size = size;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Diamond/Attributes/VertexDataAttribute.cs b/Diamond/Attributes/VertexDataAttribute.cs
new file mode 100644
index 0000000..589acd2
--- /dev/null
+++ b/Diamond/Attributes/VertexDataAttribute.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Diamond.Attributes
+{
+ ///
+ /// Use this struct's contents as vertex attribute information
+ ///
+ [AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
+ public sealed class VertexDataAttribute : Attribute
+ {
+ ///
+ /// The VertexAttribDivisor for all attribs associated with this struct
+ ///
+ public int Divisor { get; }
+
+ ///
+ /// Mark a struct as a source for a VAO's attrib pointer information
+ ///
+ ///
+ public VertexDataAttribute(int divisor = 0)
+ {
+ Divisor = divisor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Diamond/Diamond.csproj b/Diamond/Diamond.csproj
index 0e47752..f616b86 100644
--- a/Diamond/Diamond.csproj
+++ b/Diamond/Diamond.csproj
@@ -52,6 +52,8 @@
+
+
@@ -59,6 +61,7 @@
+
diff --git a/Diamond/NLog.config b/Diamond/NLog.config
index 65bd082..b68bf14 100644
--- a/Diamond/NLog.config
+++ b/Diamond/NLog.config
@@ -15,6 +15,11 @@
fileName="${basedir}/logs/${date:format=yyyMMdd_HHmmss:cached=true}.log"
layout="${fileHeader}: ${message}" />
+
+
diff --git a/Diamond/Shaders/Program.cs b/Diamond/Shaders/Program.cs
index fc8b027..7df7a9c 100644
--- a/Diamond/Shaders/Program.cs
+++ b/Diamond/Shaders/Program.cs
@@ -28,8 +28,9 @@ namespace Diamond.Shaders
}
///
- /// Use this program. If program is null, use the default program.
+ /// Use a program.
///
+ /// The program to use, or null to use the default program
public static void Use(Program program)
{
if (program != null && !program.Linked)
@@ -158,7 +159,7 @@ namespace Diamond.Shaders
///
public void Use() => Use(this);
- #region Attribute Locations
+ #region Locations
private readonly Dictionary _attributes = new Dictionary();
private readonly Dictionary _uniforms = new Dictionary();
@@ -257,7 +258,9 @@ namespace Diamond.Shaders
var program = new Program();
foreach (var shader in shaders)
+ {
program.Attach(shader);
+ }
program.Link();
diff --git a/Diamond/VertexArray.cs b/Diamond/VertexArray.cs
new file mode 100644
index 0000000..9caddd3
--- /dev/null
+++ b/Diamond/VertexArray.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Diamond.Attributes;
+using Diamond.Shaders;
+using NLog;
+using OpenTK.Graphics.OpenGL4;
+
+namespace Diamond
+{
+ ///
+ /// Wrap an OpenGL Vertex Array Object
+ ///
+ public sealed class VertexArray : GLObject
+ {
+ private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+
+ #region Static
+
+ private static VertexArray _current;
+
+ ///
+ /// The currently bound VAO, or null if no vao is bound.
+ ///
+ public static VertexArray Current
+ {
+ get => _current;
+ set => Bind(value);
+ }
+
+ ///
+ /// Bind a VAO to the context
+ ///
+ /// The vao to bind, or null to unbind the current vao
+ public static void Bind(VertexArray vao)
+ {
+ GL.BindVertexArray(vao?.Id ?? 0);
+ Logger.Trace("Bound {0}", (object) vao ?? "default vao");
+
+ _current = vao;
+ }
+
+ #endregion
+
+ #region ctor, Delete()
+
+ ///
+ internal VertexArray()
+ : base(GL.GenVertexArray())
+ {
+ Logger.Debug("Created {0}", this);
+ }
+
+ ///
+ protected override void Delete()
+ {
+ Logger.Debug("Disposing {0}", this);
+ GL.DeleteVertexArray(Id);
+ }
+
+ #endregion
+
+ #region Properties
+
+ #region Queries
+
+ #endregion
+
+ #region Stored
+
+ private Buffer _elementArrayBuffer;
+
+ ///
+ /// The ElementArrayBuffer associated with this VAO
+ ///
+ public Buffer ElementArrayBuffer
+ {
+ get => _elementArrayBuffer;
+ set
+ {
+ Current = this;
+ Buffer.ElementArrayBuffer = _elementArrayBuffer = value;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Attach a VBO to this VAO. Use attributes to infer the
+ /// vertex attrib pointer information
+ ///
+ /// The struct to use for vertex attrib information. Must be marked with VertexDataAttribute
+ /// The buffer to add
+ public void Attach(Buffer buffer) where T : struct
+ {
+ var vda = (VertexDataAttribute) typeof(T)
+ .GetCustomAttributes(typeof(VertexDataAttribute), false)
+ .FirstOrDefault();
+
+ if (vda == null)
+ {
+ Logger.Error("Cannot attach buffer {0} to {1}, {2} is missing VertexDataAttribute", buffer, this,
+ typeof(T).Name);
+ return;
+ }
+
+ Current = this;
+ Buffer.ArrayBuffer = buffer;
+
+ var stride = Marshal.SizeOf();
+ foreach (var field in typeof(T).GetFields())
+ {
+ Logger.Debug("Analyzing {0}", field.Name);
+ var offset = Marshal.OffsetOf(field.Name);
+ foreach (var vai in field.GetCustomAttributes(false))
+ {
+ Logger.Debug(
+ $"Enabling {vai.Attribute}, {vai.Size}, {vai.Type}, {vai.Normalized}, {stride}, {offset}");
+
+ GL.EnableVertexAttribArray(vai.Attribute);
+ GL.VertexAttribPointer(vai.Attribute, vai.Size, vai.Type, vai.Normalized, stride, offset);
+ }
+ }
+ }
+
+ ///
+ /// Bind this vao
+ ///
+ public void Bind() => Bind(this);
+
+ #endregion
+
+ #region Factory Methods
+
+ ///
+ /// Create an empty vertex array object. This contains no binding information
+ /// by default, and must have any vertex buffer objects attached manually.
+ ///
+ /// A new Vertex Array Object, or null if creation failed
+ public static VertexArray Create()
+ {
+ return new VertexArray();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/hexworld/HexRender.cs b/hexworld/HexRender.cs
index 303a29e..cfa669b 100644
--- a/hexworld/HexRender.cs
+++ b/hexworld/HexRender.cs
@@ -5,10 +5,23 @@ using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
using Diamond;
+using Diamond.Attributes;
using Buffer = Diamond.Buffer;
namespace hexworld
{
+ [VertexData()]
+ public struct Vert
+ {
+ [VertexAttrib(0, 2)]
+ public Vector2 Position;
+
+ public Vert(float x, float y)
+ {
+ Position = new Vector2(x, y);
+ }
+ }
+
public class HexRender : GameWindow
{
public HexRender(int width, int height)
@@ -20,12 +33,12 @@ namespace hexworld
Y = (DisplayDevice.Default.Height - Height) / 2;
}
- private int _triVao;
- private Buffer _triVbo;
+ private VertexArray _triVao;
+ private Buffer _triVbo;
private Program _whitePgm;
- private int _recVao;
- private Buffer _recVbo;
+ private VertexArray _recVao;
+ private Buffer _recVbo;
private Program _redPgm;
///
@@ -39,36 +52,30 @@ namespace hexworld
{
_whitePgm = Program.FromShaders(vs, red);
_redPgm = Program.FromShaders(vs, white);
-
- _triVbo = Buffer.FromData(new float[]
- {
- -.8f, -.8f,
- +.8f, -.8f,
- +.0f, +.8f
- });
- _triVao = GL.GenVertexArray();
- GL.BindVertexArray(_triVao);
- Program.Current = _redPgm;
- Buffer.ArrayBuffer = _triVbo;
- GL.EnableVertexAttribArray(0);
- GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
-
- _recVbo = Buffer.FromData(new float[]
- {
- -.9f, -.5f,
- +.9f, -.5f,
- +.9f, +.5f,
- +.9f, +.5f,
- -.9f, +.5f,
- -.9f, -.5f,
- });
- _recVao = GL.GenVertexArray();
- GL.BindVertexArray(_recVao);
- Program.Current = _whitePgm;
- Buffer.ArrayBuffer = _recVbo;
- GL.EnableVertexAttribArray(0);
- GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0);
}
+
+ _triVbo = Buffer.FromData(new[]
+ {
+ new Vert(-.8f, -.8f),
+ new Vert(+.8f, -.8f),
+ new Vert(+.0f, +.8f)
+ });
+ Program.Current = _redPgm;
+ _triVao = VertexArray.Create();
+ _triVao.Attach(_triVbo);
+
+ _recVbo = Buffer.FromData(new[]
+ {
+ new Vert(-.9f, -.5f),
+ new Vert(+.9f, -.5f),
+ new Vert(+.9f, +.5f),
+ new Vert(+.9f, +.5f),
+ new Vert(-.9f, +.5f),
+ new Vert(-.9f, -.5f)
+ });
+ Program.Current = _whitePgm;
+ _recVao = VertexArray.Create();
+ _recVao.Attach(_recVbo);
}
///
@@ -93,11 +100,11 @@ namespace hexworld
GL.Clear(ClearBufferMask.ColorBufferBit);
Program.Current = _redPgm;
- GL.BindVertexArray(_triVao);
+ VertexArray.Current = _triVao;
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
Program.Current = _whitePgm;
- GL.BindVertexArray(_recVao);
+ VertexArray.Current = _recVao;
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
SwapBuffers();