RegionIO/internal/worldgen/simplex_noise.go

68 lines
1.9 KiB
Go

package worldgen
import "math"
// simplexNoise is the 2D path of vanilla's SimplexNoise. Biome placement
// counts use a single octave constructed from LegacyRandomSource seed 2345.
type simplexNoise struct {
p [256]int
}
func newSimplexNoise(random RandomSource) *simplexNoise {
// SimplexNoise always consumes three offsets even though its 2D sampler
// does not add them to the input coordinates.
random.NextDouble()
random.NextDouble()
random.NextDouble()
n := &simplexNoise{}
for i := range n.p {
n.p[i] = i
}
for i := range n.p {
j := i + int(random.NextIntN(int32(256-i)))
n.p[i], n.p[j] = n.p[j], n.p[i]
}
return n
}
func (n *simplexNoise) perm(index int) int { return n.p[index&255] }
func (n *simplexNoise) value2D(x, y float64) float64 {
sqrt3 := math.Sqrt(3)
f2 := 0.5 * (sqrt3 - 1)
g2 := (3 - sqrt3) / 6
skew := (x + y) * f2
i, j := int(math.Floor(x+skew)), int(math.Floor(y+skew))
unskew := float64(i+j) * g2
x0, y0 := x-(float64(i)-unskew), y-(float64(j)-unskew)
i1, j1 := 0, 1
if x0 > y0 {
i1, j1 = 1, 0
}
x1, y1 := x0-float64(i1)+g2, y0-float64(j1)+g2
x2, y2 := x0-1+2*g2, y0-1+2*g2
ii, jj := i&255, j&255
g0 := n.perm(ii+n.perm(jj)) % 12
g1 := n.perm(ii+i1+n.perm(jj+j1)) % 12
g2i := n.perm(ii+1+n.perm(jj+1)) % 12
return 70 * (simplexCorner(g0, x0, y0, 0.5) +
simplexCorner(g1, x1, y1, 0.5) + simplexCorner(g2i, x2, y2, 0.5))
}
func simplexCorner(gradientIndex int, x, y, radius float64) float64 {
attenuation := radius - x*x - y*y
if attenuation < 0 {
return 0
}
attenuation *= attenuation
g := gradient[gradientIndex]
return attenuation * attenuation * (g[0]*x + g[1]*y)
}
var biomeInfoNoise = newSimplexNoise(NewLegacy(2345))
// BiomeInfoNoise is Biome.BIOME_INFO_NOISE.getValue(x, z, false). It is
// world-seed independent and drives noise-based vegetation attempt counts.
func BiomeInfoNoise(x, z float64) float64 {
return biomeInfoNoise.value2D(x, z)
}