Removed most files, began rewriting program, shader, globject
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,26 +51,11 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Buffers\Buffer.cs" />
|
|
||||||
<Compile Include="Buffers\VertexDataInfo.cs" />
|
|
||||||
<Compile Include="Render\Camera.cs" />
|
|
||||||
<Compile Include="Render\RenderGroup.cs" />
|
|
||||||
<Compile Include="Wrappers\BufferWrap.cs" />
|
|
||||||
<Compile Include="GLObject.cs" />
|
<Compile Include="GLObject.cs" />
|
||||||
<Compile Include="Wrappers\ProgramWrap.cs" />
|
|
||||||
<Compile Include="Wrappers\ShaderWrap.cs" />
|
|
||||||
<Compile Include="Wrappers\TextureWrap.cs" />
|
|
||||||
<Compile Include="Util\SubArray.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="Shaders\Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Shaders\Shader.cs" />
|
<Compile Include="Shaders\Shader.cs" />
|
||||||
<Compile Include="Textures\Texture.cs" />
|
|
||||||
<Compile Include="Buffers\VertexPointerAttribute.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="NLog.config">
|
<Content Include="NLog.config">
|
||||||
|
|||||||
@@ -1,26 +1,64 @@
|
|||||||
using System;
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace Diamond
|
namespace Diamond
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Provide managed access to OpenGL objects
|
|
||||||
/// </summary>
|
|
||||||
public abstract class GLObject : IDisposable
|
public abstract class GLObject : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logger for all GLObjects
|
/// The logger for GLObject-related info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected static readonly Logger Logger = LogManager.GetLogger(nameof(GLObject));
|
protected internal static Logger Logger = LogManager.GetLogger(nameof(GLObject));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of this GLObject used for identification
|
/// The OpenGL object name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; protected set; } = nameof(GLObject);
|
public int Id { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate Dispose to underlying wrapper class
|
/// Force object name assignment
|
||||||
/// </summary>
|
/// </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,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,291 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Diamond.Wrappers;
|
using System.Text;
|
||||||
using OpenTK.Graphics.OpenGL4;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
|
||||||
namespace Diamond.Shaders
|
namespace Diamond.Shaders
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages an OpenGL Program object
|
/// Wrap and OpenGL program object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Program : GLObject
|
public class Program : GLObject
|
||||||
{
|
{
|
||||||
internal readonly ProgramWrap Wrapper;
|
#region Static
|
||||||
|
|
||||||
|
private static Program _current;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently active program. Manually invoking glUseProgram will break this.
|
/// The currently active program
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Program Current { get; private set; }
|
public static Program Current
|
||||||
|
{
|
||||||
|
get => _current;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!value?.Linked ?? false)
|
||||||
|
{
|
||||||
|
Logger.Error("Cannot use program {0}", value);
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
// keep a cache of uniform and attributes to prevent repeated queries
|
Logger.Debug("Using program {0}", value);
|
||||||
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
|
GL.UseProgram((_current = value)?.Id ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor, Delete()
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a program object wrapper
|
||||||
|
/// </summary>
|
||||||
|
private Program()
|
||||||
|
: base(GL.CreateProgram())
|
||||||
|
{
|
||||||
|
Logger.Debug("Created {0}", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Delete()
|
||||||
|
{
|
||||||
|
Logger.Debug("Disposing {0}", this);
|
||||||
|
GL.DeleteProgram(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
#region Queries
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of active uniforms
|
||||||
|
/// </summary>
|
||||||
|
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of active attributes
|
||||||
|
/// </summary>
|
||||||
|
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Stored
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The InfoLog for this program
|
||||||
|
/// </summary>
|
||||||
|
public string InfoLog { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The link status of this program
|
||||||
|
/// </summary>
|
||||||
|
public bool Linked { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a property of this program
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="param">The program property to get</param>
|
||||||
|
/// <returns>The int value of the program property</returns>
|
||||||
|
private int Get(GetProgramParameterName param)
|
||||||
|
{
|
||||||
|
GL.GetProgram(Id, param, out int res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to link this program
|
||||||
|
/// </summary>
|
||||||
|
public void Link()
|
||||||
|
{
|
||||||
|
_attributes.Clear();
|
||||||
|
_uniforms.Clear();
|
||||||
|
|
||||||
|
Logger.Debug("Linking {0}", this);
|
||||||
|
GL.LinkProgram(Id);
|
||||||
|
// link status can only change after link attempt
|
||||||
|
Linked = Get(GetProgramParameterName.LinkStatus) != 0;
|
||||||
|
|
||||||
|
if (Linked)
|
||||||
|
{
|
||||||
|
Logger.Trace("Successfully linked {0}", this);
|
||||||
|
|
||||||
|
for (var i = 0; i < ActiveAttributes; i++)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(256);
|
||||||
|
GL.GetActiveAttrib(Id, i, sb.Capacity, out int length, out int size, out ActiveAttribType type, sb);
|
||||||
|
var name = sb.ToString();
|
||||||
|
_attributes[name] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < ActiveUniforms; i++)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(256);
|
||||||
|
GL.GetActiveUniform(Id, i, sb.Capacity, out int length, out int size, out ActiveUniformType type,
|
||||||
|
sb);
|
||||||
|
var name = sb.ToString();
|
||||||
|
_uniforms[name] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InfoLog = GL.GetProgramInfoLog(Id).Trim();
|
||||||
|
|
||||||
|
Logger.Error("Failed to link {0}", this);
|
||||||
|
Logger.Trace("InfoLog for {0}:\n{1}", this, InfoLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use this program
|
||||||
|
/// <para></para>
|
||||||
|
/// Equivalent to <code>Program.Current = value</code>
|
||||||
|
/// </summary>
|
||||||
|
public void Use() => Current = this;
|
||||||
|
|
||||||
|
#region Attribute Locations
|
||||||
|
|
||||||
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
|
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
|
||||||
|
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
|
||||||
internal Program(ProgramWrap wrapper, string name)
|
|
||||||
{
|
|
||||||
Wrapper = wrapper;
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the program has a uniform
|
/// Check if this program has an active attribute
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the uniform</param>
|
/// <param name="name">The attribute name</param>
|
||||||
/// <returns>Whether the program has this uniform</returns>
|
/// <returns>Whether the progrma has an active attribute</returns>
|
||||||
public bool HasUniform(string name)
|
public bool HasAttribute(string name) => _attributes.ContainsKey(name);
|
||||||
{
|
|
||||||
return _uniforms.ContainsKey(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the location of a uniform
|
/// Check if this program has an active uniform
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the uniform</param>
|
/// <param name="name">The uniform name</param>
|
||||||
/// <returns>The location of the uniform</returns>
|
/// <returns>Whether the progrma has an active uniform</returns>
|
||||||
public int UniformLocation(string name)
|
public bool HasUniform(string name) => _uniforms.ContainsKey(name);
|
||||||
{
|
|
||||||
if (HasUniform(name)) return _uniforms[name];
|
|
||||||
throw new KeyNotFoundException($"Shader {this} does not contain uniform {name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if the program has an attribute
|
/// Get the location of an attribute by name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the attribute</param>
|
/// <param name="name">The attribute name</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>
|
|
||||||
/// <returns>The location of the attribute</returns>
|
/// <returns>The location of the attribute</returns>
|
||||||
public int AttributeLocation(string name)
|
public int AttributeLocation(string name)
|
||||||
{
|
{
|
||||||
if (HasAttribute(name)) return _attributes[name];
|
if (!HasAttribute(name))
|
||||||
throw new KeyNotFoundException($"Shader {this} does not contain attribute {name}");
|
throw new InvalidOperationException($"{this} does not have active attribute '{name}'");
|
||||||
|
return _attributes[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use this Program to render. Also updates Program.Current
|
/// Get the location of an uniform by name
|
||||||
/// </summary>
|
/// </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();
|
if (!HasUniform(name)) throw new InvalidOperationException($"{this} does not have active uniform '{name}'");
|
||||||
Current = this;
|
return _uniforms[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method to try to link this program
|
/// Attach a shader to 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
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shader">The shader to attach</param>
|
/// <param name="shader">The shader to attach</param>
|
||||||
private void Attach(Shader shader)
|
public void Attach(Shader shader)
|
||||||
{
|
{
|
||||||
Wrapper.Attach(shader.Wrapper);
|
Logger.Debug("Attaching {0} to {1}", shader, this);
|
||||||
|
GL.AttachShader(Id, shader.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"Program {Wrapper} \'{Name}\'";
|
/// <inheritdoc />
|
||||||
|
public override string ToString() =>
|
||||||
|
$"'Program {Id}'";
|
||||||
|
|
||||||
public override void Dispose()
|
#endregion
|
||||||
{
|
|
||||||
Logger.Debug("Disposing {0}", this);
|
|
||||||
Wrapper.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Factory Methods
|
#region Factory Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a program from compiled shaders
|
/// Create and link a program from precompiled shaders
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of this GLObject</param>
|
/// <param name="shaders">The shaders used in this program</param>
|
||||||
/// <param name="shaders">The shaders to use in this program</param>
|
/// <returns>A linked program, or null if initialization failed</returns>
|
||||||
/// <returns>The linked program, or null if initialization failed</returns>
|
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
|
||||||
public static Program FromShaders(string name, params Shader[] shaders) => FromShaders(name,
|
|
||||||
(IEnumerable<Shader>) shaders);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a program from compiled shaders
|
/// Create and link a program from precompiled shaders
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of this GLObject</param>
|
/// <param name="shaders">The shaders used in this program</param>
|
||||||
/// <param name="shaders">The shaders to use in this program</param>
|
/// <returns>A linked program, or null if initialization failed</returns>
|
||||||
/// <returns>The linked program, or null if initialization failed</returns>
|
public static Program FromShaders(IEnumerable<Shader> shaders)
|
||||||
public static Program FromShaders(string name, IEnumerable<Shader>shaders)
|
|
||||||
{
|
{
|
||||||
if (shaders == null)
|
if (shaders == null)
|
||||||
{
|
{
|
||||||
Logger.Error("Cannot create program {0} with no shaders.", name);
|
Logger.Error("Cannot create program from no shaders.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var wrapper = new ProgramWrap();
|
var program = new Program();
|
||||||
var service = new Program(wrapper, name);
|
|
||||||
|
|
||||||
Logger.Debug("Created {0}", service);
|
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
foreach (var shader in shaders)
|
||||||
{
|
{
|
||||||
if (shader == null)
|
if (shader == null)
|
||||||
{
|
{
|
||||||
Logger.Error("One or more shaders failed to compile - cannot create program {0}", name);
|
Logger.Error("One or more shaders is null - cannot create program");
|
||||||
service.Dispose();
|
program.Dispose();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
program.Attach(shader);
|
||||||
service.Attach(shader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var linked = service.Link();
|
program.Link();
|
||||||
|
|
||||||
if (!linked)
|
if (program.Linked) return program;
|
||||||
{
|
|
||||||
Logger.Warn("Failed to link {0}", service);
|
|
||||||
Logger.Debug("InfoLog for {0}", service);
|
|
||||||
service.Dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Debug("Successfully linked {0}", service);
|
program.Dispose();
|
||||||
|
return null;
|
||||||
return service;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a program from compiled shaders
|
/// Create and link a program from glsl source files. Shader types must be inferrable from file extensions.
|
||||||
|
/// <para></para>
|
||||||
|
/// See <see cref="Shader.FromFile(string,ShaderType)"/> for file extension details
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shaders">The shaders to use in this program</param>
|
/// <param name="files"></param>
|
||||||
/// <returns>The linked program, or null if initialization failed</returns>
|
/// <returns>The linked program, or null if initialization failed</returns>
|
||||||
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
|
public static Program FromFiles(params string[] files) => FromFiles((IEnumerable<string>) files);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a program from compiled shaders
|
/// Create and link a program from glsl source files. Shader types must be inferrable from file extensions.
|
||||||
|
/// <para></para>
|
||||||
|
/// See <see cref="Shader.FromFile(string,ShaderType)"/> for file extension details
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shaders">The shaders to use in this program</param>
|
/// <param name="files"></param>
|
||||||
/// <returns>The linked program, or null if initialization failed</returns>
|
/// <returns>The linked program, or null if initialization failed</returns>
|
||||||
public static Program FromShaders(IEnumerable<Shader> shaders)
|
public static Program FromFiles(IEnumerable<string> files)
|
||||||
{
|
{
|
||||||
var shaderList = shaders.ToList(); // prevent multiple enumeration
|
if (files == null)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Logger.Warn("Cannot create a program from no shaders.");
|
Logger.Warn("Cannot create a program from no shaders");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var shaders = paths.Select(path => Shader.FromFile(path)).ToList();
|
var shaders = files.Select(Shader.FromFile).ToList();
|
||||||
|
|
||||||
var program = FromShaders(shaders);
|
var program = FromShaders(shaders);
|
||||||
|
|
||||||
foreach (var shader in shaders)
|
foreach (var shader in shaders)
|
||||||
|
{
|
||||||
shader?.Dispose();
|
shader?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,165 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Diamond.Wrappers;
|
|
||||||
using OpenTK.Graphics.OpenGL4;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
|
||||||
namespace Diamond.Shaders
|
namespace Diamond.Shaders
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manges a OpenGL Shader object
|
/// Wrap an OpenGL shader object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Shader : GLObject
|
public sealed class Shader : GLObject
|
||||||
{
|
{
|
||||||
internal readonly ShaderWrap Wrapper;
|
#region Constructor, Delete()
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The source used to create this shader
|
/// Create a shader object wrapper
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// The type of this shader
|
/// The type of this shader
|
||||||
/// </summary>
|
/// </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;
|
GL.GetShader(Id, param, out int res);
|
||||||
Source = source;
|
return res;
|
||||||
Type = type;
|
|
||||||
Name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => Name == null
|
/// <summary>
|
||||||
? $"{Wrapper}"
|
/// Try to compile this shader
|
||||||
: $"{Wrapper} \'{Name}\'";
|
/// </summary>
|
||||||
|
public void Compile()
|
||||||
public override void Dispose()
|
|
||||||
{
|
{
|
||||||
Logger.Debug("Disposing {0}", this);
|
Logger.Debug("Compiling {0}", this);
|
||||||
Wrapper.Dispose();
|
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
|
#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>
|
private static readonly Dictionary<string, ShaderType> Extensions = new Dictionary<string, ShaderType>
|
||||||
{
|
{
|
||||||
[".vs"] = ShaderType.VertexShader,
|
[".vs"] = ShaderType.VertexShader,
|
||||||
[".vert"] = ShaderType.VertexShader,
|
[".vert"] = ShaderType.VertexShader,
|
||||||
[".fs"] = ShaderType.FragmentShader,
|
|
||||||
[".frag"] = ShaderType.FragmentShader,
|
|
||||||
[".gs"] = ShaderType.GeometryShader,
|
[".gs"] = ShaderType.GeometryShader,
|
||||||
[".geom"] = ShaderType.GeometryShader,
|
[".geom"] = ShaderType.GeometryShader,
|
||||||
|
[".fs"] = ShaderType.FragmentShader,
|
||||||
|
[".frag"] = ShaderType.FragmentShader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create and compile a shader from glsl source code
|
/// Create and compile a shader from source
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The glsl source</param>
|
/// <param name="source">The GLSL source for the shader</param>
|
||||||
/// <param name="type">The type of shader to create</param>
|
/// <param name="type">The type of the shader</param>
|
||||||
/// <param name="name">The name of this GLObject</param>
|
/// <returns>The compiled shader, or null if initialization failed</returns>
|
||||||
/// <returns>The compiled Shader, or null if initialization failed</returns>
|
public static Shader FromSource(string source, ShaderType type)
|
||||||
public static Shader FromSource(string source, ShaderType type, string name = "Shader")
|
|
||||||
{
|
{
|
||||||
var wrapper = new ShaderWrap(type);
|
var shader = new Shader(type) {Source = source};
|
||||||
var service = new Shader(wrapper, source, type, name);
|
shader.Compile();
|
||||||
|
|
||||||
Logger.Debug("Created {0}", service);
|
if (!shader.Compiled)
|
||||||
|
|
||||||
wrapper.Source = source;
|
|
||||||
wrapper.Compile();
|
|
||||||
|
|
||||||
if (!wrapper.Compiled)
|
|
||||||
{
|
{
|
||||||
Logger.Warn("Failed to compile {0}", service);
|
shader.Dispose();
|
||||||
Logger.Debug("InfoLog for {0}", service);
|
|
||||||
service.Dispose();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Debug("Successfully compiled {0}", service);
|
return shader;
|
||||||
|
|
||||||
return service;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create and compile a shader from a glsl source file
|
/// Create and compile a shader from a source file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to the glsl source file</param>
|
/// <param name="path">The path to the source file</param>
|
||||||
/// <param name="type">The type of the shader to create</param>
|
/// <param name="type">The type of the shader</param>
|
||||||
/// <param name="name">The name of this GLObject</param>
|
/// <returns>The compiled shader, or null if initialization failed</returns>
|
||||||
/// <returns></returns>
|
public static Shader FromFile(string path, ShaderType type)
|
||||||
public static Shader FromFile(string path, ShaderType type, string name = null)
|
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
{
|
{
|
||||||
@@ -98,21 +167,20 @@ namespace Diamond.Shaders
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == null)
|
var source = File.ReadAllText(path);
|
||||||
name = Path.GetFileNameWithoutExtension(path);
|
return FromSource(source, type);
|
||||||
|
|
||||||
return FromSource(File.ReadAllText(path), type, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create and compile a shader from a glsl source file. Shader type is inferred from file extension.
|
/// Create and compile a shader from a source file, and infer the type of the shader from the file extension.
|
||||||
/// Extension must be .vs, .vert, .fs, .frag, .gs, or .geom. This can optionally be followed by .glsl or .txt,
|
/// <para></para>
|
||||||
/// but the shader type extension must be present.
|
/// 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>
|
/// </summary>
|
||||||
/// <param name="path">The path to the glsl source file</param>
|
/// <param name="path"></param>
|
||||||
/// <param name="name">The name of this GLObject</param>
|
/// <returns></returns>
|
||||||
/// <returns>The compiled shader, or null if initialization failed or shader type cannot be inferred</returns>
|
public static Shader FromFile(string path)
|
||||||
public static Shader FromFile(string path, string name = null)
|
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
{
|
{
|
||||||
@@ -121,12 +189,12 @@ namespace Diamond.Shaders
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ext = Path.GetExtension(path);
|
var ext = Path.GetExtension(path);
|
||||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
var file = Path.GetFileNameWithoutExtension(path);
|
||||||
|
|
||||||
// get sub-extension if real extension is not valid
|
// get sub-extension if real extension is not valid
|
||||||
if (ext != null)
|
if (ext != null)
|
||||||
if (!Extensions.ContainsKey(ext))
|
if (!Extensions.ContainsKey(ext))
|
||||||
ext = Path.GetExtension(fileName);
|
ext = Path.GetExtension(file);
|
||||||
|
|
||||||
// if no extension, no sub-extension, or invalid sub-extension
|
// if no extension, no sub-extension, or invalid sub-extension
|
||||||
if (ext == null || !Extensions.ContainsKey(ext))
|
if (ext == null || !Extensions.ContainsKey(ext))
|
||||||
@@ -136,10 +204,7 @@ namespace Diamond.Shaders
|
|||||||
}
|
}
|
||||||
|
|
||||||
var type = Extensions[ext];
|
var type = Extensions[ext];
|
||||||
if (name == null)
|
return FromFile(path, type);
|
||||||
name = fileName;
|
|
||||||
|
|
||||||
return FromFile(path, type, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,9 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Diamond.Render;
|
|
||||||
using Diamond.Shaders;
|
using Diamond.Shaders;
|
||||||
using Diamond.Textures;
|
|
||||||
using Diamond.Util;
|
using Diamond.Util;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@@ -15,142 +13,31 @@ namespace hexworld
|
|||||||
{
|
{
|
||||||
public class HexRender : GameWindow
|
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)
|
public HexRender(int width, int height)
|
||||||
: base(width, height, new GraphicsMode(32, 24, 0, 0))
|
: base(width, height, new GraphicsMode(32, 24, 0, 0))
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = Height;
|
Height = height;
|
||||||
X = (DisplayDevice.Default.Width - Width) / 2;
|
X = (DisplayDevice.Default.Width - Width) / 2;
|
||||||
Y = (DisplayDevice.Default.Height - Height) / 2;
|
Y = (DisplayDevice.Default.Height - Height) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Program _pgm;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnUnload(EventArgs e)
|
||||||
|
{
|
||||||
|
base.OnUnload(e);
|
||||||
|
|
||||||
|
_pgm.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override void OnLoad(EventArgs e)
|
protected override void OnLoad(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnLoad(e);
|
base.OnLoad(e);
|
||||||
|
|
||||||
_texPgm = Program.FromFiles("res/obj.vs.glsl", "res/obj.fs.glsl");
|
_pgm = Program.FromFiles("res/obj.fs.glsl", "res/obj.vs.glsl");
|
||||||
|
|
||||||
_doorTex = Texture.FromFile("res/door.png");
|
|
||||||
_grassTex = Texture.FromFile("res/grass.png");
|
|
||||||
_stoneTex = Texture.FromFile("res/stone.png");
|
|
||||||
|
|
||||||
var dir = "res";
|
|
||||||
|
|
||||||
var json = JObject.Parse(File.ReadAllText("res/level.json"));
|
|
||||||
|
|
||||||
var allTiles = json["tiles"]
|
|
||||||
.GroupBy(ti => ti["tex"])
|
|
||||||
.Select(g => g
|
|
||||||
.Select(ti => ti["pos"])
|
|
||||||
.Select(pos => pos.ToObject<Vector3>())
|
|
||||||
.Select(pos => new TileData(pos))
|
|
||||||
.ToArray())
|
|
||||||
.Select(arr => new SubArray<TileData>(arr))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
_tileVbos = VertexBuffer.FromArrays(allTiles, 0, "tiles");
|
|
||||||
|
|
||||||
var vertexBuffers = json["models"]
|
|
||||||
.Select(path => (string) path)
|
|
||||||
.Select(path => Path.Combine(dir, path))
|
|
||||||
.Select(VertexBuffer.FromWavefront)
|
|
||||||
.SelectMany(meshes => meshes)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
_meshVbos = vertexBuffers.ToDictionary(vbo => vbo.Name, vbo => vbo);
|
|
||||||
|
|
||||||
_camera = new Camera();
|
|
||||||
|
|
||||||
_renderGroups = new List<RenderGroup<TileData, ObjVertex>>
|
|
||||||
{
|
|
||||||
new RenderGroup<TileData, ObjVertex>()
|
|
||||||
{
|
|
||||||
Vertices = _meshVbos["Cube"],
|
|
||||||
Instance = _tileVbos[0],
|
|
||||||
Program = _texPgm,
|
|
||||||
Texture = _stoneTex,
|
|
||||||
Camera = _camera,
|
|
||||||
},
|
|
||||||
new RenderGroup<TileData, ObjVertex>()
|
|
||||||
{
|
|
||||||
Vertices = _meshVbos["Cube"],
|
|
||||||
Instance = _tileVbos[1],
|
|
||||||
Program = _texPgm,
|
|
||||||
Texture = _grassTex,
|
|
||||||
Camera = _camera,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnUpdateFrame(e);
|
|
||||||
|
|
||||||
_time += e.Time;
|
|
||||||
|
|
||||||
_camera.View = Matrix4.CreateRotationZ((float) _time / 3) *
|
|
||||||
Matrix4.LookAt(10 * Vector3.One, Vector3.Zero, Vector3.UnitZ);
|
|
||||||
_camera.Projection = Matrix4.CreateOrthographic(Width / 100f, Height / 100f, -100, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnRenderFrame(FrameEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnRenderFrame(e);
|
|
||||||
|
|
||||||
GL.Viewport(ClientRectangle);
|
|
||||||
|
|
||||||
GL.ClearColor(0.2392157F, 0.5607843F, 0.9960784F, 1f);
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
||||||
|
|
||||||
GL.Enable(EnableCap.DepthTest);
|
|
||||||
GL.DepthFunc(DepthFunction.Lequal);
|
|
||||||
GL.Enable(EnableCap.Blend);
|
|
||||||
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
|
||||||
|
|
||||||
foreach (var renderGroup in _renderGroups)
|
|
||||||
{
|
|
||||||
renderGroup.Draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
SwapBuffers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user