using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; using static System.Runtime.InteropServices.JavaScript.JSType; namespace Voxel { static class Renderer { private static int _ssbo; private static int _vao; private static bool _buffersDirty; private static Dictionary<(int, int), int> _chunkBufferSizes = new Dictionary<(int, int), int>(); private static Shader _shader; private static readonly Texture _texture; private static World? _world; static Renderer() { string vertexPath = "Shaders/shader.vert"; string fragmentPath = "Shaders/shader.frag"; string texturePath = "atlas.png"; _shader = new Shader(vertexPath, fragmentPath); _texture = new Texture(texturePath); _shader.SetInt("uTexture", 0); _ssbo = GL.GenBuffer(); _vao = GL.GenVertexArray(); GL.BindVertexArray(_vao); GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo); GL.BufferData(BufferTarget.ShaderStorageBuffer, 1024 * 1024 * 128, IntPtr.Zero, BufferUsageHint.DynamicDraw); GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, 0, _ssbo); } public static void Render() { GL.BindVertexArray(_vao); GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo); _shader.Use(); _shader.SetMatrix4("view", Camera.view); _shader.SetVector3("cameraPosition", Camera.Position); _shader.SetMatrix4("projection", Camera.projection); if (_buffersDirty) { UpdateAllChunksBuffer(); _buffersDirty = false; } RenderWorld(); RenderUi(); } private static void UpdateAllChunksBuffer() { if (_world == null) return; int offset = 0; foreach (Chunk chunk in _world.GetAllChunks()) { ChunkMesh chunkMesh = chunk.GetChunkMesh(); if (chunkMesh.NeedsUpdate) { byte[] data = chunkMesh.GetPackedData(); GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)offset * 8, chunkMesh.Size * 8, data); } _chunkBufferSizes[(chunk.X, chunk.Y)] = offset; offset += chunkMesh.Size * 8; } } private static void RenderUi() { GL.Disable(EnableCap.DepthTest); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); //_uiShader.Use(); //Matrix4 projection = Matrix4.CreateOrthographicOffCenter( // 0, screenWidth, screenHeight, 0, -1, 1); //_uiShader.SetMatrix4("projection", projection); // Bind UI texture atlas //_uiTexture.Bind(); // Draw all UI sprites (batch by texture for efficiency) //foreach (var sprite in _uiSprites) //{ //sprite.Draw(); //} // Restore 3D settings GL.Disable(EnableCap.Blend); GL.Enable(EnableCap.DepthTest); } private static void RenderWorld() { if (_world == null) return; foreach (Chunk chunk in _world.GetAllChunks()) { ChunkMesh chunkMesh = chunk.GetChunkMesh(); if (chunkMesh.Size == 0) continue; if (!_chunkBufferSizes.TryGetValue((chunk.X, chunk.Y), out int offset)) continue; _shader.SetInt("chunkX", chunk.X); _shader.SetInt("chunkY", chunk.Y); //GL.MemoryBarrier(MemoryBarrierFlags.ShaderStorageBarrierBit); GL.DrawArrays(PrimitiveType.Triangles, offset * 6, chunkMesh.Size * 6); } } public static void SetWorld(World world) { _world = world; _buffersDirty = true; } public static void MarkBuffersDirty() { _buffersDirty = true; } } }