diff --git a/mc/gen/noise.go b/mc/gen/noise.go index f3f4fe2..417f969 100644 --- a/mc/gen/noise.go +++ b/mc/gen/noise.go @@ -50,6 +50,10 @@ func lerp(part, from, to float64) float64 { return from + part*(to-from) } +func lerp32(part, from, to float32) float32 { + return from + part*(to-from) +} + func (n PerlinNoise) Sample(x, y, z float64, yamp, ymin float64) (v float64) { var h1, h2, h3 uint8 var t1, t2, t3 float64 diff --git a/mc/gen/spline.go b/mc/gen/spline.go new file mode 100644 index 0000000..4fbf357 --- /dev/null +++ b/mc/gen/spline.go @@ -0,0 +1,209 @@ +package gen + +type Spline interface { + Sample(t []float32) float32 +} + +type FixedSpline struct { + Val float32 +} + +func (s FixedSpline) Sample(t []float32) float32 { + return s.Val +} + +type SubSpline struct { + Loc, Der float32 + Val Spline +} +type SplineA struct { + TargetIndex int + Points []SubSpline +} + +func ternery[T any](a bool, b T, c T) T { + if a { + return b + } + return c +} + +func (s SplineA) Sample(in []float32) float32 { + f := in[s.TargetIndex] + + i := 0 + for _, p := range s.Points { + if f <= p.Loc { + break + } + i++ + } + if i == 0 || i == len(s.Points) { + if i != 0 { + i-- + } + v := s.Points[i].Val.Sample(in) + return v + s.Points[i].Der*(f-s.Points[i].Loc) + } + s1 := s.Points[i-1] + s2 := s.Points[i] + g := s.Points[i-1].Loc + h := s.Points[i].Loc + k := (f - g) / (h - g) + l := s.Points[i-1].Der + m := s.Points[i].Der + n := s1.Val.Sample(in) + o := s2.Val.Sample(in) + p := l*(h-g) - (o - n) + q := -m*(h-g) + (o - n) + r := lerp32(k, n, o) + k*(1.0-k)*lerp32(k, p, q) + return r +} + +const ( + splineIndexContinentalNess = iota + splineIndexErosion + splineIndexWeirdness +) + +func getOffsetValue(weirdness float32, continentalness float32) float32 { + f0 := 1.0 - (1.0-continentalness)*0.5 + f1 := 0.5 * (1.0 - continentalness) + f2 := (weirdness + 1.17) * 0.46082947 + off := f2*f0 - f1 + if weirdness < -0.7 { + if off > -0.2222 { + return off + } + return -0.2222 + } + if off > 0 { + return off + } + return 0 + +} + +func createSpline38219(f float32, bl bool) Spline { + s := SplineA{ + TargetIndex: splineIndexWeirdness, + Points: nil, + } + i := getOffsetValue(-1.0, f) + k := getOffsetValue(1.0, f) + l := 1.0 - (1.0-f)*0.5 + u := 0.5 * (1.0 - f) + l = u/(0.46082947*l) - 1.17 + + if -0.65 < l && l < 1.0 { + u = getOffsetValue(-0.65, f) + p := getOffsetValue(-0.75, f) + q := (p - i) * 4.0 + r := getOffsetValue(l, f) + z := (k - r) / (1.0 - l) + + s.Points = append(s.Points, SubSpline{-1.0, q, FixedSpline{i}}) + s.Points = append(s.Points, SubSpline{-0.75, 0, FixedSpline{p}}) + s.Points = append(s.Points, SubSpline{-0.65, 0, FixedSpline{u}}) + s.Points = append(s.Points, SubSpline{l - 0.01, 0, FixedSpline{r}}) + s.Points = append(s.Points, SubSpline{l, z, FixedSpline{r}}) + s.Points = append(s.Points, SubSpline{1.0, z, FixedSpline{k}}) + } else { + u = (k - i) * 0.5 + if bl { + s.Points = append(s.Points, SubSpline{-1.0, 0, FixedSpline{ternery(i > 0.2, i, 0.2)}}) + s.Points = append(s.Points, SubSpline{0.0, u, FixedSpline{lerp32(0.5, i, k)}}) + } else { + s.Points = append(s.Points, SubSpline{-1.0, u, FixedSpline{i}}) + } + s.Points = append(s.Points, SubSpline{1.0, u, FixedSpline{k}}) + } + return s +} + +func createFlatOffsetSpline(f, g, h, i, j, k float32) Spline { + l := 0.5 * (g - f) + if l < k { + l = k + } + m := 5.0 * (h - g) + n := m + if l < m { + n = l + } + + return SplineA{ + TargetIndex: splineIndexWeirdness, + Points: []SubSpline{ + {-1, l, FixedSpline{f}}, + {-0.4, n, FixedSpline{g}}, + {0, m, FixedSpline{h}}, + {0.4, 2.0 * (i - h), FixedSpline{i}}, + {1, 0.7 * (j - i), FixedSpline{j}}, + }, + } +} + +func landSpline(f, g, h, i, j, k float32, bl bool) Spline { + s1 := createSpline38219(lerp32(i, 0.6, 1.5), bl) + s2 := createSpline38219(lerp32(i, 0.6, 1.0), bl) + s3 := createSpline38219(i, bl) + ih := 0.5 * i + s4 := createFlatOffsetSpline(f-0.15, ih, ih, ih, i*0.6, 0.5) + s5 := createFlatOffsetSpline(f, j*i, g*i, ih, i*0.6, 0.5) + s6 := createFlatOffsetSpline(f, j, j, g, h, 0.5) + s7 := createFlatOffsetSpline(f, j, j, g, h, 0.5) + s8 := SplineA{ + TargetIndex: splineIndexWeirdness, + Points: []SubSpline{ + {-1, 0, FixedSpline{f}}, + {-0.4, 0, s6}, + {0, 0, FixedSpline{h + 0.07}}, + }, + } + s9 := createFlatOffsetSpline(-0.02, k, k, g, h, 0) + s := SplineA{ + TargetIndex: splineIndexErosion, + Points: []SubSpline{ + {-0.85, 0, s1}, + {-0.7, 0, s2}, + {-0.4, 0, s3}, + {-0.35, 0, s4}, + {-0.1, 0, s5}, + {0.2, 0, s6}, + }, + } + if bl { + s.Points = append(s.Points, SubSpline{0.4, 0, s7}) + s.Points = append(s.Points, SubSpline{0.45, 0, s8}) + s.Points = append(s.Points, SubSpline{0.55, 0, s8}) + s.Points = append(s.Points, SubSpline{0.58, 0, s7}) + } + s.Points = append(s.Points, SubSpline{0.7, 0, s9}) + return s +} + +func CreateBiomeDepthSpline() Spline { + sp1 := landSpline(-0.15, 0.00, 0.0, 0.1, 0.00, -0.03, false) + sp2 := landSpline(-0.10, 0.03, 0.1, 0.1, 0.01, -0.03, false) + sp3 := landSpline(-0.10, 0.03, 0.1, 0.7, 0.01, -0.03, true) + sp4 := landSpline(-0.05, 0.03, 0.1, 1.0, 0.01, 0.01, true) + + return SplineA{ + TargetIndex: splineIndexContinentalNess, + Points: []SubSpline{ + {Loc: -1.10, Val: FixedSpline{Val: 0.044}, Der: 0.0}, + {Loc: -1.02, Val: FixedSpline{Val: -0.2222}, Der: 0.0}, + {Loc: -0.51, Val: FixedSpline{Val: -0.2222}, Der: 0.0}, + {Loc: -0.44, Val: FixedSpline{Val: -0.12}, Der: 0.0}, + {Loc: -0.18, Val: FixedSpline{Val: -0.12}, Der: 0.0}, + {Loc: -0.16, Val: sp1, Der: 0.0}, + {Loc: -0.15, Val: sp1, Der: 0.0}, + {Loc: -0.10, Val: sp2, Der: 0.0}, + {Loc: 0.25, Val: sp3, Der: 0.0}, + {Loc: 1.00, Val: sp4, Der: 0.0}, + }, + } +} + +var BiomeDepthSpline = CreateBiomeDepthSpline() diff --git a/mc/gen/spline_test.go b/mc/gen/spline_test.go new file mode 100644 index 0000000..7d14f4e --- /dev/null +++ b/mc/gen/spline_test.go @@ -0,0 +1,10 @@ +package gen + +import "testing" + +func TestSpline(t *testing.T) { + v := BiomeDepthSpline.Sample([]float32{0.236413047, 0.220234469, 0.139604524, -0.339296669}) + if v != 0.025035858154296875 { + t.Errorf("bad value: %f", v) + } +}