diff --git a/BlockData.cs b/BlockData.cs new file mode 100644 index 0000000..13ac6f0 --- /dev/null +++ b/BlockData.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public class BlockData + { + + } +} diff --git a/Blocks.cs b/Blocks.cs new file mode 100644 index 0000000..842ea11 --- /dev/null +++ b/Blocks.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public enum Blocks : byte + { + Air = 0, + Stone = 1, + } + + public enum Orientation: uint + { + West = 0, // + X + East = 1, // - X + Top = 2, // + Y + Bottom = 3,// - Y + North = 4, // + Z + South = 5, // - Z + } + + public enum Texture: uint + { + Stone = 1 + } +} diff --git a/Camera.cs b/Camera.cs new file mode 100644 index 0000000..91efbb2 --- /dev/null +++ b/Camera.cs @@ -0,0 +1,73 @@ +using OpenTK.Mathematics; +using System.Security.Cryptography; + +namespace Voxel +{ + static class Camera + { + public static Vector3 Position; + + public static float Pitch = 0f; + public static float Yaw = -90f; + public static float FOV = 70f; + public static float Speed = 0.5f; + + public static Matrix4 view => + Matrix4.LookAt(Position, Position + Front, Vector3.UnitY); + + public static Matrix4 projection; + + public static Vector3 Front + { + get + { + Vector3 front; + front.X = MathF.Cos(MathHelper.DegreesToRadians(Yaw)) * MathF.Cos(MathHelper.DegreesToRadians(Pitch)); + front.Y = MathF.Sin(MathHelper.DegreesToRadians(Pitch)); + front.Z = MathF.Sin(MathHelper.DegreesToRadians(Yaw)) * MathF.Cos(MathHelper.DegreesToRadians(Pitch)); + return front.Normalized(); + } + } + + public static void Update(float time) + { + float moveSpeed = Speed * time; + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.LeftShift)) + { + moveSpeed *= 10; + } + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.W)) + { + Position += Front * moveSpeed; + } + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.S)) + { + Position += Front * -moveSpeed; + } + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.A)) + { + Position += Vector3.Cross(Front, Vector3.UnitY).Normalized() * -moveSpeed; + } + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.D)) + { + Position += Vector3.Cross(Front, Vector3.UnitY).Normalized() * moveSpeed; + } + } + + public static void UpdateMouse(Vector2 delta) + { + float fov = MathHelper.DegreesToRadians(60f); + float aspectRatio = 800f / 600f; + float near = 0.1f; + float far = 100f; + + projection = Matrix4.CreatePerspectiveFieldOfView(fov, aspectRatio, near, far); + + float sensitivity = 0.1f; + Yaw += delta.X * sensitivity; + Pitch -= delta.Y * sensitivity; + + Pitch = MathHelper.Clamp(Pitch, -89f, 89f); + } + } +} diff --git a/Chunk.cs b/Chunk.cs new file mode 100644 index 0000000..230b141 --- /dev/null +++ b/Chunk.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public class Chunk + { + public readonly int Size = 16; + public readonly int Height = 256; + public readonly int PositionX; + public readonly int PositionY; + + private Dictionary _blockData; + private byte[] _blocks; + + public Chunk(int positionX, int positionY) + { + PositionX = positionX; + PositionY = positionY; + + _blockData = new Dictionary(); + _blocks = new byte[Size * Size * Height]; + + Initialize(); + } + + public void SetBlock(int x, int y, int z, byte blockId) + { + int i = GetBlockIndex(x, y, z); + _blocks[i] = blockId; + } + + public byte GetBlock(int x, int y, int z) + { + int i = GetBlockIndex(x, y, z); + return _blocks[i]; + } + + public BlockData GetBlockData(int x, int y, int z) + { + return new BlockData(); + } + + private void Initialize() + { + for (int x = 0; x < Size; x++) + { + for (int z = 0; z < Size; z++) + { + for (int y = 0; y < Size; y++) + { + byte blockId = 1; + SetBlock(x, y, z, blockId); + Console.WriteLine( + "Set block " + + x.ToString() + ", " + + y.ToString() + ", " + + z.ToString() + " " + ); + } + } + } + } + + // todo + public (int x, int y, int z) IndexToPosition(int i) + { + int x = 0; + int y = 0; + int z = 0; + + return (x, y, z); + } + + private int GetBlockIndex(int x, int y, int z) + { + return x + y * Size + z * Size * Size; + } + + public List GetFaces() + { + List list = new List(); + + for (byte i = 0; i < 6; i++) + { + FaceData faceData = new FaceData(); + + faceData.Facing = (Orientation)i; + + list.Add(faceData); + } + + for (byte i = 0; i < 6; i++) + { + FaceData faceData = new FaceData(); + + faceData.Facing = (Orientation)i; + + faceData.X = 1; + + list.Add(faceData); + } + + for (byte i = 0; i < 6; i++) + { + FaceData faceData = new FaceData(); + + faceData.Facing = (Orientation)i; + + + faceData.X = 1; + faceData.Y = 1; + faceData.Z = 1; + + list.Add(faceData); + } + + for (byte i = 0; i < 6; i++) + { + FaceData faceData = new FaceData(); + + faceData.Facing = (Orientation)i; + + + faceData.X = 1; + faceData.Y = 2; + faceData.Z = 1; + + list.Add(faceData); + } + + for (int x = 0; x < Size; x++) + { + for (int z = 0; z < Size; z++) + { + for (int y = 0; y < Height; y++) + { + for (byte i = 0; i < 6; i++) + { + FaceData faceData = new FaceData(); + + faceData.Facing = (Orientation)i; + + faceData.X = (byte)x; + faceData.Y = (byte)y; + faceData.Z = (byte)z; + + //list.Add(faceData); + } + } + } + } + + return list; + } + } +} diff --git a/ChunkMesh.cs b/ChunkMesh.cs new file mode 100644 index 0000000..a16e930 --- /dev/null +++ b/ChunkMesh.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public class ChunkMesh + { + private List _faces; + + public ChunkMesh(Chunk chunk) + { + _faces = new List(); + } + + private void MeshBlock(int i) + { + + } + } +} diff --git a/ChunkMesher.cs b/ChunkMesher.cs new file mode 100644 index 0000000..7548ed7 --- /dev/null +++ b/ChunkMesher.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + static class ChunkMesher + { + static ChunkMesh MeshChunk(Chunk chunk) + { + ChunkMesh chunkMesh = new ChunkMesh(chunk); + + return chunkMesh; + } + } +} diff --git a/FaceData.cs b/FaceData.cs new file mode 100644 index 0000000..f3751d0 --- /dev/null +++ b/FaceData.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct FaceData + { + public Orientation Facing; + public uint X, Y, Z; + public Texture Texture; + + public uint[] Pack() + { + return new uint[] + { + X, + Y, + Z, + (uint)Facing, + (uint)Texture + }; + } + } +} diff --git a/Input.cs b/Input.cs new file mode 100644 index 0000000..ff90b6e --- /dev/null +++ b/Input.cs @@ -0,0 +1,24 @@ +using OpenTK.Windowing.GraphicsLibraryFramework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + public static class Input + { + private static Dictionary _keystates = new Dictionary(); + + public static bool GetKey(Keys key) + { + return _keystates.TryGetValue(key, out bool pressed) && pressed; + } + + public static void SetKey(Keys key, bool pressed) + { + _keystates[key] = pressed; + } + } +} diff --git a/Program.cs b/Program.cs index 73b2979..d0d1b82 100644 --- a/Program.cs +++ b/Program.cs @@ -8,6 +8,11 @@ internal class Program int sizeY = 600; string title = "Game"; + Chunk chunk = new Chunk(0, 0); + + var faces = chunk.GetFaces(); + Renderer.AddFaces(faces); + Window window = new Window(sizeX, sizeY, title); window.Run(); } diff --git a/Renderer.cs b/Renderer.cs new file mode 100644 index 0000000..3a21304 --- /dev/null +++ b/Renderer.cs @@ -0,0 +1,61 @@ +using OpenTK.Graphics.OpenGL4; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Voxel +{ + static class Renderer + { + private static int _ssbo; + private static int _vao; + private static List _faces = new List(); + private static Shader _shader; + private static bool _needsUpdate = false; + + public static void OnLoad() + { + string vertexPath = "Shaders/shader.vert"; + string fragmentPath = "Shaders/shader.frag"; + _shader = new Shader(vertexPath, fragmentPath); + + _ssbo = GL.GenBuffer(); + _vao = GL.GenVertexArray(); + + GL.BindVertexArray(_vao); + GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo); + GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, 0, _ssbo); + } + + public static void Render() + { + GL.BindVertexArray(_vao); + GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo); + + if (_needsUpdate) + { + _needsUpdate = false; + Console.WriteLine("Update buffer"); + uint[] data = _faces.SelectMany(f => f.Pack()).ToArray(); + GL.BufferData(BufferTarget.ShaderStorageBuffer, data.Length * sizeof(uint), data, BufferUsageHint.StaticRead); + } + + _shader.Use(); + _shader.SetMatrix4("view", Camera.view); + _shader.SetMatrix4("projection", Camera.projection); + + GL.DrawArrays(PrimitiveType.Triangles, 0, _faces.Count * 6); + + //Console.WriteLine("Rendered " + _faces.Count.ToString() + " faces"); + } + + public static void AddFaces(List faces) + { + _faces.AddRange(faces); + _needsUpdate = true; + } + } +} diff --git a/Shader.cs b/Shader.cs index 058865c..faeab63 100644 --- a/Shader.cs +++ b/Shader.cs @@ -1,4 +1,5 @@ using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; using System; using System.Collections.Generic; using System.Linq; @@ -10,7 +11,7 @@ namespace Voxel { public class Shader { - int handle; + private int _handle; private bool disposedValue = false; public Shader(string vertexPath, string fragmentPath) @@ -41,36 +42,47 @@ namespace Voxel // attach - handle = GL.CreateProgram(); + _handle = GL.CreateProgram(); - GL.AttachShader(handle, vertexShader); - GL.AttachShader(handle, fragmentShader); + GL.AttachShader(_handle, vertexShader); + GL.AttachShader(_handle, fragmentShader); - GL.LinkProgram(handle); + GL.LinkProgram(_handle); - GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out success); + GL.GetProgram(_handle, GetProgramParameterName.LinkStatus, out success); if (success == 0) { - string infoLog = GL.GetProgramInfoLog(handle); + string infoLog = GL.GetProgramInfoLog(_handle); Console.WriteLine(infoLog); } - GL.DetachShader(handle, vertexShader); - GL.DetachShader(handle, fragmentShader); + GL.DetachShader(_handle, vertexShader); + GL.DetachShader(_handle, fragmentShader); GL.DeleteShader(fragmentShader); GL.DeleteShader(vertexShader); } + public void SetMatrix4(string name, Matrix4 matrix) + { + int location = GL.GetUniformLocation(_handle, name); + if (location == -1) + { + Console.WriteLine($"Uniform '{name}' not found in shader."); + return; + } + GL.UniformMatrix4(location, false, ref matrix); + } + public void Use() { - GL.UseProgram(handle); + GL.UseProgram(_handle); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { - GL.DeleteProgram(handle); + GL.DeleteProgram(_handle); disposedValue = true; } diff --git a/Shaders/shader.frag b/Shaders/shader.frag index ea6443e..0e84ed4 100644 --- a/Shaders/shader.frag +++ b/Shaders/shader.frag @@ -1,9 +1,9 @@ -#version 330 core -out vec4 FragColor; +#version 430 core -in vec4 vertexColor; +out vec4 FragColor; +in vec3 fragColor; void main() { - FragColor = vertexColor; + FragColor = vec4(fragColor, 1.0); } \ No newline at end of file diff --git a/Shaders/shader.vert b/Shaders/shader.vert index 80339d4..0153833 100644 --- a/Shaders/shader.vert +++ b/Shaders/shader.vert @@ -1,10 +1,106 @@ -#version 330 core -layout (location = 0) in vec3 aPosition; +#version 430 core -out vec4 vertexColor; +struct FaceData { + uint x; + uint y; + uint z; + uint facing; // 0=+X,1=-X,2=+Y,3=-Y,4=+Z,5=-Z + uint texture; +}; + +layout(std430, binding = 0) buffer FaceBuffer { + FaceData faces[]; +}; + +out vec3 fragColor; + +vec3 getVertexOffset(uint facing, uint vertIndex) { + if (facing == 0u) { // +X + vec3 offsets[6] = vec3[6]( + vec3(0.5, -0.5, -0.5), + vec3(0.5, 0.5, 0.5), + vec3(0.5, -0.5, 0.5), + vec3(0.5, 0.5, 0.5), + vec3(0.5, -0.5, -0.5), + vec3(0.5, 0.5, -0.5) + ); + return offsets[vertIndex]; + } else if (facing == 1u) { // -X + vec3 offsets[6] = vec3[6]( + vec3(-0.5, -0.5, 0.5), + vec3(-0.5, 0.5, -0.5), + vec3(-0.5, -0.5, -0.5), + vec3(-0.5, 0.5, -0.5), + vec3(-0.5, -0.5, 0.5), + vec3(-0.5, 0.5, 0.5) + ); + return offsets[vertIndex]; + } else if (facing == 2u) { // +Y (top) + vec3 offsets[6] = vec3[6]( + vec3(-0.5, 0.5, -0.5), + vec3( 0.5, 0.5, 0.5), + vec3( 0.5, 0.5, -0.5), + vec3( 0.5, 0.5, 0.5), + vec3(-0.5, 0.5, -0.5), + vec3(-0.5, 0.5, 0.5) + ); + return offsets[vertIndex]; + } else if (facing == 3u) { // -Y (bottom) + vec3 offsets[6] = vec3[6]( + vec3(-0.5, -0.5, 0.5), + vec3( 0.5, -0.5, -0.5), + vec3( 0.5, -0.5, 0.5), + vec3( 0.5, -0.5, -0.5), + vec3(-0.5, -0.5, 0.5), + vec3(-0.5, -0.5, -0.5) + ); + return offsets[vertIndex]; + } else if (facing == 4u) { // +Z (front) + vec3 offsets[6] = vec3[6]( + vec3(-0.5, -0.5, 0.5), + vec3( 0.5, -0.5, 0.5), + vec3( 0.5, 0.5, 0.5), + vec3( 0.5, 0.5, 0.5), + vec3(-0.5, 0.5, 0.5), + vec3(-0.5, -0.5, 0.5) + ); + return offsets[vertIndex]; + } else if (facing == 5u) { // -Z (back) + vec3 offsets[6] = vec3[6]( + vec3( 0.5, -0.5, -0.5), + vec3(-0.5, -0.5, -0.5), + vec3(-0.5, 0.5, -0.5), + vec3(-0.5, 0.5, -0.5), + vec3( 0.5, 0.5, -0.5), + vec3( 0.5, -0.5, -0.5) + ); + return offsets[vertIndex]; + } +} + +uniform mat4 view; +uniform mat4 projection; void main() { - gl_Position = vec4(aPosition, 1.0); - vertexColor = vec4(aPosition + vec3(0.5,0.5,0.5), 1.0); + float[6] lightMult = float[6]( + 0.6, + 0.6, + 1.0, + 0.5, + 0.8, + 0.8 + ); + + uint faceIndex = gl_VertexID / 6; + uint vertIndex = gl_VertexID % 6; + + FaceData f = faces[faceIndex]; + vec3 basePos = vec3(f.x, f.y, f.z); + vec4 worldPos = vec4(basePos + getVertexOffset(f.facing, vertIndex), 1.0); + + + fragColor = vec3(1.0, 1.0, 1.0) * lightMult[f.facing]; + + gl_Position = projection * view * worldPos; } \ No newline at end of file diff --git a/Triangle.cs b/Triangle.cs index 3a23094..8227ed5 100644 --- a/Triangle.cs +++ b/Triangle.cs @@ -21,9 +21,9 @@ namespace Voxel float[] vertices = { - -0.5f, 0.5f, 0.0f, //top + -0.0f, 0.5f, 0.0f, //top 0.5f, -0.5f, 0.0f, // bottom right - 0.0f, 0.5f, 0.0f // bottom left + -0.5f, -0.5f, 0.0f // bottom left }; _vao = GL.GenVertexArray(); diff --git a/Window.cs b/Window.cs index 33e04c7..2ed4a47 100644 --- a/Window.cs +++ b/Window.cs @@ -1,30 +1,43 @@ using OpenTK.Graphics.OpenGL4; using OpenTK.Windowing.Common; using OpenTK.Windowing.Desktop; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Voxel { - public class Window(int width, int height, string title) : GameWindow(GameWindowSettings.Default, new NativeWindowSettings() { Size = (width, height), Title = title }) + public class Window(int width, int height, string title) : GameWindow(GameWindowSettings.Default, new NativeWindowSettings() { ClientSize = (width, height), Title = title }) { - private Triangle _triangle; + public readonly int Width = width; + public readonly int Height = height; + public uint frames = 0; + public double timeElapsed = 0; - protected override void OnUpdateFrame(FrameEventArgs args) + protected override void OnUpdateFrame(FrameEventArgs e) { - base.OnUpdateFrame(args); + base.OnUpdateFrame(e); + + if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.Escape)) + { + Close(); + } } - protected override void OnRenderFrame(FrameEventArgs args) + protected override void OnRenderFrame(FrameEventArgs e) { - base.OnRenderFrame(args); + base.OnRenderFrame(e); - GL.Clear(ClearBufferMask.ColorBufferBit); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - _triangle.Draw(); + frames++; + timeElapsed += e.Time; + if (timeElapsed >= 1) + { + Console.WriteLine("FPS: " + frames.ToString()); + timeElapsed = 0; + frames = 0; + } + + Camera.Update((float)e.Time); + Renderer.Render(); SwapBuffers(); } @@ -40,9 +53,34 @@ namespace Voxel { base.OnLoad(); - _triangle = new Triangle(); + Renderer.OnLoad(); - GL.ClearColor(0.5f, 0.5f, 0.5f, 1f); + GL.ClearColor(0.72f, 0.88f, 0.97f, 1f); + + GL.Enable(EnableCap.CullFace); + GL.Enable(EnableCap.DepthTest); + + CursorState = CursorState.Grabbed; + VSync = VSyncMode.On; + } + + protected override void OnMouseMove(MouseMoveEventArgs e) + { + base.OnMouseMove(e); + + Camera.UpdateMouse(e.Delta); + } + + protected override void OnKeyUp(KeyboardKeyEventArgs e) + { + base.OnKeyUp(e); + Input.SetKey(e.Key, false); + } + + protected override void OnKeyDown(KeyboardKeyEventArgs e) + { + base.OnKeyDown(e); + Input.SetKey(e.Key, true); } } }