Added Mesh class, removed attrib pointer management from Program, shifted to mesh. MUST use Program.UseDefault instead of GL.Use(0) to prevent crash, but meshes can't render with it anyway.
This commit is contained in:
@@ -8,7 +8,7 @@ using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond.Buffers
|
||||
{
|
||||
public class GLBuffer : GLObject
|
||||
public class GLBuffer<T> : GLObject where T : struct
|
||||
{
|
||||
public readonly BufferTarget Target;
|
||||
public readonly BufferUsageHint Usage;
|
||||
@@ -27,7 +27,7 @@ namespace Diamond.Buffers
|
||||
|
||||
protected override void Delete() => GL.DeleteBuffer(Id);
|
||||
|
||||
public void Data<T>(T[] data) where T : struct
|
||||
public void Data(T[] data)
|
||||
{
|
||||
var size = Marshal.SizeOf<T>();
|
||||
Bind();
|
||||
|
||||
@@ -45,42 +45,6 @@ namespace Diamond.Buffers
|
||||
yield return Array[Offset + i];
|
||||
}
|
||||
|
||||
public static T1[] Join<T1>(params SubArray<T1>[] subArrays) => Join((IEnumerable<SubArray<T1>>) subArrays);
|
||||
|
||||
public static T1[] Join<T1>(IEnumerable<SubArray<T1>> subArrays)
|
||||
{
|
||||
HashSet<T1[]> uniqueArrays = new HashSet<T1[]>();
|
||||
foreach (var subArray in subArrays)
|
||||
{
|
||||
uniqueArrays.Add(subArray.Array);
|
||||
}
|
||||
|
||||
if (uniqueArrays.Count == 0) return new T1[0];
|
||||
if (uniqueArrays.Count == 1) return uniqueArrays.ToArray()[0];
|
||||
|
||||
var length = 0;
|
||||
var offsets = new Dictionary<T1[], int>();
|
||||
foreach (var uniqueArray in uniqueArrays)
|
||||
{
|
||||
offsets[uniqueArray] = length;
|
||||
length += uniqueArray.Length;
|
||||
}
|
||||
|
||||
var array = new T1[length];
|
||||
foreach (var uniqueArray in uniqueArrays)
|
||||
{
|
||||
System.Array.ConstrainedCopy(uniqueArray, 0, array, offsets[uniqueArray], uniqueArray.Length);
|
||||
}
|
||||
|
||||
foreach (var subArray in subArrays)
|
||||
{
|
||||
subArray.Offset = offsets[subArray.Array];
|
||||
subArray.Array = array;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
var arr = new T[Length];
|
||||
@@ -96,4 +60,44 @@ namespace Diamond.Buffers
|
||||
return $"[{string.Join(", ", this)}]";
|
||||
}
|
||||
}
|
||||
|
||||
public static class SubArray
|
||||
{
|
||||
public static T[] Join<T>(params SubArray<T>[] subArrays) => Join((IEnumerable<SubArray<T>>)subArrays);
|
||||
|
||||
public static T[] Join<T>(IEnumerable<SubArray<T>> subArrays)
|
||||
{
|
||||
HashSet<T[]> uniqueArrays = new HashSet<T[]>();
|
||||
foreach (var subArray in subArrays)
|
||||
{
|
||||
uniqueArrays.Add(subArray.Array);
|
||||
}
|
||||
|
||||
if (uniqueArrays.Count == 0) return new T[0];
|
||||
if (uniqueArrays.Count == 1) return uniqueArrays.ToArray()[0];
|
||||
|
||||
var length = 0;
|
||||
var offsets = new Dictionary<T[], int>();
|
||||
foreach (var uniqueArray in uniqueArrays)
|
||||
{
|
||||
offsets[uniqueArray] = length;
|
||||
length += uniqueArray.Length;
|
||||
}
|
||||
|
||||
var array = new T[length];
|
||||
foreach (var uniqueArray in uniqueArrays)
|
||||
{
|
||||
System.Array.ConstrainedCopy(uniqueArray, 0, array, offsets[uniqueArray], uniqueArray.Length);
|
||||
}
|
||||
|
||||
foreach (var subArray in subArrays)
|
||||
{
|
||||
subArray.Offset = offsets[subArray.Array];
|
||||
subArray.Array = array;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ namespace Diamond.Buffers
|
||||
[AttributeUsage(AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class VertexDataAttribute : Attribute
|
||||
{
|
||||
public int Divisor { get; set; } = 0;
|
||||
|
||||
public VertexDataAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using Diamond.Shaders;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond.Buffers
|
||||
@@ -32,12 +36,6 @@ namespace Diamond.Buffers
|
||||
/// </summary>
|
||||
public bool Normalized { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The divisor to Instanced draw calls
|
||||
/// Corresponds to the <code>divisor</code> parameter to <code>glVertexAttribDivisor</code>
|
||||
/// </summary>
|
||||
public int Divisor { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of this attribute within each element
|
||||
/// Corresponds to the <code>offset</code> parameter to <code>glVertexAttribPointer</code>
|
||||
@@ -50,4 +48,85 @@ namespace Diamond.Buffers
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
public class VertexDataInfo
|
||||
{
|
||||
public readonly IReadOnlyCollection<VertexPointerAttribute> Pointers;
|
||||
public readonly int Stride;
|
||||
public readonly int Divisor;
|
||||
|
||||
private VertexDataInfo(IList<VertexPointerAttribute> pointers, int stride, int divisor)
|
||||
{
|
||||
Pointers = new ReadOnlyCollection<VertexPointerAttribute>(pointers);
|
||||
Stride = stride;
|
||||
Divisor = divisor;
|
||||
}
|
||||
|
||||
public void EnableVertexPointers()
|
||||
{
|
||||
if (Program.Current == null)
|
||||
{
|
||||
throw new Exception("Cant render a mesh with no active shader."); // todo make an exception here.
|
||||
}
|
||||
|
||||
foreach (var attr in Pointers)
|
||||
{
|
||||
if (!Program.Current.TryGetAttribute(attr.Name, out int loc)) continue;
|
||||
GL.EnableVertexAttribArray(loc);
|
||||
GL.VertexAttribDivisor(loc, Divisor);
|
||||
}
|
||||
}
|
||||
|
||||
public void DisableVertexPointers()
|
||||
{
|
||||
if (Program.Current == null)
|
||||
{
|
||||
throw new Exception("Cant render a mesh with no active shader."); // todo make an exception here.
|
||||
}
|
||||
|
||||
foreach (var attr in Pointers)
|
||||
{
|
||||
if (!Program.Current.TryGetAttribute(attr.Name, out int loc)) continue;
|
||||
GL.DisableVertexAttribArray(loc);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<Type, VertexDataInfo> attribCache =
|
||||
new Dictionary<Type, VertexDataInfo>();
|
||||
|
||||
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);
|
||||
|
||||
if (vertexDataAttributes.Length != 1)
|
||||
{
|
||||
throw new Exception("Can't use type {typeof(T)} as mesh data."); // todo make an exception here.
|
||||
}
|
||||
|
||||
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);
|
||||
if (attrs.Length == 0) continue;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ namespace Diamond.Shaders
|
||||
{
|
||||
public class Program : GLObject
|
||||
{
|
||||
public static Program Current { get; private set; }
|
||||
|
||||
private Dictionary<string, int> uniforms = new Dictionary<string, int>();
|
||||
private Dictionary<string, int> attributes = new Dictionary<string, int>();
|
||||
|
||||
@@ -84,57 +86,42 @@ namespace Diamond.Shaders
|
||||
return id;
|
||||
}
|
||||
|
||||
public void SetAttribPointers(GLBuffer buff, Type vertexType)
|
||||
public void SetAttribPointers<T>(GLBuffer<T> buff) where T : struct
|
||||
{
|
||||
if (vertexType.GetCustomAttributes(typeof(VertexDataAttribute), false).Length == 0)
|
||||
{
|
||||
throw new ShaderException($"Cannot attach buffer {buff} to program {this}" +
|
||||
$" with vertex {vertexType} because it has no" +
|
||||
$" VertexData attribute.");
|
||||
}
|
||||
|
||||
var attribList = new List<VertexPointerAttribute>();
|
||||
var Stride = Marshal.SizeOf(vertexType);
|
||||
|
||||
foreach (var fieldInfo in vertexType.GetFields())
|
||||
{
|
||||
var attrs = fieldInfo.GetCustomAttributes(typeof(VertexPointerAttribute), false);
|
||||
if (attrs.Length == 0) continue;
|
||||
|
||||
var offset = (int) Marshal.OffsetOf(vertexType, fieldInfo.Name);
|
||||
foreach (var attr in attrs)
|
||||
{
|
||||
var vpa = (VertexPointerAttribute) attr;
|
||||
vpa.Offset = offset;
|
||||
attribList.Add(vpa);
|
||||
}
|
||||
}
|
||||
var vdi = VertexDataInfo.GetInfo<T>();
|
||||
|
||||
buff.Bind();
|
||||
foreach (var attr in attribList)
|
||||
foreach (var attr in vdi.Pointers)
|
||||
{
|
||||
if (!TryGetAttribute(attr.Name, out int loc)) continue;
|
||||
GL.VertexAttribPointer(loc, attr.Size, attr.Type, attr.Normalized, Stride, attr.Offset);
|
||||
GL.VertexAttribDivisor(loc, attr.Divisor);
|
||||
GL.VertexAttribPointer(loc, attr.Size, attr.Type, attr.Normalized, vdi.Stride, attr.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableAllAttribArrays()
|
||||
// public void EnableAllAttribArrays()
|
||||
// {
|
||||
// foreach (var loc in attributes.Values)
|
||||
// GL.EnableVertexAttribArray(loc);
|
||||
// }
|
||||
//
|
||||
// public void DisableAllAttribArrays()
|
||||
// {
|
||||
// foreach (var loc in attributes.Values)
|
||||
// GL.DisableVertexAttribArray(loc);
|
||||
// }
|
||||
|
||||
public void Use()
|
||||
{
|
||||
foreach (var loc in attributes.Values)
|
||||
GL.EnableVertexAttribArray(loc);
|
||||
GL.UseProgram(Id);
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public void DisableAllAttribArrays()
|
||||
public static void UseDefault()
|
||||
{
|
||||
foreach (var loc in attributes.Values)
|
||||
GL.DisableVertexAttribArray(loc);
|
||||
GL.UseProgram(0);
|
||||
Current = null;
|
||||
}
|
||||
|
||||
public void Use() => GL.UseProgram(Id);
|
||||
|
||||
public static void UseDefault() => GL.UseProgram(0);
|
||||
|
||||
public static Program FromShaders(params Shader[] shaders)
|
||||
{
|
||||
var p = new Program();
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace hexworld
|
||||
private Texture _stone;
|
||||
private Texture _gray;
|
||||
|
||||
private GLBuffer _tileGLBuffer;
|
||||
private GLBuffer _vertexGLBuffer;
|
||||
private GLBuffer<Tile> _tileGLBuffer;
|
||||
private GLBuffer<Vertex> _vertexGLBuffer;
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace hexworld
|
||||
|
||||
_pgm.Dispose();
|
||||
|
||||
// _tileGLBuffer.Dispose();
|
||||
_tileGLBuffer.Dispose();
|
||||
_vertexGLBuffer.Dispose();
|
||||
|
||||
_grass.Dispose();
|
||||
@@ -50,9 +50,9 @@ namespace hexworld
|
||||
private SubArray<Tile> _stoneTiles;
|
||||
private SubArray<Tile> _grayTiles;
|
||||
|
||||
private SubArray<Vertex> _cubeVertices;
|
||||
private SubArray<Vertex> _panelVertices;
|
||||
private SubArray<Vertex> _sidesVertices;
|
||||
private Mesh<Vertex> _cubeMesh;
|
||||
private Mesh<Vertex> _panelMesh;
|
||||
private Mesh<Vertex> _sidesMesh;
|
||||
|
||||
private Tile[] _allTiles;
|
||||
private Vertex[] _allVertices;
|
||||
@@ -100,12 +100,9 @@ namespace hexworld
|
||||
}
|
||||
}
|
||||
|
||||
_cubeVertices = new SubArray<Vertex>(
|
||||
JsonConvert.DeserializeObject<Vertex[]>(File.ReadAllText(@"res\data_vert_cubes.json")));
|
||||
_panelVertices = new SubArray<Vertex>(
|
||||
JsonConvert.DeserializeObject<Vertex[]>(File.ReadAllText(@"res\data_vert_panels.json")));
|
||||
_sidesVertices = new SubArray<Vertex>(
|
||||
JsonConvert.DeserializeObject<Vertex[]>(File.ReadAllText(@"res\data_vert_sides.json")));
|
||||
_cubeMesh = Mesh.FromJson<Vertex>(File.ReadAllText(@"res\data_vert_cubes.json"));
|
||||
_panelMesh = Mesh.FromJson<Vertex>(File.ReadAllText(@"res\data_vert_panels.json"));
|
||||
_sidesMesh = Mesh.FromJson<Vertex>(File.ReadAllText(@"res\data_vert_sides.json"));
|
||||
|
||||
_grassTiles = new SubArray<Tile>(
|
||||
JsonConvert.DeserializeObject<Tile[]>(File.ReadAllText(@"res\data_tile_grass.json")));
|
||||
@@ -114,17 +111,17 @@ namespace hexworld
|
||||
_grayTiles = new SubArray<Tile>(
|
||||
JsonConvert.DeserializeObject<Tile[]>(File.ReadAllText(@"res\data_tile_gray.json")));
|
||||
|
||||
_allTiles = SubArray<Tile>.Join(_stoneTiles, _grassTiles, _grayTiles);
|
||||
_allVertices = SubArray<Vertex>.Join(_panelVertices, _cubeVertices, _sidesVertices);
|
||||
_allTiles = SubArray.Join(_stoneTiles, _grassTiles, _grayTiles);
|
||||
_allVertices = Mesh.Join(_panelMesh, _cubeMesh, _sidesMesh);
|
||||
|
||||
_tileGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw);
|
||||
_tileGLBuffer = new GLBuffer<Tile>(BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw);
|
||||
_tileGLBuffer.Data(_allTiles);
|
||||
|
||||
_vertexGLBuffer = new GLBuffer(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw);
|
||||
_vertexGLBuffer = new GLBuffer<Vertex>(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw);
|
||||
_vertexGLBuffer.Data(_allVertices);
|
||||
|
||||
_pgm.SetAttribPointers(_tileGLBuffer, typeof(Tile));
|
||||
_pgm.SetAttribPointers(_vertexGLBuffer, typeof(Vertex));
|
||||
_pgm.SetAttribPointers(_tileGLBuffer);
|
||||
_pgm.SetAttribPointers(_vertexGLBuffer);
|
||||
|
||||
_grass = Texture.FromBitmap(new Bitmap(@"res\grass.png"));
|
||||
_stone = Texture.FromBitmap(new Bitmap(@"res\stone.png"));
|
||||
@@ -172,7 +169,6 @@ namespace hexworld
|
||||
GL.CullFace(CullFaceMode.Back);
|
||||
|
||||
_pgm.Use();
|
||||
_pgm.EnableAllAttribArrays();
|
||||
|
||||
_grass.Bind(0);
|
||||
_stone.Bind(1);
|
||||
@@ -182,27 +178,19 @@ namespace hexworld
|
||||
GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view);
|
||||
GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj);
|
||||
|
||||
GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles,
|
||||
_cubeVertices.Offset, _cubeVertices.Length,
|
||||
_grassTiles.Length, _grassTiles.Offset);
|
||||
_cubeMesh.DrawInstanced(_grassTiles);
|
||||
|
||||
GL.Uniform1(_pgm.GetUniform("tex"), 1);
|
||||
GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view);
|
||||
GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj);
|
||||
|
||||
GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles,
|
||||
_panelVertices.Offset, _panelVertices.Length,
|
||||
_stoneTiles.Length, _stoneTiles.Offset);
|
||||
_panelMesh.DrawInstanced(_stoneTiles);
|
||||
|
||||
GL.Uniform1(_pgm.GetUniform("tex"), 2);
|
||||
GL.UniformMatrix4(_pgm.GetUniform("view"), false, ref _view);
|
||||
GL.UniformMatrix4(_pgm.GetUniform("proj"), false, ref _proj);
|
||||
|
||||
GL.DrawArraysInstancedBaseInstance(PrimitiveType.Triangles,
|
||||
_sidesVertices.Offset, _sidesVertices.Length,
|
||||
_grayTiles.Length, _grayTiles.Offset);
|
||||
|
||||
_pgm.DisableAllAttribArrays();
|
||||
_sidesMesh.DrawInstanced(_grayTiles);
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
78
hexworld/Mesh.cs
Normal file
78
hexworld/Mesh.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Diamond.Buffers;
|
||||
using Diamond.Shaders;
|
||||
using Newtonsoft.Json;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace hexworld
|
||||
{
|
||||
public class Mesh<T> where T : struct
|
||||
{
|
||||
public SubArray<T> Vertices;
|
||||
public PrimitiveType Primitive;
|
||||
|
||||
private static VertexDataInfo tVdi;
|
||||
private static List<VertexPointerAttribute> attribs;
|
||||
|
||||
static Mesh()
|
||||
{
|
||||
tVdi = VertexDataInfo.GetInfo<T>();
|
||||
}
|
||||
|
||||
public Mesh(T[] vertices, PrimitiveType primitive = PrimitiveType.Triangles)
|
||||
: this(new SubArray<T>(vertices), primitive)
|
||||
{
|
||||
}
|
||||
|
||||
public Mesh(SubArray<T> vertices, PrimitiveType primitive = PrimitiveType.Triangles)
|
||||
{
|
||||
Vertices = vertices;
|
||||
Primitive = primitive;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
tVdi.EnableVertexPointers();
|
||||
|
||||
GL.DrawArrays(Primitive, Vertices.Offset, Vertices.Length);
|
||||
|
||||
tVdi.DisableVertexPointers();
|
||||
}
|
||||
|
||||
public void DrawInstanced<TI>(SubArray<TI> instanceArray) where TI : struct
|
||||
{
|
||||
var tiVdi = VertexDataInfo.GetInfo<TI>();
|
||||
|
||||
tVdi.EnableVertexPointers();
|
||||
tiVdi.EnableVertexPointers();
|
||||
|
||||
GL.DrawArraysInstancedBaseInstance(Primitive, Vertices.Offset, Vertices.Length, instanceArray.Length,
|
||||
instanceArray.Offset);
|
||||
|
||||
tVdi.DisableVertexPointers();
|
||||
tiVdi.DisableVertexPointers();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mesh
|
||||
{
|
||||
public static Mesh<T> FromJson<T>(string json) where T : struct
|
||||
{
|
||||
var vertices = JsonConvert.DeserializeObject<T[]>(json);
|
||||
return new Mesh<T>(vertices);
|
||||
}
|
||||
|
||||
|
||||
public static T[] Join<T>(params Mesh<T>[] meshes) where T : struct => Join((IEnumerable<Mesh<T>>) meshes);
|
||||
|
||||
public static T[] Join<T>(IEnumerable<Mesh<T>> meshes) where T : struct
|
||||
{
|
||||
return SubArray.Join(meshes.Select(x => x.Vertices));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ using OpenTK;
|
||||
|
||||
namespace hexworld
|
||||
{
|
||||
[VertexData]
|
||||
[VertexData(Divisor = 1)]
|
||||
public struct Tile
|
||||
{
|
||||
[JsonProperty("pos")]
|
||||
[VertexPointer("glbpos", 3, Divisor = 1)]
|
||||
[VertexPointer("glbpos", 3)]
|
||||
public Vector3 Position;
|
||||
|
||||
public Tile(Vector3 position)
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Mesh.cs" />
|
||||
<Compile Include="Tile.cs" />
|
||||
<Compile Include="Vertex.cs" />
|
||||
<Compile Include="HexRender.cs" />
|
||||
|
||||
Reference in New Issue
Block a user