Compare commits
11 Commits
master
...
restructur
| Author | SHA1 | Date | |
|---|---|---|---|
| a4dbb9aa6f | |||
| 5663331abf | |||
| 330b243779 | |||
| 942174f6bc | |||
| 61d5c62e5c | |||
| 17d7c75df5 | |||
| 2b48366c0a | |||
| 93d138623d | |||
| 9760093f39 | |||
| bc41ccd65b | |||
| ffc525a0fd |
43
Diamond/Attributes/VertexAttribAttribute.cs
Normal file
43
Diamond/Attributes/VertexAttribAttribute.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this field as a source for vertex attribute data
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class VertexAttribAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The attribute ID to send this value
|
||||
/// </summary>
|
||||
public int Attribute { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of elements for this attribute
|
||||
/// </summary>
|
||||
public int Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this attribute should be normalized
|
||||
/// </summary>
|
||||
public bool Normalized { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The eleemnt type for this attribute
|
||||
/// </summary>
|
||||
public VertexAttribPointerType Type { get; set; } = VertexAttribPointerType.Float;
|
||||
|
||||
/// <summary>
|
||||
/// Mark a field as a source for a vertex attribute
|
||||
/// </summary>
|
||||
/// <param name="attribute">The attribute ID for this value</param>
|
||||
/// <param name="size">The number of elements for this attribute</param>
|
||||
public VertexAttribAttribute(int attribute, int size)
|
||||
{
|
||||
Attribute = attribute;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Diamond/Attributes/VertexDataAttribute.cs
Normal file
29
Diamond/Attributes/VertexDataAttribute.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Diamond.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this struct's contents as vertex attribute information
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class VertexDataAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The VertexAttribDivisor for all attribs associated with this struct
|
||||
/// </summary>
|
||||
public int Divisor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Mark a struct as a source for a VAO's attrib pointer information
|
||||
/// </summary>
|
||||
/// <param name="divisor"></param>
|
||||
public VertexDataAttribute(int divisor = 0)
|
||||
{
|
||||
Divisor = divisor;
|
||||
}
|
||||
}
|
||||
}
|
||||
286
Diamond/Buffer.cs
Normal file
286
Diamond/Buffer.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using NLog;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrap an OpenGL buffer object
|
||||
/// </summary>
|
||||
public class Buffer : GLObject
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#region Static
|
||||
|
||||
private static readonly Dictionary<BufferTarget, Buffer> BoundBuffers;
|
||||
|
||||
/// <inheritdoc/>
|
||||
static Buffer()
|
||||
{
|
||||
BoundBuffers = new Dictionary<BufferTarget, Buffer>();
|
||||
foreach (var value in Enum.GetValues(typeof(BufferTarget)).Cast<BufferTarget>())
|
||||
BoundBuffers[value] = null;
|
||||
}
|
||||
|
||||
#region ArrayBuffer
|
||||
|
||||
/// <summary>
|
||||
/// The buffer will be used as a source for vertex data, but the connection is only made when
|
||||
/// glVertexAttribPointer is called. The pointer field of this function is taken as a byte
|
||||
/// offset from the beginning of whatever buffer is currently bound to this target.
|
||||
/// </summary>
|
||||
public static Buffer ArrayBuffer
|
||||
{
|
||||
get => BoundBuffers[BufferTarget.ArrayBuffer];
|
||||
set => Bind(BufferTarget.ArrayBuffer, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ElementArrayBuffer
|
||||
|
||||
/// <summary>
|
||||
/// All rendering functions of the form gl*Draw*Elements* will use the pointer field as a byte
|
||||
/// offset from the beginning of the buffer object bound to this target. The indices used for
|
||||
/// indexed rendering will be taken from the buffer object. Note that this binding target is
|
||||
/// part of a Vertex Array Objects state, so a VAO must be bound before binding a buffer here.
|
||||
/// </summary>
|
||||
public static Buffer ElementArrayBuffer
|
||||
{
|
||||
get => BoundBuffers[BufferTarget.ArrayBuffer];
|
||||
set => Bind(BufferTarget.ElementArrayBuffer, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DrawIndirectBuffer
|
||||
|
||||
/// <summary>
|
||||
/// The buffer bound to this target will be used as the source for the indirect data when performing
|
||||
/// indirect rendering. This is only available in core OpenGL 4.0 or with ARB_draw_indirect.
|
||||
/// </summary>
|
||||
public static Buffer DrawIndirectBuffer
|
||||
{
|
||||
get => BoundBuffers[BufferTarget.DrawIndirectBuffer];
|
||||
set => Bind(BufferTarget.DrawIndirectBuffer, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Bind a buffer to a target. If buffer is null, unbinds the target.
|
||||
/// </summary>
|
||||
/// <param name="target">The binding target</param>
|
||||
/// <param name="buffer">The buffer to bind, or 0 if null</param>
|
||||
public static void Bind(BufferTarget target, Buffer buffer)
|
||||
{
|
||||
GL.BindBuffer(target, buffer?.Id ?? 0);
|
||||
Logger.Debug("Bound {0} to {1}", (object) buffer ?? "default buffer", target);
|
||||
|
||||
BoundBuffers[target] = buffer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ctor, Delete()
|
||||
|
||||
/// <summary>
|
||||
/// Create a buffer object wrapper
|
||||
/// </summary>
|
||||
internal Buffer()
|
||||
: base(GL.GenBuffer())
|
||||
{
|
||||
Logger.Debug("Created {0}", this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Delete()
|
||||
{
|
||||
Logger.Debug("Disposing {0}", this);
|
||||
GL.DeleteBuffer(Id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
#region Queries
|
||||
|
||||
/// <summary>
|
||||
/// The usage hint for the current data store
|
||||
/// </summary>
|
||||
public BufferUsageHint Usage => (BufferUsageHint) Get(BufferParameterName.BufferUsage);
|
||||
|
||||
/// <summary>
|
||||
/// The size of the current data store in bytes
|
||||
/// </summary>
|
||||
public int Size => Get(BufferParameterName.BufferSize);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stored
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a property of this buffer
|
||||
/// </summary>
|
||||
/// <param name="param">The property to get</param>
|
||||
/// <returns>The int value of the property</returns>
|
||||
public int Get(BufferParameterName param)
|
||||
{
|
||||
ArrayBuffer = this;
|
||||
GL.GetBufferParameter(BufferTarget.ArrayBuffer, param, out int res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind this buffer to a target
|
||||
/// </summary>
|
||||
/// <param name="target">The binding target</param>
|
||||
public void Bind(BufferTarget target) => Bind(target, this);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() =>
|
||||
$"'Buffer {Id}'";
|
||||
|
||||
/// <summary>
|
||||
/// Upload data to this buffer. Deletes the existing data store and creates a new one
|
||||
/// from the marshalled size of T.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// BufferUsageHint should be based off of what the user will be doing with the buffer.
|
||||
///
|
||||
/// DRAW: The user will be writing data to the buffer, but the user will not read it.
|
||||
/// READ: The user will not be writing data, but the user will be reading it back.
|
||||
/// COPY: The user will be neither writing nor reading the data.
|
||||
///
|
||||
/// STATIC: The user will set the data once.
|
||||
/// DYNAMIC: The user will set the data occasionally.
|
||||
/// STREAM: The user will be changing the data after every use.Or almost every use.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">Data element type, used with Marshal.SizeOf to calculate size of data store</typeparam>
|
||||
/// <param name="data">Values to upload</param>
|
||||
/// <param name="usage">Usage hint for for this data.</param>
|
||||
public void Upload<T>(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw) where T : struct
|
||||
{
|
||||
Logger.Debug("Updating {0} data (replacing store)", this);
|
||||
ArrayBuffer = this;
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, data.SizeInBytes(), data, usage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload the data to this buffer within a range. Does not delete the existing data store.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to upload. The range is also applied to this array.</param>
|
||||
/// <param name="offset">The offset index of data to begin uploading</param>
|
||||
/// <param name="count">The number of T elements to upload</param>
|
||||
/// <typeparam name="T">Data element type. Offsets are applied according to the size of this in bytes.</typeparam>
|
||||
public void Upload<T>(T[] data, int offset, int count) where T : struct
|
||||
{
|
||||
Logger.Debug("Updating {0} data range ({1} for {2}] from type {3}",
|
||||
this, offset, count, typeof(T).Name);
|
||||
ArrayBuffer = this;
|
||||
GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr) offset, count, data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer, initialized with an array
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// BufferUsageHint should be based off of what the user will be doing with the buffer.
|
||||
///
|
||||
/// DRAW: The user will be writing data to the buffer, but the user will not read it.
|
||||
/// READ: The user will not be writing data, but the user will be reading it back.
|
||||
/// COPY: The user will be neither writing nor reading the data.
|
||||
///
|
||||
/// STATIC: The user will set the data once.
|
||||
/// DYNAMIC: The user will set the data occasionally.
|
||||
/// STREAM: The user will be changing the data after every use.Or almost every use.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">Data element type.</typeparam>
|
||||
/// <param name="data">Data used to initialize the buffer</param>
|
||||
/// <param name="usage">The usage hint for this buffer</param>
|
||||
/// <returns>The initialized buffer, or null if initialization failed</returns>
|
||||
public static Buffer<T> FromData<T>(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw)
|
||||
where T : struct
|
||||
{
|
||||
var buffer = new Buffer<T>();
|
||||
|
||||
buffer.Upload(data, usage);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic overload for Buffer. Caches properties like data, usage, and size.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data type used for all operations</typeparam>
|
||||
public sealed class Buffer<T> : Buffer where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached usage hint for the buffer's storage
|
||||
/// </summary>
|
||||
public new BufferUsageHint Usage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the buffer data storage in T units.
|
||||
/// </summary>
|
||||
public new int Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The data in this buffer. Not copied to the buffer until Upload() is called.
|
||||
/// </summary>
|
||||
public T[] Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Upload data to this buffer. Deletes the existing data store and creates a new one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// BufferUsageHint should be based off of what the user will be doing with the buffer.
|
||||
///
|
||||
/// DRAW: The user will be writing data to the buffer, but the user will not read it.
|
||||
/// READ: The user will not be writing data, but the user will be reading it back.
|
||||
/// COPY: The user will be neither writing nor reading the data.
|
||||
///
|
||||
/// STATIC: The user will set the data once.
|
||||
/// DYNAMIC: The user will set the data occasionally.
|
||||
/// STREAM: The user will be changing the data after every use.Or almost every use.
|
||||
/// </remarks>
|
||||
/// <param name="data">Values to upload</param>
|
||||
/// <param name="usage">Usage hint for for this data.</param>
|
||||
public void Upload(T[] data, BufferUsageHint usage = BufferUsageHint.StaticDraw)
|
||||
{
|
||||
base.Upload<T>(data, usage);
|
||||
Data = data;
|
||||
Usage = usage;
|
||||
Size = data.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upload the data to this buffer within a range. Does not delete the existing data store.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset index of data to begin uploading</param>
|
||||
/// <param name="count">The number of T elements to upload</param>
|
||||
public void Upload(int offset, int count)
|
||||
{
|
||||
base.Upload<T>(Data, offset, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\Diamond.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -35,7 +36,7 @@
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.4.4\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll</HintPath>
|
||||
@@ -51,26 +52,16 @@
|
||||
<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="Attributes\VertexAttribAttribute.cs" />
|
||||
<Compile Include="Attributes\VertexDataAttribute.cs" />
|
||||
<Compile Include="Buffer.cs" />
|
||||
<Compile Include="Extensions.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" />
|
||||
<Compile Include="VertexArray.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="NLog.config">
|
||||
|
||||
26
Diamond/Extensions.cs
Normal file
26
Diamond/Extensions.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Diamond
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
private static readonly Dictionary<Type, int> ByteSizes = new Dictionary<Type, int>();
|
||||
|
||||
public static int SizeInBytes(this Type type)
|
||||
{
|
||||
if (!ByteSizes.ContainsKey(type))
|
||||
ByteSizes[type] = Marshal.SizeOf(type);
|
||||
return ByteSizes[type];
|
||||
}
|
||||
|
||||
public static int SizeInBytes<T>(this T[] arr) where T : struct
|
||||
{
|
||||
return typeof(T).SizeInBytes() * arr.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,64 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace Diamond
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide managed access to OpenGL objects
|
||||
/// A wrapper class for any OpenGL object
|
||||
/// </summary>
|
||||
public abstract class GLObject : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Logger for all GLObjects
|
||||
/// </summary>
|
||||
protected static readonly Logger Logger = LogManager.GetLogger(nameof(GLObject));
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLogger.xsd"
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLogger.xsd NLog.xsd"
|
||||
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
|
||||
autoReload="true"
|
||||
throwExceptions="false"
|
||||
internalLoggerLevel="Off" internalLoggerFile="c:\temp\nlog-internal.log">
|
||||
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
|
||||
|
||||
<variable name="Layout" value="[${level}] ${logger}: ${message}" />
|
||||
|
||||
<!-- optional, add some variables
|
||||
https://github.com/nlog/NLog/wiki/Configuration-file#variables
|
||||
-->
|
||||
<variable name="myvar" value="myvalue"/>
|
||||
|
||||
<!--
|
||||
See https://github.com/nlog/nlog/wiki/Configuration-file
|
||||
for information on customizing logging rules and outputs.
|
||||
-->
|
||||
<targets>
|
||||
<target xsi:type="File" name="file" fileName="$Log/${shortdate}.log"
|
||||
layout="${Layout}"/>
|
||||
<target xsi:type="Debugger" name="debug" layout="${Layout}"/>
|
||||
|
||||
<!--
|
||||
add your targets here
|
||||
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
|
||||
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Write events to a file with the date in the filename.
|
||||
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
|
||||
layout="${longdate} ${uppercase:${level}} ${message}" />
|
||||
-->
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Info" writeTo="file"/>
|
||||
<logger name="*" minlevel="Trace" writeTo="debug"/>
|
||||
<!-- add your logging rules here -->
|
||||
|
||||
<!--
|
||||
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
|
||||
<logger name="*" minlevel="Debug" writeTo="f" />
|
||||
-->
|
||||
</rules>
|
||||
</nlog>
|
||||
|
||||
2087
Diamond/NLog.xsd
2087
Diamond/NLog.xsd
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,233 +1,307 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diamond.Wrappers;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond.Shaders
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages an OpenGL Program object
|
||||
/// Wrap and OpenGL program object
|
||||
/// </summary>
|
||||
public class Program : GLObject
|
||||
public sealed class Program : GLObject
|
||||
{
|
||||
internal readonly ProgramWrap Wrapper;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#region Static
|
||||
|
||||
private static Program _current;
|
||||
|
||||
/// <summary>
|
||||
/// The currently active program. Manually invoking glUseProgram will break this.
|
||||
/// The currently active program, or null if the default program is active.
|
||||
/// </summary>
|
||||
public static Program Current { get; private set; }
|
||||
public static Program Current
|
||||
{
|
||||
get => _current;
|
||||
set => Use(value);
|
||||
}
|
||||
|
||||
// keep a cache of uniform and attributes to prevent repeated queries
|
||||
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
|
||||
/// <summary>
|
||||
/// Use a program.
|
||||
/// </summary>
|
||||
/// <param name="program">The program to use, or null to use the default program</param>
|
||||
public static void Use(Program program)
|
||||
{
|
||||
if (program != null && !program.Linked)
|
||||
{
|
||||
Logger.Error("Cannot use {0} because it is not linked", program);
|
||||
program = null;
|
||||
}
|
||||
|
||||
GL.UseProgram(program?.Id ?? 0);
|
||||
Logger.Trace("Using {0}", (object) program ?? "default program");
|
||||
|
||||
_current = program;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ctor, Delete()
|
||||
|
||||
/// <inheritdoc />
|
||||
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>
|
||||
/// Gets the number of active uniforms
|
||||
/// </summary>
|
||||
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of active attributes
|
||||
/// </summary>
|
||||
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stored
|
||||
|
||||
/// <summary>
|
||||
/// The InfoLog for this program since it was last linked
|
||||
/// </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. invokes glGetProgram and returns an int result.
|
||||
/// </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. If linking fails, the InfoLog is updated, and attribute and uniform caches are reset.
|
||||
/// If linking is successful, attribute and uniform caches are generated
|
||||
/// </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. Equivalent to Program.Use(this);
|
||||
/// </summary>
|
||||
public void Use() => Use(this);
|
||||
|
||||
#region 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 program 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 program 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. Throws InvalidOperationException if the attribute
|
||||
/// is not found. This can be checked with HasAttribute
|
||||
/// </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. Throws InvalidOperationException if the uniform
|
||||
/// is not found. This can be checked with HasUniform
|
||||
/// </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. If shader is null, does nothing. If shader is not compiled
|
||||
/// it is still attached but program link will likely fail.
|
||||
/// </summary>
|
||||
/// <param name="shader">The shader to attach</param>
|
||||
private void Attach(Shader shader)
|
||||
public void Attach(Shader shader)
|
||||
{
|
||||
Wrapper.Attach(shader.Wrapper);
|
||||
if (shader == null)
|
||||
{
|
||||
Logger.Error("Cannot attach null shader to {0}", this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shader.Compiled)
|
||||
Logger.Warn("{0} is not compiled. Program link will likely fail.", shader);
|
||||
|
||||
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. If any shader is null or uncompiled it is still
|
||||
/// attached, although program link will likely fail.
|
||||
/// </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);
|
||||
|
||||
/// <summary>
|
||||
/// Create a program from compiled 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)
|
||||
{
|
||||
if (shaders == null)
|
||||
{
|
||||
Logger.Error("Cannot create program {0} with no shaders.", name);
|
||||
return null;
|
||||
}
|
||||
|
||||
var wrapper = new ProgramWrap();
|
||||
var service = new Program(wrapper, name);
|
||||
|
||||
Logger.Debug("Created {0}", service);
|
||||
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
if (shader == null)
|
||||
{
|
||||
Logger.Error("One or more shaders failed to compile - cannot create program {0}", name);
|
||||
service.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
service.Attach(shader);
|
||||
}
|
||||
|
||||
var linked = service.Link();
|
||||
|
||||
if (!linked)
|
||||
{
|
||||
Logger.Warn("Failed to link {0}", service);
|
||||
Logger.Debug("InfoLog for {0}", service);
|
||||
service.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
Logger.Debug("Successfully linked {0}", service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a program from compiled shaders
|
||||
/// </summary>
|
||||
/// <param name="shaders">The shaders to use in this program</param>
|
||||
/// <returns>The linked program, or null if initialization failed</returns>
|
||||
/// <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. If any shader is null or uncompiled it is still
|
||||
/// attached, although program link will likely fail.
|
||||
/// </summary>
|
||||
/// <param name="shaders">The shaders to use in this program</param>
|
||||
/// <returns>The linked program, or null if initialization failed</returns>
|
||||
/// <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)
|
||||
{
|
||||
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 (shaders == null)
|
||||
{
|
||||
Logger.Warn("Cannot create a program from no shaders.");
|
||||
Logger.Error("Cannot create program from no shaders.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var shaders = paths.Select(path => Shader.FromFile(path)).ToList();
|
||||
var program = new Program();
|
||||
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
program.Attach(shader);
|
||||
}
|
||||
|
||||
program.Link();
|
||||
|
||||
if (program.Linked) return program;
|
||||
|
||||
program.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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="files"></param>
|
||||
/// <returns>The linked program, or null if initialization failed</returns>
|
||||
public static Program FromFiles(params string[] files) => FromFiles((IEnumerable<string>) files);
|
||||
|
||||
/// <summary>
|
||||
/// 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="files"></param>
|
||||
/// <returns>The linked program, or null if initialization failed</returns>
|
||||
public static Program FromFiles(IEnumerable<string> files)
|
||||
{
|
||||
if (files == null)
|
||||
{
|
||||
Logger.Warn("Cannot create a program from no shaders");
|
||||
return null;
|
||||
}
|
||||
|
||||
var shaders = files.Select(Shader.FromFile).ToList();
|
||||
|
||||
var program = FromShaders(shaders);
|
||||
|
||||
foreach (var shader in shaders)
|
||||
{
|
||||
shader?.Dispose();
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
@@ -1,96 +1,168 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Diamond.Wrappers;
|
||||
using NLog;
|
||||
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;
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#region ctor, 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 +170,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 +192,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 +207,7 @@ namespace Diamond.Shaders
|
||||
}
|
||||
|
||||
var type = Extensions[ext];
|
||||
if (name == null)
|
||||
name = fileName;
|
||||
|
||||
return FromFile(path, type, name);
|
||||
return FromFile(path, type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,7 @@ namespace Diamond.Util
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (Length == 0)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Diamond/VertexArray.cs
Normal file
162
Diamond/VertexArray.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrap an OpenGL Vertex Array Object
|
||||
/// </summary>
|
||||
public sealed class VertexArray : GLObject
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#region Static
|
||||
|
||||
private static VertexArray _current;
|
||||
|
||||
/// <summary>
|
||||
/// The currently bound VAO, or null if no vao is bound.
|
||||
/// </summary>
|
||||
public static VertexArray Current
|
||||
{
|
||||
get => _current;
|
||||
set => Bind(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind a VAO to the context
|
||||
/// </summary>
|
||||
/// <param name="vao">The vao to bind, or null to unbind the current vao</param>
|
||||
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()
|
||||
|
||||
/// <inheritdoc />
|
||||
internal VertexArray()
|
||||
: base(GL.GenVertexArray())
|
||||
{
|
||||
Logger.Debug("Created {0}", this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Delete()
|
||||
{
|
||||
Logger.Debug("Disposing {0}", this);
|
||||
GL.DeleteVertexArray(Id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
#region Queries
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stored
|
||||
|
||||
private Buffer _elementArrayBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The ElementArrayBuffer associated with this VAO
|
||||
/// </summary>
|
||||
public Buffer ElementArrayBuffer
|
||||
{
|
||||
get => _elementArrayBuffer;
|
||||
set
|
||||
{
|
||||
Logger.Debug("Using {0} as ElementArrayBuffer for {1}", (object)value ?? "default buffer", this);
|
||||
Current = this;
|
||||
Buffer.ElementArrayBuffer = _elementArrayBuffer = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Attach a VBO to this VAO. Use attributes to infer the
|
||||
/// vertex attrib pointer information
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The struct to use for vertex attrib information. Must be marked with VertexDataAttribute</typeparam>
|
||||
/// <param name="buffer">The buffer to add</param>
|
||||
public void Attach<T>(Buffer<T> 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;
|
||||
}
|
||||
|
||||
Logger.Debug("Attaching {0} to {1}", buffer, this);
|
||||
|
||||
Current = this;
|
||||
Buffer.ArrayBuffer = buffer;
|
||||
|
||||
var stride = Marshal.SizeOf<T>();
|
||||
foreach (var field in typeof(T).GetFields())
|
||||
{
|
||||
var offset = Marshal.OffsetOf<T>(field.Name);
|
||||
foreach (var vai in field.GetCustomAttributes<VertexAttribAttribute>(false))
|
||||
{
|
||||
Logger.Debug($"Enabling attribute (id:{vai.Attribute}, size:{vai.Size}, " +
|
||||
$"type:{vai.Type}, norm:{vai.Normalized}, stride:{stride}, offset:{offset})");
|
||||
|
||||
GL.EnableVertexAttribArray(vai.Attribute);
|
||||
GL.VertexAttribPointer(vai.Attribute, vai.Size, vai.Type, vai.Normalized, stride, offset);
|
||||
GL.VertexAttribDivisor(vai.Attribute, vda.Divisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind this vao
|
||||
/// </summary>
|
||||
public void Bind() => Bind(this);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() =>
|
||||
$"'Vertex Array {Id}'";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty vertex array object. This contains no binding information
|
||||
/// by default, and must have any vertex buffer objects attached manually.
|
||||
/// </summary>
|
||||
/// <returns>A new Vertex Array Object, or null if creation failed</returns>
|
||||
public static VertexArray Create()
|
||||
{
|
||||
return new VertexArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="NLog" version="4.4.3" targetFramework="net461" />
|
||||
<package id="NLog.Config" version="4.4.3" targetFramework="net461" />
|
||||
<package id="NLog.Schema" version="4.4.3" targetFramework="net461" />
|
||||
<package id="NLog" version="4.4.4" targetFramework="net461" />
|
||||
<package id="NLog.Config" version="4.4.4" targetFramework="net461" />
|
||||
<package id="NLog.Schema" version="4.4.4" targetFramework="net461" />
|
||||
<package id="OpenTK" version="2.0.0" targetFramework="net452" />
|
||||
</packages>
|
||||
@@ -1,154 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Diamond.Render;
|
||||
using System.Diagnostics;
|
||||
using Diamond.Shaders;
|
||||
using Diamond.Textures;
|
||||
using Diamond.Util;
|
||||
using Newtonsoft.Json.Linq;
|
||||
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, 3)] public Vector3 Position;
|
||||
|
||||
public Vert(float x, float y, float z = 0)
|
||||
{
|
||||
Position = new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
[VertexData(1)]
|
||||
public struct Entity
|
||||
{
|
||||
[VertexAttrib(1, 3)] public Vector3 Position;
|
||||
|
||||
public Entity(float x, float y, float z = 0)
|
||||
{
|
||||
Position = new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
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 Buffer<Vert> _sqrBuff;
|
||||
private Buffer<uint> _sqrElem;
|
||||
private VertexArray _sqrAo;
|
||||
|
||||
private Buffer<Entity> _entBuff;
|
||||
|
||||
private Program _pgm;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnUnload(EventArgs e)
|
||||
{
|
||||
base.OnUnload(e);
|
||||
|
||||
_sqrAo?.Dispose();
|
||||
_sqrBuff?.Dispose();
|
||||
_sqrElem?.Dispose();
|
||||
|
||||
_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>>
|
||||
using (var vs = Shader.FromFile("res/direct.vs.glsl"))
|
||||
using (var white = Shader.FromFile("res/white.fs.glsl"))
|
||||
{
|
||||
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);
|
||||
_pgm = Program.FromShaders(vs, white);
|
||||
}
|
||||
|
||||
_sqrBuff = Buffer.FromData(new[]
|
||||
{
|
||||
new Vert(-.5f, -.5f),
|
||||
new Vert(+.5f, -.5f),
|
||||
new Vert(+.5f, +.5f),
|
||||
new Vert(-.5f, +.5f),
|
||||
});
|
||||
|
||||
_sqrElem = Buffer.FromData(new uint[]
|
||||
{
|
||||
0, 1, 2, 2, 3, 0
|
||||
});
|
||||
|
||||
_entBuff = Buffer.FromData(new[]
|
||||
{
|
||||
new Entity(-1, -1),
|
||||
new Entity(-1, 1),
|
||||
new Entity(1, -1),
|
||||
new Entity(1, 1),
|
||||
});
|
||||
|
||||
Program.Current = _pgm;
|
||||
_sqrAo = VertexArray.Create();
|
||||
_sqrAo.Attach(_sqrBuff);
|
||||
_sqrAo.Attach(_entBuff);
|
||||
_sqrAo.ElementArrayBuffer = _sqrElem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
GL.Enable(EnableCap.DepthTest);
|
||||
GL.DepthFunc(DepthFunction.Lequal);
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
var proj = Matrix4.CreateOrthographic(6f, 6f * Height / Width, -10f, 10f);
|
||||
var view = Matrix4.LookAt(Vector3.Zero, -Vector3.One, Vector3.UnitZ);
|
||||
|
||||
foreach (var renderGroup in _renderGroups)
|
||||
{
|
||||
renderGroup.Draw();
|
||||
}
|
||||
Program.Current = _pgm;
|
||||
VertexArray.Current = _sqrAo;
|
||||
GL.UniformMatrix4(0, false, ref proj);
|
||||
GL.UniformMatrix4(1, false, ref view);
|
||||
GL.DrawElementsInstanced(PrimitiveType.Triangles, _sqrElem.Size, DrawElementsType.UnsignedInt, IntPtr.Zero,
|
||||
_entBuff.Size);
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{AD9ED057-FB47-44CB-8839-22924B409706}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>hexworld</RootNamespace>
|
||||
<AssemblyName>hexworld</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
@@ -19,10 +19,12 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -33,6 +35,9 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
@@ -71,6 +76,15 @@
|
||||
<None Include="res\obj.fs.glsl">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="res\red.fs.glsl">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="res\white.fs.glsl">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="res\direct.vs.glsl">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="res\obj.vs.glsl">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
13
hexworld/res/direct.vs.glsl
Normal file
13
hexworld/res/direct.vs.glsl
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 440
|
||||
|
||||
in vec3 v;
|
||||
|
||||
in vec3 pos;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 view;
|
||||
|
||||
void main ()
|
||||
{
|
||||
gl_Position = proj * view * vec4(pos + v, 1);
|
||||
}
|
||||
6
hexworld/res/red.fs.glsl
Normal file
6
hexworld/res/red.fs.glsl
Normal file
@@ -0,0 +1,6 @@
|
||||
#version 440
|
||||
|
||||
void main ()
|
||||
{
|
||||
gl_FragColor = vec4(1,0,0,1);
|
||||
}
|
||||
6
hexworld/res/white.fs.glsl
Normal file
6
hexworld/res/white.fs.glsl
Normal file
@@ -0,0 +1,6 @@
|
||||
#version 440
|
||||
|
||||
void main ()
|
||||
{
|
||||
gl_FragColor = vec4(1);
|
||||
}
|
||||
Reference in New Issue
Block a user