62 lines
2.0 KiB
C#
62 lines
2.0 KiB
C#
using OpenTK.Mathematics;
|
|
using VoxelEngine.Core;
|
|
using VoxelEngine.World.Blocks;
|
|
using VoxelChunk = VoxelEngine.World.Chunk.Chunk;
|
|
|
|
namespace VoxelEngine.World.WorldGen;
|
|
|
|
// Carves caves into an already-terrain-filled chunk. Stateless after
|
|
// construction; the carve methods are pure functions of (chunk, chunkPos).
|
|
// Safe to share across worker threads.
|
|
public sealed class CaveCarver
|
|
{
|
|
private readonly int _seed;
|
|
private readonly BlockRegistry _blocks;
|
|
private readonly Configuration _cfg;
|
|
private readonly FastNoiseLite _caveNoise;
|
|
|
|
public CaveCarver(int seed, BlockRegistry blocks, Configuration cfg)
|
|
{
|
|
this._seed = seed;
|
|
this._blocks = blocks;
|
|
this._cfg = cfg;
|
|
|
|
this._caveNoise = new FastNoiseLite(seed + 100);
|
|
this._caveNoise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
|
|
this._caveNoise.SetFrequency(cfg.CaveNoiseFrequency);
|
|
}
|
|
|
|
public void Carve(VoxelChunk chunk, Vector3i chunkPos)
|
|
{
|
|
this.CarveNoiseCaves(chunk, chunkPos);
|
|
// Worm pass added in Task 6.
|
|
}
|
|
|
|
// Per-voxel 3D simplex threshold. Public for direct testability,
|
|
// matching the pattern of WorldGenerator.BlockAt.
|
|
public void CarveNoiseCaves(VoxelChunk chunk, Vector3i chunkPos)
|
|
{
|
|
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 y = 0; y < VoxelChunk.Size; y++)
|
|
for (int z = 0; z < VoxelChunk.Size; z++)
|
|
{
|
|
int worldY = baseY + y;
|
|
if (worldY <= 0) continue;
|
|
|
|
ushort id = chunk[x, y, z];
|
|
if (id == this._blocks.Air.Id) continue;
|
|
if (id == this._blocks.Water.Id) continue;
|
|
|
|
float density = this._caveNoise.GetNoise(baseX + x, worldY, baseZ + z);
|
|
if (density < this._cfg.CaveNoiseThreshold)
|
|
{
|
|
chunk[x, y, z] = this._blocks.Air.Id;
|
|
}
|
|
}
|
|
}
|
|
}
|