From 9bb894ffddc22c6c2bd020973de0459a46f62ce2 Mon Sep 17 00:00:00 2001 From: FrederikJA Date: Sat, 6 Apr 2024 00:59:20 +0200 Subject: [PATCH] Mesh abstraction --- JAngine/Rendering/Mesh.cs | 186 ++++++++++++++++-- JAngine/Rendering/OpenGL/Buffer.cs | 37 +++- JAngine/Rendering/OpenGL/BufferBinding.cs | 2 + .../Rendering/OpenGL/BufferDataReference.cs | 16 ++ JAngine/Rendering/OpenGL/IBuffer.cs | 8 +- JAngine/Rendering/OpenGL/VertexArray.cs | 7 + Sandbox/Program.cs | 43 ++-- 7 files changed, 254 insertions(+), 45 deletions(-) create mode 100644 JAngine/Rendering/OpenGL/BufferDataReference.cs diff --git a/JAngine/Rendering/Mesh.cs b/JAngine/Rendering/Mesh.cs index 9210457..f25941b 100644 --- a/JAngine/Rendering/Mesh.cs +++ b/JAngine/Rendering/Mesh.cs @@ -17,34 +17,180 @@ public ShaderAttributeAttribute(string nameInShader) public sealed class Mesh: IDisposable { private readonly List _vaos; - private readonly Dictionary _buffers; - private readonly IBuffer _ebo; + private readonly Dictionary _vertexBuffers; + private readonly Dictionary _instanceBuffers; + private readonly Buffer _ebo; - public Mesh(Window window, string name, IBuffer indexBuffer) + public Mesh(Window window, string name) { Window = window; Name = name; _vaos = new List(); - _ebo = indexBuffer; - _buffers = new Dictionary(TypeComparer.Default); + _ebo = new Buffer(window, $"{name}.buffer"); + _vertexBuffers = new Dictionary(TypeComparer.Default); + _instanceBuffers = new Dictionary(); } public Window Window { get; } public string Name { get; } - public void AddAttribute(int divisor = 0, params T[] data) where T : unmanaged + public void AddIndices(ReadOnlySpan indices) { - if (_buffers.TryGetValue(typeof(T), out (IBuffer buffer, int divisor) tuple)) + _ebo.Add(indices); + } + + public void AddIndex(uint index) + { + _ebo.Add(index); + } + + public IEnumerable GetIndices() + { + return _ebo.ToList(); + } + + public void ClearIndices() + { + Span s = stackalloc uint[_ebo.Count]; + s.Fill(0); + _ebo.SetSubData(0, s); + } + + public void AddVertexAttribute() where T : unmanaged + { + if (_vertexBuffers.TryGetValue(typeof(T), out IBuffer? buffer)) + { + throw new Exception($"Mesh {Name} already contains a vertex attribute for {typeof(T).FullName}"); + } + buffer = new Buffer(Window, $"{Name}.vbuffer"); + _vertexBuffers.Add(typeof(T), buffer); + + foreach (VertexArray vao in _vaos) + { + AddAttributes(vao, buffer, 0); + } + } + + private Buffer GetVertexBuffer() where T : unmanaged + { + if (!_vertexBuffers.TryGetValue(typeof(T), out IBuffer? buffer)) + { + throw new Exception($"Mesh {Name} contains no vertex attribute for {typeof(T).FullName}"); + } + + Buffer buf = (Buffer)buffer; + return buf; + } + + public BufferDataReference AddVertex(T data) + where T : unmanaged + { + Buffer buf = GetVertexBuffer(); + return new BufferDataReference(buf, buf.Add(data)); + } + + public IEnumerable> AddVertices(ReadOnlySpan datas) + where T : unmanaged + { + Buffer buf = GetVertexBuffer(); + int index = buf.Add(datas); + List> references = new (); + foreach (T d in datas) + { + references.Add(new BufferDataReference(buf, index++)); + } + + return references; + } + + public BufferDataReference GetVertex(int index) + where T : unmanaged + { + Buffer buf = GetVertexBuffer(); + return new BufferDataReference(buf, index); + } + + public IEnumerable> GetVertices(int start = 0, int count = -1) + where T : unmanaged + { + Buffer buf = GetVertexBuffer(); + if (count == -1) { - throw new Exception($"Mesh {Name} already contains an attribute for {typeof(T).FullName}"); + count = buf.Count; } - tuple.buffer = new Buffer(Window, $"{Name}.buffer"); - tuple.divisor = divisor; - _buffers.Add(typeof(T), tuple); + + for (int i = start; i < start + count; i++) + { + yield return new BufferDataReference(buf, i); + } + } + + public void AddInstanceAttribute() where T : unmanaged + { + if (_instanceBuffers.TryGetValue(typeof(T), out IBuffer? buffer)) + { + throw new Exception($"Mesh {Name} already contains an instance attribute for {typeof(T).FullName}"); + } + buffer = new Buffer(Window, $"{Name}.ibuffer"); + _instanceBuffers.Add(typeof(T), buffer); foreach (VertexArray vao in _vaos) { - AddAttributes(vao, tuple.buffer, tuple.divisor); + AddAttributes(vao, buffer, 1); + } + } + + private Buffer GetInstanceBuffer() where T : unmanaged + { + if (!_instanceBuffers.TryGetValue(typeof(T), out IBuffer? buffer)) + { + throw new Exception($"Mesh {Name} contains no instance attribute for {typeof(T).FullName}"); + } + + Buffer buf = (Buffer)buffer; + return buf; + } + + public BufferDataReference AddInstance(T data) + where T : unmanaged + { + Buffer buf = GetInstanceBuffer(); + return new BufferDataReference(buf, buf.Add(data)); + } + + public IEnumerable> AddInstances(ReadOnlySpan datas) + where T : unmanaged + { + Buffer buf = GetInstanceBuffer(); + int index = buf.Add(datas); + List> references = new (); + foreach (T d in datas) + { + references.Add(new BufferDataReference(buf, index++)); + } + + return references; + } + + public BufferDataReference GetInstance(int index) + where T : unmanaged + { + Buffer buf = GetInstanceBuffer(); + return new BufferDataReference(buf, index); + } + + public IEnumerable> GetInstances(int start = 0, int count = -1) + where T : unmanaged + { + Buffer buf = GetInstanceBuffer(); + if (count == -1) + { + count = buf.Count; + } + + for (int i = start; i < start + count; i++) + { + yield return new BufferDataReference(buf, i); } } @@ -53,9 +199,13 @@ public void BindToShader(Shader shader) VertexArray vao = new VertexArray(Window, Name + ".vao", shader, _ebo); _vaos.Add(vao); - foreach ((IBuffer buffer, int divisor) in _buffers.Values) + foreach (IBuffer buffer in _vertexBuffers.Values) { - AddAttributes(vao, buffer, divisor); + AddAttributes(vao, buffer, 0); + } + foreach (IBuffer buffer in _instanceBuffers.Values) + { + AddAttributes(vao, buffer, 1); } } @@ -65,7 +215,7 @@ private static void AddAttributes(VertexArray vao, IBuffer buffer, int divisor) foreach (FieldInfo field in buffer.UnderlyingType.GetRuntimeFields()) { - if (!Mesh.CsTypesToAttributes.TryGetValue(field.FieldType, + if (!CsTypesToAttributes.TryGetValue(field.FieldType, out (int attributeCount, int count, Type type) tuple)) { throw new Exception( @@ -90,7 +240,11 @@ private static void AddAttributes(VertexArray vao, IBuffer buffer, int divisor) public void Dispose() { _ebo.Dispose(); - foreach ((IBuffer buffer, _) in _buffers.Values) + foreach (IBuffer buffer in _vertexBuffers.Values) + { + buffer.Dispose(); + } + foreach (IBuffer buffer in _instanceBuffers.Values) { buffer.Dispose(); } diff --git a/JAngine/Rendering/OpenGL/Buffer.cs b/JAngine/Rendering/OpenGL/Buffer.cs index c519af7..ea52255 100644 --- a/JAngine/Rendering/OpenGL/Buffer.cs +++ b/JAngine/Rendering/OpenGL/Buffer.cs @@ -54,7 +54,10 @@ public void EnsureCapacity(int size) { capacity *= 2; } + + T[] oldData = _data; _data = new T[capacity]; + Array.Copy(oldData, _data, oldData.Length); if (!_window.ReplaceUpdateUnique(this, UpdateDataEvent.SkipInstance)) { _window.QueueUpdateUnique(this, UpdateDataEvent.SkipInstance); @@ -71,10 +74,15 @@ public T this[Index index] public Span this[Range range] => _data[range]; - public void SetSubData(int offset, params T[] data) + public void SetSubData(int offset, T data) + { + SetSubData(offset, stackalloc T[1]{data}); + } + + public void SetSubData(int offset, ReadOnlySpan data) { EnsureCapacity(offset + data.Length); - Array.Copy(data, 0, _data, offset, data.Length); + Array.Copy(data.ToArray(), 0, _data, offset, data.Length); int lastIndex = offset + data.Length; if (Count < lastIndex) { @@ -86,6 +94,29 @@ public void SetSubData(int offset, params T[] data) _window.QueueUpdateUnique(this, UpdateDataEvent.Default); } + public int Add(T value) + { + int index = Count; + EnsureCapacity(index + 1); + SetSubData(index, value); + return index; + } + + public int Add(ReadOnlySpan value) + { + int index = Count; + EnsureCapacity(index + value.Length); + SetSubData(index, value); + return index; + } + + public void Clear() + { + Array.Clear(_data); + _window.ReplaceUpdateUnique(this, UpdateDataEvent.SkipInstance); + _window.QueueUpdateUnique(this, UpdateCapacityEvent.Singleton); + } + public int FindIndex(T value) { for (var i = 0; i < _data.Length; i++) @@ -126,7 +157,7 @@ unsafe void IGlObject.DispatchEvent(IGlEvent glEvent) } Gl.NamedBufferSubData(_handle, (IntPtr)_firstUpdateIndex * sizeof(T), - (IntPtr)(_firstUpdateIndex - _lastUpdateIndex) * sizeof(T), ref _data[_firstUpdateIndex]); + (IntPtr)(_lastUpdateIndex - _firstUpdateIndex) * sizeof(T), ref _data[_firstUpdateIndex]); (_firstUpdateIndex, _lastUpdateIndex) = (_data.Length - 1, 0); break; case DisposeEvent: diff --git a/JAngine/Rendering/OpenGL/BufferBinding.cs b/JAngine/Rendering/OpenGL/BufferBinding.cs index 6f9ff9e..8e85b3c 100644 --- a/JAngine/Rendering/OpenGL/BufferBinding.cs +++ b/JAngine/Rendering/OpenGL/BufferBinding.cs @@ -25,6 +25,8 @@ internal event Action? OnChange _onAction -= value; } } + + internal bool IsQueued { get; set; } public BufferBinding AddAttribute(string attributeName, int count, int divisor = 0) { diff --git a/JAngine/Rendering/OpenGL/BufferDataReference.cs b/JAngine/Rendering/OpenGL/BufferDataReference.cs new file mode 100644 index 0000000..dfe3d70 --- /dev/null +++ b/JAngine/Rendering/OpenGL/BufferDataReference.cs @@ -0,0 +1,16 @@ +namespace JAngine.Rendering.OpenGL; + +public sealed class BufferDataReference + where T : unmanaged +{ + private readonly IBuffer _buffer; + + public BufferDataReference(IBuffer buffer, int index) + { + _buffer = buffer; + Index = index; + } + + public int Index { get; } + public T Data { get => _buffer[Index]; set => _buffer.SetSubData(Index, value); } +} diff --git a/JAngine/Rendering/OpenGL/IBuffer.cs b/JAngine/Rendering/OpenGL/IBuffer.cs index d476949..8598430 100644 --- a/JAngine/Rendering/OpenGL/IBuffer.cs +++ b/JAngine/Rendering/OpenGL/IBuffer.cs @@ -1,6 +1,8 @@ +using System.Collections; + namespace JAngine.Rendering.OpenGL; -public interface IBuffer: IGlObject, IDisposable +public interface IBuffer: IGlObject, IEnumerable, IDisposable { public Type UnderlyingType { get; } public int Capacity { get; } @@ -14,7 +16,9 @@ public interface IBuffer : IBuffer, IEnumerable Type IBuffer.UnderlyingType => typeof(T); public T this[Index index] { get; set; } public Span this[Range range] { get; } - public void SetSubData(int offset, params T[] data); + public void SetSubData(int offset, T data); + public void SetSubData(int offset, ReadOnlySpan data); + public void Clear(); public int FindIndex(T value); } diff --git a/JAngine/Rendering/OpenGL/VertexArray.cs b/JAngine/Rendering/OpenGL/VertexArray.cs index 224c41e..8a0028e 100644 --- a/JAngine/Rendering/OpenGL/VertexArray.cs +++ b/JAngine/Rendering/OpenGL/VertexArray.cs @@ -42,6 +42,12 @@ public BufferBinding BindBuffer(IBuffer buffer, int offset = 0) private void UpdateBinding(BufferBinding binding) { + if (binding.IsQueued) + { + return; + } + + binding.IsQueued = true; _window.QueueUpdate(this, binding); } @@ -55,6 +61,7 @@ void IGlObject.DispatchEvent(IGlEvent glEvent) Gl.VertexArrayElementBuffer(_handle, _ebo.Handle); break; case BufferBinding binding: + binding.IsQueued = false; uint relativeOffset = 0; foreach (BufferBinding.Attribute attribute in binding.GetAttributes()) { diff --git a/Sandbox/Program.cs b/Sandbox/Program.cs index d7b6ae0..4626f7f 100644 --- a/Sandbox/Program.cs +++ b/Sandbox/Program.cs @@ -13,34 +13,29 @@ vertexShader.Dispose(); fragmentShader.Dispose(); - Mesh2D mesh = new Mesh2D(window, "Square", new Vertex2D[] - { - new Vertex2D(0, 0), - new Vertex2D(0, 1), - new Vertex2D(1, 1), - new Vertex2D(1, 0), - }, new uint[] { 0, 1, 2, 0, 2, 3 }); + Mesh mesh = new Mesh(window, "Square"); + // mesh.AddIndices(new uint[] { 0, 1, 2, 0, 2, 3 }); + mesh.AddVertexAttribute(); + // mesh.AddVertices(new Vertex2D[]{ + // new Vertex2D(0, 0), + // new Vertex2D(0, 0.5f), + // new Vertex2D(1, 1), + // new Vertex2D(1, 0), + // }); + mesh.AddInstanceAttribute(); mesh.AddInstance(new Instance2D(Matrix4x4.Identity)); mesh.BindToShader(shader); - - KeyBinding a = new KeyBinding(Key.P | Key.LShift | Key.Release); - a.Enabled = false; - KeyBinding b = new KeyBinding(Key.Escape | Key.Press); - a.OnActivate += () => + + window.AddKeyBinding(Key.A | Key.Press, () => { - a.Enabled = false; - b.Enabled = true; - Log.Info("Menu"); - }; - b.OnActivate += () => + //mesh.GetVertex(0).Data = new Vertex2D(Random.Shared.NextSingle(), Random.Shared.NextSingle()); + BufferDataReference reference = mesh.AddVertex(new Vertex2D(Random.Shared.NextSingle(), Random.Shared.NextSingle())); + mesh.AddIndex((uint)reference.Index); + }); + window.AddKeyBinding(Key.Escape | Key.Press, () => { - a.Enabled = true; - b.Enabled = false; - Log.Info("Game"); - }; - window.AddKeyBinding(a); - window.AddKeyBinding(b); - window.AddKeyBinding(Key.MouseLeft | Key.Press, () => Log.Info("Pew")); + mesh.ClearIndices(); + }); Window.Run(); }