using System.Runtime.CompilerServices; namespace Voxel { public class Chunk { public const int Size = 16; public const int Height = 256; public const int TotalBlocks = Size * Size * Height; public readonly int X; public readonly int Y; private ChunkMesh _chunkMesh; private Dictionary _blockData; private Blocks[] _blocks; public Dictionary Neighbors = new(); 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 }; private static readonly Dictionary FaceToNeighborMap = new() { { 0, Orientation.West }, // +X face { 1, Orientation.East }, // -X face { 4, Orientation.North }, // +Z face { 5, Orientation.South } // -Z face }; public Chunk(int x, int y) { X = x; Y = y; _blockData = new Dictionary(); _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) { if (i < 0 || i >= TotalBlocks) return; _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(); // TODO: Implement } 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 < Height; y++) { Blocks block = Worldgen.GetBlock(y, height); SetBlock(x, y, z, block, false); } } } UpdateChunkMesh(); } public (int x, int y, int z) IndexToPosition(int i) { if (i < 0 || i >= TotalBlocks) return (0, 0, 0); int y = i >> 8; // y * 256 int remainder = i & 0xFF; // Lower 8 bits: x + z * 16 int z = remainder >> 4; // z * 16 int x = remainder & 0xF; // x return (x, y, z); } public (int x, int y, int z) GetWorldCoordinates(int localX, int localY, int localZ) { return (localX + (X * Size), localY, localZ + (Y * Size)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetBlockIndex(int x, int y, int z) { // bit shifting if ((uint)x >= Size || (uint)y >= Height || (uint)z >= Size) return -1; return x + (z << 4) + (y << 8); // x * 1, z * 16, y * 256 } public void UpdateChunkMesh() { List faces = new List(TotalBlocks / 2); // Approximate capacity GenerateFaces(faces); _chunkMesh.SetFaces(faces); } private void GenerateFaces(List faces) { for (int x = 0; x < Size; x++) { for (int z = 0; z < Size; z++) { for (int y = 0; y < Height; y++) { ProcessBlockFaces(x, y, z, faces); } } } } private void ProcessBlockFaces(int x, int y, int z, List faces) { int blockIndex = GetBlockIndex(x, y, z); if (blockIndex == -1) return; Blocks block = _blocks[blockIndex]; if (block == Blocks.Air) return; var blockDef = BlockDefinitions.Blocks[block]; for (int face = 0; face < 6; face++) { if (ShouldAddFace(x, y, z, face)) { AddFace(x, y, z, face, blockDef.FaceTextures[face], faces); } } } private bool ShouldAddFace(int x, int y, int z, int face) { var offset = Offsets[face]; int nx = x + offset.dx; int ny = y + offset.dy; int nz = z + offset.dz; int neighborIndex = GetBlockIndex(nx, ny, nz); if (neighborIndex != -1) { return _blocks[neighborIndex] == Blocks.Air; } return IsFaceVisibleAtChunkBoundary(x, y, z, face); } private bool IsFaceVisibleAtChunkBoundary(int x, int y, int z, int face) { if (!FaceToNeighborMap.TryGetValue(face, out Orientation neighborOrientation)) { // top bottom faces always visible at chunk bounds return true; } if (!Neighbors.TryGetValue(neighborOrientation, out Chunk neighbor) || neighbor == null) { return true; // no neighbor, face is visible } // Calculate coordinates in neighbor chunk var offset = Offsets[face]; int localX = x; int localZ = z; if (offset.dx != 0) // east, west face { localX = WrapCoordinate(x + offset.dx, Size); } else if (offset.dz != 0) // north, south face { localZ = WrapCoordinate(z + offset.dz, Size); } return neighbor.GetBlock(localX, y, localZ) == Blocks.Air; } private int WrapCoordinate(int coord, int size) { if (coord < 0) return coord + size; if (coord >= size) return coord - size; return coord; } private void AddFace(int x, int y, int z, int face, Textures texture, List faces) { faces.Add(new FaceData { Facing = (Orientation)face, Texture = texture, X = (byte)x, Y = (byte)y, Z = (byte)z }); } public ChunkMesh GetChunkMesh() { return _chunkMesh; } } }