Initial commit: RegionIO Minecraft server core (26.1.2/protocol 775)
Vanilla-faithful overworld generator (final_density + multi-noise biomes), full connection lifecycle (status/login/configuration/play), chunk streaming, creative block editing, and the protocol/nbt/registry infrastructure.
This commit is contained in:
commit
a7bb9496ae
146 changed files with 217621 additions and 0 deletions
158
internal/worldgen/biome.go
Normal file
158
internal/worldgen/biome.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package worldgen
|
||||
|
||||
import "math"
|
||||
|
||||
// This file reproduces net.minecraft.world.level.biome.Climate, the multi-noise
|
||||
// biome selector. A point in climate space is six quantized coordinates
|
||||
// (temperature, humidity, continentalness, erosion, weirdness, depth); the
|
||||
// finder returns the biome whose parameter range is closest to the point by the
|
||||
// vanilla fitDistance metric.
|
||||
//
|
||||
// Coordinates are quantized to long via Math.round(v * 10000.0) exactly as the
|
||||
// vanilla Climate.quantizeCoord does, and fitDistance is the sum of squared
|
||||
// coordinate differences (no per-axis weighting) — matching the vanilla
|
||||
// TargetPoint/ParameterPoint fitness. Range membership uses the inclusive-lower
|
||||
// / exclusive-upper half-open convention vanilla applies to each axis band.
|
||||
|
||||
// quantize converts a climate coordinate to its long representation. Vanilla's
|
||||
// Climate.quantizeCoord is Math.round(v * 10000.0); Go's math.Round halves
|
||||
// away from zero, matching Java for these inputs.
|
||||
func quantize(v float64) int64 {
|
||||
return int64(math.Round(v * 10000.0))
|
||||
}
|
||||
|
||||
// Quantize is the exported form of quantize, for the biome table builder in the
|
||||
// world package.
|
||||
func Quantize(v float64) int64 { return quantize(v) }
|
||||
|
||||
// AxisCount is the number of climate coordinates (temperature, humidity,
|
||||
// continentalness, erosion, weirdness, depth).
|
||||
const AxisCount = 6
|
||||
|
||||
// TargetPoint is a fully-specified climate point: the value the biome finder
|
||||
// tries to match against parameter ranges. Fields are pre-quantized longs.
|
||||
type TargetPoint struct {
|
||||
Temperature, Humidity, Continentalness, Erosion, Weirdness, Depth int64
|
||||
}
|
||||
|
||||
// NewTargetPoint quantizes six float climate coordinates into a TargetPoint.
|
||||
func NewTargetPoint(temp, humid, cont, ero, weird, depth float64) TargetPoint {
|
||||
return TargetPoint{
|
||||
Temperature: quantize(temp),
|
||||
Humidity: quantize(humid),
|
||||
Continentalness: quantize(cont),
|
||||
Erosion: quantize(ero),
|
||||
Weirdness: quantize(weird),
|
||||
Depth: quantize(depth),
|
||||
}
|
||||
}
|
||||
|
||||
// fitDistance is the vanilla Climate.fitness metric: the sum of squared
|
||||
// differences between two points across all six axes. The squared sum is the
|
||||
// comparison key; smaller is a better match.
|
||||
func fitDistance(a, b TargetPoint) int64 {
|
||||
dx := a.Temperature - b.Temperature
|
||||
dh := a.Humidity - b.Humidity
|
||||
dc := a.Continentalness - b.Continentalness
|
||||
de := a.Erosion - b.Erosion
|
||||
dw := a.Weirdness - b.Weirdness
|
||||
dd := a.Depth - b.Depth
|
||||
return dx*dx + dh*dh + dc*dc + de*de + dw*dw + dd*dd
|
||||
}
|
||||
|
||||
// ClimateRange is one axis's [min, max] half-open band on a biome parameter.
|
||||
type ClimateRange struct {
|
||||
Min, Max int64
|
||||
}
|
||||
|
||||
// contains reports whether the quantized coordinate v falls in [min, max).
|
||||
func (r ClimateRange) contains(v int64) bool { return v >= r.Min && v < r.Max }
|
||||
|
||||
// BiomeParameter is one biome entry's full climate signature plus its name.
|
||||
// Each axis is a half-open range; offset is the extra depth offset (always 0 in
|
||||
// the overworld surface table, but kept for parity/future cave biomes).
|
||||
type BiomeParameter struct {
|
||||
Name string
|
||||
// ranges[0..5] = temperature, humidity, continentalness, erosion, weirdness, depth.
|
||||
Ranges [AxisCount]ClimateRange
|
||||
Offset int64
|
||||
}
|
||||
|
||||
// paramCentre returns the centre of the entry's climate ranges as a TargetPoint
|
||||
// (depth centre folded in). Pre-computing this once lets the finder compare by
|
||||
// distance to the centre, then verify range membership — mirroring how the
|
||||
// vanilla finder prunes by fitness then tests the band.
|
||||
func (p *BiomeParameter) centre() TargetPoint {
|
||||
mid := func(r ClimateRange) int64 { return (r.Min + r.Max) / 2 }
|
||||
return TargetPoint{
|
||||
Temperature: mid(p.Ranges[0]),
|
||||
Humidity: mid(p.Ranges[1]),
|
||||
Continentalness: mid(p.Ranges[2]),
|
||||
Erosion: mid(p.Ranges[3]),
|
||||
Weirdness: mid(p.Ranges[4]),
|
||||
Depth: mid(p.Ranges[5]),
|
||||
}
|
||||
}
|
||||
|
||||
// ParameterTable is the set of biome parameters the finder searches.
|
||||
type ParameterTable struct {
|
||||
entries []tableEntry
|
||||
}
|
||||
|
||||
// tableEntry pairs a parameter with its precomputed centre for fast pruning.
|
||||
type tableEntry struct {
|
||||
param BiomeParameter
|
||||
centre TargetPoint
|
||||
}
|
||||
|
||||
// NewParameterTable builds a searchable table from raw biome parameters.
|
||||
func NewParameterTable(params []BiomeParameter) *ParameterTable {
|
||||
t := &ParameterTable{entries: make([]tableEntry, len(params))}
|
||||
for i, p := range params {
|
||||
t.entries[i] = tableEntry{param: p, centre: p.centre()}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// FindBiome returns the name of the biome whose range best matches point, by
|
||||
// the vanilla fitDistance metric among entries whose ranges all contain point.
|
||||
// If no entry's ranges contain point (should not happen for the overworld table,
|
||||
// which tiles climate space), it falls back to the nearest centre.
|
||||
func (t *ParameterTable) FindBiome(point TargetPoint) string {
|
||||
var best string
|
||||
bestDist := int64(math.MaxInt64)
|
||||
var fallback string
|
||||
fallbackDist := int64(math.MaxInt64)
|
||||
|
||||
for _, e := range t.entries {
|
||||
// Distance to centre is the pruning key (precomputed). Track it always
|
||||
// so we have a fallback if no range contains the point.
|
||||
d := fitDistance(point, e.centre)
|
||||
if d < fallbackDist {
|
||||
fallbackDist = d
|
||||
fallback = e.param.Name
|
||||
}
|
||||
// Only consider entries whose ranges actually contain the point.
|
||||
if !containsAll(e.param.Ranges, point) {
|
||||
continue
|
||||
}
|
||||
if d < bestDist {
|
||||
bestDist = d
|
||||
best = e.param.Name
|
||||
}
|
||||
}
|
||||
if best != "" {
|
||||
return best
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// containsAll reports whether every range contains its corresponding coordinate.
|
||||
func containsAll(ranges [AxisCount]ClimateRange, p TargetPoint) bool {
|
||||
return ranges[0].contains(p.Temperature) &&
|
||||
ranges[1].contains(p.Humidity) &&
|
||||
ranges[2].contains(p.Continentalness) &&
|
||||
ranges[3].contains(p.Erosion) &&
|
||||
ranges[4].contains(p.Weirdness) &&
|
||||
ranges[5].contains(p.Depth)
|
||||
}
|
||||
96
internal/worldgen/biome_test.go
Normal file
96
internal/worldgen/biome_test.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuantize(t *testing.T) {
|
||||
cases := []struct {
|
||||
v float64
|
||||
want int64
|
||||
}{
|
||||
{0.0, 0},
|
||||
{0.5, 5000},
|
||||
{-1.0, -10000},
|
||||
{1.0, 10000},
|
||||
{-0.15, -1500},
|
||||
{0.55, 5500},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := quantize(c.v); got != c.want {
|
||||
t.Errorf("quantize(%v) = %d, want %d", c.v, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFitDistanceZero confirms identical points are zero-distance and distinct
|
||||
// points are positive; the exact value is not asserted to stay robust to
|
||||
// representation choices.
|
||||
func TestFitDistance(t *testing.T) {
|
||||
a := NewTargetPoint(0, 0, 0, 0, 0, 0)
|
||||
if got := fitDistance(a, a); got != 0 {
|
||||
t.Errorf("fitDistance(a,a) = %d, want 0", got)
|
||||
}
|
||||
b := NewTargetPoint(1, 0, 0, 0, 0, 0)
|
||||
// 10000^2 per axis of difference.
|
||||
if got := fitDistance(a, b); got != 10000*10000 {
|
||||
t.Errorf("fitDistance for 1.0 temp diff = %d, want %d", got, int64(10000*10000))
|
||||
}
|
||||
}
|
||||
|
||||
// TestRangeContains checks the half-open [min, max) band used by the finder.
|
||||
func TestRangeContains(t *testing.T) {
|
||||
r := ClimateRange{Min: 0, Max: 100}
|
||||
if !r.contains(0) {
|
||||
t.Error("min should be inclusive")
|
||||
}
|
||||
if r.contains(100) {
|
||||
t.Error("max should be exclusive")
|
||||
}
|
||||
if !r.contains(50) {
|
||||
t.Error("interior should contain")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSampleColumnDeterministic verifies the same seed/coords give the same
|
||||
// biome and a different seed gives (almost certainly) a different one.
|
||||
func TestSampleColumnDeterministic(t *testing.T) {
|
||||
od1, err := LoadOverworldFinalDensity(1)
|
||||
if err != nil {
|
||||
t.Fatalf("load seed 1: %v", err)
|
||||
}
|
||||
od2, err := LoadOverworldFinalDensity(99999)
|
||||
if err != nil {
|
||||
t.Fatalf("load seed 99999: %v", err)
|
||||
}
|
||||
|
||||
p1a := SampleColumn(od1, 63, 100, 200)
|
||||
p1b := SampleColumn(od1, 63, 100, 200)
|
||||
if p1a != p1b {
|
||||
t.Error("same seed/coords should produce identical TargetPoint")
|
||||
}
|
||||
|
||||
p2 := SampleColumn(od2, 63, 100, 200)
|
||||
if p1a == p2 {
|
||||
// Not a hard failure (collisions exist), but flag it for inspection.
|
||||
t.Log("note: different seed produced identical climate point at (100,200)")
|
||||
}
|
||||
}
|
||||
|
||||
// TestClimateFieldsLoaded confirms the loader populates all six climate axes
|
||||
// from the noise_router (regression guard for the loader change).
|
||||
func TestClimateFieldsLoaded(t *testing.T) {
|
||||
od, err := LoadOverworldFinalDensity(42)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
if od.Final == nil {
|
||||
t.Fatal("Final density not loaded")
|
||||
}
|
||||
dfs := []DensityFunction{od.Temperature, od.Humidity, od.Continentalness, od.Erosion, od.Weirdness, od.Depth}
|
||||
for i, df := range dfs {
|
||||
if df == nil {
|
||||
t.Errorf("climate axis %d not loaded", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
102
internal/worldgen/blended.go
Normal file
102
internal/worldgen/blended.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package worldgen
|
||||
|
||||
// BlendedNoise is the old_blended_noise density function: the legacy 3D
|
||||
// terrain noise built from min/max limit noises and a main noise. Transcribed
|
||||
// from the official BlendedNoise; the building-block noises are validated
|
||||
// bit-for-bit against captured reference values.
|
||||
type BlendedNoise struct {
|
||||
minLimit, maxLimit, main *PerlinNoise
|
||||
xzScale, yScale, xzFactor, yFactor float64
|
||||
smearScaleMultiplier float64
|
||||
xzMultiplier, yMultiplier float64
|
||||
maxValue float64
|
||||
}
|
||||
|
||||
// NewBlendedNoise builds a BlendedNoise from r (legacy seeding: three Perlin
|
||||
// stacks drawn sequentially) and the scale parameters.
|
||||
func NewBlendedNoise(r RandomSource, xzScale, yScale, xzFactor, yFactor, smearScaleMultiplier float64) *BlendedNoise {
|
||||
b := &BlendedNoise{
|
||||
minLimit: legacyOctaves(r, -15, 0),
|
||||
maxLimit: legacyOctaves(r, -15, 0),
|
||||
main: legacyOctaves(r, -7, 0),
|
||||
xzScale: xzScale,
|
||||
yScale: yScale,
|
||||
xzFactor: xzFactor,
|
||||
yFactor: yFactor,
|
||||
smearScaleMultiplier: smearScaleMultiplier,
|
||||
}
|
||||
b.xzMultiplier = 684.412 * xzScale
|
||||
b.yMultiplier = 684.412 * yScale
|
||||
b.maxValue = b.minLimit.MaxBrokenValue(b.yMultiplier)
|
||||
return b
|
||||
}
|
||||
|
||||
// legacyOctaves creates a legacy PerlinNoise over the inclusive octave range
|
||||
// [firstOctave, lastOctave], all amplitudes 1 (PerlinNoise.makeAmplitudes).
|
||||
func legacyOctaves(r RandomSource, firstOctave, lastOctave int) *PerlinNoise {
|
||||
count := lastOctave - firstOctave + 1
|
||||
amps := make([]float64, count)
|
||||
for i := range amps {
|
||||
amps[i] = 1.0
|
||||
}
|
||||
return NewLegacyPerlinNoise(r, firstOctave, amps)
|
||||
}
|
||||
|
||||
// Compute samples the blended noise at (x, y, z).
|
||||
func (b *BlendedNoise) Compute(c FunctionContext) float64 {
|
||||
limitX := c.X * b.xzMultiplier
|
||||
limitY := c.Y * b.yMultiplier
|
||||
limitZ := c.Z * b.xzMultiplier
|
||||
mainX := limitX / b.xzFactor
|
||||
mainY := limitY / b.yFactor
|
||||
mainZ := limitZ / b.xzFactor
|
||||
limitSmear := b.yMultiplier * b.smearScaleMultiplier
|
||||
mainSmear := limitSmear / b.yFactor
|
||||
|
||||
mainNoiseValue := 0.0
|
||||
pow := 1.0
|
||||
for i := 0; i < 8; i++ {
|
||||
if oct := b.main.GetOctaveNoise(i); oct != nil {
|
||||
mainNoiseValue += oct.NoiseY(wrap(mainX*pow), wrap(mainY*pow), wrap(mainZ*pow), mainSmear*pow, mainY*pow) / pow
|
||||
}
|
||||
pow /= 2.0
|
||||
}
|
||||
|
||||
factor := (mainNoiseValue/10.0 + 1.0) / 2.0
|
||||
isMax := factor >= 1.0
|
||||
isMin := factor <= 0.0
|
||||
|
||||
blendMin, blendMax := 0.0, 0.0
|
||||
pow = 1.0
|
||||
for i := 0; i < 16; i++ {
|
||||
wx := wrap(limitX * pow)
|
||||
wy := wrap(limitY * pow)
|
||||
wz := wrap(limitZ * pow)
|
||||
yScalePow := limitSmear * pow
|
||||
if !isMax {
|
||||
if oct := b.minLimit.GetOctaveNoise(i); oct != nil {
|
||||
blendMin += oct.NoiseY(wx, wy, wz, yScalePow, limitY*pow) / pow
|
||||
}
|
||||
}
|
||||
if !isMin {
|
||||
if oct := b.maxLimit.GetOctaveNoise(i); oct != nil {
|
||||
blendMax += oct.NoiseY(wx, wy, wz, yScalePow, limitY*pow) / pow
|
||||
}
|
||||
}
|
||||
pow /= 2.0
|
||||
}
|
||||
|
||||
return clampedLerp(factor, blendMin/512.0, blendMax/512.0) / 128.0
|
||||
}
|
||||
|
||||
// clampedLerp is Mth.clampedLerp(factor, min, max): min if factor<0, max if
|
||||
// factor>1, otherwise linear interpolation.
|
||||
func clampedLerp(factor, min, max float64) float64 {
|
||||
if factor < 0 {
|
||||
return min
|
||||
}
|
||||
if factor > 1 {
|
||||
return max
|
||||
}
|
||||
return min + factor*(max-min)
|
||||
}
|
||||
29
internal/worldgen/blended_test.go
Normal file
29
internal/worldgen/blended_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func approx(t *testing.T, name string, got, want float64) {
|
||||
t.Helper()
|
||||
if math.Abs(got-want) > 1e-12 {
|
||||
t.Fatalf("%s = %v, want %v", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImprovedNoise5Arg(t *testing.T) {
|
||||
n := NewImprovedNoise(NewXoroshiro(42))
|
||||
approx(t, "imp5(1.5,2.5,3.5,0.1,0.2)", n.NoiseY(1.5, 2.5, 3.5, 0.1, 0.2), 0.33416541490816576)
|
||||
approx(t, "imp5(100.1,64,-200.7,0.5,1.3)", n.NoiseY(100.1, 64.0, -200.7, 0.5, 1.3), -0.31688479572345046)
|
||||
}
|
||||
|
||||
func TestLegacyPerlinNoise(t *testing.T) {
|
||||
pn := legacyOctaves(NewXoroshiro(42), -15, 0) // PerlinNoise.createLegacyForBlendedNoise(-15..0)
|
||||
|
||||
approx(t, "octave0.xo", pn.GetOctaveNoise(0).Xo, 190.83062484342904)
|
||||
approx(t, "octave15.xo", pn.GetOctaveNoise(15).Xo, 128.19773398126475)
|
||||
approx(t, "maxBrokenValue(85.5515)", pn.MaxBrokenValue(85.5515), 87.55150000000002)
|
||||
approx(t, "pn5(0.5,0.5,0.5,0.3,1.1)", pn.GetValueY(0.5, 0.5, 0.5, 0.3, 1.1), 0.03454101275150972)
|
||||
approx(t, "pn5(12.3,45.6,-78.9,0.3,1.1)", pn.GetValueY(12.3, 45.6, -78.9, 0.3, 1.1), 0.03853671559715216)
|
||||
}
|
||||
16
internal/worldgen/blendedcompute_test.go
Normal file
16
internal/worldgen/blendedcompute_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package worldgen
|
||||
import ("math";"testing")
|
||||
func TestBlendedCompute(t *testing.T){
|
||||
bn:=NewBlendedNoise(NewXoroshiro(42),0.25,0.125,80.0,160.0,8.0)
|
||||
cases:=[]struct{x,y,z float64;want float64}{
|
||||
{0,64,0,-0.012282880040235755},
|
||||
{100,40,-200,0.007126388725845459},
|
||||
{1234,80,-5678,-0.13408933571823986},
|
||||
{-37,128,99,-0.0932958728408956},
|
||||
{8,200,8,0.0075622598474688885},
|
||||
}
|
||||
for _,c:=range cases{
|
||||
got:=bn.Compute(FunctionContext{X:c.x,Y:c.y,Z:c.z})
|
||||
if math.Abs(got-c.want)>1e-12 { t.Fatalf("bn(%v,%v,%v)=%v want %v (diff %v)",c.x,c.y,c.z,got,c.want,got-c.want) }
|
||||
}
|
||||
}
|
||||
37
internal/worldgen/climate_sampler.go
Normal file
37
internal/worldgen/climate_sampler.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package worldgen
|
||||
|
||||
// This file samples the climate density functions into a TargetPoint for the
|
||||
// biome finder. The climate router keys are 2D (flat_cache + y_scale=0) except
|
||||
// depth, which is 3D. For surface biome selection we fix depth to 0.0, matching
|
||||
// the depth=0 (surface) entries of the biome parameter table; underground and
|
||||
// cave biomes use depth=1.0 / non-zero offset and are a later milestone.
|
||||
|
||||
// SampleColumn evaluates the six climate parameters at block (wx, wz) using od
|
||||
// and returns the TargetPoint for surface biome lookup. seaLevelY is the Y at
|
||||
// which to sample the 2D climate noises (callers pass the world sea level).
|
||||
func SampleColumn(od *OverworldDensity, seaLevelY int, wx, wz int) TargetPoint {
|
||||
ctx := FunctionContext{X: float64(wx), Y: float64(seaLevelY), Z: float64(wz)}
|
||||
|
||||
temp := computeOrZero(od.Temperature, ctx)
|
||||
humid := computeOrZero(od.Humidity, ctx)
|
||||
cont := computeOrZero(od.Continentalness, ctx)
|
||||
ero := computeOrZero(od.Erosion, ctx)
|
||||
weird := computeOrZero(od.Weirdness, ctx)
|
||||
|
||||
// Surface layer: depth axis is fixed at 0.0 so only the depth=0 (surface)
|
||||
// biome parameter entries match. The real 3D depth is consulted in the
|
||||
// per-cell milestone.
|
||||
const surfaceDepth = 0.0
|
||||
|
||||
return NewTargetPoint(temp, humid, cont, ero, weird, surfaceDepth)
|
||||
}
|
||||
|
||||
// computeOrZero evaluates df at ctx, returning 0 when df is nil (a climate key
|
||||
// absent from the router). This keeps sampling robust without special-casing
|
||||
// each axis at the call site.
|
||||
func computeOrZero(df DensityFunction, ctx FunctionContext) float64 {
|
||||
if df == nil {
|
||||
return 0
|
||||
}
|
||||
return df.Compute(ctx)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "minecraft:old_blended_noise",
|
||||
"smear_scale_multiplier": 8.0,
|
||||
"xz_factor": 80.0,
|
||||
"xz_scale": 0.25,
|
||||
"y_factor": 160.0,
|
||||
"y_scale": 0.125
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:min",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": 0.37,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:cave_entrance",
|
||||
"xz_scale": 0.75,
|
||||
"y_scale": 0.5
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:y_clamped_gradient",
|
||||
"from_value": 0.3,
|
||||
"from_y": -10,
|
||||
"to_value": 0.0,
|
||||
"to_y": 30
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": "minecraft:overworld/caves/spaghetti_roughness_function",
|
||||
"argument2": {
|
||||
"type": "minecraft:clamp",
|
||||
"input": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:max",
|
||||
"argument1": {
|
||||
"type": "minecraft:weird_scaled_sampler",
|
||||
"input": {
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_3d_rarity",
|
||||
"xz_scale": 2.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
},
|
||||
"noise": "minecraft:spaghetti_3d_1",
|
||||
"rarity_value_mapper": "type_1"
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:weird_scaled_sampler",
|
||||
"input": {
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_3d_rarity",
|
||||
"xz_scale": 2.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
},
|
||||
"noise": "minecraft:spaghetti_3d_2",
|
||||
"rarity_value_mapper": "type_1"
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.0765,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -0.011499999999999996,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_3d_thickness",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"max": 1.0,
|
||||
"min": -1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"type": "minecraft:range_choice",
|
||||
"input": {
|
||||
"type": "minecraft:interpolated",
|
||||
"argument": {
|
||||
"type": "minecraft:range_choice",
|
||||
"input": "minecraft:y",
|
||||
"max_exclusive": 321.0,
|
||||
"min_inclusive": -60.0,
|
||||
"when_in_range": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:noodle",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
},
|
||||
"when_out_of_range": -1.0
|
||||
}
|
||||
},
|
||||
"max_exclusive": 0.0,
|
||||
"min_inclusive": -1000000.0,
|
||||
"when_in_range": 64.0,
|
||||
"when_out_of_range": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:interpolated",
|
||||
"argument": {
|
||||
"type": "minecraft:range_choice",
|
||||
"input": "minecraft:y",
|
||||
"max_exclusive": 321.0,
|
||||
"min_inclusive": -60.0,
|
||||
"when_in_range": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.07500000000000001,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -0.025,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:noodle_thickness",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"when_out_of_range": 0.0
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 1.5,
|
||||
"argument2": {
|
||||
"type": "minecraft:max",
|
||||
"argument1": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": {
|
||||
"type": "minecraft:interpolated",
|
||||
"argument": {
|
||||
"type": "minecraft:range_choice",
|
||||
"input": "minecraft:y",
|
||||
"max_exclusive": 321.0,
|
||||
"min_inclusive": -60.0,
|
||||
"when_in_range": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:noodle_ridge_a",
|
||||
"xz_scale": 2.6666666666666665,
|
||||
"y_scale": 2.6666666666666665
|
||||
},
|
||||
"when_out_of_range": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": {
|
||||
"type": "minecraft:interpolated",
|
||||
"argument": {
|
||||
"type": "minecraft:range_choice",
|
||||
"input": "minecraft:y",
|
||||
"max_exclusive": 321.0,
|
||||
"min_inclusive": -60.0,
|
||||
"when_in_range": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:noodle_ridge_b",
|
||||
"xz_scale": 2.6666666666666665,
|
||||
"y_scale": 2.6666666666666665
|
||||
},
|
||||
"when_out_of_range": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 2.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:pillar",
|
||||
"xz_scale": 25.0,
|
||||
"y_scale": 0.3
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -1.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -1.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:pillar_rareness",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:cube",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": 0.55,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 0.55,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:pillar_thickness",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"type": "minecraft:clamp",
|
||||
"input": {
|
||||
"type": "minecraft:max",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:weird_scaled_sampler",
|
||||
"input": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_2d_modulator",
|
||||
"xz_scale": 2.0,
|
||||
"y_scale": 1.0
|
||||
},
|
||||
"noise": "minecraft:spaghetti_2d",
|
||||
"rarity_value_mapper": "type_2"
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 0.083,
|
||||
"argument2": "minecraft:overworld/caves/spaghetti_2d_thickness_modulator"
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:cube",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": 0.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 8.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_2d_elevation",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:y_clamped_gradient",
|
||||
"from_value": 8.0,
|
||||
"from_y": -64,
|
||||
"to_value": -40.0,
|
||||
"to_y": 320
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": "minecraft:overworld/caves/spaghetti_2d_thickness_modulator"
|
||||
}
|
||||
}
|
||||
},
|
||||
"max": 1.0,
|
||||
"min": -1.0
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.95,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -0.35000000000000003,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_2d_thickness",
|
||||
"xz_scale": 2.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"type": "minecraft:cache_once",
|
||||
"argument": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.05,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -0.05,
|
||||
"argument2": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_roughness_modulator",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.4,
|
||||
"argument2": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:spaghetti_roughness",
|
||||
"xz_scale": 1.0,
|
||||
"y_scale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:shifted_noise",
|
||||
"noise": "minecraft:continentalness",
|
||||
"shift_x": "minecraft:shift_x",
|
||||
"shift_y": 0.0,
|
||||
"shift_z": "minecraft:shift_z",
|
||||
"xz_scale": 0.25,
|
||||
"y_scale": 0.0
|
||||
}
|
||||
}
|
||||
11
internal/worldgen/data/density_function/overworld/depth.json
Normal file
11
internal/worldgen/data/density_function/overworld/depth.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:y_clamped_gradient",
|
||||
"from_value": 1.5,
|
||||
"from_y": -64,
|
||||
"to_value": -1.5,
|
||||
"to_y": 320
|
||||
},
|
||||
"argument2": "minecraft:overworld/offset"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:shifted_noise",
|
||||
"noise": "minecraft:erosion",
|
||||
"shift_x": "minecraft:shift_x",
|
||||
"shift_y": 0.0,
|
||||
"shift_z": "minecraft:shift_z",
|
||||
"xz_scale": 0.25,
|
||||
"y_scale": 0.0
|
||||
}
|
||||
}
|
||||
890
internal/worldgen/data/density_function/overworld/factor.json
Normal file
890
internal/worldgen/data/density_function/overworld/factor.json
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:cache_2d",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": 10.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": {
|
||||
"type": "minecraft:blend_alpha"
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -10.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:spline",
|
||||
"spline": {
|
||||
"coordinate": "minecraft:overworld/continents",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.19,
|
||||
"value": 3.95
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.15,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.6,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 6.25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 2.67
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.35,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 6.25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.25,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 6.25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.1,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 2.67
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 6.3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 6.25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.35,
|
||||
"value": 6.25
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 6.25
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 6.25
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.55,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 6.25
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 6.25
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.62,
|
||||
"value": 6.25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.1,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.6,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.47
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 2.67
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.35,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.47
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.25,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.47
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.1,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 2.67
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 6.3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.47
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.35,
|
||||
"value": 5.47
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 5.47
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 5.47
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.55,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 5.47
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 5.47
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.62,
|
||||
"value": 5.47
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.6,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.08
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 2.67
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.35,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.08
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.25,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.08
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.1,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 2.67
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 6.3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 5.08
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.35,
|
||||
"value": 5.08
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 5.08
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 5.08
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.55,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.9,
|
||||
"value": 5.08
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.69,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.0,
|
||||
"value": 5.08
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.1,
|
||||
"value": 0.625
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.62,
|
||||
"value": 5.08
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.06,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.6,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 2.67
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.35,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.25,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.1,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.05,
|
||||
"value": 2.67
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": 6.3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.05,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.7,
|
||||
"value": 1.56
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.4,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.7,
|
||||
"value": 1.56
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.45,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.7,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.15,
|
||||
"value": 1.37
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.55,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.7,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.2,
|
||||
"value": 6.3
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.2,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.15,
|
||||
"value": 1.37
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.58,
|
||||
"value": 4.69
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:cache_2d",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": 0.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": {
|
||||
"type": "minecraft:blend_alpha"
|
||||
},
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:spline",
|
||||
"spline": {
|
||||
"coordinate": "minecraft:overworld/continents",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.11,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.03,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.63
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.78,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.315
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.15
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5775,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.315
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.15
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.375,
|
||||
"value": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.65,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/erosion",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.63
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.63
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.78,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.63
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.5775,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges_folded",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.19999999,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.44999996,
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 1.0,
|
||||
"value": {
|
||||
"coordinate": "minecraft:overworld/ridges",
|
||||
"points": [
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.01,
|
||||
"value": 0.63
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": 0.01,
|
||||
"value": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"derivative": 0.0,
|
||||
"location": -0.375,
|
||||
"value": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1523
internal/worldgen/data/density_function/overworld/offset.json
Normal file
1523
internal/worldgen/data/density_function/overworld/offset.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:shifted_noise",
|
||||
"noise": "minecraft:ridge",
|
||||
"shift_x": "minecraft:shift_x",
|
||||
"shift_y": 0.0,
|
||||
"shift_z": "minecraft:shift_z",
|
||||
"xz_scale": 0.25,
|
||||
"y_scale": 0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"type": "minecraft:mul",
|
||||
"argument1": -3.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.3333333333333333,
|
||||
"argument2": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": -0.6666666666666666,
|
||||
"argument2": {
|
||||
"type": "minecraft:abs",
|
||||
"argument": "minecraft:overworld/ridges"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"type": "minecraft:add",
|
||||
"argument1": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": 4.0,
|
||||
"argument2": {
|
||||
"type": "minecraft:quarter_negative",
|
||||
"argument": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": {
|
||||
"type": "minecraft:add",
|
||||
"argument1": "minecraft:overworld/depth",
|
||||
"argument2": {
|
||||
"type": "minecraft:mul",
|
||||
"argument1": "minecraft:overworld/jaggedness",
|
||||
"argument2": {
|
||||
"type": "minecraft:half_negative",
|
||||
"argument": {
|
||||
"type": "minecraft:noise",
|
||||
"noise": "minecraft:jagged",
|
||||
"xz_scale": 1500.0,
|
||||
"y_scale": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": "minecraft:overworld/factor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"argument2": "minecraft:overworld/base_3d_noise"
|
||||
}
|
||||
10
internal/worldgen/data/density_function/shift_x.json
Normal file
10
internal/worldgen/data/density_function/shift_x.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:cache_2d",
|
||||
"argument": {
|
||||
"type": "minecraft:shift_a",
|
||||
"argument": "minecraft:offset"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
internal/worldgen/data/density_function/shift_z.json
Normal file
10
internal/worldgen/data/density_function/shift_z.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"type": "minecraft:flat_cache",
|
||||
"argument": {
|
||||
"type": "minecraft:cache_2d",
|
||||
"argument": {
|
||||
"type": "minecraft:shift_b",
|
||||
"argument": "minecraft:offset"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
internal/worldgen/data/density_function/y.json
Normal file
7
internal/worldgen/data/density_function/y.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "minecraft:y_clamped_gradient",
|
||||
"from_value": -4064.0,
|
||||
"from_y": -4064,
|
||||
"to_value": 4062.0,
|
||||
"to_y": 4062
|
||||
}
|
||||
1
internal/worldgen/data/density_function/zero.json
Normal file
1
internal/worldgen/data/density_function/zero.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
0.0
|
||||
6
internal/worldgen/data/noise/aquifer_barrier.json
Normal file
6
internal/worldgen/data/noise/aquifer_barrier.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -3
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -5
|
||||
}
|
||||
6
internal/worldgen/data/noise/aquifer_lava.json
Normal file
6
internal/worldgen/data/noise/aquifer_lava.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -1
|
||||
}
|
||||
9
internal/worldgen/data/noise/badlands_pillar.json
Normal file
9
internal/worldgen/data/noise/badlands_pillar.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -2
|
||||
}
|
||||
6
internal/worldgen/data/noise/badlands_pillar_roof.json
Normal file
6
internal/worldgen/data/noise/badlands_pillar_roof.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
8
internal/worldgen/data/noise/badlands_surface.json
Normal file
8
internal/worldgen/data/noise/badlands_surface.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
9
internal/worldgen/data/noise/calcite.json
Normal file
9
internal/worldgen/data/noise/calcite.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -9
|
||||
}
|
||||
14
internal/worldgen/data/noise/cave_cheese.json
Normal file
14
internal/worldgen/data/noise/cave_cheese.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
0.5,
|
||||
1.0,
|
||||
2.0,
|
||||
1.0,
|
||||
2.0,
|
||||
1.0,
|
||||
0.0,
|
||||
2.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
8
internal/worldgen/data/noise/cave_entrance.json
Normal file
8
internal/worldgen/data/noise/cave_entrance.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
0.4,
|
||||
0.5,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/cave_layer.json
Normal file
6
internal/worldgen/data/noise/cave_layer.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/clay_bands_offset.json
Normal file
6
internal/worldgen/data/noise/clay_bands_offset.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
14
internal/worldgen/data/noise/continentalness.json
Normal file
14
internal/worldgen/data/noise/continentalness.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
2.0,
|
||||
2.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -9
|
||||
}
|
||||
14
internal/worldgen/data/noise/continentalness_large.json
Normal file
14
internal/worldgen/data/noise/continentalness_large.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
2.0,
|
||||
2.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -11
|
||||
}
|
||||
10
internal/worldgen/data/noise/erosion.json
Normal file
10
internal/worldgen/data/noise/erosion.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -9
|
||||
}
|
||||
10
internal/worldgen/data/noise/erosion_large.json
Normal file
10
internal/worldgen/data/noise/erosion_large.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -11
|
||||
}
|
||||
9
internal/worldgen/data/noise/gravel.json
Normal file
9
internal/worldgen/data/noise/gravel.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
14
internal/worldgen/data/noise/gravel_layer.json
Normal file
14
internal/worldgen/data/noise/gravel_layer.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.013333333333333334
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
9
internal/worldgen/data/noise/ice.json
Normal file
9
internal/worldgen/data/noise/ice.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -4
|
||||
}
|
||||
9
internal/worldgen/data/noise/iceberg_pillar.json
Normal file
9
internal/worldgen/data/noise/iceberg_pillar.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
6
internal/worldgen/data/noise/iceberg_pillar_roof.json
Normal file
6
internal/worldgen/data/noise/iceberg_pillar_roof.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -3
|
||||
}
|
||||
8
internal/worldgen/data/noise/iceberg_surface.json
Normal file
8
internal/worldgen/data/noise/iceberg_surface.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
21
internal/worldgen/data/noise/jagged.json
Normal file
21
internal/worldgen/data/noise/jagged.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -16
|
||||
}
|
||||
7
internal/worldgen/data/noise/nether/temperature.json
Normal file
7
internal/worldgen/data/noise/nether/temperature.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
7
internal/worldgen/data/noise/nether/vegetation.json
Normal file
7
internal/worldgen/data/noise/nether/vegetation.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/nether_state_selector.json
Normal file
6
internal/worldgen/data/noise/nether_state_selector.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -4
|
||||
}
|
||||
9
internal/worldgen/data/noise/nether_wart.json
Normal file
9
internal/worldgen/data/noise/nether_wart.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.9
|
||||
],
|
||||
"firstOctave": -3
|
||||
}
|
||||
9
internal/worldgen/data/noise/netherrack.json
Normal file
9
internal/worldgen/data/noise/netherrack.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.35
|
||||
],
|
||||
"firstOctave": -3
|
||||
}
|
||||
6
internal/worldgen/data/noise/noodle.json
Normal file
6
internal/worldgen/data/noise/noodle.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/noodle_ridge_a.json
Normal file
6
internal/worldgen/data/noise/noodle_ridge_a.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/noodle_ridge_b.json
Normal file
6
internal/worldgen/data/noise/noodle_ridge_b.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/noodle_thickness.json
Normal file
6
internal/worldgen/data/noise/noodle_thickness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
9
internal/worldgen/data/noise/offset.json
Normal file
9
internal/worldgen/data/noise/offset.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -3
|
||||
}
|
||||
6
internal/worldgen/data/noise/ore_gap.json
Normal file
6
internal/worldgen/data/noise/ore_gap.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -5
|
||||
}
|
||||
6
internal/worldgen/data/noise/ore_vein_a.json
Normal file
6
internal/worldgen/data/noise/ore_vein_a.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/ore_vein_b.json
Normal file
6
internal/worldgen/data/noise/ore_vein_b.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/ore_veininess.json
Normal file
6
internal/worldgen/data/noise/ore_veininess.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
9
internal/worldgen/data/noise/packed_ice.json
Normal file
9
internal/worldgen/data/noise/packed_ice.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
11
internal/worldgen/data/noise/patch.json
Normal file
11
internal/worldgen/data/noise/patch.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.013333333333333334
|
||||
],
|
||||
"firstOctave": -5
|
||||
}
|
||||
7
internal/worldgen/data/noise/pillar.json
Normal file
7
internal/worldgen/data/noise/pillar.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/pillar_rareness.json
Normal file
6
internal/worldgen/data/noise/pillar_rareness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/pillar_thickness.json
Normal file
6
internal/worldgen/data/noise/pillar_thickness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
9
internal/worldgen/data/noise/powder_snow.json
Normal file
9
internal/worldgen/data/noise/powder_snow.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
11
internal/worldgen/data/noise/ridge.json
Normal file
11
internal/worldgen/data/noise/ridge.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
2.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
14
internal/worldgen/data/noise/soul_sand_layer.json
Normal file
14
internal/worldgen/data/noise/soul_sand_layer.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.013333333333333334
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_2d.json
Normal file
6
internal/worldgen/data/noise/spaghetti_2d.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_2d_elevation.json
Normal file
6
internal/worldgen/data/noise/spaghetti_2d_elevation.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_2d_modulator.json
Normal file
6
internal/worldgen/data/noise/spaghetti_2d_modulator.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -11
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_2d_thickness.json
Normal file
6
internal/worldgen/data/noise/spaghetti_2d_thickness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -11
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_3d_1.json
Normal file
6
internal/worldgen/data/noise/spaghetti_3d_1.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_3d_2.json
Normal file
6
internal/worldgen/data/noise/spaghetti_3d_2.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -7
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_3d_rarity.json
Normal file
6
internal/worldgen/data/noise/spaghetti_3d_rarity.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -11
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_3d_thickness.json
Normal file
6
internal/worldgen/data/noise/spaghetti_3d_thickness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
6
internal/worldgen/data/noise/spaghetti_roughness.json
Normal file
6
internal/worldgen/data/noise/spaghetti_roughness.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -5
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
8
internal/worldgen/data/noise/surface.json
Normal file
8
internal/worldgen/data/noise/surface.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
9
internal/worldgen/data/noise/surface_secondary.json
Normal file
9
internal/worldgen/data/noise/surface_secondary.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -6
|
||||
}
|
||||
6
internal/worldgen/data/noise/surface_swamp.json
Normal file
6
internal/worldgen/data/noise/surface_swamp.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0
|
||||
],
|
||||
"firstOctave": -2
|
||||
}
|
||||
11
internal/worldgen/data/noise/temperature.json
Normal file
11
internal/worldgen/data/noise/temperature.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.5,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -10
|
||||
}
|
||||
11
internal/worldgen/data/noise/temperature_large.json
Normal file
11
internal/worldgen/data/noise/temperature_large.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.5,
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -12
|
||||
}
|
||||
11
internal/worldgen/data/noise/vegetation.json
Normal file
11
internal/worldgen/data/noise/vegetation.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -8
|
||||
}
|
||||
11
internal/worldgen/data/noise/vegetation_large.json
Normal file
11
internal/worldgen/data/noise/vegetation_large.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"amplitudes": [
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"firstOctave": -10
|
||||
}
|
||||
2599
internal/worldgen/data/overworld.json
Normal file
2599
internal/worldgen/data/overworld.json
Normal file
File diff suppressed because it is too large
Load diff
250
internal/worldgen/density.go
Normal file
250
internal/worldgen/density.go
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
package worldgen
|
||||
|
||||
import "math"
|
||||
|
||||
// FunctionContext is the sample point for a density function (block coords).
|
||||
// During chunk generation, interp holds the precomputed cell-interpolated value
|
||||
// for each Interpolated node (indexed by node); it is nil for plain evaluation.
|
||||
type FunctionContext struct {
|
||||
X, Y, Z float64
|
||||
interp []float64
|
||||
}
|
||||
|
||||
// WithInterp returns a copy of c carrying the given per-node interpolated values.
|
||||
func (c FunctionContext) WithInterp(v []float64) FunctionContext {
|
||||
c.interp = v
|
||||
return c
|
||||
}
|
||||
|
||||
// Interpolated marks a sub-function that vanilla samples on the cell-corner grid
|
||||
// and trilinearly interpolates (the heavy 3D terrain noise). During generation
|
||||
// the value is looked up by Index; otherwise the inner function is evaluated.
|
||||
type Interpolated struct {
|
||||
Inner DensityFunction
|
||||
Index int
|
||||
}
|
||||
|
||||
func (n *Interpolated) Compute(c FunctionContext) float64 {
|
||||
if c.interp != nil {
|
||||
return c.interp[n.Index]
|
||||
}
|
||||
return n.Inner.Compute(c)
|
||||
}
|
||||
|
||||
// DensityFunction is a node in the density-function tree. Compute returns the
|
||||
// density at the given point; positive conventionally means "solid".
|
||||
//
|
||||
// This is the interpreter engine; only the node types we currently need are
|
||||
// implemented. The full vanilla set (splines, blend_density, caches, etc.) can
|
||||
// be added incrementally without changing this interface.
|
||||
type DensityFunction interface {
|
||||
Compute(c FunctionContext) float64
|
||||
}
|
||||
|
||||
// Constant is a fixed value.
|
||||
type Constant float64
|
||||
|
||||
func (c Constant) Compute(FunctionContext) float64 { return float64(c) }
|
||||
|
||||
type binaryOp struct {
|
||||
a, b DensityFunction
|
||||
op func(x, y float64) float64
|
||||
}
|
||||
|
||||
func (n binaryOp) Compute(c FunctionContext) float64 { return n.op(n.a.Compute(c), n.b.Compute(c)) }
|
||||
|
||||
// Add, Mul, Min, Max combine two density functions pointwise.
|
||||
func Add(a, b DensityFunction) DensityFunction {
|
||||
return binaryOp{a, b, func(x, y float64) float64 { return x + y }}
|
||||
}
|
||||
func Mul(a, b DensityFunction) DensityFunction {
|
||||
return binaryOp{a, b, func(x, y float64) float64 { return x * y }}
|
||||
}
|
||||
func Min(a, b DensityFunction) DensityFunction {
|
||||
return binaryOp{a, b, func(x, y float64) float64 {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}}
|
||||
}
|
||||
func Max(a, b DensityFunction) DensityFunction {
|
||||
return binaryOp{a, b, func(x, y float64) float64 {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}}
|
||||
}
|
||||
|
||||
// YClampedGradient is the y_clamped_gradient node: a linear map of Y from
|
||||
// [fromY, toY] onto [fromV, toV], clamped outside that range.
|
||||
type YClampedGradient struct {
|
||||
FromY, ToY, FromV, ToV float64
|
||||
}
|
||||
|
||||
func (g YClampedGradient) Compute(c FunctionContext) float64 {
|
||||
return clampedMap(c.Y, g.FromY, g.ToY, g.FromV, g.ToV)
|
||||
}
|
||||
|
||||
// NoiseDF samples a NormalNoise, scaling the input coordinates (the "noise" /
|
||||
// "shifted_noise" family, without the shift inputs).
|
||||
type NoiseDF struct {
|
||||
Noise *NormalNoise
|
||||
XZScale, YScale float64
|
||||
}
|
||||
|
||||
func (n NoiseDF) Compute(c FunctionContext) float64 {
|
||||
return n.Noise.GetValue(c.X*n.XZScale, c.Y*n.YScale, c.Z*n.XZScale)
|
||||
}
|
||||
|
||||
type unaryOp struct {
|
||||
a DensityFunction
|
||||
op func(float64) float64
|
||||
}
|
||||
|
||||
func (n unaryOp) Compute(c FunctionContext) float64 { return n.op(n.a.Compute(c)) }
|
||||
|
||||
// Abs, Square, Cube, HalfNegative, QuarterNegative, Squeeze are the unary
|
||||
// transforms used by the vanilla density tree.
|
||||
func Abs(a DensityFunction) DensityFunction { return unaryOp{a, math.Abs} }
|
||||
func Square(a DensityFunction) DensityFunction { return unaryOp{a, func(x float64) float64 { return x * x }} }
|
||||
func Cube(a DensityFunction) DensityFunction { return unaryOp{a, func(x float64) float64 { return x * x * x }} }
|
||||
func HalfNegative(a DensityFunction) DensityFunction {
|
||||
return unaryOp{a, func(x float64) float64 {
|
||||
if x > 0 {
|
||||
return x
|
||||
}
|
||||
return x * 0.5
|
||||
}}
|
||||
}
|
||||
func QuarterNegative(a DensityFunction) DensityFunction {
|
||||
return unaryOp{a, func(x float64) float64 {
|
||||
if x > 0 {
|
||||
return x
|
||||
}
|
||||
return x * 0.25
|
||||
}}
|
||||
}
|
||||
func Squeeze(a DensityFunction) DensityFunction {
|
||||
return unaryOp{a, func(x float64) float64 {
|
||||
d := clamp(x, -1, 1)
|
||||
return d/2.0 - d*d*d/24.0
|
||||
}}
|
||||
}
|
||||
|
||||
// Clamp constrains a density function to [min, max].
|
||||
func Clamp(a DensityFunction, min, max float64) DensityFunction {
|
||||
return unaryOp{a, func(x float64) float64 { return clamp(x, min, max) }}
|
||||
}
|
||||
|
||||
// RangeChoice picks whenInRange if input is within [min, max), else whenOut.
|
||||
type RangeChoice struct {
|
||||
Input DensityFunction
|
||||
Min, Max float64
|
||||
WhenInRange DensityFunction
|
||||
WhenOutOfRange DensityFunction
|
||||
}
|
||||
|
||||
func (r RangeChoice) Compute(c FunctionContext) float64 {
|
||||
d := r.Input.Compute(c)
|
||||
if d >= r.Min && d < r.Max {
|
||||
return r.WhenInRange.Compute(c)
|
||||
}
|
||||
return r.WhenOutOfRange.Compute(c)
|
||||
}
|
||||
|
||||
// ShiftedNoise samples a NormalNoise at coordinates scaled and offset by shift
|
||||
// density functions (the workhorse of climate/terrain inputs).
|
||||
type ShiftedNoise struct {
|
||||
ShiftX, ShiftY, ShiftZ DensityFunction
|
||||
XZScale, YScale float64
|
||||
Noise *NormalNoise
|
||||
}
|
||||
|
||||
func (s ShiftedNoise) Compute(c FunctionContext) float64 {
|
||||
x := c.X*s.XZScale + s.ShiftX.Compute(c)
|
||||
y := c.Y*s.YScale + s.ShiftY.Compute(c)
|
||||
z := c.Z*s.XZScale + s.ShiftZ.Compute(c)
|
||||
return s.Noise.GetValue(x, y, z)
|
||||
}
|
||||
|
||||
// shiftNoise samples the offset noise at quarter scale, times four.
|
||||
func shiftNoise(noise *NormalNoise, x, y, z float64) float64 {
|
||||
return noise.GetValue(x*0.25, y*0.25, z*0.25) * 4.0
|
||||
}
|
||||
|
||||
// ShiftA shifts along X/Z (used by shift_x): noise(x, 0, z).
|
||||
type ShiftA struct{ Noise *NormalNoise }
|
||||
|
||||
func (s ShiftA) Compute(c FunctionContext) float64 { return shiftNoise(s.Noise, c.X, 0, c.Z) }
|
||||
|
||||
// ShiftB shifts with swapped axes (used by shift_z): noise(z, x, 0).
|
||||
type ShiftB struct{ Noise *NormalNoise }
|
||||
|
||||
func (s ShiftB) Compute(c FunctionContext) float64 { return shiftNoise(s.Noise, c.Z, c.X, 0) }
|
||||
|
||||
// WeirdScaledSampler scales a noise sample by a rarity derived from an input
|
||||
// density function (used by the spaghetti caves).
|
||||
type WeirdScaledSampler struct {
|
||||
Input DensityFunction
|
||||
Noise *NormalNoise
|
||||
Rarity func(float64) float64
|
||||
}
|
||||
|
||||
func (w WeirdScaledSampler) Compute(c FunctionContext) float64 {
|
||||
rarity := w.Rarity(w.Input.Compute(c))
|
||||
return rarity * math.Abs(w.Noise.GetValue(c.X/rarity, c.Y/rarity, c.Z/rarity))
|
||||
}
|
||||
|
||||
// SpaghettiRarity2D is the type_2 rarity mapping.
|
||||
func SpaghettiRarity2D(v float64) float64 {
|
||||
switch {
|
||||
case v < -0.75:
|
||||
return 0.5
|
||||
case v < -0.5:
|
||||
return 0.75
|
||||
case v < 0.5:
|
||||
return 1.0
|
||||
case v < 0.75:
|
||||
return 2.0
|
||||
default:
|
||||
return 3.0
|
||||
}
|
||||
}
|
||||
|
||||
// SpaghettiRarity3D is the type_1 rarity mapping.
|
||||
func SpaghettiRarity3D(v float64) float64 {
|
||||
switch {
|
||||
case v < -0.5:
|
||||
return 0.75
|
||||
case v < 0.0:
|
||||
return 1.0
|
||||
case v < 0.5:
|
||||
return 1.5
|
||||
default:
|
||||
return 2.0
|
||||
}
|
||||
}
|
||||
|
||||
func clamp(v, lo, hi float64) float64 {
|
||||
if v < lo {
|
||||
return lo
|
||||
}
|
||||
if v > hi {
|
||||
return hi
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// clampedMap linearly maps v from [inMin,inMax] to [outMin,outMax], clamped.
|
||||
func clampedMap(v, inMin, inMax, outMin, outMax float64) float64 {
|
||||
if v <= inMin {
|
||||
return outMin
|
||||
}
|
||||
if v >= inMax {
|
||||
return outMax
|
||||
}
|
||||
t := (v - inMin) / (inMax - inMin)
|
||||
return outMin + t*(outMax-outMin)
|
||||
}
|
||||
7
internal/worldgen/density_bench_test.go
Normal file
7
internal/worldgen/density_bench_test.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package worldgen
|
||||
import "testing"
|
||||
func BenchmarkFinalDensity(b *testing.B) {
|
||||
od,_ := LoadOverworldFinalDensity(0)
|
||||
b.ReportAllocs()
|
||||
for i:=0;i<b.N;i++ { _ = od.Final.Compute(FunctionContext{X:float64(i&255),Y:64,Z:float64(i>>8)}) }
|
||||
}
|
||||
117
internal/worldgen/improved_noise.go
Normal file
117
internal/worldgen/improved_noise.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package worldgen
|
||||
|
||||
import "math"
|
||||
|
||||
// gradient is SimplexNoise.GRADIENT: the 16 (with repeats) 3D gradient vectors
|
||||
// used by Perlin gradient hashing.
|
||||
var gradient = [16][3]float64{
|
||||
{1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0},
|
||||
{1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1},
|
||||
{0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1},
|
||||
{1, 1, 0}, {0, -1, 1}, {-1, 1, 0}, {0, -1, -1},
|
||||
}
|
||||
|
||||
// ImprovedNoise is a single Perlin noise octave (ImprovedNoise), with random
|
||||
// offsets and a 256-entry permutation table.
|
||||
type ImprovedNoise struct {
|
||||
Xo, Yo, Zo float64
|
||||
p [256]int
|
||||
}
|
||||
|
||||
// NewImprovedNoise constructs an ImprovedNoise, consuming three doubles for the
|
||||
// offsets and 256 bounded ints for the Fisher–Yates permutation shuffle.
|
||||
func NewImprovedNoise(r RandomSource) *ImprovedNoise {
|
||||
n := &ImprovedNoise{
|
||||
Xo: r.NextDouble() * 256.0,
|
||||
Yo: r.NextDouble() * 256.0,
|
||||
Zo: r.NextDouble() * 256.0,
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
n.p[i] = i
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
j := int(r.NextIntN(int32(256 - i)))
|
||||
n.p[i], n.p[i+j] = n.p[i+j], n.p[i]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *ImprovedNoise) perm(i int) int { return n.p[i&255] & 255 }
|
||||
|
||||
// Noise samples 3D Perlin noise at (x, y, z).
|
||||
func (n *ImprovedNoise) Noise(x, y, z float64) float64 {
|
||||
return n.NoiseY(x, y, z, 0, 0)
|
||||
}
|
||||
|
||||
// NoiseY is the 5-argument variant used by BlendedNoise: yScale/yFudge "smear"
|
||||
// the Y gradient sampling while the smoothstep still uses the true Y fraction.
|
||||
func (n *ImprovedNoise) NoiseY(x, y, z, yScale, yFudge float64) float64 {
|
||||
d := x + n.Xo
|
||||
e := y + n.Yo
|
||||
f := z + n.Zo
|
||||
i := int(math.Floor(d))
|
||||
j := int(math.Floor(e))
|
||||
k := int(math.Floor(f))
|
||||
xr := d - float64(i)
|
||||
yr := e - float64(j)
|
||||
zr := f - float64(k)
|
||||
|
||||
var yrFudge float64
|
||||
if yScale != 0.0 {
|
||||
fudgeLimit := yr
|
||||
if yFudge >= 0.0 && yFudge < yr {
|
||||
fudgeLimit = yFudge
|
||||
}
|
||||
yrFudge = math.Floor(fudgeLimit/yScale+1.0e-7) * yScale
|
||||
}
|
||||
return n.sampleAndLerp(i, j, k, xr, yr-yrFudge, zr, yr)
|
||||
}
|
||||
|
||||
// sampleAndLerp uses dyGrad for gradient hashing and dySmooth for the Y
|
||||
// smoothstep (they differ only in the 5-arg "smear" path).
|
||||
func (n *ImprovedNoise) sampleAndLerp(gx, gy, gz int, dx, dyGrad, dz, dySmooth float64) float64 {
|
||||
dy := dyGrad
|
||||
a := n.perm(gx)
|
||||
b := n.perm(gx + 1)
|
||||
aa := n.perm(a + gy)
|
||||
ab := n.perm(a + gy + 1)
|
||||
ba := n.perm(b + gy)
|
||||
bb := n.perm(b + gy + 1)
|
||||
|
||||
d000 := grad(n.perm(aa+gz), dx, dy, dz)
|
||||
d100 := grad(n.perm(ba+gz), dx-1, dy, dz)
|
||||
d010 := grad(n.perm(ab+gz), dx, dy-1, dz)
|
||||
d110 := grad(n.perm(bb+gz), dx-1, dy-1, dz)
|
||||
d001 := grad(n.perm(aa+gz+1), dx, dy, dz-1)
|
||||
d101 := grad(n.perm(ba+gz+1), dx-1, dy, dz-1)
|
||||
d011 := grad(n.perm(ab+gz+1), dx, dy-1, dz-1)
|
||||
d111 := grad(n.perm(bb+gz+1), dx-1, dy-1, dz-1)
|
||||
|
||||
r := smoothstep(dx)
|
||||
s := smoothstep(dySmooth)
|
||||
t := smoothstep(dz)
|
||||
return lerp3(r, s, t, d000, d100, d010, d110, d001, d101, d011, d111)
|
||||
}
|
||||
|
||||
// grad is GradientNoise: dot of the hashed gradient vector with (x, y, z).
|
||||
func grad(hash int, x, y, z float64) float64 {
|
||||
g := gradient[hash&15]
|
||||
return g[0]*x + g[1]*y + g[2]*z
|
||||
}
|
||||
|
||||
// smoothstep is Mth.smoothstep: 6t^5 - 15t^4 + 10t^3.
|
||||
func smoothstep(t float64) float64 {
|
||||
return t * t * t * (t*(t*6-15) + 10)
|
||||
}
|
||||
|
||||
func lerp(t, a, b float64) float64 { return a + t*(b-a) }
|
||||
|
||||
func lerp2(tx, ty, v00, v10, v01, v11 float64) float64 {
|
||||
return lerp(ty, lerp(tx, v00, v10), lerp(tx, v01, v11))
|
||||
}
|
||||
|
||||
func lerp3(tx, ty, tz, v000, v100, v010, v110, v001, v101, v011, v111 float64) float64 {
|
||||
return lerp(tz,
|
||||
lerp2(tx, ty, v000, v100, v010, v110),
|
||||
lerp2(tx, ty, v001, v101, v011, v111))
|
||||
}
|
||||
32
internal/worldgen/improved_noise_test.go
Normal file
32
internal/worldgen/improved_noise_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImprovedNoiseVectors(t *testing.T) {
|
||||
n := NewImprovedNoise(NewXoroshiro(42))
|
||||
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
got, want float64
|
||||
}{
|
||||
{"xo", n.Xo, 190.83062484342904},
|
||||
{"yo", n.Yo, 101.88674612737026},
|
||||
{"zo", n.Zo, 151.323544791807},
|
||||
} {
|
||||
if math.Abs(c.got-c.want) > 1e-9 {
|
||||
t.Fatalf("%s = %v, want %v", c.name, c.got, c.want)
|
||||
}
|
||||
}
|
||||
|
||||
pts := [][3]float64{{0.5, 0.5, 0.5}, {1.5, 2.5, 3.5}, {100.1, 64.0, -200.7}, {-12.3, 5.0, 7.7}}
|
||||
want := []float64{0.078420838879807, 0.42903440359153633, 0.041997176611984766, -0.07019565638436798}
|
||||
for i, p := range pts {
|
||||
got := n.Noise(p[0], p[1], p[2])
|
||||
if math.Abs(got-want[i]) > 1e-12 {
|
||||
t.Fatalf("Noise%v = %v, want %v", p, got, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
328
internal/worldgen/loader.go
Normal file
328
internal/worldgen/loader.go
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed data
|
||||
var dataFS embed.FS
|
||||
|
||||
// Loader parses the embedded datapack density-function tree into evaluatable
|
||||
// nodes, seeding noises through a RandomState. Shared sub-functions are cached
|
||||
// by name so the DAG is built once.
|
||||
type Loader struct {
|
||||
rs *RandomState
|
||||
dfCache map[string]DensityFunction
|
||||
interpolated []*Interpolated
|
||||
}
|
||||
|
||||
// OverworldDensity is the parsed final_density plus the set of Interpolated
|
||||
// nodes that the generator samples on the cell grid.
|
||||
type OverworldDensity struct {
|
||||
Final DensityFunction
|
||||
Interpolated []*Interpolated
|
||||
// Climate parameters sampled by the biome finder. Read from the same
|
||||
// noise_router as final_density. The router keys map to climate axes:
|
||||
// temperature→Temperature, vegetation→Humidity, continents→Continentalness,
|
||||
// erosion→Erosion, ridges→Weirdness, depth→Depth.
|
||||
Temperature, Humidity, Continentalness, Erosion, Weirdness, Depth DensityFunction
|
||||
}
|
||||
|
||||
// LoadOverworldFinalDensity builds the overworld final_density function for the
|
||||
// given world seed.
|
||||
func LoadOverworldFinalDensity(seed int64) (*OverworldDensity, error) {
|
||||
l := &Loader{rs: NewRandomState(seed), dfCache: make(map[string]DensityFunction)}
|
||||
var settings struct {
|
||||
NoiseRouter map[string]json.RawMessage `json:"noise_router"`
|
||||
}
|
||||
if err := l.readJSON("data/overworld.json", &settings); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var node any
|
||||
if err := json.Unmarshal(settings.NoiseRouter["final_density"], &node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
final, err := l.parseNode(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
od := &OverworldDensity{Final: final, Interpolated: l.interpolated}
|
||||
|
||||
// Parse the climate router keys used by the biome finder. Each key resolves
|
||||
// to a density function via the same parseNode/loadRef machinery as
|
||||
// final_density. A missing key is not fatal — the climate axis stays nil and
|
||||
// the sampler treats it as a constant zero — but a parse error is.
|
||||
climateKeys := map[string]*DensityFunction{
|
||||
"temperature": &od.Temperature,
|
||||
"vegetation": &od.Humidity,
|
||||
"continents": &od.Continentalness,
|
||||
"erosion": &od.Erosion,
|
||||
"ridges": &od.Weirdness,
|
||||
"depth": &od.Depth,
|
||||
}
|
||||
for key, dst := range climateKeys {
|
||||
raw, ok := settings.NoiseRouter[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var cn any
|
||||
if err := json.Unmarshal(raw, &cn); err != nil {
|
||||
return nil, fmt.Errorf("parse climate key %q: %w", key, err)
|
||||
}
|
||||
df, err := l.parseNode(cn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("climate key %q: %w", key, err)
|
||||
}
|
||||
*dst = df
|
||||
}
|
||||
return od, nil
|
||||
}
|
||||
|
||||
func (l *Loader) readJSON(path string, v any) error {
|
||||
b, err := dataFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
// parseNode builds a density function from a decoded JSON value: a number is a
|
||||
// constant, a string is a reference to another density-function file, and an
|
||||
// object is a typed node.
|
||||
func (l *Loader) parseNode(v any) (DensityFunction, error) {
|
||||
switch t := v.(type) {
|
||||
case float64:
|
||||
return Constant(t), nil
|
||||
case string:
|
||||
return l.loadRef(t)
|
||||
case map[string]any:
|
||||
return l.parseObject(t)
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected density-function node %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// loadRef loads and caches a density function referenced by resource location.
|
||||
func (l *Loader) loadRef(name string) (DensityFunction, error) {
|
||||
if df, ok := l.dfCache[name]; ok {
|
||||
return df, nil
|
||||
}
|
||||
path := "data/density_function/" + strings.TrimPrefix(name, "minecraft:") + ".json"
|
||||
var node any
|
||||
if err := l.readJSON(path, &node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
df, err := l.parseNode(node)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s: %w", name, err)
|
||||
}
|
||||
l.dfCache[name] = df
|
||||
return df, nil
|
||||
}
|
||||
|
||||
func (l *Loader) parseObject(m map[string]any) (DensityFunction, error) {
|
||||
typ, _ := m["type"].(string)
|
||||
arg := func(k string) (DensityFunction, error) { return l.parseNode(m[k]) }
|
||||
num := func(k string) float64 { f, _ := m[k].(float64); return f }
|
||||
|
||||
switch strings.TrimPrefix(typ, "minecraft:") {
|
||||
case "add", "mul", "min", "max":
|
||||
a, err := arg("argument1")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := arg("argument2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch typ[10:] {
|
||||
case "add":
|
||||
return Add(a, b), nil
|
||||
case "mul":
|
||||
return Mul(a, b), nil
|
||||
case "min":
|
||||
return Min(a, b), nil
|
||||
default:
|
||||
return Max(a, b), nil
|
||||
}
|
||||
case "abs", "square", "cube", "half_negative", "quarter_negative", "squeeze":
|
||||
a, err := arg("argument")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return unaryByName(typ[10:], a), nil
|
||||
case "clamp":
|
||||
a, err := arg("input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Clamp(a, num("min"), num("max")), nil
|
||||
case "range_choice":
|
||||
in, err := arg("input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whenIn, err := arg("when_in_range")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
whenOut, err := arg("when_out_of_range")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RangeChoice{in, num("min_inclusive"), num("max_exclusive"), whenIn, whenOut}, nil
|
||||
case "y_clamped_gradient":
|
||||
return YClampedGradient{num("from_y"), num("to_y"), num("from_value"), num("to_value")}, nil
|
||||
case "noise":
|
||||
n, err := l.noiseField(m["noise"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NoiseDF{Noise: n, XZScale: num("xz_scale"), YScale: num("y_scale")}, nil
|
||||
case "shifted_noise":
|
||||
sx, err := arg("shift_x")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sy, err := arg("shift_y")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sz, err := arg("shift_z")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := l.noiseField(m["noise"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ShiftedNoise{sx, sy, sz, num("xz_scale"), num("y_scale"), n}, nil
|
||||
case "shift_a", "shift_b":
|
||||
n, err := l.noiseField(m["argument"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ[10:] == "shift_a" {
|
||||
return ShiftA{n}, nil
|
||||
}
|
||||
return ShiftB{n}, nil
|
||||
case "old_blended_noise":
|
||||
return l.rs.BlendedNoise(num("xz_scale"), num("y_scale"), num("xz_factor"), num("y_factor"), num("smear_scale_multiplier")), nil
|
||||
case "weird_scaled_sampler":
|
||||
in, err := arg("input")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := l.noiseField(m["noise"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rarity := SpaghettiRarity3D
|
||||
if s, _ := m["rarity_value_mapper"].(string); s == "type_2" {
|
||||
rarity = SpaghettiRarity2D
|
||||
}
|
||||
return WeirdScaledSampler{in, n, rarity}, nil
|
||||
case "spline":
|
||||
return l.parseSpline(m["spline"])
|
||||
case "blend_alpha":
|
||||
return Constant(1.0), nil // no blending: alpha = 1
|
||||
case "blend_offset":
|
||||
return Constant(0.0), nil // no blending: offset = 0
|
||||
case "interpolated":
|
||||
inner, err := arg("argument")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := &Interpolated{Inner: inner, Index: len(l.interpolated)}
|
||||
l.interpolated = append(l.interpolated, n)
|
||||
return n, nil
|
||||
case "blend_density", "flat_cache", "cache_2d", "cache_once", "cache_all_in_cell":
|
||||
// 2D caches and blend wrappers are value-preserving for per-point
|
||||
// evaluation (recomputed rather than cached); only the 3D interpolated
|
||||
// marker changes the result and is handled above.
|
||||
return arg("argument")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported density-function type %q", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func unaryByName(name string, a DensityFunction) DensityFunction {
|
||||
switch name {
|
||||
case "abs":
|
||||
return Abs(a)
|
||||
case "square":
|
||||
return Square(a)
|
||||
case "cube":
|
||||
return Cube(a)
|
||||
case "half_negative":
|
||||
return HalfNegative(a)
|
||||
case "quarter_negative":
|
||||
return QuarterNegative(a)
|
||||
default: // squeeze
|
||||
return Squeeze(a)
|
||||
}
|
||||
}
|
||||
|
||||
// noiseField resolves a noise reference (a "minecraft:<name>" key, or an object
|
||||
// with a "noise" key) to a seeded NormalNoise.
|
||||
func (l *Loader) noiseField(v any) (*NormalNoise, error) {
|
||||
var key string
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
key = t
|
||||
case map[string]any:
|
||||
key, _ = t["noise"].(string)
|
||||
}
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("missing noise reference")
|
||||
}
|
||||
var params struct {
|
||||
FirstOctave int `json:"firstOctave"`
|
||||
Amplitudes []float64 `json:"amplitudes"`
|
||||
}
|
||||
path := "data/noise/" + strings.TrimPrefix(key, "minecraft:") + ".json"
|
||||
if err := l.readJSON(path, ¶ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.rs.Noise(key, params.FirstOctave, params.Amplitudes), nil
|
||||
}
|
||||
|
||||
func (l *Loader) parseSpline(v any) (DensityFunction, error) {
|
||||
m, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("spline is not an object")
|
||||
}
|
||||
coord, err := l.parseNode(m["coordinate"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pts, _ := m["points"].([]any)
|
||||
s := &CubicSpline{coordinate: coord}
|
||||
for _, p := range pts {
|
||||
pm := p.(map[string]any)
|
||||
loc, _ := pm["location"].(float64)
|
||||
der, _ := pm["derivative"].(float64)
|
||||
val, err := l.parseSplineValue(pm["value"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.locations = append(s.locations, float32(loc))
|
||||
s.derivatives = append(s.derivatives, float32(der))
|
||||
s.values = append(s.values, val)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// parseSplineValue handles a spline point's value: a number (constant), a raw
|
||||
// nested spline (object with "coordinate"), or a density-function node.
|
||||
func (l *Loader) parseSplineValue(v any) (DensityFunction, error) {
|
||||
if m, ok := v.(map[string]any); ok {
|
||||
if _, hasCoord := m["coordinate"]; hasCoord {
|
||||
return l.parseSpline(m)
|
||||
}
|
||||
}
|
||||
return l.parseNode(v)
|
||||
}
|
||||
27
internal/worldgen/loadtest_test.go
Normal file
27
internal/worldgen/loadtest_test.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestLoadOverworldDensity loads the full overworld final_density tree and
|
||||
// checks it evaluates to finite values with the expected vertical sign trend
|
||||
// (solid deep down, air high up).
|
||||
func TestLoadOverworldDensity(t *testing.T) {
|
||||
od, err := LoadOverworldFinalDensity(0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deep := od.Final.Compute(FunctionContext{X: 0, Y: -40, Z: 0})
|
||||
high := od.Final.Compute(FunctionContext{X: 0, Y: 200, Z: 0})
|
||||
if math.IsNaN(deep) || math.IsNaN(high) {
|
||||
t.Fatal("final_density produced NaN")
|
||||
}
|
||||
if !(deep > 0) {
|
||||
t.Fatalf("expected solid (positive) deep underground, got %v", deep)
|
||||
}
|
||||
if !(high < 0) {
|
||||
t.Fatalf("expected air (negative) high up, got %v", high)
|
||||
}
|
||||
}
|
||||
45
internal/worldgen/noise_test.go
Normal file
45
internal/worldgen/noise_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var noisePts = [][3]float64{{0.5, 0.5, 0.5}, {1.5, 2.5, 3.5}, {100.1, 64.0, -200.7}, {-12.3, 5.0, 7.7}}
|
||||
|
||||
func TestPerlinNoiseVectors(t *testing.T) {
|
||||
p := NewPerlinNoise(NewXoroshiro(42), -3, []float64{1, 1, 1})
|
||||
|
||||
// Octave offsets validate the positional-factory MD5 seeding chain.
|
||||
wantOff := [3][3]float64{
|
||||
{77.66507715247522, 242.19573546755112, 173.13896594232995},
|
||||
{217.18954032212207, 38.031116641324985, 29.079876730588552},
|
||||
{245.20475284681797, 184.39003372698303, 174.76798991121467},
|
||||
}
|
||||
for i, w := range wantOff {
|
||||
o := p.octaves[len(p.octaves)-1-i]
|
||||
if math.Abs(o.Xo-w[0]) > 1e-9 || math.Abs(o.Yo-w[1]) > 1e-9 || math.Abs(o.Zo-w[2]) > 1e-9 {
|
||||
t.Fatalf("octave[%d] offsets = %v,%v,%v want %v", i, o.Xo, o.Yo, o.Zo, w)
|
||||
}
|
||||
}
|
||||
|
||||
want := []float64{0.14203479195685254, -0.2004829169283356, 0.10959099511010406, 0.02936359094335893}
|
||||
for i, pt := range noisePts {
|
||||
if got := p.GetValue(pt[0], pt[1], pt[2]); math.Abs(got-want[i]) > 1e-12 {
|
||||
t.Fatalf("PerlinNoise%v = %v, want %v", pt, got, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalNoiseVectors(t *testing.T) {
|
||||
n := NewNormalNoise(NewXoroshiro(42), -3, []float64{1, 1, 1})
|
||||
if math.Abs(n.MaxValue()-5.0) > 1e-12 {
|
||||
t.Fatalf("maxValue = %v, want 5.0", n.MaxValue())
|
||||
}
|
||||
want := []float64{0.08875533507209354, -0.1338868205633287, -0.18990226335882565, 0.008404386832678992}
|
||||
for i, pt := range noisePts {
|
||||
if got := n.GetValue(pt[0], pt[1], pt[2]); math.Abs(got-want[i]) > 1e-12 {
|
||||
t.Fatalf("NormalNoise%v = %v, want %v", pt, got, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
49
internal/worldgen/normal_noise.go
Normal file
49
internal/worldgen/normal_noise.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package worldgen
|
||||
|
||||
// normalInputFactor is NormalNoise.INPUT_FACTOR, the frequency offset applied
|
||||
// to the second Perlin field so the two octave stacks don't align.
|
||||
const normalInputFactor = 1.0181268882175227
|
||||
|
||||
// NormalNoise combines two PerlinNoise fields, scaled so the result has a
|
||||
// normalized deviation. This is the noise type referenced by density functions.
|
||||
type NormalNoise struct {
|
||||
first *PerlinNoise
|
||||
second *PerlinNoise
|
||||
valueFactor float64
|
||||
maxValue float64
|
||||
}
|
||||
|
||||
// NewNormalNoise builds a NormalNoise from the same parameters vanilla uses:
|
||||
// two PerlinNoise stacks drawn sequentially from r, plus a value factor derived
|
||||
// from the span of non-zero amplitudes.
|
||||
func NewNormalNoise(r RandomSource, firstOctave int, amplitudes []float64) *NormalNoise {
|
||||
n := &NormalNoise{
|
||||
first: NewPerlinNoise(r, firstOctave, amplitudes),
|
||||
second: NewPerlinNoise(r, firstOctave, amplitudes),
|
||||
}
|
||||
|
||||
min, max := len(amplitudes), 0
|
||||
for i, a := range amplitudes {
|
||||
if a != 0 {
|
||||
if i < min {
|
||||
min = i
|
||||
}
|
||||
if i > max {
|
||||
max = i
|
||||
}
|
||||
}
|
||||
}
|
||||
expectedDeviation := 0.1 * (1.0 + 1.0/float64(max-min+1))
|
||||
n.valueFactor = (1.0 / 6.0) / expectedDeviation
|
||||
n.maxValue = (n.first.MaxValue() + n.second.MaxValue()) * n.valueFactor
|
||||
return n
|
||||
}
|
||||
|
||||
// GetValue samples the combined noise at (x, y, z).
|
||||
func (n *NormalNoise) GetValue(x, y, z float64) float64 {
|
||||
return (n.first.GetValue(x, y, z) +
|
||||
n.second.GetValue(x*normalInputFactor, y*normalInputFactor, z*normalInputFactor)) * n.valueFactor
|
||||
}
|
||||
|
||||
// MaxValue returns the theoretical maximum magnitude.
|
||||
func (n *NormalNoise) MaxValue() float64 { return n.maxValue }
|
||||
122
internal/worldgen/perlin_noise.go
Normal file
122
internal/worldgen/perlin_noise.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// PerlinNoise is an octave sum of ImprovedNoise layers, matching the official
|
||||
// PerlinNoise (non-legacy factory path).
|
||||
type PerlinNoise struct {
|
||||
octaves []*ImprovedNoise // entries may be nil for zero amplitudes
|
||||
amplitudes []float64
|
||||
firstOctave int
|
||||
lowestFreqInputFactor float64
|
||||
lowestFreqValueFactor float64
|
||||
maxValue float64
|
||||
}
|
||||
|
||||
// NewPerlinNoise builds a PerlinNoise over the given amplitudes starting at
|
||||
// firstOctave. Each octave is seeded by the positional factory's hash of
|
||||
// "octave_<n>", exactly as vanilla does.
|
||||
func NewPerlinNoise(r RandomSource, firstOctave int, amplitudes []float64) *PerlinNoise {
|
||||
count := len(amplitudes)
|
||||
p := &PerlinNoise{
|
||||
octaves: make([]*ImprovedNoise, count),
|
||||
amplitudes: amplitudes,
|
||||
firstOctave: firstOctave,
|
||||
}
|
||||
factory := r.ForkPositional()
|
||||
for k := 0; k < count; k++ {
|
||||
if amplitudes[k] != 0 {
|
||||
octave := firstOctave + k
|
||||
p.octaves[k] = NewImprovedNoise(factory.FromHashOf("octave_" + strconv.Itoa(octave)))
|
||||
}
|
||||
}
|
||||
p.lowestFreqInputFactor = math.Pow(2, float64(firstOctave))
|
||||
p.lowestFreqValueFactor = math.Pow(2, float64(count-1)) / (math.Pow(2, float64(count)) - 1)
|
||||
p.maxValue = p.edgeValue(2.0)
|
||||
return p
|
||||
}
|
||||
|
||||
// NewLegacyPerlinNoise builds a PerlinNoise with the legacy (non-positional)
|
||||
// octave seeding used by BlendedNoise: octaves are drawn sequentially from r,
|
||||
// starting with the zero octave, then descending. Skipped (zero-amplitude)
|
||||
// octaves consume a fixed number of draws.
|
||||
func NewLegacyPerlinNoise(r RandomSource, firstOctave int, amplitudes []float64) *PerlinNoise {
|
||||
octaves := len(amplitudes)
|
||||
zeroIdx := -firstOctave
|
||||
p := &PerlinNoise{
|
||||
octaves: make([]*ImprovedNoise, octaves),
|
||||
amplitudes: amplitudes,
|
||||
firstOctave: firstOctave,
|
||||
}
|
||||
|
||||
zeroOctave := NewImprovedNoise(r) // always drawn
|
||||
if zeroIdx >= 0 && zeroIdx < octaves && amplitudes[zeroIdx] != 0 {
|
||||
p.octaves[zeroIdx] = zeroOctave
|
||||
}
|
||||
for i := zeroIdx - 1; i >= 0; i-- {
|
||||
if i < octaves && amplitudes[i] != 0 {
|
||||
p.octaves[i] = NewImprovedNoise(r)
|
||||
} else {
|
||||
r.ConsumeCount(262) // skipOctave
|
||||
}
|
||||
}
|
||||
|
||||
p.lowestFreqInputFactor = math.Pow(2, float64(-zeroIdx))
|
||||
p.lowestFreqValueFactor = math.Pow(2, float64(octaves-1)) / (math.Pow(2, float64(octaves)) - 1)
|
||||
p.maxValue = p.edgeValue(2.0)
|
||||
return p
|
||||
}
|
||||
|
||||
// GetOctaveNoise returns the i-th octave from the high-frequency end (vanilla's
|
||||
// reverse indexing), or nil if that octave's amplitude is zero.
|
||||
func (p *PerlinNoise) GetOctaveNoise(i int) *ImprovedNoise {
|
||||
return p.octaves[len(p.octaves)-1-i]
|
||||
}
|
||||
|
||||
// MaxBrokenValue is PerlinNoise.maxBrokenValue: edgeValue(yScale + 2).
|
||||
func (p *PerlinNoise) MaxBrokenValue(yScale float64) float64 { return p.edgeValue(yScale + 2.0) }
|
||||
|
||||
// GetValue samples the octave sum at (x, y, z).
|
||||
func (p *PerlinNoise) GetValue(x, y, z float64) float64 { return p.GetValueY(x, y, z, 0, 0) }
|
||||
|
||||
// GetValueY is the 5-argument octave sum used with Y-smearing.
|
||||
func (p *PerlinNoise) GetValueY(x, y, z, yScale, yFudge float64) float64 {
|
||||
d := 0.0
|
||||
inputFactor := p.lowestFreqInputFactor
|
||||
valueFactor := p.lowestFreqValueFactor
|
||||
for i, oct := range p.octaves {
|
||||
if oct != nil {
|
||||
g := oct.NoiseY(wrap(x*inputFactor), wrap(y*inputFactor), wrap(z*inputFactor),
|
||||
yScale*inputFactor, yFudge*inputFactor)
|
||||
d += p.amplitudes[i] * g * valueFactor
|
||||
}
|
||||
inputFactor *= 2.0
|
||||
valueFactor /= 2.0
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// MaxValue returns the theoretical maximum magnitude.
|
||||
func (p *PerlinNoise) MaxValue() float64 { return p.maxValue }
|
||||
|
||||
func (p *PerlinNoise) edgeValue(x float64) float64 {
|
||||
e := 0.0
|
||||
valueFactor := p.lowestFreqValueFactor
|
||||
for i, oct := range p.octaves {
|
||||
if oct != nil {
|
||||
e += p.amplitudes[i] * x * valueFactor
|
||||
}
|
||||
valueFactor /= 2.0
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// wrap is PerlinNoise.wrap: folds large coordinates back near the origin to
|
||||
// preserve floating-point precision. The constant is 2^25.
|
||||
func wrap(value float64) float64 {
|
||||
const period = 3.3554432e7
|
||||
return value - float64(int64(math.Floor(value/period+0.5)))*period
|
||||
}
|
||||
220
internal/worldgen/random.go
Normal file
220
internal/worldgen/random.go
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
// Package worldgen ports Minecraft's noise-based terrain generation: the random
|
||||
// sources, Perlin/normal noise, and (later) the density-function interpreter.
|
||||
//
|
||||
// Implementations mirror the official 26.1.2 server bit-for-bit; values are
|
||||
// verified against vectors captured from the real classes (see random_test.go).
|
||||
package worldgen
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// md5Seed mirrors RandomSupport.seedFromHashOf: the MD5 digest of name split
|
||||
// into two big-endian 64-bit halves.
|
||||
func md5Seed(name string) (lo, hi uint64) {
|
||||
sum := md5.Sum([]byte(name))
|
||||
return binary.BigEndian.Uint64(sum[0:8]), binary.BigEndian.Uint64(sum[8:16])
|
||||
}
|
||||
|
||||
// Mixing constants from RandomSupport.
|
||||
const (
|
||||
goldenRatio64 = 0x9E3779B97F4A7C15
|
||||
silverRatio64 = 0x6A09E667F3BCC909
|
||||
)
|
||||
|
||||
// mixStafford13 is RandomSupport.mixStafford13, a 64-bit avalanche mix.
|
||||
func mixStafford13(z uint64) uint64 {
|
||||
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9
|
||||
z = (z ^ (z >> 27)) * 0x94D049BB133111EB
|
||||
return z ^ (z >> 31)
|
||||
}
|
||||
|
||||
// seed128 is RandomSupport.Seed128bit.
|
||||
type seed128 struct{ lo, hi uint64 }
|
||||
|
||||
// upgradeSeedTo128bit mirrors RandomSupport.upgradeSeedTo128bit: derive a
|
||||
// 128-bit seed from a 64-bit one, then avalanche-mix both halves.
|
||||
func upgradeSeedTo128bit(seed uint64) seed128 {
|
||||
lo := seed ^ silverRatio64
|
||||
hi := lo + goldenRatio64
|
||||
return seed128{mixStafford13(lo), mixStafford13(hi)}
|
||||
}
|
||||
|
||||
// RandomSource is the subset of Minecraft's RandomSource we use.
|
||||
type RandomSource interface {
|
||||
NextLong() int64
|
||||
NextInt() int32
|
||||
NextIntN(bound int32) int32
|
||||
NextDouble() float64
|
||||
NextFloat() float32
|
||||
NextBoolean() bool
|
||||
// ForkPositional returns a factory for deriving deterministic child sources
|
||||
// (used to seed noise octaves by name).
|
||||
ForkPositional() PositionalRandomFactory
|
||||
// ConsumeCount advances the generator by n draws (used to skip noise octaves).
|
||||
ConsumeCount(n int)
|
||||
}
|
||||
|
||||
// PositionalRandomFactory derives child RandomSources deterministically.
|
||||
type PositionalRandomFactory interface {
|
||||
// FromHashOf seeds a child source from the MD5 hash of name.
|
||||
FromHashOf(name string) RandomSource
|
||||
}
|
||||
|
||||
// --- Xoroshiro128++ ---
|
||||
|
||||
// Xoroshiro is XoroshiroRandomSource backed by Xoroshiro128PlusPlus.
|
||||
type Xoroshiro struct{ lo, hi uint64 }
|
||||
|
||||
// NewXoroshiro seeds a Xoroshiro source from a 64-bit seed.
|
||||
func NewXoroshiro(seed int64) *Xoroshiro {
|
||||
s := upgradeSeedTo128bit(uint64(seed))
|
||||
return newXoroshiroFrom(s.lo, s.hi)
|
||||
}
|
||||
|
||||
func newXoroshiroFrom(lo, hi uint64) *Xoroshiro {
|
||||
if lo == 0 && hi == 0 {
|
||||
lo, hi = goldenRatio64, silverRatio64
|
||||
}
|
||||
return &Xoroshiro{lo: lo, hi: hi}
|
||||
}
|
||||
|
||||
// nextBits advances the Xoroshiro128++ state and returns the raw 64-bit output.
|
||||
func (x *Xoroshiro) nextBits() uint64 {
|
||||
l, m := x.lo, x.hi
|
||||
n := bits.RotateLeft64(l+m, 17) + l
|
||||
m ^= l
|
||||
x.lo = bits.RotateLeft64(l, 49) ^ m ^ (m << 21)
|
||||
x.hi = bits.RotateLeft64(m, 28)
|
||||
return n
|
||||
}
|
||||
|
||||
func (x *Xoroshiro) NextLong() int64 { return int64(x.nextBits()) }
|
||||
func (x *Xoroshiro) NextInt() int32 { return int32(x.nextBits()) }
|
||||
|
||||
// NextIntN mirrors XoroshiroRandomSource.nextInt(bound): Lemire's multiply-shift
|
||||
// with rejection for an unbiased result.
|
||||
func (x *Xoroshiro) NextIntN(bound int32) int32 {
|
||||
l := uint64(uint32(x.NextInt()))
|
||||
m := l * uint64(bound)
|
||||
low := uint32(m)
|
||||
if low < uint32(bound) {
|
||||
threshold := uint32(-bound) % uint32(bound)
|
||||
for low < threshold {
|
||||
l = uint64(uint32(x.NextInt()))
|
||||
m = l * uint64(bound)
|
||||
low = uint32(m)
|
||||
}
|
||||
}
|
||||
return int32(m >> 32)
|
||||
}
|
||||
|
||||
func (x *Xoroshiro) NextDouble() float64 {
|
||||
return float64(x.nextBits()>>11) * 0x1.0p-53
|
||||
}
|
||||
|
||||
func (x *Xoroshiro) NextFloat() float32 {
|
||||
return float32(x.nextBits()>>40) * 0x1.0p-24
|
||||
}
|
||||
|
||||
func (x *Xoroshiro) NextBoolean() bool { return x.nextBits()&1 != 0 }
|
||||
|
||||
// ConsumeCount advances the underlying generator n times.
|
||||
func (x *Xoroshiro) ConsumeCount(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
x.nextBits()
|
||||
}
|
||||
}
|
||||
|
||||
// ForkPositional consumes two outputs to seed a positional factory.
|
||||
func (x *Xoroshiro) ForkPositional() PositionalRandomFactory {
|
||||
return &xoroshiroPositional{seedLo: x.nextBits(), seedHi: x.nextBits()}
|
||||
}
|
||||
|
||||
type xoroshiroPositional struct{ seedLo, seedHi uint64 }
|
||||
|
||||
// FromHashOf mirrors XoroshiroPositionalRandomFactory.fromHashOf: MD5 the name
|
||||
// into a 128-bit seed, XOR with the factory seed, no avalanche mixing.
|
||||
func (f *xoroshiroPositional) FromHashOf(name string) RandomSource {
|
||||
lo, hi := md5Seed(name)
|
||||
return newXoroshiroFrom(lo^f.seedLo, hi^f.seedHi)
|
||||
}
|
||||
|
||||
// --- Legacy LCG (java.util.Random) ---
|
||||
|
||||
const (
|
||||
lcgMultiplier = 0x5DEECE66D
|
||||
lcgAddend = 0xB
|
||||
lcgMask = (1 << 48) - 1
|
||||
)
|
||||
|
||||
// Legacy is LegacyRandomSource: java.util.Random's 48-bit LCG.
|
||||
type Legacy struct{ seed uint64 }
|
||||
|
||||
// NewLegacy seeds a Legacy source, applying Java's seed scramble.
|
||||
func NewLegacy(seed int64) *Legacy {
|
||||
return &Legacy{seed: (uint64(seed) ^ lcgMultiplier) & lcgMask}
|
||||
}
|
||||
|
||||
// next returns the top `b` bits of the next LCG state.
|
||||
func (r *Legacy) next(b uint) int32 {
|
||||
r.seed = (r.seed*lcgMultiplier + lcgAddend) & lcgMask
|
||||
return int32(r.seed >> (48 - b))
|
||||
}
|
||||
|
||||
func (r *Legacy) NextInt() int32 { return r.next(32) }
|
||||
func (r *Legacy) NextLong() int64 { return int64(r.next(32))<<32 + int64(r.next(32)) }
|
||||
|
||||
// NextIntN mirrors BitRandomSource.nextInt(bound): power-of-two fast path,
|
||||
// otherwise modulo with rejection to avoid bias.
|
||||
func (r *Legacy) NextIntN(bound int32) int32 {
|
||||
if bound&-bound == bound { // power of two
|
||||
return int32((int64(bound) * int64(r.next(31))) >> 31)
|
||||
}
|
||||
for {
|
||||
j := r.next(31)
|
||||
k := j % bound
|
||||
if j-k+(bound-1) >= 0 {
|
||||
return k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Legacy) NextDouble() float64 {
|
||||
hi := int64(r.next(26))
|
||||
lo := int64(r.next(27))
|
||||
return float64(hi<<27+lo) * 0x1.0p-53
|
||||
}
|
||||
|
||||
func (r *Legacy) NextFloat() float32 { return float32(r.next(24)) * 0x1.0p-24 }
|
||||
func (r *Legacy) NextBoolean() bool { return r.next(1) != 0 }
|
||||
|
||||
// ConsumeCount advances the LCG n times.
|
||||
func (r *Legacy) ConsumeCount(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
r.next(32)
|
||||
}
|
||||
}
|
||||
|
||||
// ForkPositional mirrors LegacyRandomSource.forkPositional.
|
||||
func (r *Legacy) ForkPositional() PositionalRandomFactory {
|
||||
return &legacyPositional{seed: uint64(r.NextLong())}
|
||||
}
|
||||
|
||||
type legacyPositional struct{ seed uint64 }
|
||||
|
||||
// FromHashOf mirrors LegacyPositionalRandomFactory.fromHashOf: seed from the
|
||||
// Java String.hashCode of name XORed with the factory seed.
|
||||
func (f *legacyPositional) FromHashOf(name string) RandomSource {
|
||||
return NewLegacy(int64(int32(javaStringHashCode(name))) ^ int64(f.seed))
|
||||
}
|
||||
|
||||
func javaStringHashCode(s string) int32 {
|
||||
var h int32
|
||||
for i := 0; i < len(s); i++ {
|
||||
h = 31*h + int32(s[i])
|
||||
}
|
||||
return h
|
||||
}
|
||||
73
internal/worldgen/random_test.go
Normal file
73
internal/worldgen/random_test.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package worldgen
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Reference vectors captured from the official 26.1.2 server classes
|
||||
// (XoroshiroRandomSource / LegacyRandomSource seeded with 42), consumed in
|
||||
// order: 5x NextLong, 5x NextIntN(100), then doubles/floats.
|
||||
|
||||
func TestXoroshiroVectors(t *testing.T) {
|
||||
x := NewXoroshiro(42)
|
||||
|
||||
wantLong := []int64{
|
||||
-4695948378737616609, 7341713790291473579, -7542733514721318211,
|
||||
4888889476139319686, 8419651034331256779,
|
||||
}
|
||||
for i, w := range wantLong {
|
||||
if got := x.NextLong(); got != w {
|
||||
t.Fatalf("NextLong[%d] = %d, want %d", i, got, w)
|
||||
}
|
||||
}
|
||||
|
||||
wantInt := []int32{28, 93, 40, 73, 75}
|
||||
for i, w := range wantInt {
|
||||
if got := x.NextIntN(100); got != w {
|
||||
t.Fatalf("NextIntN[%d] = %d, want %d", i, got, w)
|
||||
}
|
||||
}
|
||||
|
||||
wantDouble := []float64{0.4990607418038817, 0.4922907789978952, 0.09296765457327383}
|
||||
for i, w := range wantDouble {
|
||||
if got := x.NextDouble(); math.Abs(got-w) > 1e-15 {
|
||||
t.Fatalf("NextDouble[%d] = %v, want %v", i, got, w)
|
||||
}
|
||||
}
|
||||
|
||||
wantFloat := []float32{0.1058414, 0.583224, 0.34108514}
|
||||
for i, w := range wantFloat {
|
||||
if got := x.NextFloat(); math.Abs(float64(got-w)) > 1e-6 {
|
||||
t.Fatalf("NextFloat[%d] = %v, want %v", i, got, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyVectors(t *testing.T) {
|
||||
r := NewLegacy(42)
|
||||
|
||||
wantLong := []int64{
|
||||
-5025562857975149833, -5843495416241995736, 5694868678511409995,
|
||||
5111195811822994797, -6169532649852302182,
|
||||
}
|
||||
for i, w := range wantLong {
|
||||
if got := r.NextLong(); got != w {
|
||||
t.Fatalf("NextLong[%d] = %d, want %d", i, got, w)
|
||||
}
|
||||
}
|
||||
|
||||
wantInt := []int32{82, 2, 76, 92, 76}
|
||||
for i, w := range wantInt {
|
||||
if got := r.NextIntN(100); got != w {
|
||||
t.Fatalf("NextIntN[%d] = %d, want %d", i, got, w)
|
||||
}
|
||||
}
|
||||
|
||||
wantDouble := []float64{0.6904257605024213, 0.762090173108902, 0.998178600062844}
|
||||
for i, w := range wantDouble {
|
||||
if got := r.NextDouble(); math.Abs(got-w) > 1e-15 {
|
||||
t.Fatalf("NextDouble[%d] = %v, want %v", i, got, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue