This commit is contained in:
maxwes08
2025-09-09 10:57:28 +02:00
parent 3678eaa5f8
commit b6655d71d9
6 changed files with 148 additions and 81 deletions

View File

@@ -6,6 +6,7 @@
public static int Height = 256;
public readonly int X;
public readonly int Y;
private ChunkMesh _chunkMesh;
private Dictionary<ushort, BlockData> _blockData;
private Blocks[] _blocks;
@@ -17,14 +18,18 @@
_blockData = new Dictionary<ushort, BlockData>();
_blocks = new Blocks[Size * Size * Height];
_chunkMesh = new ChunkMesh(X, Y);
Initialize();
}
public void SetBlock(int x, int y, int z, Blocks block)
{
Console.WriteLine(x.ToString() + ", " + y.ToString());
int i = GetBlockIndex(x, y, z);
if (i == -1) return;
_blocks[i] = block;
UpdateChunkMesh();
}
public void SetBlockIndex(int i, Blocks block)
@@ -35,6 +40,7 @@
public Blocks GetBlock(int x, int y, int z)
{
int i = GetBlockIndex(x, y, z);
if (i == -1) return Blocks.Air;
return _blocks[i];
}
@@ -51,6 +57,7 @@
_blocks[i] = Blocks.Stone;
for (int i = Size * Size * 15; i < Size * Size * 16; i++)
_blocks[i] = Blocks.Grass;
UpdateChunkMesh();
}
// todo
@@ -66,7 +73,7 @@
private int GetBlockIndex(int x, int y, int z)
{
if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15)
return 0;
return -1;
return x + z * Size + y * Size * Size;
}
@@ -81,14 +88,10 @@
( 0, 0, -1) // -Z
};
public ChunkMesh GetChunkMesh()
private void UpdateChunkMesh()
{
ChunkMesh chunkMesh = new ChunkMesh(X, Y);
List<FaceData> faces = new List<FaceData>(Size * Size * Height / 2);
// offsets table
for (int x = 0; x < Size; x++)
{
for (int z = 0; z < Size; z++)
@@ -100,19 +103,8 @@
int indexBase = y * Size * Size + z * Size + x;
Blocks block = _blocks[indexBase];
if (block == Blocks.Air) continue; // ignore if air
int nx = x + Offsets[face].dx;
int ny = y + Offsets[face].dy;
int nz = z + Offsets[face].dz;
// check neighbor, ignore if at chunk edge
if (nx >= 0 && nx < Size &&
ny >= 0 && ny < Height &&
nz >= 0 && nz < Size &&
_blocks[GetBlockIndex(nx, ny, nz)] != 0)
continue;
void AddFace()
{
FaceData faceData = new FaceData();
faceData.Facing = (Orientation)face;
@@ -124,13 +116,37 @@
faces.Add(faceData);
}
}
}
if (block == Blocks.Air) continue; // ignore if air
int nx = x + Offsets[face].dx;
int ny = y + Offsets[face].dy;
int nz = z + Offsets[face].dz;
// check neighbor, ignore if at chunk edge
int ni = GetBlockIndex(nx, ny, nz);
if (GetBlockIndex(nx, ny, nz) == -1)
{
AddFace();
continue;
}
chunkMesh.SetFaces(faces);
if (_blocks[ni] == Blocks.Air)
{
AddFace();
continue;
}
}
}
}
}
_chunkMesh.SetFaces(faces);
}
return chunkMesh;
public ChunkMesh GetChunkMesh()
{
return _chunkMesh;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -10,22 +11,36 @@ namespace Voxel
{
public int X;
public int Y;
public byte[] PackedData;
public bool NeedsUpdate = true;
private byte[] _packedData;
public bool NeedsUpdate = false;
public int Length = 0;
private List<FaceData> _faces;
public int SSBO;
public ChunkMesh(int x, int y)
{
PackedData = new byte[0];
_packedData = new byte[0];
X = x;
Y = y;
SSBO = GL.GenBuffer();
}
public void SetFaces(List<FaceData> faces)
{
Length = faces.Count;
PackedData = faces.SelectMany(f => f.Pack()).ToArray();
_faces = faces;
NeedsUpdate = true;
}
public byte[] GetPackedData()
{
if (NeedsUpdate)
{
_packedData = _faces.SelectMany(f => f.Pack()).ToArray();
NeedsUpdate = false;
}
return _packedData;
}
}
}

View File

@@ -20,12 +20,10 @@ namespace Voxel
public void BreakBlock()
{
var (hit, x, y, z) = _world.Raycast(5f); // max 5 blocks
if (hit != Blocks.Air)
{
var (success, hit, x, y, z) = _world.Raycast(Camera.Position, Camera.Front * 10, 100);
if (!success) return;
_world.SetBlock(x, y, z, Blocks.Air);
}
}
public void Update(float deltaTime)
{

View File

@@ -15,14 +15,17 @@ internal class Program
window.Player = player;
Chunk chunk0 = new Chunk(0, 0);
Chunk chunk1 = new Chunk(0, 1);
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
Chunk chunk = new Chunk(1+ x, 1 +y);
world.AddChunk(chunk0);
world.AddChunk(chunk1);
world.AddChunk(chunk);
}
}
Renderer.AddChunkMesh(chunk0.GetChunkMesh());
Renderer.AddChunkMesh(chunk1.GetChunkMesh());
Renderer.SetWorld(world);
window.Run();
}

View File

@@ -9,7 +9,7 @@ namespace Voxel
private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>();
private static Shader _shader;
private static Texture _texture;
private static bool _needsUpdate = false;
private static World _world;
static Renderer()
{
@@ -35,32 +35,31 @@ namespace Voxel
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
//if (_needsUpdate)
//{
// _needsUpdate = false;
// byte[] data = _faces.SelectMany(f => f.Pack()).ToArray();
// GL.BufferData(BufferTarget.ShaderStorageBuffer, data.Length, data, BufferUsageHint.StaticRead);
//}
_shader.Use();
_shader.SetMatrix4("view", Camera.view);
_shader.SetMatrix4("projection", Camera.projection);
for (int i = 0; i < _chunkMeshes.Count; i++)
RenderWorld();
}
private static void RenderWorld()
{
ChunkMesh chunkMesh = _chunkMeshes[i];
_shader.SetInt("chunkX", chunkMesh.X);
_shader.SetInt("chunkY", chunkMesh.Y);
GL.BufferData(BufferTarget.ShaderStorageBuffer, chunkMesh.PackedData.Length, chunkMesh.PackedData, BufferUsageHint.StaticRead);
if (_world == null) return;
foreach (Chunk chunk in _world.GetAllChunks())
{
ChunkMesh chunkMesh = chunk.GetChunkMesh();
byte[] data = chunkMesh.GetPackedData();
_shader.SetInt("chunkX", chunk.X);
_shader.SetInt("chunkY", chunk.Y);
GL.BufferData(BufferTarget.ShaderStorageBuffer, chunkMesh.Length * 8, data, BufferUsageHint.StaticRead);
GL.DrawArrays(PrimitiveType.Triangles, 0, chunkMesh.Length * 6);
}
//Console.WriteLine("Rendered " + _faces.Count.ToString() + " faces");
}
public static void AddChunkMesh(ChunkMesh chunkMesh)
public static void SetWorld(World world)
{
_chunkMeshes.Add(chunkMesh);
_world = world;
}
}
}

View File

@@ -12,32 +12,27 @@ namespace Voxel
_chunks = new Dictionary<(int, int), Chunk>();
}
// Get a chunk at world coordinates, returns null if not loaded
public Chunk GetChunk(int chunkX, int chunkZ)
{
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
return chunk;
}
// Add a chunk
public void AddChunk(Chunk chunk)
{
_chunks[(chunk.X, chunk.Y)] = chunk;
}
// Remove a chunk
public void RemoveChunk(int chunkX, int chunkZ)
{
_chunks.Remove((chunkX, chunkZ));
}
// Iterate over all chunks
public IEnumerable<Chunk> GetAllChunks()
{
return _chunks.Values;
}
// Optional: get a block at world coordinates
public Blocks GetBlock(int worldX, int worldY, int worldZ)
{
int chunkX = worldX / Chunk.Size;
@@ -51,7 +46,6 @@ namespace Voxel
return chunk.GetBlock(localX, worldY, localZ);
}
// Optional: set a block at world coordinates
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
{
int chunkX = worldX / Chunk.Size;
@@ -65,32 +59,74 @@ namespace Voxel
chunk.SetBlock(localX, worldY, localZ, block);
}
public (Blocks block, int x, int y, int z) Raycast(float length)
public (bool success, Blocks block, int x, int y, int z) Raycast(Vector3 origin, Vector3 direction, float maxDistance)
{
Vector3 start = Camera.Position;
Vector3 dir = Camera.Front.Normalized();
int x = (int)MathF.Floor(origin.X);
int y = (int)MathF.Floor(origin.Y);
int z = (int)MathF.Floor(origin.Z);
float stepSize = 0.01f;
float distanceTraveled = 0f;
int stepX = direction.X > 0 ? 1 : -1;
int stepY = direction.Y > 0 ? 1 : -1;
int stepZ = direction.Z > 0 ? 1 : -1;
while (distanceTraveled < length)
float tDeltaX = direction.X != 0 ? MathF.Abs(1 / direction.X) : float.MaxValue;
float tDeltaY = direction.Y != 0 ? MathF.Abs(1 / direction.Y) : float.MaxValue;
float tDeltaZ = direction.Z != 0 ? MathF.Abs(1 / direction.Z) : float.MaxValue;
float tMaxX = direction.X > 0
? (MathF.Floor(origin.X) + 1 - origin.X) * tDeltaX
: (origin.X - MathF.Floor(origin.X)) * tDeltaX;
float tMaxY = direction.Y > 0
? (MathF.Floor(origin.Y) + 1 - origin.Y) * tDeltaY
: (origin.Y - MathF.Floor(origin.Y)) * tDeltaY;
float tMaxZ = direction.Z > 0
? (MathF.Floor(origin.Z) + 1 - origin.Z) * tDeltaZ
: (origin.Z - MathF.Floor(origin.Z)) * tDeltaZ;
float distance = 0f;
while (distance <= maxDistance)
{
Vector3 point = start + dir * distanceTraveled;
int bx = (int)MathF.Floor(point.X);
int by = (int)MathF.Floor(point.Y);
int bz = (int)MathF.Floor(point.Z);
Blocks block = GetBlock(bx, by, bz);
Blocks block = GetBlock(x, y, z);
if (block != Blocks.Air)
{
return (block, bx, by, bz);
return (true, block, x, y, z);
}
distanceTraveled += stepSize;
// step to next voxel
if (tMaxX < tMaxY)
{
if (tMaxX < tMaxZ)
{
x += stepX;
distance = tMaxX;
tMaxX += tDeltaX;
}
else
{
z += stepZ;
distance = tMaxZ;
tMaxZ += tDeltaZ;
}
}
else
{
if (tMaxY < tMaxZ)
{
y += stepY;
distance = tMaxY;
tMaxY += tDeltaY;
}
else
{
z += stepZ;
distance = tMaxZ;
tMaxZ += tDeltaZ;
}
}
}
return (Blocks.Air, 0, 0, 0);
return (false, Blocks.Air, 0, 0, 0);
}
public void Update()