From a324ff0d16910574f8fa431325c268b7dceb1c2b Mon Sep 17 00:00:00 2001 From: grillkol Date: Mon, 20 Apr 2026 15:36:30 +0200 Subject: [PATCH] added barebones ui rendering --- .vscode/launch.json | 26 ++++++++++++++ .vscode/tasks.json | 41 +++++++++++++++++++++ Assets/ascii.png | Bin 0 -> 24110 bytes Graphics/Renderer.cs | 31 ++++++++++++++++ Shaders/ui.frag | 16 +++++++++ Shaders/ui.vert | 18 ++++++++++ UI/GuiBatcher.cs | 83 +++++++++++++++++++++++++++++++++++++++++++ UI/UIElement.cs | 6 ---- UI/UiVertex.cs | 15 ++++++++ 9 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Assets/ascii.png create mode 100644 Shaders/ui.frag create mode 100644 Shaders/ui.vert create mode 100644 UI/GuiBatcher.cs delete mode 100644 UI/UIElement.cs create mode 100644 UI/UiVertex.cs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7466f32 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net8.0/Voxel.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..095f25b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Voxel.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Voxel.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/Voxel.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Assets/ascii.png b/Assets/ascii.png new file mode 100644 index 0000000000000000000000000000000000000000..2cb9ecb81175b682ce206514d15b4b9e568eaf24 GIT binary patch literal 24110 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®9*BT#|AzajHz#DGuAuDHo?m)FJmx|wI}xv4cqd+ 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 _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); @@ -33,6 +42,10 @@ namespace Voxel.Graphics 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() @@ -44,6 +57,7 @@ namespace Voxel.Graphics _shader.SetMatrix4("view", Camera.view); _shader.SetVector3("cameraPosition", Camera.Position); _shader.SetMatrix4("projection", Camera.projection); + _texture.Bind(TextureUnit.Texture0); if (_buffersDirty) { @@ -110,7 +124,24 @@ namespace Voxel.Graphics 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, WindowWidth, WindowHeight, 0, -1, 1); + _uiShader.SetMatrix4("projection", projection); + + _uiTexture.Bind(TextureUnit.Texture0); + _guiBatcher.DrawString("Voxel Engine v0.1", 15, 70, 20, Vector4.One); + + _guiBatcher.Render(); + + GL.Disable(EnableCap.Blend); + GL.Enable(EnableCap.DepthTest); + GL.Enable(EnableCap.CullFace); } private static void RenderWorld() diff --git a/Shaders/ui.frag b/Shaders/ui.frag new file mode 100644 index 0000000..83043a0 --- /dev/null +++ b/Shaders/ui.frag @@ -0,0 +1,16 @@ +#version 440 core + +in vec2 TexCoord; +in vec4 Color; + +out vec4 FragColor; + +uniform sampler2D uTexture; // Ensure this name matches C# exactly + +void main() +{ + vec4 texColor = texture(uTexture, TexCoord); + + // If you don't use texColor, uTexture gets deleted by the compiler + FragColor = texColor * Color; +} \ No newline at end of file diff --git a/Shaders/ui.vert b/Shaders/ui.vert new file mode 100644 index 0000000..59770e6 --- /dev/null +++ b/Shaders/ui.vert @@ -0,0 +1,18 @@ +#version 440 core + +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aTexCoord; +layout (location = 2) in vec4 aColor; + +out vec2 TexCoord; +out vec4 Color; + +uniform mat4 projection; // Ensure this name matches C# exactly + +void main() +{ + // If you don't multiply by projection, the compiler deletes the uniform + gl_Position = projection * vec4(aPos, 0.0, 1.0); + TexCoord = aTexCoord; + Color = aColor; +} \ No newline at end of file diff --git a/UI/GuiBatcher.cs b/UI/GuiBatcher.cs new file mode 100644 index 0000000..5aba069 --- /dev/null +++ b/UI/GuiBatcher.cs @@ -0,0 +1,83 @@ +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; + +public class GuiBatcher +{ + private List _vertices = new List(); + private int _vbo; + private int _vao; + + public GuiBatcher() + { + _vao = GL.GenVertexArray(); + _vbo = GL.GenBuffer(); + + GL.BindVertexArray(_vao); + GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); + + // Position (2 floats) + GL.EnableVertexAttribArray(0); + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 0); + // TexCoord (2 floats) + GL.EnableVertexAttribArray(1); + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 8 * sizeof(float), 2 * sizeof(float)); + // Color (4 floats) + GL.EnableVertexAttribArray(2); + GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, 8 * sizeof(float), 4 * sizeof(float)); + } + + public void DrawQuad(float x, float y, float w, float h, Vector4 uv, Vector4 color) + { + // Two triangles (Standard Minecraft Blit) + _vertices.Add(new UiVertex(x, y, uv.X, uv.Y, color)); // TL + _vertices.Add(new UiVertex(x + w, y, uv.Z, uv.Y, color)); // TR + _vertices.Add(new UiVertex(x, y + h, uv.X, uv.W, color)); // BL + + _vertices.Add(new UiVertex(x + w, y, uv.Z, uv.Y, color)); // TR + _vertices.Add(new UiVertex(x + w, y + h, uv.Z, uv.W, color)); // BR + _vertices.Add(new UiVertex(x, y + h, uv.X, uv.W, color)); // BL + } + + public void Render() + { + if (_vertices.Count == 0) return; + + GL.BindVertexArray(_vao); + GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); + + var data = _vertices.ToArray(); + GL.BufferData(BufferTarget.ArrayBuffer, data.Length * 8 * sizeof(float), data, BufferUsageHint.StreamDraw); + + GL.DrawArrays(PrimitiveType.Triangles, 0, _vertices.Count); + _vertices.Clear(); // Reset for next frame + } + + public void DrawString(string text, float x, float y, float size, Vector4 color) + { + float charStep = 1.0f / 16.0f; + float currentX = x; + + foreach (char c in text) + { + int ascii = (int)c; + int row = ascii / 16; + int col = ascii % 16; + + float uStart = col * charStep; + float uEnd = uStart + charStep; + + float vStart = 1.0f - (row * charStep); + float vEnd = vStart - charStep; + + Vector4 uv = new Vector4( + uStart, // U start + vStart, // V start + uEnd, // U end + vEnd // V end + ); + + DrawQuad(currentX, y, size, size, uv, color); + currentX += size * 0.8f; // Tighten horizontal spacing + } + } +} \ No newline at end of file diff --git a/UI/UIElement.cs b/UI/UIElement.cs deleted file mode 100644 index 2be6585..0000000 --- a/UI/UIElement.cs +++ /dev/null @@ -1,6 +0,0 @@ -// wip - -public class UIElement -{ - -} \ No newline at end of file diff --git a/UI/UiVertex.cs b/UI/UiVertex.cs new file mode 100644 index 0000000..a4afad7 --- /dev/null +++ b/UI/UiVertex.cs @@ -0,0 +1,15 @@ +using OpenTK.Mathematics; + +struct UiVertex +{ + public Vector2 Position; + public Vector2 TexCoord; + public Vector4 Color; + + public UiVertex(float x, float y, float u, float v, Vector4 color) + { + Position = new Vector2(x, y); + TexCoord = new Vector2(u, v); + Color = color; + } +} \ No newline at end of file