using VoxelEngine.World.Chunk; namespace VoxelEngine.World.Streaming; // Snapshots of the six cardinal neighbor chunks' block arrays, taken on the // main thread at job-dispatch time. Each slot is null if that neighbor is not // currently loaded. The ChunkMesher reads only these arrays from a worker // thread — never the live World. public sealed class NeighborSnapshots { public ushort[]? NegX { get; } public ushort[]? PosX { get; } public ushort[]? NegY { get; } public ushort[]? PosY { get; } public ushort[]? NegZ { get; } public ushort[]? PosZ { get; } // Snapshots take ownership of the arrays passed in. Callers must hand // in fresh copies (not live references into a chunk's _ids buffer) — // the snapshot does NOT copy defensively. This is enforced at the // single call-site in ChunkStreamer.SnapshotNeighbors which allocates // fresh arrays. The arrays must not be mutated after construction. public NeighborSnapshots( ushort[]? negX, ushort[]? posX, ushort[]? negY, ushort[]? posY, ushort[]? negZ, ushort[]? posZ) { this.NegX = negX; this.PosX = posX; this.NegY = negY; this.PosY = posY; this.NegZ = negZ; this.PosZ = posZ; } // Returns true if the out-of-chunk position (nx, ny, nz) — exactly one of // which is out of [0, Chunk.Chunk.Size) — is occupied by an opaque block // in the snapshot. Returns false if the relevant neighbor is null. public bool IsOpaqueOutside(int nx, int ny, int nz, Func isOpaque) { ushort[]? n; int lx = nx, ly = ny, lz = nz; if (nx < 0) { n = this.NegX; lx = Chunk.Chunk.Size - 1; } else if (nx >= Chunk.Chunk.Size) { n = this.PosX; lx = 0; } else if (ny < 0) { n = this.NegY; ly = Chunk.Chunk.Size - 1; } else if (ny >= Chunk.Chunk.Size) { n = this.PosY; ly = 0; } else if (nz < 0) { n = this.NegZ; lz = Chunk.Chunk.Size - 1; } else if (nz >= Chunk.Chunk.Size) { n = this.PosZ; lz = 0; } else return false; // not actually outside — shouldn't happen, mesher guards if (n == null) return false; int index = (lx * Chunk.Chunk.Size + ly) * Chunk.Chunk.Size + lz; return isOpaque(n[index]); } }