diff --git a/Diamond/Buffers/Buffer.cs b/Diamond/Buffers/Buffer.cs index b308dcc..3f8b037 100644 --- a/Diamond/Buffers/Buffer.cs +++ b/Diamond/Buffers/Buffer.cs @@ -17,7 +17,7 @@ namespace Diamond.Buffers internal readonly BufferWrap Wrapper; private readonly VertexDataInfo _vdi; - private readonly int _size; + protected readonly int Size; /// /// The target for this buffer; its type @@ -38,7 +38,7 @@ namespace Diamond.Buffers { Wrapper = wrapper; Name = name; - _size = Marshal.SizeOf(); + Size = Marshal.SizeOf(); _vdi = VertexDataInfo.GetInfo(); } @@ -46,7 +46,7 @@ namespace Diamond.Buffers /// Upload data to this buffer /// /// The data to upload - public void Data(T[] data) => Wrapper.Data(_size, data); + public void Data(T[] data) => Wrapper.Data(Size, data); /// /// Upload a range of data to this buffer @@ -54,7 +54,7 @@ namespace Diamond.Buffers /// The range offset /// The range length /// The data to upload, offset and length apply to both this and the target - public void Data(int offset, int count, T[] data) => Wrapper.SubData(_size, offset, count, data); + public void Data(int offset, int count, T[] data) => Wrapper.SubData(Size, offset, count, data); /// /// Upload a range of data to this buffer diff --git a/Diamond/Diamond.csproj b/Diamond/Diamond.csproj index 04290a5..3a6e789 100644 --- a/Diamond/Diamond.csproj +++ b/Diamond/Diamond.csproj @@ -64,7 +64,7 @@ - + diff --git a/Diamond/Render/RenderGroup.cs b/Diamond/Render/RenderGroup.cs index 0e2bd0f..0c250d8 100644 --- a/Diamond/Render/RenderGroup.cs +++ b/Diamond/Render/RenderGroup.cs @@ -13,23 +13,15 @@ namespace Diamond.Render /// The type of data to use as Vertex information public class RenderGroup where TInstance : struct where TVertex : struct { - /// - /// The range of instance values to render - /// - public SubArray Instances; /// /// The range of vertex values to render /// - public Mesh Vertices; + public VertexBuffer Vertices; /// - /// The buffer to use for instance data + /// The range of instance values to render /// - public Buffer InstanceBuffer; - /// - /// The buffer to use for instance data - /// - public Buffer VertexBuffer; + public VertexBuffer Instance; /// /// The program to use to render this Rendergroup @@ -53,9 +45,6 @@ namespace Diamond.Render { Program.Use(); - InstanceBuffer.PointTo(Program); - VertexBuffer.PointTo(Program); - Texture.Bind(0); var texLoc = Program.UniformLocation("tex"); @@ -69,7 +58,10 @@ namespace Diamond.Render if (projLoc.HasValue) GL.UniformMatrix4(projLoc.Value, false, ref Camera.Projection); - Vertices.DrawInstanced(Instances); + if (Instance != null) + Vertices.DrawInstanced(Instance); + else + Vertices.Draw(); } } } \ No newline at end of file diff --git a/Diamond/Render/VertexBuffer.cs b/Diamond/Render/VertexBuffer.cs new file mode 100644 index 0000000..cc072d6 --- /dev/null +++ b/Diamond/Render/VertexBuffer.cs @@ -0,0 +1,203 @@ +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 +{ + public class VertexBuffer : IDisposable where T : struct + { + private Logger Logger = LogManager.GetLogger("VertexBuffer"); + + public string Name { get; set; } + public Buffer Buffer; + public SubArray Vertices; + public PrimitiveType Primitive; + + private static readonly VertexDataInfo tVdi; + + static VertexBuffer() + { + tVdi = VertexDataInfo.GetInfo(); + } + + internal VertexBuffer(Buffer buffer, SubArray vertices, PrimitiveType primitive, string name) + { + Buffer = buffer; + Vertices = vertices; + Primitive = primitive; + Name = name; + } + + public void Draw() + { + Buffer.PointTo(Program.Current); + + tVdi.EnableVertexPointers(); + + GL.DrawArrays(Primitive, Vertices.Offset, Vertices.Length); + + tVdi.DisableVertexPointers(); + } + + public void DrawInstanced(VertexBuffer instance) where TI : struct + { + var tiVdi = VertexBuffer.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(); + } + } + + public static class VertexBuffer + { + public static VertexBuffer[] FromArrays(T[][] arrays, PrimitiveType primitive = PrimitiveType.Triangles, + string name = null) where T : struct + { + var buffer = Buffer.Empty(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, name); + + var vertices = arrays.SelectMany(x => x).ToArray(); + buffer.Data(vertices); + + var vertBuffers = new List>(); + + var offset = 0; + foreach (var array in arrays) + { + vertBuffers.Add(new VertexBuffer( + buffer, + new SubArray(vertices, offset, array.Length), + primitive, name)); + offset += array.Length; + } + + return vertBuffers.ToArray(); + } + + public static VertexBuffer[] FromArrays(IEnumerable> arrays, + PrimitiveType primitive = PrimitiveType.Triangles, string name = null) where T : struct => + FromArrays(arrays.Select(x => x.ToArray()).ToArray(), primitive, name); + + public static VertexBuffer[] FromWavefront(string file) + { + var lines = File.ReadAllLines(file); + + var buffer = Buffer.Empty(BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, + $"{file} buffer"); + + var vs = new List(); + var vts = new List(); + var vns = new List(); + var faces = new List(); + var vertices = new List(); + + var name = file; + + var vertBuffers = new List>(); + + foreach (var line in lines) + { + if (line.StartsWith("#")) continue; + + var items = line.Split(' '); + + switch (items[0]) + { + case "v": + var v = new Vector3( + float.Parse(items[1]), + float.Parse(items[2]), + float.Parse(items[3])); + vs.Add(v); + break; + case "vt": + var vt = new Vector2( + float.Parse(items[1]), + 1 - float.Parse(items[2])); + vts.Add(vt); + break; + case "vn": + var vn = new Vector3( + float.Parse(items[1]), + float.Parse(items[2]), + float.Parse(items[3])); + vns.Add(vn); + break; + case "f": + for (var i = 1; i < 4; i++) + { + var inds = items[i].Split('/'); + var vi = inds[0] == "" ? Vector3.Zero : vs[int.Parse(inds[0]) - 1]; + var vti = inds[1] == "" ? Vector2.Zero : vts[int.Parse(inds[1]) - 1]; + var vni = inds[2] == "" ? Vector3.Zero : vns[int.Parse(inds[2]) - 1]; + var f = new ObjVertex(vi, vti, vni); + faces.Add(f); + } + break; + case "o": + if (faces.Count > 0) + { + var count = vertices.Count; + vertices.AddRange(faces); + vertBuffers.Add(new VertexBuffer( + buffer, + new SubArray(vertices.ToArray(), count, faces.Count), + PrimitiveType.Triangles, + name)); + } + name = items[1]; + faces.Clear(); + break; + default: + break; + } + } + + if (faces.Count > 0) + { + var count = vertices.Count; + vertices.AddRange(faces); + vertBuffers.Add(new VertexBuffer( + buffer, new SubArray(vertices.ToArray(), count, faces.Count), + PrimitiveType.Triangles, + name)); + } + + var data = vertices.ToArray(); + + foreach (var vertexBuffer in vertBuffers) + vertexBuffer.Vertices.Array = data; + + buffer.Data(vertices.ToArray()); + + return vertBuffers.ToArray(); + } + } +} \ No newline at end of file diff --git a/Diamond/Util/Mesh.cs b/Diamond/Util/Mesh.cs deleted file mode 100644 index 2b81c1b..0000000 --- a/Diamond/Util/Mesh.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Diamond.Buffers; -using NLog; -using OpenTK; -using OpenTK.Graphics.OpenGL4; - -namespace Diamond.Util -{ - public class Mesh where T : struct - { - private static readonly Logger Logger = LogManager.GetLogger("Mesh"); - - public SubArray Vertices; - public PrimitiveType Primitive; - - public string Name { get; set; } - - private static readonly VertexDataInfo tVdi; - - static Mesh() - { - tVdi = VertexDataInfo.GetInfo(); - } - - public Mesh(T[] vertices, PrimitiveType primitive = PrimitiveType.Triangles) - : this(new SubArray(vertices), primitive) - { - } - - public Mesh(SubArray 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(SubArray instanceArray) where TI : struct - { - var tiVdi = VertexDataInfo.GetInfo(); - - if (tiVdi.Divisor == 0) - { - Logger.Error("Cannot render mesh with instances of type {0} - Divisor is 0", typeof(TI).Name); - } - - 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[] FromObj(string file, bool join = true) - { - var lines = File.ReadAllLines(file); - - var meshes = new List>(); - var name = file; - var vs = new List(); - var vts = new List(); - var vns = new List(); - var faces = new List(); - - foreach (var line in lines) - { - if (line.StartsWith("#")) continue; - - var items = line.Split(' '); - - switch (items[0]) - { - case "v": - var v = new Vector3( - float.Parse(items[1]), - float.Parse(items[2]), - float.Parse(items[3])); - vs.Add(v); - break; - case "vt": - var vt = new Vector2( - float.Parse(items[1]), - 1 - float.Parse(items[2])); - vts.Add(vt); - break; - case "vn": - var vn = new Vector3( - float.Parse(items[1]), - float.Parse(items[2]), - float.Parse(items[3])); - vns.Add(vn); - break; - case "f": - for (var i = 1; i < 4; i++) - { - var inds = items[i].Split('/'); - var vi = inds[0] == "" ? Vector3.Zero : vs[int.Parse(inds[0]) - 1]; - var vti = inds[1] == "" ? Vector2.Zero : vts[int.Parse(inds[1]) - 1]; - var vni = inds[2] == "" ? Vector3.Zero : vns[int.Parse(inds[2]) - 1]; - var f = new ObjVertex(vi, vti, vni); - faces.Add(f); - } - break; - case "o": - if (faces.Count > 0) - { - meshes.Add(new Mesh(faces.ToArray()) {Name = name}); - faces.Clear(); - } - name = items[1]; - break; - } - } - - if (faces.Count > 0) - meshes.Add(new Mesh(faces.ToArray()) {Name = name}); - - if (join) - Join(meshes); - - return meshes.ToArray(); - } - - public static T[] Join(params Mesh[] meshes) where T : struct => Join((IEnumerable>) meshes); - - public static T[] Join(IEnumerable> meshes) where T : struct - { - return SubArray.Join(meshes.Select(x => x.Vertices)); - } - } -} \ No newline at end of file diff --git a/Diamond/Util/SubArray.cs b/Diamond/Util/SubArray.cs index 327f4c1..dd10975 100644 --- a/Diamond/Util/SubArray.cs +++ b/Diamond/Util/SubArray.cs @@ -123,58 +123,58 @@ namespace Diamond.Util /// public static class SubArray { - /// - /// Make multiple subArrays cover the same array - /// - /// The element type - /// The SubArrays to join - /// The new underlying array - public static T[] Join(params SubArray[] subArrays) => Join((IEnumerable>) subArrays); - - /// - /// Make multiple subArrays cover the same array - /// - /// The element type - /// The SubArrays to join - /// The new underlying array - public static T[] Join(IEnumerable> subArrays) - { - // todo: this breaks in the case: Join(a, b); Join(b, c) - // possibly create "multiarray" class or similar which would manage these operations - // and prevent this case from arising - - var subArrList = subArrays.ToList(); // prevent multiple enumeration - - HashSet uniqueArrays = new HashSet(); - foreach (var subArray in subArrList) - { - 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(); - 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 subArrList) - { - subArray.Offset = offsets[subArray.Array]; - subArray.Array = array; - } - - return array; - } +// /// +// /// Make multiple subArrays cover the same array +// /// +// /// The element type +// /// The SubArrays to join +// /// The new underlying array +// public static T[] Join(params SubArray[] subArrays) => Join((IEnumerable>) subArrays); +// +// /// +// /// Make multiple subArrays cover the same array +// /// +// /// The element type +// /// The SubArrays to join +// /// The new underlying array +// public static T[] Join(IEnumerable> subArrays) +// { +// // todo: this breaks in the case: Join(a, b); Join(b, c) +// // possibly create "multiarray" class or similar which would manage these operations +// // and prevent this case from arising +// +// var subArrList = subArrays.ToList(); // prevent multiple enumeration +// +// HashSet uniqueArrays = new HashSet(); +// foreach (var subArray in subArrList) +// { +// 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(); +// 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 subArrList) +// { +// subArray.Offset = offsets[subArray.Array]; +// subArray.Array = array; +// } +// +// return array; +// } } } \ No newline at end of file diff --git a/hexworld/HexRender.cs b/hexworld/HexRender.cs index 2e75920..02fe5d1 100644 --- a/hexworld/HexRender.cs +++ b/hexworld/HexRender.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using Diamond.Buffers; @@ -43,10 +44,10 @@ namespace hexworld #endregion - private SubArray _floorTiles; - private SubArray _doorTiles; + private VertexBuffer _floorTiles; + private VertexBuffer _doorTiles; - private Mesh _cubeMesh; + private VertexBuffer _cubeMesh; private Camera _camera; @@ -86,34 +87,29 @@ namespace hexworld .Select(arr => new SubArray(arr)) .ToArray(); - _doorTiles = allTiles[0]; - _floorTiles = allTiles[1]; + var tileVbo = VertexBuffer.FromArrays(allTiles, 0, "tiles"); + + _doorTiles = tileVbo[0]; + _floorTiles = tileVbo[1]; var cubeMesh = json["models"] .Select(path => (string) path) .Select(path => Path.Combine(dir, path)) - .Select(path => Mesh.FromObj(path, false)) + .Select(VertexBuffer.FromWavefront) .SelectMany(meshes => meshes) .First(mesh => mesh.Name == "Cube"); _cubeMesh = cubeMesh; - _tileBuffer = Buffer.FromData(SubArray.Join(_doorTiles, _floorTiles), BufferTarget.ArrayBuffer, - BufferUsageHint.DynamicDraw, "tile"); - _meshBuffer = Buffer.FromData(cubeMesh.Vertices.ToArray(), BufferTarget.ArrayBuffer, - BufferUsageHint.StaticDraw, "mesh"); - _camera = new Camera(); _renderGroup = new RenderGroup() { Camera = _camera, - Vertices = _cubeMesh, - VertexBuffer = _meshBuffer, + Instance = _floorTiles, Program = _texPgm, - Texture = _grassTex, - InstanceBuffer = _tileBuffer, - Instances = _floorTiles + Vertices = _cubeMesh, + Texture = _grassTex }; }