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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ namespace Voxel
private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>(); private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>();
private static Shader _shader; private static Shader _shader;
private static Texture _texture; private static Texture _texture;
private static bool _needsUpdate = false; private static World _world;
static Renderer() static Renderer()
{ {
@@ -35,32 +35,31 @@ namespace Voxel
GL.BindVertexArray(_vao); GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo); 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.Use();
_shader.SetMatrix4("view", Camera.view); _shader.SetMatrix4("view", Camera.view);
_shader.SetMatrix4("projection", Camera.projection); _shader.SetMatrix4("projection", Camera.projection);
for (int i = 0; i < _chunkMeshes.Count; i++) 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);
GL.DrawArrays(PrimitiveType.Triangles, 0, chunkMesh.Length * 6);
}
//Console.WriteLine("Rendered " + _faces.Count.ToString() + " faces");
} }
public static void AddChunkMesh(ChunkMesh chunkMesh) private static void RenderWorld()
{ {
_chunkMeshes.Add(chunkMesh); 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);
}
}
public static void SetWorld(World world)
{
_world = world;
} }
} }
} }

View File

@@ -12,32 +12,27 @@ namespace Voxel
_chunks = new Dictionary<(int, int), Chunk>(); _chunks = new Dictionary<(int, int), Chunk>();
} }
// Get a chunk at world coordinates, returns null if not loaded
public Chunk GetChunk(int chunkX, int chunkZ) public Chunk GetChunk(int chunkX, int chunkZ)
{ {
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk); _chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
return chunk; return chunk;
} }
// Add a chunk
public void AddChunk(Chunk chunk) public void AddChunk(Chunk chunk)
{ {
_chunks[(chunk.X, chunk.Y)] = chunk; _chunks[(chunk.X, chunk.Y)] = chunk;
} }
// Remove a chunk
public void RemoveChunk(int chunkX, int chunkZ) public void RemoveChunk(int chunkX, int chunkZ)
{ {
_chunks.Remove((chunkX, chunkZ)); _chunks.Remove((chunkX, chunkZ));
} }
// Iterate over all chunks
public IEnumerable<Chunk> GetAllChunks() public IEnumerable<Chunk> GetAllChunks()
{ {
return _chunks.Values; return _chunks.Values;
} }
// Optional: get a block at world coordinates
public Blocks GetBlock(int worldX, int worldY, int worldZ) public Blocks GetBlock(int worldX, int worldY, int worldZ)
{ {
int chunkX = worldX / Chunk.Size; int chunkX = worldX / Chunk.Size;
@@ -51,7 +46,6 @@ namespace Voxel
return chunk.GetBlock(localX, worldY, localZ); return chunk.GetBlock(localX, worldY, localZ);
} }
// Optional: set a block at world coordinates
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block) public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
{ {
int chunkX = worldX / Chunk.Size; int chunkX = worldX / Chunk.Size;
@@ -65,32 +59,74 @@ namespace Voxel
chunk.SetBlock(localX, worldY, localZ, block); 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; int x = (int)MathF.Floor(origin.X);
Vector3 dir = Camera.Front.Normalized(); int y = (int)MathF.Floor(origin.Y);
int z = (int)MathF.Floor(origin.Z);
float stepSize = 0.01f; int stepX = direction.X > 0 ? 1 : -1;
float distanceTraveled = 0f; 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; Blocks block = GetBlock(x, y, z);
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);
if (block != Blocks.Air) 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() public void Update()