pub fn mix64(x: u64) -> u64 {
let mut x = x;
x ^= x >> 33;
x = x.wrapping_mul(0xff51_afd7_ed55_8ccd);
x ^= x >> 29;
x = x.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
x ^= x >> 32;
x
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct XorShift(u64);
impl XorShift {
pub const fn new(seed: u64) -> Self {
Self(seed | 1)
}
pub fn next(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
pub fn below(&mut self, bound: u32) -> u32 {
if bound == 0 {
return 0;
}
(self.next() % bound as u64) as u32
}
pub fn unit(&mut self) -> f32 {
(self.next() >> 40) as f32 / (1u64 << 24) as f32
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ValueNoise {
seed: u32,
}
impl ValueNoise {
pub const fn new(seed: u32) -> Self {
Self { seed }
}
pub fn corner(&self, xi: i32, zi: i32, octave: u32) -> f32 {
let mut h = self.seed ^ octave.wrapping_mul(0x9E37_79B9);
h ^= (xi as u32).wrapping_mul(0x85EB_CA6B);
h = h.rotate_left(13);
h ^= (zi as u32).wrapping_mul(0xC2B2_AE35);
h = h.rotate_left(17);
h ^= h >> 15;
h = h.wrapping_mul(0x2545_F491);
h ^= h >> 13;
(h >> 8) as f32 / (1u32 << 24) as f32
}
pub fn octave(&self, x: f32, z: f32, scale: f32, octave: u32) -> f32 {
let (gx, gz) = (x / scale, z / scale);
let (xi, zi) = (gx.floor(), gz.floor());
let (fx, fz) = (crate::smoothing::smoothstep(gx - xi), crate::smoothing::smoothstep(gz - zi));
let (xi, zi) = (xi as i32, zi as i32);
let a = self.corner(xi, zi, octave);
let b = self.corner(xi + 1, zi, octave);
let c = self.corner(xi, zi + 1, octave);
let d = self.corner(xi + 1, zi + 1, octave);
let top = a + (b - a) * fx;
let bottom = c + (d - c) * fx;
top + (bottom - top) * fz
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_values_are_pinned_because_agreement_is_the_point() {
assert_eq!(mix64(0), 0);
assert_eq!(mix64(1), 0x5f49_31e5_1b58_8313);
assert_eq!(mix64(0xDEAD_BEEF), 0xa376_9b68_a0c4_0fcc);
let mut rng = XorShift::new(0x5EED);
assert_eq!(rng.next(), 0x0000_1794_81ea_4510);
assert_eq!(rng.next(), 0xc7ba_5713_31ee_d59a);
let noise = ValueNoise::new(0x0CEA_11CE);
assert_eq!(noise.corner(3, -7, 1).to_bits(), 0x3eb7_c66e);
}
#[test]
fn a_zero_seed_still_produces_a_stream() {
let mut rng = XorShift::new(0);
assert_ne!(rng.next(), 0);
assert_ne!(rng.next(), rng.next());
}
#[test]
fn draws_stay_inside_their_bounds() {
let mut rng = XorShift::new(42);
for _ in 0..1000 {
assert!(rng.below(7) < 7);
let unit = rng.unit();
assert!((0.0..1.0).contains(&unit), "unit was {unit}");
}
assert_eq!(rng.below(0), 0);
}
#[test]
fn noise_is_continuous_across_a_lattice_edge() {
let noise = ValueNoise::new(99);
let at_corner = noise.octave(12.0 * 5.0, 4.0 * 5.0, 5.0, 0);
assert!((at_corner - noise.corner(12, 4, 0)).abs() < 1e-6);
let just_left = noise.octave(59.999, 20.0, 5.0, 0);
let just_right = noise.octave(60.001, 20.0, 5.0, 0);
assert!((just_left - just_right).abs() < 1e-3, "a crease at the cell edge");
}
#[test]
fn two_octaves_are_two_different_landscapes() {
let noise = ValueNoise::new(7);
let same = (0..64).filter(|i| {
(noise.corner(*i, 0, 0) - noise.corner(*i, 0, 1)).abs() < 1e-6
}).count();
assert!(same < 3, "{same} of 64 corners agree across octaves");
}
}