3D per-cell biomes (4x4x4) with surface/underground/cave layers
- Chunk stores per-section biome arrays (64 cells/section); flat generators keep the uniform single-valued fallback. - New writeBiomePalette uses min 1 bpe and direct at registry width (65 biomes). - Climate sampler splits 2D axes (sampled once per column) from 3D depth (per cell), keeping per-cell cost to a single density-function compute. - Full biome parameter table (surface + underground twins + lush/dripstone/ deep_dark caves) with depth as a true range, not a binary layer. - fillBiomes3D fills the 1536 cells/chunk in parallel; <0.3ms overhead vs baseline chunk gen (benchmark-verified). - Tests: cave-biome resolution, per-cell variation, flat-world regression, registry-range validity, plus chunk-gen and per-cell benchmarks.
This commit is contained in:
parent
a7bb9496ae
commit
d3142e7687
7 changed files with 431 additions and 75 deletions
|
|
@ -34,11 +34,7 @@ func NewVanillaGenerator(seed int64) Generator {
|
|||
}
|
||||
|
||||
func generateVanilla(od *worldgen.OverworldDensity, seed int64, cx, cz int32) *Chunk {
|
||||
// Surface biome is sampled at the chunk centre column. Climate noises are
|
||||
// 2D at this stage (depth fixed to surface), so one sample per chunk is
|
||||
// representative; the per-cell milestone will sample the 4×4×4 grid.
|
||||
biome := BiomeAt(od, int(cx)*16+8, int(cz)*16+8)
|
||||
c := NewChunk(cx, cz, biome)
|
||||
c := NewChunk(cx, cz, BiomePlains) // per-cell biomes override below
|
||||
baseX, baseZ := int(cx)*16, int(cz)*16
|
||||
|
||||
grids := make([]cornerGrid, len(od.Interpolated))
|
||||
|
|
@ -86,10 +82,54 @@ func generateVanilla(od *worldgen.OverworldDensity, seed int64, cx, cz int32) *C
|
|||
}
|
||||
}
|
||||
}
|
||||
fillBiomes3D(c, od, baseX, baseZ)
|
||||
decorate(c, cx, cz, seed, &surfTop, &grass)
|
||||
return c
|
||||
}
|
||||
|
||||
// fillBiomes3D assigns a per-cell 4×4×4 biome to every section of the chunk.
|
||||
// The five 2D climate axes are sampled once per column (256 calls) and reused
|
||||
// across Y; the 3D depth axis is evaluated per cell (1536 calls, but each is a
|
||||
// single density-function compute). The biome columns are processed in parallel
|
||||
// to keep generation fast.
|
||||
func fillBiomes3D(c *Chunk, od *worldgen.OverworldDensity, baseX, baseZ int) {
|
||||
var s2D [16][16]worldgen.Sample2D
|
||||
var wg sync.WaitGroup
|
||||
for lx := 0; lx < 16; lx++ {
|
||||
wg.Add(1)
|
||||
go func(lx int) {
|
||||
defer wg.Done()
|
||||
for lz := 0; lz < 16; lz++ {
|
||||
s2D[lx][lz] = worldgen.SampleColumn2D(od, SeaLevel, baseX+lx, baseZ+lz)
|
||||
}
|
||||
}(lx)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// One biome per 4×4×4 cell. Sampling at the cell corner (bx*4, bz*4) is
|
||||
// representative because the 2D climate noises vary slowly relative to a
|
||||
// 4-block cell; depth carries the vertical variation.
|
||||
for bx := 0; bx < biomeCellsXZ; bx++ {
|
||||
wg.Add(1)
|
||||
go func(bx int) {
|
||||
defer wg.Done()
|
||||
lx := bx * biomeCellSize
|
||||
for bz := 0; bz < biomeCellsXZ; bz++ {
|
||||
lz := bz * biomeCellSize
|
||||
col2D := s2D[lx][lz]
|
||||
for si := 0; si < SectionCount; si++ {
|
||||
for by := 0; by < biomeCellsXZ; by++ {
|
||||
wy := MinY + si*16 + by*biomeCellSize
|
||||
biome := BiomeAt3D(od, col2D, baseX+lx, wy, baseZ+lz)
|
||||
c.SetBiome(lx, wy, lz, biome)
|
||||
}
|
||||
}
|
||||
}
|
||||
}(bx)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// fillVanillaColumn lays the blocks for one column and returns the top solid
|
||||
// index and whether the surface is grassy land (suitable for trees). Beaches
|
||||
// (sand) form a narrow ring around the waterline; deep water floors use gravel;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue