Migrating to Factory/Wrapper - GLBuffer and VertexBuffer complete

This commit is contained in:
2017-03-01 01:12:59 -05:00
parent e94ff90a88
commit f51140b7db
6 changed files with 131 additions and 221 deletions

View File

@@ -1,41 +1,151 @@
using System;
using System.Runtime.InteropServices;
using Diamond.Shaders;
using Diamond.Util;
using NLog;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Buffers
{
public class GLBuffer<T> : GLWrapper where T : struct
internal class GLBufferWrapper : GLWrapper
{
public readonly BufferTarget Target;
public readonly BufferUsageHint Usage;
public BufferTarget Target { get; }
public BufferUsageHint Usage { get; set; }
public GLBuffer(BufferTarget target, BufferUsageHint usage = BufferUsageHint.StaticDraw)
: base((uint) GL.GenBuffer())
internal GLBufferWrapper(BufferTarget target, BufferUsageHint usage)
{
Id = GL.GenBuffer();
Target = target;
Usage = usage;
}
public void Bind()
{
GL.BindBuffer(Target, Id);
}
public void Bind() => GL.BindBuffer(Target, Id);
protected override void Delete() => GL.DeleteBuffer(Id);
public void Data(T[] data)
public void Data<T>(int size, T[] data) where T : struct
{
var size = Marshal.SizeOf<T>();
Bind();
GL.BufferData(Target, (IntPtr) (size * data.Length), data, Usage);
}
public void SubData(SubArray<T> data)
public void SubData<T>(int size, int offset, int count, T[] data) where T : struct
{
var size = Marshal.SizeOf<T>();
Bind();
GL.BufferSubData(Target, (IntPtr) (data.Offset * size), (IntPtr) (data.Length * size), data.Array);
GL.BufferSubData(Target, (IntPtr) (offset * size), (IntPtr) (count * size), data);
}
public override void GLDelete() => GL.DeleteBuffer(Id);
}
public class GLBuffer<T> : GLObject where T : struct
{
private GLBufferWrapper _buffer;
internal override GLWrapper Wrapper => _buffer;
private int _size;
public BufferTarget Target => _buffer.Target;
public BufferUsageHint Usage
{
get => _buffer.Usage;
set => _buffer.Usage = value;
}
internal GLBuffer(GLBufferWrapper buffer, string name)
{
_buffer = buffer;
Name = name;
_size = Marshal.SizeOf<T>();
}
public void Data(T[] data) => _buffer.Data(_size, data);
public void Data(int offset, int count, T[] data) => _buffer.SubData(_size, offset, count, data);
public void Data(SubArray<T> data) => Data(data.Offset, data.Length, data.Array);
public override string ToString() => Name == null ? $"{Target} ({Id})" : $"{Target} {Name} ({Id})";
}
public static class GLBuffer
{
internal static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public static GLBuffer<T> Empty<T>(BufferTarget target, BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct
{
var wrapper = new GLBufferWrapper(target, usage);
var service = new GLBuffer<T>(wrapper, name);
Logger.Debug("Created {0}", service);
return service;
}
public static GLBuffer<T> FromData<T>(T[] data, BufferTarget target,
BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct
{
var service = Empty<T>(target, usage, name);
service?.Data(data);
return service;
}
}
public class VertexBuffer<T> : GLBuffer<T> where T : struct
{
private readonly VertexDataInfo _vdi;
private readonly GLBufferWrapper _buffer;
internal VertexBuffer(GLBufferWrapper buffer, string name)
: base(buffer, name)
{
_vdi = VertexDataInfo.GetInfo<T>();
_buffer = buffer;
}
public void PointTo(Program program)
{
_buffer.Bind();
foreach (var attr in _vdi.Pointers)
{
var loc = program.AttributeLocation(attr.Name);
if (loc.HasValue)
GL.VertexAttribPointer((int) loc, attr.Size, attr.Type, attr.Normalized, _vdi.Stride, attr.Offset);
}
}
}
public static class VertexBuffer
{
public static VertexBuffer<T> Empty<T>(BufferTarget target, BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct
{
if (typeof(T).GetCustomAttributes(typeof(VertexDataAttribute), false).Length == 0)
{
GLBuffer.Logger.Warn("Cannot use type {0} to create a VertexBuffer", typeof(T));
return null;
}
var wrapper = new GLBufferWrapper(target, usage);
var service = new VertexBuffer<T>(wrapper, name);
GLBuffer.Logger.Debug("Created {0}", service);
return service;
}
public static VertexBuffer<T> FromData<T>(T[] data, BufferTarget target,
BufferUsageHint usage = BufferUsageHint.StaticDraw,
string name = null) where T : struct
{
var service = Empty<T>(target, usage, name);
service?.Data(data);
return service;
}
}
}

View File

@@ -56,9 +56,7 @@
<Compile Include="Util\SubArray.cs" />
<Compile Include="Buffers\VertexDataAttribute.cs" />
<Compile Include="GLWrapper.cs" />
<Compile Include="Level\Level.cs" />
<Compile Include="Level\TileData.cs" />
<Compile Include="Level\TileGroup.cs" />
<Compile Include="Mesh.cs" />
<Compile Include="ObjVertex.cs" />
<Compile Include="Shaders\Program.cs" />

View File

@@ -1,163 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using Diamond.Buffers;
using Diamond.Shaders;
using Diamond.Textures;
using Diamond.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
namespace Diamond.Level
{
public class Level : IDisposable
{
public Dictionary<string, Program> Programs { get; private set; }
private TileData[] _allTiles;
private ObjVertex[] _allVertices;
private Mesh<ObjVertex>[] _meshes;
private TileGroup[] _tileGroups;
private Texture[] _textures;
private GLBuffer<TileData> _tileBuffer;
private GLBuffer<ObjVertex> _vertexBuffer;
private void InitializeBuffers()
{
_tileBuffer = new GLBuffer<TileData>(BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw);
_tileBuffer.Data(_allTiles);
_vertexBuffer = new GLBuffer<ObjVertex>(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw);
_vertexBuffer.Data(_allVertices);
}
public static Level LoadLevel(string file)
{
var levelData = JObject.Parse(File.ReadAllText(file));
var dir = Path.GetDirectoryName(file);
// this is horrendous, but not as bad as trying to directly deserialize it.
var meshes = levelData["models"]
.Select(path => Mesh.FromObj(Path.Combine(dir, (string) path), false))
.SelectMany(objects => objects)
.ToArray();
var meshDict = meshes
.ToDictionary(mesh => mesh.Name, mesh => mesh);
var allVertices = Mesh.Join(meshes);
var programs = levelData["shaders"]
.Select(shader => new
{
name = (string) shader["name"],
program = Program.FromFiles(
shader["files"]
.Select(path => Path.Combine(dir, (string) path))
.ToArray())
})
.ToDictionary(s => s.name, s => s.program);
var texturePaths = levelData["textures"]
.Select(path => (string) path)
.ToArray();
var textures = texturePaths.Select(path => Texture.FromBitmap(new Bitmap(Path.Combine(dir, path))))
.ToArray();
var textureMap = texturePaths.Select((path, i) => new {path = path, i = i})
.ToDictionary(v => v.path, v => v.i);
var tilegroups = levelData["tiles"]
.Select(tile => new
{
info = new
{
mesh = meshDict[(string) tile["mesh"]],
shader = programs[(string) tile["shader"]],
texture = textureMap[(string) tile["tex"]]
},
pos = tile["pos"].ToObject<Vector3>()
})
.GroupBy(tile => tile.info)
.Select(group => new TileGroup(group.Key.mesh, group.Key.shader, group.Key.texture,
new SubArray<TileData>(
group.Select(data => new TileData(data.pos))
.ToArray())))
.ToArray();
var tileArrays = tilegroups
.Select(group => group.Tiles);
var allTiles = SubArray.Join(tileArrays);
var level = new Level
{
_allTiles = allTiles,
_allVertices = allVertices,
_meshes = meshes,
_tileGroups = tilegroups,
Programs = programs,
_textures = textures
};
level.InitializeBuffers();
return level;
}
public void Draw()
{
for (var i = 0; i < _textures.Length; i++)
{
var texture = _textures[i];
texture.Bind(i);
}
foreach (var tileGroup in _tileGroups)
{
var pgm = tileGroup.Program;
pgm.Use();
var loc = pgm.UniformLocation("tex");
if (!loc.HasValue)
continue;
GL.Uniform1((int)loc, tileGroup.Texture);
pgm.SetAttribPointers(_vertexBuffer);
pgm.SetAttribPointers(_tileBuffer);
tileGroup.Mesh.DrawInstanced(tileGroup.Tiles);
}
}
public void Dispose()
{
_tileBuffer?.Dispose();
_vertexBuffer?.Dispose();
foreach (var texture in _textures)
texture?.Dispose();
foreach (var program in Programs.Values)
program?.Dispose();
GC.SuppressFinalize(this);
}
~Level()
{
Dispose();
}
}
}

View File

@@ -1,22 +0,0 @@
using Diamond.Buffers;
using Diamond.Shaders;
using Diamond.Util;
namespace Diamond.Level
{
internal class TileGroup
{
public SubArray<TileData> Tiles;
public Mesh<ObjVertex> Mesh;
public Program Program;
public int Texture;
public TileGroup(Mesh<ObjVertex> mesh, Program program, int texture, SubArray<TileData> tiles)
{
Mesh = mesh;
Program = program;
Tiles = tiles;
Texture = texture;
}
}
}

View File

@@ -51,7 +51,7 @@ namespace Diamond.Shaders
public class Program : GLObject
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
internal static readonly Logger Logger = LogManager.GetCurrentClassLogger();
internal ProgramWrapper _program;
internal override GLWrapper Wrapper => _program;
@@ -61,7 +61,7 @@ namespace Diamond.Shaders
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
private Program(ProgramWrapper program, string name)
internal Program(ProgramWrapper program, string name)
{
_program = program;
Name = name;
@@ -79,19 +79,6 @@ namespace Diamond.Shaders
return null;
}
public void SetAttribPointers<T>(GLBuffer<T> buff) where T : struct
{
var vdi = VertexDataInfo.GetInfo<T>();
buff.Bind();
foreach (var attr in vdi.Pointers)
{
var loc = AttributeLocation(attr.Name);
if (loc.HasValue)
GL.VertexAttribPointer((int) loc, attr.Size, attr.Type, attr.Normalized, vdi.Stride, attr.Offset);
}
}
public void Use()
{
GL.UseProgram(Id);
@@ -126,7 +113,7 @@ namespace Diamond.Shaders
return true;
}
public override string ToString() => $"\'{Name}\' ({Id})";
public override string ToString() => $"Program \'{Name}\' ({Id})";
#region Factory Methods

View File

@@ -46,7 +46,7 @@ namespace Diamond.Shaders
public class Shader : GLObject
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
internal static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ShaderWrapper _shader;
internal override GLWrapper Wrapper => _shader;
@@ -54,7 +54,7 @@ namespace Diamond.Shaders
public string Source { get; }
public ShaderType Type { get; }
private Shader(ShaderWrapper shader, string source, ShaderType type, string name)
internal Shader(ShaderWrapper shader, string source, ShaderType type, string name)
{
_shader = shader;
Source = source;