Removed most files, began rewriting program, shader, globject

This commit is contained in:
2017-03-08 23:45:37 -05:00
parent 9f64d7f2cd
commit ffc525a0fd
20 changed files with 392 additions and 1665 deletions

View File

@@ -1,161 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Diamond.Shaders;
using Diamond.Util;
using Diamond.Wrappers;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Buffers
{
/// <summary>
/// Manages an OpenGL Buffer object
/// </summary>
/// <typeparam name="T">The type of data used for this buffer</typeparam>
public class Buffer<T> : GLObject where T : struct
{
internal readonly BufferWrap Wrapper;
private readonly VertexDataInfo _vdi;
/// <summary>
/// The target for this buffer; its type
/// </summary>
public BufferTarget Target => Wrapper.Target;
/// <summary>
/// The usage hint for this buffer. Use StaticDraw for one-time uploads to
/// vertex buffers, and DynamicDraw for repeated uploads to vertex buffers.
/// </summary>
public BufferUsageHint Usage
{
get => Wrapper.Usage;
set => Wrapper.Usage = value;
}
internal Buffer(BufferWrap wrapper, string name)
{
Wrapper = wrapper;
Name = name;
_vdi = VertexDataInfo.GetInfo<T>();
}
/// <summary>
/// Upload data to this buffer
/// </summary>
/// <param name="data">The data to upload</param>
public void Data(T[] data) => Wrapper.Data(_vdi.Stride, data);
/// <summary>
/// Upload a range of data to this buffer
/// </summary>
/// <param name="offset">The range offset</param>
/// <param name="count">The range length</param>
/// <param name="data">The data to upload, offset and length apply to both this and the target</param>
public void Data(int offset, int count, T[] data) => Wrapper.SubData(_vdi.Stride, offset, count, data);
/// <summary>
/// Upload a range of data to this buffer
/// </summary>
/// <param name="data">The data to upload</param>
public void Data(SubArray<T> data) => Data(data.Offset, data.Length, data.Array);
/// <summary>
/// Point this buffer to a program's vertex attributes. T must have [VertexDataAttribute], and all fields
/// of T must have [VertexPointerAttribute] to infer vertex pointer locations.
/// </summary>
/// <param name="program">The program to point this buffer to</param>
public void PointTo(Program program)
{
if (_vdi == null)
{
var exception = new InvalidOperationException($"Cannot use type {typeof(T)} to create a Vertex Buffer");
Logger.Error(exception);
throw exception;
}
Wrapper.Bind();
foreach (var attr in _vdi.Pointers)
{
if (program.HasAttribute(attr.Name))
GL.VertexAttribPointer(program.AttributeLocation(attr.Name), attr.Size, attr.Type, attr.Normalized,
_vdi.Stride, attr.Offset);
}
}
public override string ToString() => Name == null
? $"Buffer<{typeof(T).Name}> {Wrapper}"
: $"Buffer<{typeof(T).Name}> {Wrapper} \'{Name}\'";
public override void Dispose()
{
Logger.Debug("Disposing {0}", this);
Wrapper.Dispose();
}
#region Factory Methods
/// <summary>
/// Create an empty buffer of this type
/// </summary>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
internal static Buffer<T> Empty(BufferTarget target, BufferUsageHint usage, string name)
{
var wrapper = new BufferWrap(target, usage);
var service = new Buffer<T>(wrapper, name);
Logger.Debug("Created {0}", service);
return service;
}
/// <summary>
/// Create a buffer of this type and upload data
/// </summary>
/// <param name="data">The data to upload</param>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
internal static Buffer<T> FromData(T[] data, BufferTarget target, BufferUsageHint usage, string name = null)
{
var service = Empty(target, usage, name);
service?.Data(data);
return service;
}
#endregion
}
/// <summary>
/// Class for static Buffer operations and public factory methods
/// </summary>
public static class Buffer
{
/// <summary>
/// Create an empty buffer of this type
/// </summary>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
public static Buffer<T> Empty<T>(BufferTarget target, BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct => Buffer<T>.Empty(target, usage, name);
/// <summary>
/// Create a buffer of this type and upload data
/// </summary>
/// <param name="data">The data to upload</param>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
public static Buffer<T> FromData<T>(T[] data, BufferTarget target,
BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct => Buffer<T>.FromData(data, target, usage, name);
}
}

View File

@@ -1,25 +0,0 @@
using System;
namespace Diamond.Buffers
{
/// <summary>
/// Marks a struct as vertex data that can be sent to a shader attribute
/// </summary>
[AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
public sealed class VertexDataAttribute : Attribute
{
/// <summary>
/// The pointer divisor for this type. A value of 0 indicates per-vertex, a value of 1+
/// indicates every n instances
/// </summary>
public int Divisor { get; set; } = 0;
/// <summary>
/// Mark a struct with information about how to iterate over vertex data of this type.
/// All fields of this struct must have [VertexPointer]
/// </summary>
public VertexDataAttribute()
{
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using Diamond.Shaders;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Buffers
{
/// <summary>
/// Get vertex pointer information about a struct to infer how to point shader attributes to it
/// </summary>
public class VertexDataInfo
{
/// <summary>
/// All shader attributes supported by this type
/// </summary>
public IReadOnlyCollection<VertexPointerAttribute> Pointers { get; }
/// <summary>
/// The size of this type in bytes
/// </summary>
public int Stride { get; }
/// <summary>
/// The pointer divisor for this type. A value of 0 indicates per-vertex, a value of 1+
/// indicates every n instances
/// </summary>
public readonly int Divisor;
/// <summary>
/// Create an info class with pre-computed values
/// </summary>
/// <param name="pointers">The type's supported attributes</param>
/// <param name="stride">The type's stride</param>
/// <param name="divisor">The type's pointer divisor</param>
private VertexDataInfo(IList<VertexPointerAttribute> pointers, int stride, int divisor)
{
Pointers = new ReadOnlyCollection<VertexPointerAttribute>(pointers);
Stride = stride;
Divisor = divisor;
}
/// <summary>
/// Enable the attributes associated with this type on Program.Current
/// </summary>
public void EnableVertexPointers()
{
if (Program.Current == null)
throw new InvalidOperationException("Cant render a mesh with no active shader.");
foreach (var attr in Pointers)
{
if (!Program.Current.HasAttribute(attr.Name))
continue;
var loc = Program.Current.AttributeLocation(attr.Name);
GL.EnableVertexAttribArray(loc);
GL.VertexAttribDivisor(loc, Divisor);
}
}
/// <summary>
/// Disable the attributes associated with this type on Program.Current
/// </summary>
public void DisableVertexPointers()
{
if (Program.Current == null)
throw new InvalidOperationException("Cant render a mesh with no active shader.");
foreach (var attr in Pointers)
{
if (!Program.Current.HasAttribute(attr.Name))
continue;
var loc = Program.Current.AttributeLocation(attr.Name);
GL.DisableVertexAttribArray(loc);
}
}
/// <summary>
/// A cache of already computed information to prevent redundant reflection calls
/// </summary>
private static readonly Dictionary<Type, VertexDataInfo> attribCache =
new Dictionary<Type, VertexDataInfo>();
/// <summary>
/// Get the VertexDataInfo for a particular type
/// </summary>
/// <typeparam name="T">The type to analyse</typeparam>
/// <returns>The VertexDataInfo for the type, or null if the type is not supported</returns>
public static VertexDataInfo GetInfo<T>() 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<VertexPointerAttribute>();
var stride = Marshal.SizeOf<T>();
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<T>(fieldInfo.Name);
foreach (var attr in attrs)
{
var vpa = (VertexPointerAttribute) attr;
vpa.Offset = offset;
attribList.Add(vpa);
}
}
return new VertexDataInfo(attribList, stride, divisor);
}
}
}

View File

@@ -1,54 +0,0 @@
using System;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Buffers
{
/// <summary>
/// Marks a field as an attribute to be sent to a shader. Must be used on public fields of a struct.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public sealed class VertexPointerAttribute : Attribute
{
/// <summary>
/// The attribute name that the values of this field should point to
/// </summary>
public string Name { get; }
/// <summary>
/// The number of elements in this attribute
/// Corresponds to the <code>size</code> parameter to <code>glVertexAttribPointer</code>.
/// </summary>
public int Size { get; }
/// <summary>
/// The element type of the attribute
/// Corresponds to the <code>type</code> parameter to <code>glVertexAttribPointer</code>
/// </summary>
public VertexAttribPointerType Type { get; set; } = VertexAttribPointerType.Float;
/// <summary>
/// Whether to normalize the values of this attribute
/// Corresponds to the <code>normalized</code> parameter to <code>glVertexAttribPointer</code>
/// </summary>
public bool Normalized { get; set; } = false;
/// <summary>
/// The offset of this attribute within each element
/// Corresponds to the <code>offset</code> parameter to <code>glVertexAttribPointer</code>
/// </summary>
// 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;
/// <summary>
/// Mark a field with information about how to point a shader attribute to it
/// </summary>
/// <param name="name">The name of the attribute to point to this</param>
/// <param name="size">The number of elements to read from this field</param>
public VertexPointerAttribute(string name, int size)
{
Name = name;
Size = size;
}
}
}

View File

@@ -51,26 +51,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Buffers\Buffer.cs" />
<Compile Include="Buffers\VertexDataInfo.cs" />
<Compile Include="Render\Camera.cs" />
<Compile Include="Render\RenderGroup.cs" />
<Compile Include="Wrappers\BufferWrap.cs" />
<Compile Include="GLObject.cs" />
<Compile Include="Wrappers\ProgramWrap.cs" />
<Compile Include="Wrappers\ShaderWrap.cs" />
<Compile Include="Wrappers\TextureWrap.cs" />
<Compile Include="Util\SubArray.cs" />
<Compile Include="Buffers\VertexDataAttribute.cs" />
<Compile Include="Wrappers\Wrapper.cs" />
<Compile Include="Util\TileData.cs" />
<Compile Include="Render\VertexBuffer.cs" />
<Compile Include="Util\ObjVertex.cs" />
<Compile Include="Shaders\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shaders\Shader.cs" />
<Compile Include="Textures\Texture.cs" />
<Compile Include="Buffers\VertexPointerAttribute.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NLog.config">

View File

@@ -1,26 +1,64 @@
using System;
using NLog;
using OpenTK.Graphics;
namespace Diamond
{
/// <summary>
/// Provide managed access to OpenGL objects
/// </summary>
public abstract class GLObject : IDisposable
{
/// <summary>
/// Logger for all GLObjects
/// The logger for GLObject-related info
/// </summary>
protected static readonly Logger Logger = LogManager.GetLogger(nameof(GLObject));
protected internal static Logger Logger = LogManager.GetLogger(nameof(GLObject));
/// <summary>
/// Name of this GLObject used for identification
/// The OpenGL object name
/// </summary>
public string Name { get; protected set; } = nameof(GLObject);
public int Id { get; private set; }
/// <summary>
/// Delegate Dispose to underlying wrapper class
/// Force object name assignment
/// </summary>
public abstract void Dispose();
/// <param name="id">The OpenGL object name</param>
protected GLObject(int id)
{
Id = id;
}
/// <summary>
/// Free this object name on the GPU
/// </summary>
protected abstract void Delete();
#region IDisposable
/// <inheritdoc />
protected virtual void Dispose(bool disposing)
{
if (GraphicsContext.CurrentContext != null)
Delete();
else
Logger.Error("Cannot delete {0} because there is no graphics context.", this);
if (disposing)
{
// no managed resources to dispose
}
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <inheritdoc />
~GLObject()
{
Dispose(false);
}
#endregion
}
}

View File

@@ -1,71 +0,0 @@
using OpenTK;
namespace Diamond.Render
{
/// <summary>
/// Manages a projection and view matrix
/// </summary>
public class Camera
{
private Vector3 _position = Vector3.Zero;
private Vector3 _target = -Vector3.One;
private Vector3 _up = Vector3.UnitZ;
/// <summary>
/// The view matrix
/// </summary>
public Matrix4 View;
/// <summary>
/// The projection matrix
/// </summary>
public Matrix4 Projection;
/// <summary>
/// Sets and updates the position of the view matrix
/// </summary>
public Vector3 Position
{
set
{
_position = value;
UpdateView();
}
get => _position;
}
/// <summary>
/// Sets and updates the target of the view matrix
/// </summary>
public Vector3 Target
{
set
{
_target = value;
UpdateView();
}
get => _target;
}
/// <summary>
/// Sets and updates the up vector of the view matrix
/// </summary>
public Vector3 Up
{
set
{
_up = value;
UpdateView();
}
get => _up;
}
/// <summary>
/// Recalculate the view matrix
/// </summary>
private void UpdateView()
{
View = Matrix4.LookAt(_position, _target, _up);
}
}
}

View File

@@ -1,61 +0,0 @@
using Diamond.Shaders;
using Diamond.Textures;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Render
{
/// <summary>
/// Manage a group of buffers, ranges, and uniforms to render
/// </summary>
/// <typeparam name="TInstance">The type of data to use as Instance information</typeparam>
/// <typeparam name="TVertex">The type of data to use as Vertex information</typeparam>
public class RenderGroup<TInstance, TVertex> where TInstance : struct where TVertex : struct
{
/// <summary>
/// The range of vertex values to render
/// </summary>
public VertexBuffer<TVertex> Vertices;
/// <summary>
/// The range of instance values to render
/// </summary>
public VertexBuffer<TInstance> Instance;
/// <summary>
/// The program to use to render this Rendergroup
/// </summary>
public Program Program;
/// <summary>
/// The Texture to use for this Rendergroup
/// </summary>
public Texture Texture;
/// <summary>
/// View and Projection information for this Rendergroup
/// </summary>
public Camera Camera;
/// <summary>
/// Draw this rendergroup using the predefined settings.
/// </summary>
public void Draw()
{
Program.Use();
Texture.Bind(0);
if (Program.HasUniform("tex"))
GL.Uniform1(Program.UniformLocation("tex"), 0);
if (Program.HasUniform("view"))
GL.UniformMatrix4(Program.UniformLocation("view"), false, ref Camera.View);
if (Program.HasUniform("proj"))
GL.UniformMatrix4(Program.UniformLocation("proj"), false, ref Camera.Projection);
if (Instance != null)
Vertices.DrawInstanced(Instance);
else
Vertices.Draw();
}
}
}

View File

@@ -1,228 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Diamond.Buffers;
using Diamond.Shaders;
using Diamond.Util;
using NLog;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using Buffer = Diamond.Buffers.Buffer;
namespace Diamond.Render
{
/// <summary>
/// Manage a vertex buffer object
/// </summary>
/// <typeparam name="T">Buffer data type</typeparam>
public class VertexBuffer<T> : IDisposable where T : struct
{
private Logger Logger = LogManager.GetLogger("VertexBuffer");
/// <summary>
/// The name of this buffer object for identification
/// </summary>
public string Name { get; set; }
/// <summary>
/// The underlying buffer for this object
/// </summary>
public Buffer<T> Buffer;
/// <summary>
/// A subset of the Buffer's array for this buffer
/// </summary>
public SubArray<T> Vertices;
/// <summary>
/// Primitive type to render this object
/// </summary>
public PrimitiveType Primitive;
/// <summary>
/// Vertex data info for this type of data
/// </summary>
private static readonly VertexDataInfo tVdi = VertexDataInfo.GetInfo<T>();
internal VertexBuffer(Buffer<T> buffer, SubArray<T> vertices, PrimitiveType primitive, string name)
{
Buffer = buffer;
Vertices = vertices;
Primitive = primitive;
Name = name;
}
/// <summary>
/// Render this buffer
/// </summary>
public void Draw()
{
Buffer.PointTo(Program.Current);
tVdi.EnableVertexPointers();
GL.DrawArrays(Primitive, Vertices.Offset, Vertices.Length);
tVdi.DisableVertexPointers();
}
/// <summary>
/// Render this buffer using a second buffer as instance data
/// </summary>
/// <typeparam name="TI"></typeparam>
/// <param name="instance"></param>
public void DrawInstanced<TI>(VertexBuffer<TI> instance) where TI : struct
{
var tiVdi = VertexBuffer<TI>.tVdi;
if (tiVdi.Divisor == 0)
{
Logger.Error("Cannot render mesh with instanced of type {0} - Divisor is 0", typeof(TI).Name);
return;
}
Buffer.PointTo(Program.Current);
instance.Buffer.PointTo(Program.Current);
tVdi.EnableVertexPointers();
tiVdi.EnableVertexPointers();
GL.DrawArraysInstancedBaseInstance(Primitive, Vertices.Offset, Vertices.Length, instance.Vertices.Length,
instance.Vertices.Offset);
tVdi.DisableVertexPointers();
tiVdi.DisableVertexPointers();
}
public void Dispose()
{
Buffer.Dispose();
}
}
/// <summary>
/// Static operations for vertex buffers
/// </summary>
public static class VertexBuffer
{
public static VertexBuffer<T>[] FromArrays<T>(T[][] arrays, PrimitiveType primitive = PrimitiveType.Triangles,
string name = null) where T : struct
{
var buffer = Buffer.Empty<T>(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, name);
var vertices = arrays.SelectMany(x => x).ToArray();
buffer.Data(vertices);
var vertBuffers = new List<VertexBuffer<T>>();
var offset = 0;
foreach (var array in arrays)
{
vertBuffers.Add(new VertexBuffer<T>(
buffer,
new SubArray<T>(vertices, offset, array.Length),
primitive, name));
offset += array.Length;
}
return vertBuffers.ToArray();
}
public static VertexBuffer<T>[] FromArrays<T>(IEnumerable<IEnumerable<T>> arrays,
PrimitiveType primitive = PrimitiveType.Triangles, string name = null) where T : struct =>
FromArrays(arrays.Select(x => x.ToArray()).ToArray(), primitive, name);
public static VertexBuffer<ObjVertex>[] FromWavefront(string file)
{
var lines = File.ReadAllLines(file).Where(l => !l.StartsWith("#")).Select(l => l.Split(' ')).ToArray();
var buffer = Buffer<ObjVertex>.Empty(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw,
$"{file} buffer");
var name = file;
var vertBuffers = new List<VertexBuffer<ObjVertex>>();
// get all positional data
var vs = lines
.Where(items => items[0] == "v") // only "v" lines
.Select(items => new Vector3( // get a vector3 from the values
float.Parse(items[1]),
float.Parse(items[2]),
float.Parse(items[3])))
.ToArray();
// get all uv data
var vts = lines
.Where(items => items[0] == "vt") // only "vt" lines
.Select(items => new Vector2( // get a vector2 from the values
float.Parse(items[1]),
1 - float.Parse(items[2]))) // flip along y
.ToArray();
// get all normal data
var vns = lines
.Where(items => items[0] == "vn") // only "vn" lines
.Select(items => new Vector3( // get a vector3 from the values
float.Parse(items[1]),
float.Parse(items[2]),
float.Parse(items[3])))
.ToArray();
// get all vertex data
var vertices = lines
.Where(items => items[0] == "f") // only "f" liens
.Select(items => items.Skip(1)) // skip the "f" item
.Select(items => items // get each vertex from the triangle
.Select(inds => inds.Split('/'))) // split items into indices
.SelectMany(inds => inds) // collapse nested index array into array of indexes
.Select(inds => new ObjVertex( // get vertexdata from the value data at each index
inds[0] == "" ? Vector3.Zero : vs[int.Parse(inds[0]) - 1],
inds[1] == "" ? Vector2.Zero : vts[int.Parse(inds[1]) - 1],
inds[2] == "" ? Vector3.Zero : vns[int.Parse(inds[2]) - 1]))
.ToArray();
buffer.Data(vertices); // upload vertex data to the buffer
var offset = 0; // offset of each vertexbuffer
var count = 0; // length of each vertexbuffer
foreach (var items in lines.Where(items => items[0] == "o" || items[0] == "f"))
{
if (items[0] != "f")
{
if (count > 0)
{
vertBuffers.Add(new VertexBuffer<ObjVertex>(
buffer,
new SubArray<ObjVertex>(vertices, offset, count),
PrimitiveType.Triangles,
name));
}
// reset for next vbo and move offset to the end of this one
name = items[1];
offset += count;
count = 0;
}
else
{
count += items.Length - 1; // size of current vbo increases by that many vertices
}
}
// at the last vbo, or only vbo.
if (count > 0)
{
vertBuffers.Add(new VertexBuffer<ObjVertex>(
buffer,
new SubArray<ObjVertex>(vertices, offset, count),
PrimitiveType.Triangles,
name));
}
return vertBuffers.ToArray();
}
}
}

View File

@@ -1,233 +1,291 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Diamond.Wrappers;
using System.Text;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Shaders
{
/// <summary>
/// Manages an OpenGL Program object
/// Wrap and OpenGL program object
/// </summary>
public class Program : GLObject
{
internal readonly ProgramWrap Wrapper;
#region Static
private static Program _current;
/// <summary>
/// The currently active program. Manually invoking glUseProgram will break this.
/// The currently active program
/// </summary>
public static Program Current { get; private set; }
public static Program Current
{
get => _current;
set
{
if (!value?.Linked ?? false)
{
Logger.Error("Cannot use program {0}", value);
value = null;
}
// keep a cache of uniform and attributes to prevent repeated queries
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
Logger.Debug("Using program {0}", value);
GL.UseProgram((_current = value)?.Id ?? 0);
}
}
#endregion
#region Constructor, Delete()
/// <summary>
/// Create a program object wrapper
/// </summary>
private Program()
: base(GL.CreateProgram())
{
Logger.Debug("Created {0}", this);
}
/// <inheritdoc />
protected override void Delete()
{
Logger.Debug("Disposing {0}", this);
GL.DeleteProgram(Id);
}
#endregion
#region Properties
#region Queries
/// <summary>
/// The number of active uniforms
/// </summary>
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
/// <summary>
/// The number of active attributes
/// </summary>
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
#endregion
#region Stored
/// <summary>
/// The InfoLog for this program
/// </summary>
public string InfoLog { get; private set; }
/// <summary>
/// The link status of this program
/// </summary>
public bool Linked { get; private set; }
#endregion
#endregion
#region Methods
/// <summary>
/// Get a property of this program
/// </summary>
/// <param name="param">The program property to get</param>
/// <returns>The int value of the program property</returns>
private int Get(GetProgramParameterName param)
{
GL.GetProgram(Id, param, out int res);
return res;
}
/// <summary>
/// Try to link this program
/// </summary>
public void Link()
{
_attributes.Clear();
_uniforms.Clear();
Logger.Debug("Linking {0}", this);
GL.LinkProgram(Id);
// link status can only change after link attempt
Linked = Get(GetProgramParameterName.LinkStatus) != 0;
if (Linked)
{
Logger.Trace("Successfully linked {0}", this);
for (var i = 0; i < ActiveAttributes; i++)
{
var sb = new StringBuilder(256);
GL.GetActiveAttrib(Id, i, sb.Capacity, out int length, out int size, out ActiveAttribType type, sb);
var name = sb.ToString();
_attributes[name] = i;
}
for (var i = 0; i < ActiveUniforms; i++)
{
var sb = new StringBuilder(256);
GL.GetActiveUniform(Id, i, sb.Capacity, out int length, out int size, out ActiveUniformType type,
sb);
var name = sb.ToString();
_uniforms[name] = i;
}
}
else
{
InfoLog = GL.GetProgramInfoLog(Id).Trim();
Logger.Error("Failed to link {0}", this);
Logger.Trace("InfoLog for {0}:\n{1}", this, InfoLog);
}
}
/// <summary>
/// Use this program
/// <para></para>
/// Equivalent to <code>Program.Current = value</code>
/// </summary>
public void Use() => Current = this;
#region Attribute Locations
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
internal Program(ProgramWrap wrapper, string name)
{
Wrapper = wrapper;
Name = name;
}
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
/// <summary>
/// Check if the program has a uniform
/// Check if this program has an active attribute
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <returns>Whether the program has this uniform</returns>
public bool HasUniform(string name)
{
return _uniforms.ContainsKey(name);
}
/// <param name="name">The attribute name</param>
/// <returns>Whether the progrma has an active attribute</returns>
public bool HasAttribute(string name) => _attributes.ContainsKey(name);
/// <summary>
/// Get the location of a uniform
/// Check if this program has an active uniform
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <returns>The location of the uniform</returns>
public int UniformLocation(string name)
{
if (HasUniform(name)) return _uniforms[name];
throw new KeyNotFoundException($"Shader {this} does not contain uniform {name}");
}
/// <param name="name">The uniform name</param>
/// <returns>Whether the progrma has an active uniform</returns>
public bool HasUniform(string name) => _uniforms.ContainsKey(name);
/// <summary>
/// Check if the program has an attribute
/// Get the location of an attribute by name
/// </summary>
/// <param name="name">The name of the attribute</param>
/// <returns>Whether the program has this attribute</returns>
public bool HasAttribute(string name)
{
return _attributes.ContainsKey(name);
}
/// <summary>
/// Get the location of an attribute
/// </summary>
/// <param name="name">The name of the attribute</param>
/// <param name="name">The attribute name</param>
/// <returns>The location of the attribute</returns>
public int AttributeLocation(string name)
{
if (HasAttribute(name)) return _attributes[name];
throw new KeyNotFoundException($"Shader {this} does not contain attribute {name}");
if (!HasAttribute(name))
throw new InvalidOperationException($"{this} does not have active attribute '{name}'");
return _attributes[name];
}
/// <summary>
/// Use this Program to render. Also updates Program.Current
/// Get the location of an uniform by name
/// </summary>
public void Use()
/// <param name="name">The uniform name</param>
/// <returns>The location of the uniform</returns>
public int UniformLocation(string name)
{
Wrapper.Use();
Current = this;
if (!HasUniform(name)) throw new InvalidOperationException($"{this} does not have active uniform '{name}'");
return _uniforms[name];
}
/// <summary>
/// Use the default shader to render
/// </summary>
//? Could create static Program instance which wraps the default shader
// ie Shader.Default.Use()
// would also allow sending arrays to the default attribs like gl_Vertex etc.
public static void UseDefault()
{
GL.UseProgram(0);
Current = null;
}
#endregion
/// <summary>
/// Helper method to try to link this program
/// </summary>
/// <returns></returns>
private bool Link()
{
_uniforms.Clear();
_attributes.Clear();
Wrapper.Link();
if (!Wrapper.Linked)
return false;
for (var i = 0; i < Wrapper.ActiveUniforms; i++)
_uniforms[Wrapper.UniformName(i)] = i;
for (var i = 0; i < Wrapper.ActiveAttributes; i++)
_attributes[Wrapper.AttributeName(i)] = i;
return true;
}
/// <summary>
/// Helper method to attach a shader to this program
/// Attach a shader to this program
/// </summary>
/// <param name="shader">The shader to attach</param>
private void Attach(Shader shader)
public void Attach(Shader shader)
{
Wrapper.Attach(shader.Wrapper);
Logger.Debug("Attaching {0} to {1}", shader, this);
GL.AttachShader(Id, shader.Id);
}
public override string ToString() => $"Program {Wrapper} \'{Name}\'";
/// <inheritdoc />
public override string ToString() =>
$"'Program {Id}'";
public override void Dispose()
{
Logger.Debug("Disposing {0}", this);
Wrapper.Dispose();
}
#endregion
#region Factory Methods
/// <summary>
/// Create a program from compiled shaders
/// Create and link a program from precompiled shaders
/// </summary>
/// <param name="name">The name of this GLObject</param>
/// <param name="shaders">The shaders to use in this program</param>
/// <returns>The linked program, or null if initialization failed</returns>
public static Program FromShaders(string name, params Shader[] shaders) => FromShaders(name,
(IEnumerable<Shader>) shaders);
/// <param name="shaders">The shaders used in this program</param>
/// <returns>A linked program, or null if initialization failed</returns>
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
/// <summary>
/// Create a program from compiled shaders
/// Create and link a program from precompiled shaders
/// </summary>
/// <param name="name">The name of this GLObject</param>
/// <param name="shaders">The shaders to use in this program</param>
/// <returns>The linked program, or null if initialization failed</returns>
public static Program FromShaders(string name, IEnumerable<Shader>shaders)
/// <param name="shaders">The shaders used in this program</param>
/// <returns>A linked program, or null if initialization failed</returns>
public static Program FromShaders(IEnumerable<Shader> shaders)
{
if (shaders == null)
{
Logger.Error("Cannot create program {0} with no shaders.", name);
Logger.Error("Cannot create program from no shaders.");
return null;
}
var wrapper = new ProgramWrap();
var service = new Program(wrapper, name);
Logger.Debug("Created {0}", service);
var program = new Program();
foreach (var shader in shaders)
{
if (shader == null)
{
Logger.Error("One or more shaders failed to compile - cannot create program {0}", name);
service.Dispose();
Logger.Error("One or more shaders is null - cannot create program");
program.Dispose();
return null;
}
service.Attach(shader);
program.Attach(shader);
}
var linked = service.Link();
program.Link();
if (!linked)
{
Logger.Warn("Failed to link {0}", service);
Logger.Debug("InfoLog for {0}", service);
service.Dispose();
return null;
}
if (program.Linked) return program;
Logger.Debug("Successfully linked {0}", service);
return service;
program.Dispose();
return null;
}
/// <summary>
/// Create a program from compiled shaders
/// Create and link a program from glsl source files. Shader types must be inferrable from file extensions.
/// <para></para>
/// See <see cref="Shader.FromFile(string,ShaderType)"/> for file extension details
/// </summary>
/// <param name="shaders">The shaders to use in this program</param>
/// <param name="files"></param>
/// <returns>The linked program, or null if initialization failed</returns>
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
public static Program FromFiles(params string[] files) => FromFiles((IEnumerable<string>) files);
/// <summary>
/// Create a program from compiled shaders
/// Create and link a program from glsl source files. Shader types must be inferrable from file extensions.
/// <para></para>
/// See <see cref="Shader.FromFile(string,ShaderType)"/> for file extension details
/// </summary>
/// <param name="shaders">The shaders to use in this program</param>
/// <param name="files"></param>
/// <returns>The linked program, or null if initialization failed</returns>
public static Program FromShaders(IEnumerable<Shader> shaders)
public static Program FromFiles(IEnumerable<string> files)
{
var shaderList = shaders.ToList(); // prevent multiple enumeration
var shaderNames = shaderList.Select(s => s.Name);
string name = $"[{string.Join(", ", shaderNames)}]";
return FromShaders(name, shaderList);
}
/// <summary>
/// Create shaders from glsl source files, and create a program using them.
/// Shader types must be inferrable from file extensions.
/// </summary>
/// <param name="paths">The glsl source files</param>
/// <returns>The linked program, or null if initialization faileds</returns>
public static Program FromFiles(params string[] paths)
{
if (paths == null)
if (files == null)
{
Logger.Warn("Cannot create a program from no shaders.");
Logger.Warn("Cannot create a program from no shaders");
return null;
}
var shaders = paths.Select(path => Shader.FromFile(path)).ToList();
var shaders = files.Select(Shader.FromFile).ToList();
var program = FromShaders(shaders);
foreach (var shader in shaders)
{
shader?.Dispose();
}
return program;
}

View File

@@ -1,96 +1,165 @@
using System.Collections.Generic;
using System.IO;
using Diamond.Wrappers;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Shaders
{
/// <summary>
/// Manges a OpenGL Shader object
/// Wrap an OpenGL shader object
/// </summary>
public class Shader : GLObject
public sealed class Shader : GLObject
{
internal readonly ShaderWrap Wrapper;
#region Constructor, Delete()
/// <summary>
/// The source used to create this shader
/// Create a shader object wrapper
/// </summary>
public string Source { get; }
/// <param name="type">The type of this shader</param>
private Shader(ShaderType type)
: base(GL.CreateShader(type))
{
ShaderType = type;
Logger.Debug("Created {0}", this);
}
/// <inheritdoc />
protected override void Delete()
{
Logger.Debug("Disposing {0}", this);
GL.DeleteShader(Id);
}
#endregion
#region Properties
#region Queries
#endregion
#region Stored
/// <summary>
/// Store the source code to prevent repeated queries to glGetShaderSource
/// </summary>
private string _source;
/// <summary>
/// GLSL source code for this shader
/// </summary>
public string Source
{
get => _source;
set
{
_source = value;
GL.ShaderSource(Id, _source);
Logger.Debug("Set shader source for {0}", this);
}
}
/// <summary>
/// The compilation status of this shader
/// </summary>
public bool Compiled { get; private set; }
/// <summary>
/// The type of this shader
/// </summary>
public ShaderType Type { get; }
public ShaderType ShaderType { get; private set; }
internal Shader(ShaderWrap wrapper, string source, ShaderType type, string name)
/// <summary>
/// The InfoLog for this program
/// </summary>
public string InfoLog { get; private set; }
#endregion
#endregion
#region Methods
/// <summary>
/// Get a property of this shader
/// </summary>
/// <param name="param">The shader property to get</param>
/// <returns>The int value of the shader property</returns>
private int Get(ShaderParameter param)
{
Wrapper = wrapper;
Source = source;
Type = type;
Name = name;
GL.GetShader(Id, param, out int res);
return res;
}
public override string ToString() => Name == null
? $"{Wrapper}"
: $"{Wrapper} \'{Name}\'";
public override void Dispose()
/// <summary>
/// Try to compile this shader
/// </summary>
public void Compile()
{
Logger.Debug("Disposing {0}", this);
Wrapper.Dispose();
Logger.Debug("Compiling {0}", this);
GL.CompileShader(Id);
// compilation status can only change after glCompileShader
Compiled = Get(ShaderParameter.CompileStatus) != 0;
if (Compiled)
Logger.Trace("Successfully compiled {0}", this);
else
{
InfoLog = GL.GetShaderInfoLog(Id).Trim();
Logger.Error("Failed to compile {0}", this);
Logger.Trace("InfoLog for {0}: \n{1}", this, InfoLog);
}
}
/// <inheritdoc />
public override string ToString() =>
$"'Shader {Id}: {ShaderType}'";
#endregion
#region Factory Methods
// Used to infer shader type based on file extension
/// <summary>
/// Map file extensions to appropriate shader type
/// </summary>
private static readonly Dictionary<string, ShaderType> Extensions = new Dictionary<string, ShaderType>
{
[".vs"] = ShaderType.VertexShader,
[".vert"] = ShaderType.VertexShader,
[".fs"] = ShaderType.FragmentShader,
[".frag"] = ShaderType.FragmentShader,
[".gs"] = ShaderType.GeometryShader,
[".geom"] = ShaderType.GeometryShader,
[".fs"] = ShaderType.FragmentShader,
[".frag"] = ShaderType.FragmentShader,
};
/// <summary>
/// Create and compile a shader from glsl source code
/// Create and compile a shader from source
/// </summary>
/// <param name="source">The glsl source</param>
/// <param name="type">The type of shader to create</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The compiled Shader, or null if initialization failed</returns>
public static Shader FromSource(string source, ShaderType type, string name = "Shader")
/// <param name="source">The GLSL source for the shader</param>
/// <param name="type">The type of the shader</param>
/// <returns>The compiled shader, or null if initialization failed</returns>
public static Shader FromSource(string source, ShaderType type)
{
var wrapper = new ShaderWrap(type);
var service = new Shader(wrapper, source, type, name);
var shader = new Shader(type) {Source = source};
shader.Compile();
Logger.Debug("Created {0}", service);
wrapper.Source = source;
wrapper.Compile();
if (!wrapper.Compiled)
if (!shader.Compiled)
{
Logger.Warn("Failed to compile {0}", service);
Logger.Debug("InfoLog for {0}", service);
service.Dispose();
shader.Dispose();
return null;
}
Logger.Debug("Successfully compiled {0}", service);
return service;
return shader;
}
/// <summary>
/// Create and compile a shader from a glsl source file
/// Create and compile a shader from a source file
/// </summary>
/// <param name="path">The path to the glsl source file</param>
/// <param name="type">The type of the shader to create</param>
/// <param name="name">The name of this GLObject</param>
/// <returns></returns>
public static Shader FromFile(string path, ShaderType type, string name = null)
/// <param name="path">The path to the source file</param>
/// <param name="type">The type of the shader</param>
/// <returns>The compiled shader, or null if initialization failed</returns>
public static Shader FromFile(string path, ShaderType type)
{
if (!File.Exists(path))
{
@@ -98,21 +167,20 @@ namespace Diamond.Shaders
return null;
}
if (name == null)
name = Path.GetFileNameWithoutExtension(path);
return FromSource(File.ReadAllText(path), type, name);
var source = File.ReadAllText(path);
return FromSource(source, type);
}
/// <summary>
/// Create and compile a shader from a glsl source file. Shader type is inferred from file extension.
/// Extension must be .vs, .vert, .fs, .frag, .gs, or .geom. This can optionally be followed by .glsl or .txt,
/// but the shader type extension must be present.
/// Create and compile a shader from a source file, and infer the type of the shader from the file extension.
/// <para></para>
/// File must have extension or sub-extension <code>.vs</code>, <code>.fs</code>, <code>.gs</code>, <code>.vert</code>,
/// <code>.frag</code>, or <code>.geom</code>. For example: <code>shader.fs</code>, <code>shader.vert.glsl</code>,
/// <code>shader.gs.txt</code>
/// </summary>
/// <param name="path">The path to the glsl source file</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The compiled shader, or null if initialization failed or shader type cannot be inferred</returns>
public static Shader FromFile(string path, string name = null)
/// <param name="path"></param>
/// <returns></returns>
public static Shader FromFile(string path)
{
if (!File.Exists(path))
{
@@ -121,12 +189,12 @@ namespace Diamond.Shaders
}
var ext = Path.GetExtension(path);
var fileName = Path.GetFileNameWithoutExtension(path);
var file = Path.GetFileNameWithoutExtension(path);
// get sub-extension if real extension is not valid
if (ext != null)
if (!Extensions.ContainsKey(ext))
ext = Path.GetExtension(fileName);
ext = Path.GetExtension(file);
// if no extension, no sub-extension, or invalid sub-extension
if (ext == null || !Extensions.ContainsKey(ext))
@@ -136,10 +204,7 @@ namespace Diamond.Shaders
}
var type = Extensions[ext];
if (name == null)
name = fileName;
return FromFile(path, type, name);
return FromFile(path, type);
}
#endregion

View File

@@ -1,89 +0,0 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Diamond.Wrappers;
using OpenTK.Graphics.OpenGL4;
using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat;
namespace Diamond.Textures
{
/// <summary>
/// Manages a OpenGL Texture object
/// </summary>
public class Texture : GLObject
{
internal readonly TextureWrap Wrapper;
internal Texture(TextureWrap wrapper, string name)
{
Wrapper = wrapper;
Name = name;
}
/// <summary>
/// This textures target; how it is used
/// </summary>
public TextureTarget Target => Wrapper.Target;
/// <summary>
/// Bind this texture to a particular unit
/// </summary>
public void Bind(int unit) => Wrapper.Bind(unit);
public override string ToString() => Name == null
? $"{Wrapper}"
: $"{Wrapper} \'{Name}\'";
public override void Dispose()
{
Logger.Debug("Disposing {0}", this);
Wrapper.Dispose();
}
#region Factory Methods
/// <summary>
/// Create a texture object and upload bitmap data to it
/// </summary>
/// <param name="bmp">The image to upload</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The initialized Texture, or null if initialsation failed</returns>
public static Texture FromBitmap(Bitmap bmp, string name = null)
{
var wrapper = new TextureWrap(TextureTarget.Texture2D);
var service = new Texture(wrapper, null);
Logger.Debug("Created Texture {0}", service);
wrapper.Bind();
// todo: expose texture parameters to enable setting different filters
wrapper.TexParameter(TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
wrapper.TexParameter(TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Nearest);
var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
wrapper.Image2D(PixelInternalFormat.Rgba, bmp.Width, bmp.Height, PixelFormat.Bgra, PixelType.UnsignedByte,
data.Scan0);
bmp.UnlockBits(data);
return service;
}
/// <summary>
/// Create a texture and upload the contents of an image file to it
/// </summary>
/// <param name="path">The path to the file</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The initialized Texture, or null if instantiation failed</returns>
public static Texture FromFile(string path, string name = null)
{
if (name == null)
name = Path.GetFileNameWithoutExtension(path);
return FromBitmap(new Bitmap(path), name);
}
#endregion
}
}

View File

@@ -1,46 +0,0 @@
using Diamond.Buffers;
using OpenTK;
namespace Diamond.Util
{
/// <summary>
/// Vertex buffer data for Wavefront meshes
/// </summary>
[VertexData]
public struct ObjVertex
{
/// <summary>
/// Vertex position (v)
/// </summary>
[VertexPointer("position", 3)]
[VertexPointer("v", 3)]
public Vector3 Position;
/// <summary>
/// UV coordinate (vt)
/// </summary>
[VertexPointer("uv", 2)]
[VertexPointer("vt", 2)]
public Vector2 UV;
/// <summary>
/// Vertex normal (vn)
/// </summary>
[VertexPointer("normal", 3)]
[VertexPointer("vn", 3)]
public Vector3 Normal;
/// <summary>
/// Create a new ObjVertex
/// </summary>
/// <param name="position">The vertex position (v)</param>
/// <param name="uv">The uv coordinate (vt)</param>
/// <param name="normal">The vertex normal (n)</param>
public ObjVertex(Vector3 position, Vector2 uv, Vector3 normal)
{
Position = position;
UV = uv;
Normal = normal;
}
}
}

View File

@@ -1,28 +0,0 @@
using Diamond.Buffers;
using OpenTK;
namespace Diamond.Util
{
/// <summary>
/// Vertex buffer data for instanced rendering
/// </summary>
[VertexData(Divisor = 1)]
public struct TileData
{
/// <summary>
/// The global position of the instance
/// </summary>
[VertexPointer("glbpos", 3)]
[VertexPointer("global_pos", 3)]
public Vector3 Position;
/// <summary>
/// Create a new TileData
/// </summary>
/// <param name="position">The global position of the instance</param>
public TileData(Vector3 position)
{
Position = position;
}
}
}

View File

@@ -1,79 +0,0 @@
using System;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Wrappers
{
/// <summary>
/// Wrapper class for OpenGL Buffer objects
/// </summary>
internal sealed class BufferWrap : Wrapper
{
#region Constructor, GLDelete()
internal BufferWrap(BufferTarget target, BufferUsageHint usage)
: base(GL.GenBuffer())
{
Target = target;
Usage = usage;
}
protected override void GLDelete() => GL.DeleteBuffer(Id);
#endregion
#region Properties
#region Stored
/// <summary>
/// BufferTarget parameter used in gl* calls
/// </summary>
public BufferTarget Target { get; }
/// <summary>
/// BufferUsageHint parameter using in glBufferData calls
/// </summary>
public BufferUsageHint Usage { get; set; }
#endregion
#endregion
#region Methods
/// <summary>
/// Binds this buffer (glBindBuffer)
/// </summary>
public void Bind() => GL.BindBuffer(Target, Id);
/// <summary>
/// Upload data to this buffer (glBufferData)
/// </summary>
/// <typeparam name="T">Type of value to upload</typeparam>
/// <param name="size">Size of T in bytes</param>
/// <param name="data">Values to upload</param>
public void Data<T>(int size, T[] data) where T : struct
{
Bind();
GL.BufferData(Target, (IntPtr)(size * data.Length), data, Usage);
}
/// <summary>
/// Upload a range data to this buffer (glBufferSubData)
/// </summary>
/// <typeparam name="T">Type of value to upload</typeparam>
/// <param name="size">Size of T in bytes</param>
/// <param name="offset">Offset of upload range in bytes</param>
/// <param name="count">Number of bytes to upload</param>
/// <param name="data">All values to upload (offset will be applied to both this and the target)</param>
public void SubData<T>(int size, int offset, int count, T[] data) where T : struct
{
Bind();
GL.BufferSubData(Target, (IntPtr)(offset * size), (IntPtr)(count * size), data);
}
#endregion
public override string ToString() => $"{Target} ({Id})";
}
}

View File

@@ -1,103 +0,0 @@
using System.Text;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Wrappers
{
/// <summary>
/// Wrapper class for OpenGL Program objects
/// </summary>
internal sealed class ProgramWrap : Wrapper
{
#region Constructor, GLDelete()
internal ProgramWrap()
: base(GL.CreateProgram())
{
}
protected override void GLDelete() => GL.DeleteProgram(Id);
#endregion
#region Properties
/// <summary>
/// Get the number of active uniforms for this program
/// </summary>
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
/// <summary>
/// Get the number of active attributes for this program
/// </summary>
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
/// <summary>
/// Check whether this program has been Linked
/// </summary>
public bool Linked => Get(GetProgramParameterName.LinkStatus) != 0;
/// <summary>
/// Get the InfoLog related to this program. Unless Link() failed, should be null.
/// </summary>
public string InfoLog => GL.GetProgramInfoLog(Id).Trim(); // trim to remove trailing newlines
#endregion
#region Methods
/// <summary>
/// Get a parameter from this program (glGetProgram)
/// </summary>
/// <param name="parameter">The parameter to get</param>
/// <returns>The int value of the parameter</returns>
public int Get(GetProgramParameterName parameter)
{
GL.GetProgram(Id, parameter, out int res);
return res;
}
/// <summary>
/// Attach a compiled shader to this program (glAttachShader)
/// </summary>
/// <param name="shader"></param>
public void Attach(ShaderWrap shader) => GL.AttachShader(Id, shader.Id);
/// <summary>
/// Link this program (glLinkProgram)
/// </summary>
public void Link() => GL.LinkProgram(Id);
/// <summary>
/// Use this program (glUseProgram)
/// </summary>
public void Use() => GL.UseProgram(Id);
/// <summary>
/// Get the name of the uniform at a location
/// </summary>
/// <param name="location">The uniform id</param>
/// <returns>The uniform name</returns>
public string UniformName(int location)
{
var sb = new StringBuilder(64);
GL.GetActiveUniformName(Id, location, sb.Capacity, out int length, sb);
return sb.ToString();
}
/// <summary>
/// Get the name of the attribute at a location
/// </summary>
/// <param name="location">The attribute id</param>
/// <returns>The attribute name</returns>
public string AttributeName(int location)
{
var sb = new StringBuilder(64);
GL.GetActiveAttrib(Id, location, sb.Capacity, out int length, out int size, out ActiveAttribType type, sb);
return sb.ToString();
}
#endregion
public override string ToString() => $"Program ({Id})";
}
}

View File

@@ -1,86 +0,0 @@
using System.Text;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Wrappers
{
/// <summary>
/// Wrapper class for OpenGL Shader objects
/// </summary>
internal sealed class ShaderWrap : Wrapper
{
#region Constructor, GLDelete()
internal ShaderWrap(ShaderType shaderType)
: base(GL.CreateShader(shaderType))
{
ShaderType = shaderType;
}
protected override void GLDelete() => GL.DeleteShader(Id);
#endregion
#region Properties
#region Stored
/// <summary>
/// The type of this shader - stored at creation time to prevent repeated queries
/// </summary>
public ShaderType ShaderType { get; }
#endregion
/// <summary>
/// Get or set the source of this shader (glShaderSource)
/// </summary>
public string Source
{
get
{
var sb = new StringBuilder(1024);
GL.GetShaderSource(Id, sb.Capacity, out int length, sb);
return sb.ToString();
}
set { GL.ShaderSource(Id, value); }
}
/// <summary>
/// Check the compilation status of this shader
/// </summary>
public bool Compiled
{
get
{
GL.GetShader(Id, ShaderParameter.CompileStatus, out int res);
return res != 0;
}
}
public string InfoLog => GL.GetShaderInfoLog(Id);
#endregion
#region Methods
/// <summary>
/// Get a parameter of this shader (glGetShader)
/// </summary>
/// <param name="parameter">The parameter to get</param>
/// <returns>The parameter value</returns>
public int Get(ShaderParameter parameter)
{
GL.GetShader(Id, parameter, out int res);
return res;
}
/// <summary>
/// Compile this shader (glCompileShader)
/// </summary>
public void Compile() => GL.CompileShader(Id);
#endregion
public override string ToString() => $"{ShaderType} ({Id})";
}
}

View File

@@ -1,78 +0,0 @@
using System;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Wrappers
{
/// <summary>
/// Wrapper class for OpenGL Texture objects
/// </summary>
internal sealed class TextureWrap : Wrapper
{
#region Constructor, GLDelete()
internal TextureWrap(TextureTarget target)
: base(GL.GenTexture())
{
Target = target;
}
protected override void GLDelete() => GL.DeleteTexture(Id);
#endregion
#region Properties
#region Stored
/// <summary>
/// The target for this texture; Texture type.
/// </summary>
public TextureTarget Target { get; }
#endregion
#endregion
#region Methods
/// <summary>
/// Bind this texture to the currently active TextureUnit (glBindTexture)
/// </summary>
public void Bind() => GL.BindTexture(Target, Id);
/// <summary>
/// Bind this texture to a particular TextureUnit (glActiveTexture, glBindTexture)
/// </summary>
/// <param name="unit">Unit to bind to</param>
public void Bind(int unit)
{
GL.ActiveTexture(TextureUnit.Texture0 + unit);
Bind();
}
/// <summary>
/// Set a texture parameter (glTexParameter)
/// </summary>
/// <param name="parameter">The parameter to set</param>
/// <param name="value">The value to set</param>
public void TexParameter(TextureParameterName parameter, int value) => GL.TexParameter(Target, parameter,
value);
/// <summary>
/// Upload data to this texture
/// </summary>
/// <param name="internalFormat">The number of color components in the texture</param>
/// <param name="width">The width of the texture</param>
/// <param name="height">The height of the texture</param>
/// <param name="format">The pixel format of the texture</param>
/// <param name="type">The type of the pixel data</param>
/// <param name="pixels">Location of the pixel data</param>
public void Image2D(PixelInternalFormat internalFormat, int width, int height, PixelFormat format,
PixelType type, IntPtr pixels) =>
GL.TexImage2D(Target, 0, internalFormat, width, height, 0, format, type, pixels);
#endregion
public override string ToString() => $"{Target} ({Id})";
}
}

View File

@@ -1,70 +0,0 @@
using System;
using NLog;
using OpenTK.Graphics;
namespace Diamond.Wrappers
{
internal abstract class Wrapper : IDisposable
{
/// <summary>
/// Logger for all Wrapper types.
/// </summary>
protected static readonly Logger Logger = LogManager.GetLogger("Wrapper");
/// <summary>
/// The OpenGL name of this object
/// </summary>
public int Id { get; private set; }
// Force wrapper types to generate an Id at creation time
protected Wrapper(int id)
{
Id = id;
}
public override string ToString() => $"{GetType().Name} {Id}";
#region IDisposable
/// <summary>
/// Delete this OpenGL object (glDelete*)
/// </summary>
protected abstract void GLDelete();
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
// no managed resources to dispose
if (GraphicsContext.CurrentContext == null)
Logger.Error("No graphics context, cannot delete {0}", this);
else
GLDelete();
Id = 0;
_disposed = true;
}
#region Implemented
private bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Wrapper()
{
Dispose(false);
}
#endregion
#endregion
}
}

View File

@@ -2,9 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Diamond.Render;
using Diamond.Shaders;
using Diamond.Textures;
using Diamond.Util;
using Newtonsoft.Json.Linq;
using OpenTK;
@@ -15,142 +13,31 @@ namespace hexworld
{
public class HexRender : GameWindow
{
#region Fields
#region Disposables
private Program _texPgm;
private Texture _doorTex;
private Texture _grassTex;
private Texture _stoneTex;
private Dictionary<string, VertexBuffer<ObjVertex>> _meshVbos;
private VertexBuffer<TileData>[] _tileVbos;
protected override void OnClosed(EventArgs e)
{
_texPgm?.Dispose();
_doorTex?.Dispose();
_grassTex?.Dispose();
_stoneTex?.Dispose();
foreach (var vbo in _meshVbos.Values)
vbo?.Dispose();
foreach (var vbo in _tileVbos)
vbo?.Dispose();
}
#endregion
private List<RenderGroup<TileData, ObjVertex>> _renderGroups;
private Camera _camera;
private double _time;
#endregion
public HexRender(int width, int height)
: base(width, height, new GraphicsMode(32, 24, 0, 0))
{
Width = width;
Height = Height;
Height = height;
X = (DisplayDevice.Default.Width - Width) / 2;
Y = (DisplayDevice.Default.Height - Height) / 2;
}
private Program _pgm;
/// <inheritdoc />
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
_pgm.Dispose();
}
/// <inheritdoc />
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_texPgm = Program.FromFiles("res/obj.vs.glsl", "res/obj.fs.glsl");
_doorTex = Texture.FromFile("res/door.png");
_grassTex = Texture.FromFile("res/grass.png");
_stoneTex = Texture.FromFile("res/stone.png");
var dir = "res";
var json = JObject.Parse(File.ReadAllText("res/level.json"));
var allTiles = json["tiles"]
.GroupBy(ti => ti["tex"])
.Select(g => g
.Select(ti => ti["pos"])
.Select(pos => pos.ToObject<Vector3>())
.Select(pos => new TileData(pos))
.ToArray())
.Select(arr => new SubArray<TileData>(arr))
.ToArray();
_tileVbos = VertexBuffer.FromArrays(allTiles, 0, "tiles");
var vertexBuffers = json["models"]
.Select(path => (string) path)
.Select(path => Path.Combine(dir, path))
.Select(VertexBuffer.FromWavefront)
.SelectMany(meshes => meshes)
.ToArray();
_meshVbos = vertexBuffers.ToDictionary(vbo => vbo.Name, vbo => vbo);
_camera = new Camera();
_renderGroups = new List<RenderGroup<TileData, ObjVertex>>
{
new RenderGroup<TileData, ObjVertex>()
{
Vertices = _meshVbos["Cube"],
Instance = _tileVbos[0],
Program = _texPgm,
Texture = _stoneTex,
Camera = _camera,
},
new RenderGroup<TileData, ObjVertex>()
{
Vertices = _meshVbos["Cube"],
Instance = _tileVbos[1],
Program = _texPgm,
Texture = _grassTex,
Camera = _camera,
}
};
}
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
_time += e.Time;
_camera.View = Matrix4.CreateRotationZ((float) _time / 3) *
Matrix4.LookAt(10 * Vector3.One, Vector3.Zero, Vector3.UnitZ);
_camera.Projection = Matrix4.CreateOrthographic(Width / 100f, Height / 100f, -100, 100);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Viewport(ClientRectangle);
GL.ClearColor(0.2392157F, 0.5607843F, 0.9960784F, 1f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Lequal);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
foreach (var renderGroup in _renderGroups)
{
renderGroup.Draw();
}
SwapBuffers();
_pgm = Program.FromFiles("res/obj.fs.glsl", "res/obj.vs.glsl");
}
}
}