Files
voxel/Chunk.cs
2025-09-29 10:55:37 +02:00

188 lines
5.8 KiB
C#

namespace Voxel
{
public class Chunk
{
public static int Size = 16;
public static int Height = 256;
public readonly int X;
public readonly int Y;
private ChunkMesh _chunkMesh;
private Dictionary<ushort, BlockData> _blockData;
private Blocks[] _blocks;
public Dictionary<Orientation, Chunk> Neighbors = new();
public Chunk(int x, int y)
{
X = x;
Y = y;
_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, bool updateMesh = true)
{
int i = GetBlockIndex(x, y, z);
if (i == -1) return;
_blocks[i] = block;
if (updateMesh)
{
UpdateChunkMesh();
Renderer.MarkBuffersDirty();
}
}
public void SetBlockIndex(int i, Blocks block)
{
_blocks[i] = block;
}
public Blocks GetBlock(int x, int y, int z)
{
int i = GetBlockIndex(x, y, z);
if (i == -1) return Blocks.Air;
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++)
{
var position = GetWorldCoordinates(x, 0, z);
int height = Worldgen.GetHeight(position.x, position.z);
for (int y = 0; y < 256; y++)
{
Blocks block = Worldgen.GetBlock(y, height);
SetBlock(x, y, z, block, false);
}
}
}
UpdateChunkMesh();
}
// todo
public (int x, int y, int z) IndexToPosition(int i)
{
int x = 0;
int y = 0;
int z = 0;
return (x, y, z);
}
public (int x, int y, int z) GetWorldCoordinates(int x, int y, int z)
{
x += (Size * X);
z += (Size * Y);
return (x, y, z);
}
private int GetBlockIndex(int x, int y, int z)
{
if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15)
return -1;
return x + z * Size + y * Size * Size;
}
private static readonly (int dx, int dy, int dz)[] Offsets = new (int, int, int)[6]
{
( 1, 0, 0), // +X
(-1, 0, 0), // -X
( 0, 1, 0), // +Y
( 0, -1, 0), // -Y
( 0, 0, 1), // +Z
( 0, 0, -1) // -Z
};
public void UpdateChunkMesh()
{
List<FaceData> faces = new List<FaceData>(Size * Size * Height / 2);
for (int x = 0; x < Size; x++)
{
for (int z = 0; z < Size; z++)
{
for (int y = 0; y < Height; y++)
{
for (byte face = 0; face < 6; face++)
{
int indexBase = y * Size * Size + z * Size + x;
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
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)
{
if (Neighbors.TryGetValue((Orientation)face, out Chunk neighbor) && neighbor != null)
{
int localX = nx;
int localZ = nz;
if (nx < 0) localX = nx + Size;
if (nx >= Size) localX = nx - Size;
if (nz < 0) localZ = nz + Size;
if (nz >= Size) localZ = nz - Size;
Blocks neighborBlock = neighbor.GetBlock(localX, y, localZ);
if (neighborBlock != Blocks.Air)
continue;
}
AddFace();
continue;
}
if (_blocks[ni] == Blocks.Air)
{
AddFace();
continue;
}
}
}
}
}
_chunkMesh.SetFaces(faces);
}
public ChunkMesh GetChunkMesh()
{
return _chunkMesh;
}
}
}