use rand::Rng;
use rand::SeedableRng;
use rand::seq::SliceRandom;
use rand_pcg::Pcg64Mcg;
use crate::{HeightMap, TerrainGenerator};
#[derive(Debug, Clone)]
pub struct FbmNoise {
pub seed: u64,
pub octaves: u32,
pub persistence: f32,
pub lacunarity: f32,
pub base_frequency: f32,
}
impl FbmNoise {
pub fn new(seed: u64) -> Self {
Self {
seed,
octaves: 6,
persistence: 0.5,
lacunarity: 2.0,
base_frequency: 1.0,
}
}
pub fn with_octaves(mut self, octaves: u32) -> Self {
assert!(
(1..=32).contains(&octaves),
"octaves must be in [1, 32], got {octaves}"
);
self.octaves = octaves;
self
}
pub fn with_persistence(mut self, persistence: f32) -> Self {
self.persistence = persistence;
self
}
}
struct ValueNoise {
values: [f32; 256],
perm: [u8; 512], }
impl ValueNoise {
fn new(seed: u64) -> Self {
let mut rng = Pcg64Mcg::seed_from_u64(seed);
let mut raw_perm: Vec<u8> = (0..=255u8).collect();
raw_perm.shuffle(&mut rng);
let mut perm = [0u8; 512];
perm[..256].copy_from_slice(&raw_perm);
perm[256..].copy_from_slice(&raw_perm);
let mut values = [0.0f32; 256];
for v in values.iter_mut() {
let bits: u32 = rng.random();
*v = (bits >> 9) as f32 / (1u32 << 23) as f32;
}
Self { values, perm }
}
#[inline]
fn hash(&self, ix: i32, iz: i32) -> f32 {
let xi = ix.rem_euclid(256) as usize;
let zi = iz.rem_euclid(256) as usize;
let idx = self.perm[self.perm[xi] as usize + zi] as usize;
self.values[idx]
}
fn sample(&self, x: f32, z: f32) -> f32 {
let x0 = x.floor() as i32;
let z0 = z.floor() as i32;
let fx = x - x0 as f32;
let fz = z - z0 as f32;
let u = fx * fx * fx * (fx * (fx * 6.0 - 15.0) + 10.0);
let v = fz * fz * fz * (fz * (fz * 6.0 - 15.0) + 10.0);
let v00 = self.hash(x0, z0);
let v10 = self.hash(x0 + 1, z0);
let v01 = self.hash(x0, z0 + 1);
let v11 = self.hash(x0 + 1, z0 + 1);
let a = v00 + (v10 - v00) * u;
let b = v01 + (v11 - v01) * u;
a + (b - a) * v
}
}
impl TerrainGenerator for FbmNoise {
fn generate(&self, heightmap: &mut HeightMap) {
assert!(
(1..=32).contains(&self.octaves),
"octaves must be in [1, 32], got {}",
self.octaves
);
let noise = ValueNoise::new(self.seed);
let mut max_amp = 0.0_f32;
let mut amp = 1.0_f32;
for _ in 0..self.octaves {
max_amp += amp;
amp *= self.persistence;
}
let w = heightmap.width();
let h = heightmap.height();
for z in 0..h {
for x in 0..w {
let nx = x as f32 / w as f32 * self.base_frequency;
let nz = z as f32 / h as f32 * self.base_frequency;
let mut height = 0.0_f32;
let mut frequency = 1.0_f32;
let mut amplitude = 1.0_f32;
for _ in 0..self.octaves {
height += noise.sample(nx * frequency, nz * frequency) * amplitude;
amplitude *= self.persistence;
frequency *= self.lacunarity;
}
heightmap.set(x, z, height / max_amp);
}
}
}
}