vox_engine 0.3.3

voxel game engine
use super::helpers;
use std::array;

pub struct Perlin {
    perm: [u8; 512],
    seed: u64,
}

impl Perlin {
    pub fn new(seed: u64) -> Self {
        let mut perm_init: [u8; 256] = array::from_fn(|i| i as u8);
        let mut state = seed;

        for i in (0..=255).rev() {
            state = helpers::splitmix64(&mut state);
            let j = (state % (i + 1) as u64) as usize;
            perm_init.swap(i, j);
        }

        let mut perm = [0u8; 512];

        perm[..256].copy_from_slice(&perm_init);
        perm[256..].copy_from_slice(&perm_init);

        Perlin { perm, seed }
    }

    pub fn sample(&self, x: f64, y: f64) -> f64 {
        let xi = (x.floor() as i32 & 255) as usize;
        let yi = (y.floor() as i32 & 255) as usize;

        let xf = x - x.floor();
        let yf = y - y.floor();

        let u = Self::fade(xf);
        let v = Self::fade(yf);

        let aa = self.perm[self.perm[xi] as usize + yi];
        let ab = self.perm[self.perm[xi] as usize + yi + 1];
        let ba = self.perm[self.perm[xi + 1] as usize + yi];
        let bb = self.perm[self.perm[xi + 1] as usize + yi + 1];

        let x1 = Self::lerp(Self::grad(aa, xf, yf), Self::grad(ba, xf - 1.0, yf), u);

        let x2 = Self::lerp(
            Self::grad(ab, xf, yf - 1.0),
            Self::grad(bb, xf - 1.0, yf - 1.0),
            u,
        );

        Self::lerp(x1, x2, v)
    }

    fn fade(t: f64) -> f64 {
        t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
    }

    fn lerp(a: f64, b: f64, t: f64) -> f64 {
        a + t * (b - a)
    }

    fn grad(hash: u8, x: f64, y: f64) -> f64 {
        match hash & 3 {
            0 => x + y,
            1 => -x + y,
            2 => x - y,
            3 => -x - y,
            _ => unreachable!(),
        }
    }
}