Files
voxel/Graphics/Renderer.cs

186 lines
5.8 KiB
C#

using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using Voxel.Core;
using Voxel.UI;
namespace Voxel.Graphics
{
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 Shader _uiShader;
private static GuiBatcher _guiBatcher;
private static readonly Texture _texture;
private static readonly Texture _uiTexture;
private static World? _world;
public static int WindowHeight = 1080;
public static int WindowWidth = 1920;
static Renderer()
{
string vertexPath = "Shaders/shader.vert";
string fragmentPath = "Shaders/shader.frag";
string texturePath = "Assets/atlas.png";
string uiTexturePath = "Assets/ascii.png";
_shader = new Shader(vertexPath, fragmentPath);
_texture = new Texture(texturePath);
_uiTexture = new Texture(uiTexturePath);
_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);
_guiBatcher = new GuiBatcher();
_uiShader = new Shader("Shaders/ui.vert", "Shaders/ui.frag");
_uiShader.SetInt("uTexture", 1);
}
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);
_texture.Bind(TextureUnit.Texture0);
if (_buffersDirty)
{
UpdateAllChunksBuffer();
_buffersDirty = false;
}
RenderWorld();
RenderUi();
}
public static void UpdateChunkBuffer(Chunk chunk)
{
if (_world == null || !_chunkBufferSizes.TryGetValue((chunk.X, chunk.Y), out int faceOffset))
return;
ChunkMesh chunkMesh = chunk.GetChunkMesh();
byte[] data = chunkMesh.GetPackedData();
int newByteSize = chunkMesh.Size * 4;
int currentAllocatedBytes = GetAllocatedSizeForChunk(chunk.X, chunk.Y);
if (newByteSize <= currentAllocatedBytes)
{
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)(faceOffset * 4), newByteSize, data);
}
else
{
MarkBuffersDirty();
}
}
private static int GetAllocatedSizeForChunk(int x, int y)
{
// todo, memory manager
return 0;
}
private static void UpdateAllChunksBuffer()
{
if (_world == null) return;
_chunkBufferSizes.Clear();
int faceOffset = 0;
foreach (Chunk chunk in _world.GetAllChunks())
{
ChunkMesh chunkMesh = chunk.GetChunkMesh();
_chunkBufferSizes[(chunk.X, chunk.Y)] = faceOffset;
byte[] data = chunkMesh.GetPackedData();
GL.BufferSubData(BufferTarget.ShaderStorageBuffer,
(IntPtr)(faceOffset * 4), // faceOffset * 4 = byte offset
chunkMesh.Size * 4,
data
);
faceOffset += chunkMesh.Size;
}
}
private static void RenderUi()
{
GL.Disable(EnableCap.CullFace);
GL.Disable(EnableCap.DepthTest);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
_uiShader.Use();
Matrix4 projection = Matrix4.CreateOrthographicOffCenter(0, Camera.Width, Camera.Height, 0, -1, 1);
_uiShader.SetMatrix4("projection", projection);
_uiTexture.Bind(TextureUnit.Texture0);
_guiBatcher.DrawString("Voxel Engine v0.1", 16, 112, 24, Vector4.One);
_guiBatcher.Render();
GL.Disable(EnableCap.Blend);
GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.CullFace);
}
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;
}
//temp
public static void RenderLabel(Label label)
{
_guiBatcher.DrawString(label.Text, label.X, label.Y, label.Size, label.Color);
}
}
}