This commit is contained in:
Max Westerlund
2025-09-02 13:17:15 +02:00
parent b6f9966eb9
commit 71c5f3a3aa
15 changed files with 618 additions and 37 deletions

13
BlockData.cs Normal file
View File

@@ -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
{
}
}

29
Blocks.cs Normal file
View File

@@ -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
}
}

73
Camera.cs Normal file
View File

@@ -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);
}
}
}

160
Chunk.cs Normal file
View File

@@ -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<ushort, BlockData> _blockData;
private byte[] _blocks;
public Chunk(int positionX, int positionY)
{
PositionX = positionX;
PositionY = positionY;
_blockData = new Dictionary<ushort, BlockData>();
_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<FaceData> GetFaces()
{
List<FaceData> list = new List<FaceData>();
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;
}
}
}

23
ChunkMesh.cs Normal file
View File

@@ -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<FaceData> _faces;
public ChunkMesh(Chunk chunk)
{
_faces = new List<FaceData>();
}
private void MeshBlock(int i)
{
}
}
}

18
ChunkMesher.cs Normal file
View File

@@ -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;
}
}
}

29
FaceData.cs Normal file
View File

@@ -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
};
}
}
}

24
Input.cs Normal file
View File

@@ -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<Keys, bool> _keystates = new Dictionary<Keys, bool>();
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;
}
}
}

View File

@@ -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();
}

61
Renderer.cs Normal file
View File

@@ -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<FaceData> _faces = new List<FaceData>();
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<FaceData> faces)
{
_faces.AddRange(faces);
_needsUpdate = true;
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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);
}
}
}