Added documentation to all GLObject types

This commit is contained in:
2017-03-01 16:30:54 -05:00
parent 4f9843b632
commit 88ab90f186
9 changed files with 240 additions and 59 deletions

View File

@@ -8,6 +8,10 @@ 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
{
private readonly BufferWrap _buffer;
@@ -16,8 +20,15 @@ namespace Diamond.Buffers
private readonly int _size;
/// <summary>
/// The target for this buffer; its type
/// </summary>
public BufferTarget Target => _buffer.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 => _buffer.Usage;
@@ -32,12 +43,31 @@ namespace Diamond.Buffers
_vdi = VertexDataInfo.GetInfo<T>();
}
/// <summary>
/// Upload data to this buffer
/// </summary>
/// <param name="data">The data to upload</param>
public void Data(T[] data) => _buffer.Data(_size, 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) => _buffer.SubData(_size, 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)
@@ -60,6 +90,13 @@ namespace Diamond.Buffers
? $"Buffer<{typeof(T).Name}> {Target} ({Id})"
: $"Buffer<{typeof(T).Name}> {Target} {Name} ({Id})";
/// <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);
@@ -70,6 +107,14 @@ namespace Diamond.Buffers
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);
@@ -80,11 +125,29 @@ namespace Diamond.Buffers
}
}
/// <summary>
/// Class for static Buffer operations and public factory methods
/// </summary>
public static class Buffer
{
/// <summary>
/// Create an empty buffer of this type
/// </summary>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
public static Buffer<T> Empty<T>(BufferTarget target, BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct => Buffer<T>.Empty(target, usage, name);
/// <summary>
/// Create a buffer of this type and upload data
/// </summary>
/// <param name="data">The data to upload</param>
/// <param name="target">The buffer target</param>
/// <param name="usage">The initial usage hint</param>
/// <param name="name">The name of this GLObject</param>
/// <returns>The buffer, or null if initialization failed</returns>
public static Buffer<T> FromData<T>(T[] data, BufferTarget target,
BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct => Buffer<T>.FromData(data, target, usage, name);

View File

@@ -60,13 +60,12 @@
<Compile Include="Util\SubArray.cs" />
<Compile Include="Buffers\VertexDataAttribute.cs" />
<Compile Include="Wrappers\Wrapper.cs" />
<Compile Include="Level\TileData.cs" />
<Compile Include="Util\TileData.cs" />
<Compile Include="Util\Mesh.cs" />
<Compile Include="Util\ObjVertex.cs" />
<Compile Include="Shaders\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shaders\Shader.cs" />
<Compile Include="Shaders\ShaderException.cs" />
<Compile Include="Textures\Texture.cs" />
<Compile Include="Buffers\VertexPointerAttribute.cs" />
</ItemGroup>

View File

@@ -4,17 +4,33 @@ using NLog;
namespace Diamond
{
/// <summary>
/// Provide managed access to OpenGL objects
/// </summary>
public abstract class GLObject : IDisposable
{
/// <summary>
/// Logger for all GLObjects
/// </summary>
protected static readonly Logger Logger = LogManager.GetLogger("GLObject");
private bool _disposed;
/// <summary>
/// Name of this GLObject used for identification
/// </summary>
public string Name { get; protected set; } = "GLObject";
/// <summary>
/// Underlying managed wrapper to this OpenGL object
/// </summary>
internal abstract Wrapper Wrapper { get; }
/// <summary>
/// The OpenGL name of this object
/// </summary>
public int Id => Wrapper.Id;
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (_disposed)
@@ -29,6 +45,10 @@ namespace Diamond
_disposed = true;
}
#region Implemented
private bool _disposed;
public void Dispose()
{
Dispose(true);
@@ -39,5 +59,9 @@ namespace Diamond
{
Dispose(false);
}
#endregion
#endregion
}
}

View File

@@ -6,14 +6,22 @@ using OpenTK.Graphics.OpenGL4;
namespace Diamond.Shaders
{
/// <summary>
/// Manages an OpenGL Program object
/// </summary>
public class Program : GLObject
{
private readonly ProgramWrap _program;
internal override Wrapper Wrapper => _program;
/// <summary>
/// The currently active program. Manually invoking glUseProgram will break this.
/// </summary>
public static Program Current { get; private set; }
// keep a cache of uniform and attributes to prevent repeated queries
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
internal Program(ProgramWrap program, string name)
@@ -22,24 +30,42 @@ namespace Diamond.Shaders
Name = name;
}
// todo change these to not use int? - possibly use TryGet, or return negative value if not present
/// <summary>
/// Get the location of a uniform
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <returns>The location, or no value if uniform not present</returns>
public int? UniformLocation(string name)
{
if (_uniforms.ContainsKey(name)) return _uniforms[name];
return null;
}
/// <summary>
/// Get the location of an attribute
/// </summary>
/// <param name="name">The name of the attribute</param>
/// <returns>The location, or no value if attribute not present</returns>
public int? AttributeLocation(string name)
{
if (_attributes.ContainsKey(name)) return _attributes[name];
return null;
}
/// <summary>
/// Use this Program to render. Also updates Program.Current
/// </summary>
public void Use()
{
GL.UseProgram(Id);
Current = this;
}
/// <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.
@@ -49,6 +75,10 @@ namespace Diamond.Shaders
Current = null;
}
/// <summary>
/// Helper method to try to link this program
/// </summary>
/// <returns></returns>
private bool Link()
{
_uniforms.Clear();
@@ -68,6 +98,10 @@ namespace Diamond.Shaders
return true;
}
/// <summary>
/// Helper method to attach a shader to this program
/// </summary>
/// <param name="shader">The shader to attach</param>
private void Attach(Shader shader)
{
_program.Attach((ShaderWrap) shader.Wrapper);
@@ -75,16 +109,23 @@ namespace Diamond.Shaders
public override string ToString() => $"Program \'{Name}\' ({Id})";
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
#region Factory Methods
/// <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, 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)
@@ -110,9 +151,9 @@ namespace Diamond.Shaders
service.Attach(shader);
}
service.Link();
var linked = service.Link();
if (!wrapper.Linked)
if (!linked)
{
Logger.Warn("Failed to link {0}", service);
Logger.Debug("InfoLog for {0}", service);
@@ -125,9 +166,18 @@ namespace Diamond.Shaders
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>
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
/// <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>
public static Program FromShaders(IEnumerable<Shader> shaders)
{
var shaderList = shaders.ToList(); // prevent multiple enumeration
@@ -136,14 +186,26 @@ namespace Diamond.Shaders
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)
{
var shaders = paths.Select(Shader.FromFile).ToList();
if (paths == null)
{
Logger.Warn("Cannot create a program from no shaders.");
return null;
}
var shaders = paths.Select(path => Shader.FromFile(path)).ToList();
var program = FromShaders(shaders);
foreach (var shader in shaders)
shader.Dispose();
shader?.Dispose();
return program;
}

View File

@@ -6,12 +6,22 @@ using OpenTK.Graphics.OpenGL4;
namespace Diamond.Shaders
{
/// <summary>
/// Manges a OpenGL Shader object
/// </summary>
public class Shader : GLObject
{
private readonly ShaderWrap _shader;
internal override Wrapper Wrapper => _shader;
/// <summary>
/// The source used to create this shader
/// </summary>
public string Source { get; }
/// <summary>
/// The type of this shader
/// </summary>
public ShaderType Type { get; }
internal Shader(ShaderWrap shader, string source, ShaderType type, string name)
@@ -26,14 +36,24 @@ namespace Diamond.Shaders
#region Factory Methods
// Used to infer shader type based on file extension
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,
};
/// <summary>
/// Create and compile a shader from glsl source code
/// </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")
{
var wrapper = new ShaderWrap(type);
@@ -57,7 +77,14 @@ namespace Diamond.Shaders
return service;
}
public static Shader FromFile(string path, ShaderType type)
/// <summary>
/// Create and compile a shader from a glsl 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)
{
if (!File.Exists(path))
{
@@ -65,11 +92,21 @@ namespace Diamond.Shaders
return null;
}
var name = Path.GetFileNameWithoutExtension(path);
if (name == null)
name = Path.GetFileNameWithoutExtension(path);
return FromSource(File.ReadAllText(path), type, name);
}
public static Shader FromFile(string path)
/// <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.
/// </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)
{
if (!File.Exists(path))
{
@@ -78,12 +115,14 @@ namespace Diamond.Shaders
}
var ext = Path.GetExtension(path);
var name = Path.GetFileNameWithoutExtension(path);
var fileName = Path.GetFileNameWithoutExtension(path);
// get sub-extension if real extension is not valid
if (ext != null)
if (!Extensions.ContainsKey(ext))
ext = Path.GetExtension(name);
ext = Path.GetExtension(fileName);
// if no extension, no sub-extension, or invalid sub-extension
if (ext == null || !Extensions.ContainsKey(ext))
{
Logger.Warn("Could not infer shader type from glsl file name {0}", path);
@@ -91,8 +130,10 @@ namespace Diamond.Shaders
}
var type = Extensions[ext];
if (name == null)
name = fileName;
return FromFile(path, type);
return FromFile(path, type, name);
}
#endregion

View File

@@ -1,33 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace Diamond.Shaders
{
/// <summary>
/// Exception relating to <code>Shader</code> and <code>Program</code> operations.
/// </summary>
[Serializable]
public class ShaderException : Exception
{
public ShaderException()
{
}
public ShaderException(string message)
: base(message)
{
}
public ShaderException(string message, Exception inner)
: base(message, inner)
{
}
protected ShaderException(
SerializationInfo info,
StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Diamond.Wrappers;
using NLog;
using OpenTK.Graphics.OpenGL4;
@@ -7,6 +8,9 @@ using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat;
namespace Diamond.Textures
{
/// <summary>
/// Manages a OpenGL Texture object
/// </summary>
public class Texture : GLObject
{
private readonly TextureWrap _texture;
@@ -18,15 +22,26 @@ namespace Diamond.Textures
Name = name;
}
/// <summary>
/// This textures target; how it is used
/// </summary>
public TextureTarget Target => _texture.Target;
public void Bind() => _texture.Bind();
/// <summary>
/// Bind this texture to a particular unit
/// </summary>
public void Bind(int unit) => _texture.Bind(unit);
public override string ToString() => Name == null ? $"{Target} ({Id})" : $"{Target} \'{Name}\' ({Id})";
#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);
@@ -35,6 +50,8 @@ namespace Diamond.Textures
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);
@@ -47,9 +64,18 @@ namespace Diamond.Textures
return service;
}
public static Texture FromFile(string path)
/// <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)
{
return FromBitmap(new Bitmap(path));
if (name == null)
name = Path.GetFileNameWithoutExtension(path);
return FromBitmap(new Bitmap(path), name);
}
#endregion

View File

@@ -1,7 +1,7 @@
using Diamond.Buffers;
using OpenTK;
namespace Diamond.Level
namespace Diamond.Util
{
[VertexData(Divisor = 1)]
public struct TileData

View File

@@ -2,7 +2,6 @@
using System.IO;
using System.Linq;
using Diamond.Buffers;
using Diamond.Level;
using Diamond.Shaders;
using Diamond.Textures;
using Diamond.Util;