diff --git a/.idea/.idea.BlockGame/.idea/indexLayout.xml b/.idea/.idea.BlockGame/.idea/indexLayout.xml index 5df5a80..3347e88 100644 --- a/.idea/.idea.BlockGame/.idea/indexLayout.xml +++ b/.idea/.idea.BlockGame/.idea/indexLayout.xml @@ -2,7 +2,9 @@ - + + shaders + fonts lib diff --git a/BlockGame.sln.DotSettings.user b/BlockGame.sln.DotSettings.user index e7d631f..72eba2b 100644 --- a/BlockGame.sln.DotSettings.user +++ b/BlockGame.sln.DotSettings.user @@ -20,9 +20,11 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -78,6 +80,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/src/GL/Shader.cs b/src/GL/Shader.cs index de16058..9a4d208 100644 --- a/src/GL/Shader.cs +++ b/src/GL/Shader.cs @@ -76,7 +76,6 @@ private void link(uint vert) { public void use() { GL.UseProgram(programHandle); } - // uniforms public int getUniformLocation(string name) { diff --git a/src/GL/SharedBlockVAO.cs b/src/GL/SharedBlockVAO.cs index 82a727d..f910f95 100644 --- a/src/GL/SharedBlockVAO.cs +++ b/src/GL/SharedBlockVAO.cs @@ -1,117 +1,84 @@ +using System.Runtime.CompilerServices; using Silk.NET.OpenGL; using PrimitiveType = Silk.NET.OpenGL.PrimitiveType; namespace BlockGame; - /// -/// BlockVAO but we use separated vertex attribute format/bindings +/// SharedBlockVAO but we only use one VAO / vertex format then just rebind the vertex/index buffer +/// It also uses only one buffer now instead of two /// -public sealed class SharedBlockVAO : VAO { +public sealed class SharedBlockVAO : VAO +{ public uint VAOHandle; - public uint vbo; - public uint ibo; + public uint buffer; public uint count; - public GL GL; - - public SharedBlockVAO() { - GL = Game.GL; - VAOHandle = GL.GenVertexArray(); - } - - public void upload(float[] data) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)data.Length; - fixed (float* d = data) { - GL.BufferData(BufferTargetARB.ArrayBuffer, (uint)(data.Length * sizeof(float)), d, - BufferUsageARB.DynamicDraw); - } - } - - format(); - } - public void upload(Span data) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)data.Length; - fixed (float* d = data) { - GL.BufferData(BufferTargetARB.ArrayBuffer, (uint)(data.Length * sizeof(float)), d, - BufferUsageARB.DynamicDraw); - } - } + public readonly GL GL; - format(); + public SharedBlockVAO(uint VAOHandle) { + this.VAOHandle = VAOHandle; + GL = Game.GL; } public void upload(BlockVertexPacked[] data, ushort[] indices) { unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); + buffer = GL.GenBuffer(); + GL.BindBuffer(BufferTargetARB.ArrayBuffer, buffer); count = (uint)indices.Length; + var vertexSize = (uint)(data.Length * sizeof(BlockVertexPacked)); fixed (BlockVertexPacked* d = data) { - GL.BufferData(BufferTargetARB.ArrayBuffer, (uint)(data.Length * sizeof(BlockVertexPacked)), d, - BufferUsageARB.DynamicDraw); - } - - ibo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, ibo); - fixed (ushort* d = indices) { - GL.BufferData(BufferTargetARB.ElementArrayBuffer, (uint)(indices.Length * sizeof(ushort)), d, - BufferUsageARB.DynamicDraw); + GL.BufferStorage(BufferStorageTarget.ArrayBuffer, vertexSize, d, + BufferStorageMask.None); } } format(); } - public void upload(Span data, Span indices) { + public void upload(Span data, uint _count) { unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)indices.Length; + buffer = GL.GenBuffer(); + count = (uint)(_count * 1.5); + var vertexSize = (uint)(data.Length * sizeof(BlockVertexPacked)); + GL.BindBuffer(BufferTargetARB.ArrayBuffer, buffer); fixed (BlockVertexPacked* d = data) { - GL.BufferData(BufferTargetARB.ArrayBuffer, (uint)(data.Length * sizeof(BlockVertexPacked)), d, - BufferUsageARB.DynamicDraw); + GL.BufferStorage(BufferStorageTarget.ArrayBuffer, vertexSize, d, + BufferStorageMask.None); } - ibo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, ibo); - fixed (ushort* d = indices) { - GL.BufferData(BufferTargetARB.ElementArrayBuffer, (uint)(indices.Length * sizeof(ushort)), d, - BufferUsageARB.DynamicDraw); - } } format(); } - public void format() { - unsafe { - // 18 bytes in total, 3*4 for pos, 2*2 for uv, 2 bytes for data - GL.EnableVertexAttribArray(0); - GL.EnableVertexAttribArray(1); - GL.EnableVertexAttribArray(2); - - GL.VertexAttribIFormat(0, 3, VertexAttribIType.UnsignedShort, 0); - GL.VertexAttribIFormat(1, 2, VertexAttribIType.UnsignedShort, 0 + 3 * sizeof(ushort)); - GL.VertexAttribIFormat(2, 1, VertexAttribIType.UnsignedShort, 0 + 5 * sizeof(ushort)); - - GL.VertexAttribBinding(0, 0); - GL.VertexAttribBinding(1, 0); - GL.VertexAttribBinding(2, 0); + public void upload(Span data, Span indices) { + throw new Exception("this doesn't work!"); + } - GL.BindVertexBuffer(0, vbo, 0, 6 * sizeof(ushort)); + public void format() { + // 18 bytes in total, 3*4 for pos, 2*2 for uv, 2 bytes for data + GL.EnableVertexAttribArray(0); + GL.EnableVertexAttribArray(1); + GL.EnableVertexAttribArray(2); + + GL.VertexAttribIFormat(0, 3, VertexAttribIType.UnsignedShort, 0); + GL.VertexAttribIFormat(1, 2, VertexAttribIType.UnsignedShort, 0 + 3 * sizeof(ushort)); + GL.VertexAttribIFormat(2, 1, VertexAttribIType.UnsignedShort, 0 + 5 * sizeof(ushort)); + + GL.VertexAttribBinding(0, 0); + GL.VertexAttribBinding(1, 0); + GL.VertexAttribBinding(2, 0); + } - } + public void bindVAO() { + GL.BindVertexArray(VAOHandle); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void bind() { - GL.BindVertexArray(VAOHandle); + GL.VertexArrayVertexBuffer(VAOHandle, 0, buffer, 0, 6 * sizeof(ushort)); } public uint render() { @@ -122,8 +89,6 @@ public uint render() { } public void Dispose() { - GL.DeleteBuffer(vbo); - GL.DeleteBuffer(ibo); - GL.DeleteVertexArray(VAOHandle); + GL.DeleteBuffer(buffer); } } \ No newline at end of file diff --git a/src/GL/VerySharedBlockVAO.cs b/src/GL/VerySharedBlockVAO.cs deleted file mode 100644 index c688bcc..0000000 --- a/src/GL/VerySharedBlockVAO.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Silk.NET.OpenGL; -using PrimitiveType = Silk.NET.OpenGL.PrimitiveType; - -namespace BlockGame; - -/// -/// SharedBlockVAO but we only use one VAO / vertex format then just rebind the vertex/index buffer -/// -public sealed class VerySharedBlockVAO : VAO { - public uint VAOHandle; - public uint vbo; - public uint ibo; - public uint count; - - public GL GL; - - public VerySharedBlockVAO(uint VAOHandle) { - this.VAOHandle = VAOHandle; - GL = Game.GL; - - } - - public void upload(float[] data) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)data.Length; - fixed (float* d = data) { - GL.BufferStorage(BufferStorageTarget.ArrayBuffer, (uint)(data.Length * sizeof(float)), d, - BufferStorageMask.None); - } - } - - format(); - } - - public void upload(Span data) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)data.Length; - fixed (float* d = data) { - GL.BufferStorage(BufferStorageTarget.ArrayBuffer, (uint)(data.Length * sizeof(float)), d, - BufferStorageMask.None); - } - } - - format(); - } - - public void upload(BlockVertexPacked[] data, ushort[] indices) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)indices.Length; - fixed (BlockVertexPacked* d = data) { - GL.BufferStorage(BufferStorageTarget.ArrayBuffer, (uint)(data.Length * sizeof(BlockVertexPacked)), d, - BufferStorageMask.None); - } - - ibo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, ibo); - fixed (ushort* d = indices) { - GL.BufferStorage(BufferStorageTarget.ElementArrayBuffer, (uint)(indices.Length * sizeof(ushort)), d, - BufferStorageMask.None); - } - } - - format(); - } - - public void upload(Span data, Span indices) { - unsafe { - vbo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ArrayBuffer, vbo); - count = (uint)indices.Length; - fixed (BlockVertexPacked* d = data) { - GL.BufferStorage(BufferStorageTarget.ArrayBuffer, (uint)(data.Length * sizeof(BlockVertexPacked)), d, - BufferStorageMask.None); - } - - ibo = GL.GenBuffer(); - GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, ibo); - fixed (ushort* d = indices) { - GL.BufferStorage(BufferStorageTarget.ElementArrayBuffer, (uint)(indices.Length * sizeof(ushort)), d, - BufferStorageMask.None); - } - } - - format(); - } - - public void format() { - unsafe { - // 18 bytes in total, 3*4 for pos, 2*2 for uv, 2 bytes for data - GL.EnableVertexAttribArray(0); - GL.EnableVertexAttribArray(1); - GL.EnableVertexAttribArray(2); - - GL.VertexAttribIFormat(0, 3, VertexAttribIType.UnsignedShort, 0); - GL.VertexAttribIFormat(1, 2, VertexAttribIType.UnsignedShort, 0 + 3 * sizeof(ushort)); - GL.VertexAttribIFormat(2, 1, VertexAttribIType.UnsignedShort, 0 + 5 * sizeof(ushort)); - - GL.VertexAttribBinding(0, 0); - GL.VertexAttribBinding(1, 0); - GL.VertexAttribBinding(2, 0); - - } - } - - public void bindVAO() { - GL.BindVertexArray(VAOHandle); - } - - public void bind() { - GL.BindVertexBuffer(0, vbo, 0, 6 * sizeof(ushort)); - GL.BindBuffer(GLEnum.ElementArrayBuffer, ibo); - } - - public uint render() { - unsafe { - GL.DrawElements(PrimitiveType.Triangles, count, DrawElementsType.UnsignedShort, (void*)0); - return count; - } - } - - public void Dispose() { - GL.DeleteBuffer(vbo); - GL.DeleteBuffer(ibo); - } -} \ No newline at end of file diff --git a/src/render/SubChunkRenderer.cs b/src/render/SubChunkRenderer.cs index e9fe19e..a05c77d 100644 --- a/src/render/SubChunkRenderer.cs +++ b/src/render/SubChunkRenderer.cs @@ -18,8 +18,8 @@ public sealed class SubChunkRenderer : IDisposable { private bool hasRenderOpaque; private bool hasRenderTranslucent; - private VAO? vao; - private VAO? watervao; + private SharedBlockVAO? vao; + private SharedBlockVAO? watervao; private bool hasOnlySolid; @@ -36,7 +36,6 @@ public sealed class SubChunkRenderer : IDisposable { // actually we don't need a list, regular arrays will do because it's only a few megs of space and it's shared // in the future when we want multithreaded meshing, we can just allocate like 4-8 of them and it will still be in the ballpark of 10MB private static readonly List chunkVertices = new(2048); - private static readonly List chunkIndices = new(2048); // YZX again private static readonly ushort[] neighbours = new ushort[Chunk.CHUNKSIZEEX * Chunk.CHUNKSIZEEX * Chunk.CHUNKSIZEEX]; private static readonly byte[] neighbourLights = new byte[Chunk.CHUNKSIZEEX * Chunk.CHUNKSIZEEX * Chunk.CHUNKSIZEEX]; @@ -137,6 +136,10 @@ private static bool opaqueBlocks(int b) { return b != 0 && Blocks.get(b).type != BlockType.TRANSLUCENT; } + private static bool notOpaqueBlocks(int b) { + return b == 0 || Blocks.get(b).type == BlockType.TRANSLUCENT; + } + private void ReleaseUnmanagedResources() { vao?.Dispose(); watervao?.Dispose(); @@ -160,18 +163,11 @@ public void Dispose() { /// public void meshChunk() { //sw.Restart(); - if (subChunk.world.renderer.fastChunkSwitch) { - vao?.Dispose(); - vao = new ExtremelySharedBlockVAO(subChunk.world.renderer.chunkVAO); - watervao?.Dispose(); - watervao = new ExtremelySharedBlockVAO(subChunk.world.renderer.chunkVAO); - } - else { - vao?.Dispose(); - vao = new SharedBlockVAO(); - watervao?.Dispose(); - watervao = new SharedBlockVAO(); - } + vao?.Dispose(); + vao = new SharedBlockVAO(subChunk.world.renderer.chunkVAO); + watervao?.Dispose(); + watervao = new SharedBlockVAO(subChunk.world.renderer.chunkVAO); + // if the section is empty, nothing to do // if is empty, just return, don't need to get neighbours @@ -202,17 +198,11 @@ public void meshChunk() { MeasureProfiler.SaveData(); }*/ //Console.Out.WriteLine($"PartMeshing1: {sw.Elapsed.TotalMicroseconds}us {chunkIndices.Count}"); - if (chunkIndices.Count > 0) { + if (chunkVertices.Count > 0) { hasRenderOpaque = true; - if (subChunk.world.renderer.fastChunkSwitch) { - (vao as ExtremelySharedBlockVAO).bindVAO(); - } - else { - vao.bind(); - } + vao.bindVAO(); var finalVertices = CollectionsMarshal.AsSpan(chunkVertices); - var finalIndices = CollectionsMarshal.AsSpan(chunkIndices); - vao.upload(finalVertices, finalIndices); + vao.upload(finalVertices, (uint)finalVertices.Length); //Console.Out.WriteLine($"PartMeshing1.2: {sw.Elapsed.TotalMicroseconds}us {chunkIndices.Count}"); } else { @@ -224,17 +214,12 @@ public void meshChunk() { // then we render everything which is translucent (water for now) constructVertices(VertexConstructionMode.TRANSLUCENT); //Console.Out.WriteLine($"PartMeshing1.4: {sw.Elapsed.TotalMicroseconds}us {chunkIndices.Count}"); - if (chunkIndices.Count > 0) { + if (chunkVertices.Count > 0) { hasRenderTranslucent = true; - if (subChunk.world.renderer.fastChunkSwitch) { - (watervao as ExtremelySharedBlockVAO).bindVAO(); - } - else { - watervao.bind(); - } + watervao.bindVAO(); + var tFinalVertices = CollectionsMarshal.AsSpan(chunkVertices); - var tFinalIndices = CollectionsMarshal.AsSpan(chunkIndices); - watervao.upload(tFinalVertices, tFinalIndices); + watervao.upload(tFinalVertices, (uint)tFinalVertices.Length); //Console.Out.WriteLine($"PartMeshing1.7: {sw.Elapsed.TotalMicroseconds}us {chunkIndices.Count}"); //world.sortedTransparentChunks.Add(this); } @@ -372,7 +357,7 @@ private void setupNeighbours() { bl = Blocks.DIRT.id; } - // set neighbours array element to block + // set neighbours array element to block blocksArrayRef = bl; // if neighbour is not solid, we still have to mesh this chunk even though all of it is solid // NOTE: check if it's loaded (if it isn't loaded we don't give a shit about it) @@ -407,11 +392,8 @@ unsafe private void constructVertices(VertexConstructionMode mode) { // clear arrays before starting chunkVertices.Clear(); - chunkIndices.Clear(); Span tempVertices = stackalloc BlockVertexPacked[4]; - Span tempIndices = stackalloc ushort[8].Slice(0, 6); - Vector128 indices = Vector128.Zero; Span nba = stackalloc ushort[6]; Span lba = stackalloc byte[6]; @@ -440,9 +422,6 @@ static byte getLightFromCacheUnsafe(ref byte arrayBase, int x, int y, int z) { ref ushort blockArrayRef = ref MemoryMarshal.GetArrayDataReference(subChunk.blocks.blocks); ref short offsetArray = ref MemoryMarshal.GetReference(offsetTableCompact); - Vector128 indicesMask = Vector128.Create((ushort)0, 1, 2, 0, 2, 3, 0, 0); - Vector128 complement = Vector128.Create((ushort)4, 3, 2, 4, 2, 1, 0, 0); - bool test2; for (int idx = 0; idx < Chunk.MAXINDEX; idx++) { // index for array accesses @@ -458,7 +437,7 @@ static byte getLightFromCacheUnsafe(ref byte arrayBase, int x, int y, int z) { Block bl = Blocks.get(blockArrayRef); switch (mode) { case VertexConstructionMode.OPAQUE: - if (!opaqueBlocks(blockArrayRef)) { + if (notOpaqueBlocks(blockArrayRef)) { goto increment; } break; @@ -475,9 +454,7 @@ static byte getLightFromCacheUnsafe(ref byte arrayBase, int x, int y, int z) { //float wz = section.chunkZ * Chunk.CHUNKSIZE + z; if (bl.customRender) { - var writtenIndices = bl.render(subChunk.world, new Vector3I(x, y, z), chunkVertices, chunkIndices, indices[0]); - // add the number of written indices to the indices vector - indices += Vector128.Create(writtenIndices); + var writtenIndices = bl.render(subChunk.world, new Vector3I(x, y, z), chunkVertices); goto increment; } @@ -729,19 +706,6 @@ static byte getLightFromCacheUnsafe(ref byte arrayBase, int x, int y, int z) { vertex.d = Block.packData((byte)dir, ao.Fourth, light.Fourth); chunkVertices.AddRange(tempVertices); //cv += 4; - - indices += indicesMask; - // write it back to the span - // how does this work?? vector is 8 bytes, span is only 6 - - - // write the vector indices into tempIndices while ensuring it fits into 6 bytes - Unsafe.WriteUnaligned( - ref Unsafe.As(ref MemoryMarshal.GetReference(tempIndices)), indices); - chunkIndices.AddRange(tempIndices); - - // add to the indices so they become 4 - indices += complement; //ci += 6; } increment2: @@ -818,13 +782,13 @@ public static byte average2(uint lightNibble, byte oFlags) { (lightNibble >> 8 & 0xF) * ((inv & 2) >> 1) + (lightNibble >> 16 & 0xF) * ((inv & 4) >> 2) + (lightNibble >> 24 & 0xF)) - / popcnt); + / popcnt); var block = (byte)(((lightNibble >> 4 & 0xF) * (inv & 1) + - (lightNibble >> 12 & 0xF) * ((inv & 2) >> 1) + - (lightNibble >> 20 & 0xF) * ((inv & 4) >> 2) + - (lightNibble >> 28 & 0xF)) - / popcnt); + (lightNibble >> 12 & 0xF) * ((inv & 2) >> 1) + + (lightNibble >> 20 & 0xF) * ((inv & 4) >> 2) + + (lightNibble >> 28 & 0xF)) + / popcnt); return (byte)(sky | block << 4); } diff --git a/src/render/WorldRenderer.cs b/src/render/WorldRenderer.cs index 95ebc09..25687e7 100644 --- a/src/render/WorldRenderer.cs +++ b/src/render/WorldRenderer.cs @@ -4,6 +4,7 @@ using BlockGame.util; using Molten; using Silk.NET.OpenGL; +using Silk.NET.OpenGL.Extensions.NV; using SixLabors.ImageSharp.PixelFormats; using TrippyGL; using BoundingFrustum = System.Numerics.BoundingFrustum; @@ -42,6 +43,11 @@ public class WorldRenderer { public int waterFogColour; public int waterSkyColour; + /// + /// A buffer of indices for the maximum amount of quads. + /// + public uint fatQuadIndices; + public static BoundingFrustum frustum; @@ -64,6 +70,8 @@ public WorldRenderer(World world) { GL = Game.GL; chunkVAO = GL.GenVertexArray(); + genFatQuadIndices(); + shader = new Shader(GL, "shaders/shader.vert", "shaders/shader.frag"); Game.worldShader = shader; dummyShader = new Shader(GL, "shaders/dummyShader.vert"); @@ -111,6 +119,30 @@ public WorldRenderer(World world) { setUniforms(); } + private void genFatQuadIndices() { + var indices = new ushort[65535]; + // 0 1 2 0 2 3 + for (int i = 0; i < 65535 / 6; i++) { + indices[i * 6] = (ushort)(i * 4); + indices[i * 6 + 1] = (ushort)(i * 4 + 1); + indices[i * 6 + 2] = (ushort)(i * 4 + 2); + indices[i * 6 + 3] = (ushort)(i * 4); + indices[i * 6 + 4] = (ushort)(i * 4 + 2); + indices[i * 6 + 5] = (ushort)(i * 4 + 3); + } + fatQuadIndices = GL.GenBuffer(); + GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, fatQuadIndices); + unsafe { + fixed (ushort* pIndices = indices) { + GL.BufferStorage(BufferStorageTarget.ElementArrayBuffer, (uint)(indices.Length * sizeof(ushort)), pIndices, BufferStorageMask.None); + } + } + } + + public void bindQuad() { + GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, fatQuadIndices); + } + public void setUniforms() { var dd = Settings.instance.renderDistance * Chunk.CHUNKSIZE; @@ -151,9 +183,8 @@ public void render(double interp) { // no blending solid shit! GL.Disable(EnableCap.Blend); - if (fastChunkSwitch) { - GL.BindVertexArray(chunkVAO); - } + GL.BindVertexArray(chunkVAO); + bindQuad(); var viewProj = world.player.camera.getStaticViewMatrix(interp) * world.player.camera.getProjectionMatrix(); var chunkList = CollectionsMarshal.AsSpan(world.chunkList); diff --git a/src/util/Blocks.cs b/src/util/Blocks.cs index b43b5e5..9fecb19 100644 --- a/src/util/Blocks.cs +++ b/src/util/Blocks.cs @@ -361,7 +361,7 @@ public virtual void update(World world, Vector3I pos) { } - public virtual ushort render(World world, Vector3I pos, List vertexBuffer, List indexBuffer, ushort currentIndex) { + public virtual ushort render(World world, Vector3I pos, List vertexBuffer) { return 0; }