using OpenTK.Mathematics; using VoxelEngine.Core; using VoxelEngine.World.Blocks; using VoxelChunk = VoxelEngine.World.Chunk.Chunk; namespace VoxelEngine.World.WorldGen; public sealed class WorldGenerator { private const int FillerDepth = 3; private const float CellSize = 96f; private readonly BlockRegistry _blocks; private readonly BiomeMap _biomeMap; private readonly TerrainSampler _terrain; private readonly CaveCarver _caves; public WorldGenerator(int seed, BlockRegistry blocks, Configuration cfg) { this._blocks = blocks; NoiseSource noise = new(seed); VoronoiCells cells = new(seed, WorldGenerator.CellSize); BiomeRegistry biomes = new(seed, blocks); this._biomeMap = new BiomeMap(noise, cells, biomes); this._terrain = new TerrainSampler(noise, this._biomeMap); this._caves = new CaveCarver(seed, blocks, cfg); } public int SurfaceHeightAt(int worldX, int worldZ) => this._terrain.HeightAt(worldX, worldZ); public VoxelChunk Generate(Vector3i chunkPos) { VoxelChunk chunk = new(); int baseX = chunkPos.X * VoxelChunk.Size; int baseY = chunkPos.Y * VoxelChunk.Size; int baseZ = chunkPos.Z * VoxelChunk.Size; for (int x = 0; x < VoxelChunk.Size; x++) for (int z = 0; z < VoxelChunk.Size; z++) { int worldX = baseX + x; int worldZ = baseZ + z; int height = this._terrain.HeightAt(worldX, worldZ); IBiome biome = this._biomeMap.BiomeAt(worldX, worldZ); for (int y = 0; y < VoxelChunk.Size; y++) { int worldY = baseY + y; chunk[x, y, z] = WorldGenerator.BlockAt(worldY, height, biome, this._blocks); } } this._caves.Carve(chunk, chunkPos); return chunk; } public static ushort BlockAt(int worldY, int surfaceHeight, IBiome biome, BlockRegistry blocks) { if (worldY > surfaceHeight) return worldY <= WorldConstants.SeaLevel ? blocks.Water.Id : blocks.Air.Id; if (worldY == surfaceHeight) return biome.TopBlock(worldY).Id; if (worldY >= surfaceHeight - WorldGenerator.FillerDepth) return biome.Surface.Filler.Id; return biome.Surface.Under.Id; } }