feat(world-gen): integrate CaveCarver into WorldGenerator.Generate
This commit is contained in:
@@ -55,7 +55,7 @@ public sealed class Game : GameWindow
|
|||||||
this._world = new World.World(this._registry, this._modStore);
|
this._world = new World.World(this._registry, this._modStore);
|
||||||
|
|
||||||
int seed = this._cfg.Seed ?? new Random().Next();
|
int seed = this._cfg.Seed ?? new Random().Next();
|
||||||
this._generator = new WorldGenerator(seed, this._registry);
|
this._generator = new WorldGenerator(seed, this._registry, this._cfg);
|
||||||
|
|
||||||
// Spawn at terrain surface above (0, 0).
|
// Spawn at terrain surface above (0, 0).
|
||||||
int surfaceY = this._generator.SurfaceHeightAt(0, 0);
|
int surfaceY = this._generator.SurfaceHeightAt(0, 0);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class StreamingIntegrationTests
|
|||||||
var registry = new BlockRegistry();
|
var registry = new BlockRegistry();
|
||||||
var store = new InMemoryChunkModificationStore();
|
var store = new InMemoryChunkModificationStore();
|
||||||
var world = new World.World(registry, store);
|
var world = new World.World(registry, store);
|
||||||
var gen = new WorldGenerator(seed: 1337, blocks: registry);
|
var gen = new WorldGenerator(seed: 1337, blocks: registry, cfg: new Configuration());
|
||||||
var mesher = new ChunkMesher(atlasWidthPixels: 128, atlasHeightPixels: 32);
|
var mesher = new ChunkMesher(atlasWidthPixels: 128, atlasHeightPixels: 32);
|
||||||
var executor = new SynchronousJobExecutor();
|
var executor = new SynchronousJobExecutor();
|
||||||
Func<float[], IChunkRenderer> factory = m => new FakeRenderer(m);
|
Func<float[], IChunkRenderer> factory = m => new FakeRenderer(m);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
|
using VoxelEngine.Core;
|
||||||
using VoxelEngine.World.Blocks;
|
using VoxelEngine.World.Blocks;
|
||||||
using VoxelEngine.World.Chunk;
|
using VoxelEngine.World.Chunk;
|
||||||
using VoxelEngine.World.WorldGen;
|
using VoxelEngine.World.WorldGen;
|
||||||
@@ -58,8 +59,8 @@ public class WorldGeneratorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Generate_IsDeterministic()
|
public void Generate_IsDeterministic()
|
||||||
{
|
{
|
||||||
var gen1 = new WorldGenerator(99, Blocks);
|
var gen1 = new WorldGenerator(99, Blocks, new Configuration());
|
||||||
var gen2 = new WorldGenerator(99, Blocks);
|
var gen2 = new WorldGenerator(99, Blocks, new Configuration());
|
||||||
Chunk a = gen1.Generate(new Vector3i(0, 4, 0));
|
Chunk a = gen1.Generate(new Vector3i(0, 4, 0));
|
||||||
Chunk b = gen2.Generate(new Vector3i(0, 4, 0));
|
Chunk b = gen2.Generate(new Vector3i(0, 4, 0));
|
||||||
for (int x = 0; x < Chunk.Size; x++)
|
for (int x = 0; x < Chunk.Size; x++)
|
||||||
@@ -67,4 +68,32 @@ public class WorldGeneratorTests
|
|||||||
for (int z = 0; z < Chunk.Size; z++)
|
for (int z = 0; z < Chunk.Size; z++)
|
||||||
Assert.Equal(a[x, y, z], b[x, y, z]);
|
Assert.Equal(a[x, y, z], b[x, y, z]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Generate_AppliesCaves_ProducesAirVoxelsBelowSurface()
|
||||||
|
{
|
||||||
|
var gen = new WorldGenerator(seed: 12345, Blocks, new Configuration());
|
||||||
|
// A chunk well below typical surface height — guaranteed mostly stone before carving.
|
||||||
|
Chunk c = gen.Generate(new Vector3i(0, 1, 0)); // worldY = 16..31
|
||||||
|
|
||||||
|
int airCount = 0;
|
||||||
|
for (int x = 0; x < Chunk.Size; x++)
|
||||||
|
for (int y = 0; y < Chunk.Size; y++)
|
||||||
|
for (int z = 0; z < Chunk.Size; z++)
|
||||||
|
if (c[x, y, z] == Blocks.Air.Id) airCount++;
|
||||||
|
|
||||||
|
Assert.True(airCount > 0,
|
||||||
|
"Deep chunk had zero air voxels after generation — caves not running?");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Generate_PreservesBedrockFloor_Y0_IsStone()
|
||||||
|
{
|
||||||
|
var gen = new WorldGenerator(seed: 12345, Blocks, new Configuration());
|
||||||
|
Chunk c = gen.Generate(new Vector3i(0, 0, 0)); // worldY = 0..15
|
||||||
|
|
||||||
|
for (int x = 0; x < Chunk.Size; x++)
|
||||||
|
for (int z = 0; z < Chunk.Size; z++)
|
||||||
|
Assert.Equal(Blocks.Stone.Id, c[x, 0, z]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
|
using VoxelEngine.Core;
|
||||||
using VoxelEngine.World.Blocks;
|
using VoxelEngine.World.Blocks;
|
||||||
using VoxelChunk = VoxelEngine.World.Chunk.Chunk;
|
using VoxelChunk = VoxelEngine.World.Chunk.Chunk;
|
||||||
|
|
||||||
@@ -12,8 +13,9 @@ public sealed class WorldGenerator
|
|||||||
private readonly BlockRegistry _blocks;
|
private readonly BlockRegistry _blocks;
|
||||||
private readonly BiomeMap _biomeMap;
|
private readonly BiomeMap _biomeMap;
|
||||||
private readonly TerrainSampler _terrain;
|
private readonly TerrainSampler _terrain;
|
||||||
|
private readonly CaveCarver _caves;
|
||||||
|
|
||||||
public WorldGenerator(int seed, BlockRegistry blocks)
|
public WorldGenerator(int seed, BlockRegistry blocks, Configuration cfg)
|
||||||
{
|
{
|
||||||
this._blocks = blocks;
|
this._blocks = blocks;
|
||||||
NoiseSource noise = new(seed);
|
NoiseSource noise = new(seed);
|
||||||
@@ -21,6 +23,7 @@ public sealed class WorldGenerator
|
|||||||
BiomeRegistry biomes = new(seed, blocks);
|
BiomeRegistry biomes = new(seed, blocks);
|
||||||
this._biomeMap = new BiomeMap(noise, cells, biomes);
|
this._biomeMap = new BiomeMap(noise, cells, biomes);
|
||||||
this._terrain = new TerrainSampler(noise, this._biomeMap);
|
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 int SurfaceHeightAt(int worldX, int worldZ) => this._terrain.HeightAt(worldX, worldZ);
|
||||||
@@ -46,10 +49,11 @@ public sealed class WorldGenerator
|
|||||||
chunk[x, y, z] = WorldGenerator.BlockAt(worldY, height, biome, this._blocks);
|
chunk[x, y, z] = WorldGenerator.BlockAt(worldY, height, biome, this._blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._caves.Carve(chunk, chunkPos);
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure column fill rule, extracted for testability.
|
|
||||||
public static ushort BlockAt(int worldY, int surfaceHeight, IBiome biome, BlockRegistry blocks)
|
public static ushort BlockAt(int worldY, int surfaceHeight, IBiome biome, BlockRegistry blocks)
|
||||||
{
|
{
|
||||||
if (worldY > surfaceHeight)
|
if (worldY > surfaceHeight)
|
||||||
|
|||||||
Reference in New Issue
Block a user