diff --git a/internal/worldgen/structure.go b/internal/worldgen/structure.go index b56b1f3..e15d061 100644 --- a/internal/worldgen/structure.go +++ b/internal/worldgen/structure.go @@ -153,10 +153,11 @@ func (t *Template) Place(cw ChunkWriter, cx, cz int32, originX, originY, originZ if wx >= minX && wx <= maxX && wz >= minZ && wz <= maxZ { lx := wx - minX lz := wz - minZ - // Only place if not air, or if you want structures to hollow out space, place air too. - // Jigsaw structures use structure_void to mean "don't overwrite", and air to mean "overwrite with air". - // Since we fallback to 0 for unknown blocks, we need to be careful. - // For now, place everything. + // 14851 = minecraft:structure_void + // 21736 = minecraft:jigsaw + if b.State == 14851 || b.State == 21736 { + continue + } cw.SetBlock(lx, wy, lz, b.State) } } diff --git a/internal/worldgen/structure_manager.go b/internal/worldgen/structure_manager.go index 63cdc39..9f8556a 100644 --- a/internal/worldgen/structure_manager.go +++ b/internal/worldgen/structure_manager.go @@ -14,19 +14,37 @@ func PlaceStructures(cw ChunkWriter, od *OverworldDensity, cx, cz int32, seed in if originCx%spacing == 0 && originCz%spacing == 0 { t := GetTemplate("plains_small_house_1") if t != nil { - baseY := -64 // MinY - if dx == 0 && dz == 0 { - baseY += surfTop[8][8] + 1 - } else { - // sample height at center of origin chunk - _ = SampleColumn2D(od, 63, originCx*16+8, originCz*16+8) - // We don't have a fast exact heightmap lookup without running the column, - // but this is acceptable for a quick prototype. - baseY = 70 + // Deterministically evaluate the surface height at the origin so the structure + // is flush with the ground and consistent across all chunks. + baseY := EvaluateHeight(od, originCx*16+8, originCz*16+8) + + // Only place if it's above sea level (so we don't spawn houses in the ocean) + if baseY >= 63 { + // Offset by -1 so the floor embeds into the grass, rather than floating on it. + t.Place(cw, cx, cz, originCx*16+8, baseY-1, originCz*16+8) } - t.Place(cw, cx, cz, originCx*16+8, baseY, originCz*16+8) } } } } } + +// EvaluateHeight computes the top solid block Y coordinate at (wx, wz). +func EvaluateHeight(od *OverworldDensity, wx, wz int) int { + interp := make([]float64, len(od.Interpolated)) + + // Binary search or linear scan. A linear scan from 120 down to 0 is fast enough. + for y := 120; y >= -64; y-- { + ctx := FunctionContext{X: float64(wx), Y: float64(y), Z: float64(wz)} + // Since we don't have the cell grid, we just evaluate the interpolated nodes directly! + for n, node := range od.Interpolated { + interp[n] = node.Inner.Compute(ctx) + } + ctx = ctx.WithInterp(interp) + + if od.Final.Compute(ctx) > 0 { + return y + } + } + return 64 +} diff --git a/scratch.go b/scratch.go deleted file mode 100644 index d5e2980..0000000 --- a/scratch.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -import ( - "fmt" - "regionio/internal/worldgen" -) - -func main() { - tmpl, _ := worldgen.LoadTemplate("internal/worldgen/data/structure/village/plains/houses/plains_small_house_1.nbt") - for i, b := range tmpl.Palette { - fmt.Printf("Palette[%d] = %d\n", i, b) - } -} diff --git a/world/region/r.-1.-1.mca b/world/region/r.-1.-1.mca new file mode 100644 index 0000000..aaf64ff Binary files /dev/null and b/world/region/r.-1.-1.mca differ diff --git a/world/region/r.-1.0.mca b/world/region/r.-1.0.mca new file mode 100644 index 0000000..8cd3fac Binary files /dev/null and b/world/region/r.-1.0.mca differ diff --git a/world/region/r.0.-1.mca b/world/region/r.0.-1.mca new file mode 100644 index 0000000..ce2fd58 Binary files /dev/null and b/world/region/r.0.-1.mca differ diff --git a/world/region/r.0.0.mca b/world/region/r.0.0.mca new file mode 100644 index 0000000..6473e91 Binary files /dev/null and b/world/region/r.0.0.mca differ