use rand::Rng; use crate::{Point3, Vec3}; pub struct Perlin { ranvec: Vec, perm_x: Vec, perm_y: Vec, perm_z: Vec, } impl Perlin { const POINT_COUNT: usize = 256; pub fn new() -> Self { let mut ranvec = Vec::with_capacity(Perlin::POINT_COUNT); (0..Perlin::POINT_COUNT).for_each(|_i|ranvec.push(Vec3::random(-1.0, 1.0))); Perlin { ranvec, perm_x: Perlin::perlin_generate_perm(), perm_y: Perlin::perlin_generate_perm(), perm_z: Perlin::perlin_generate_perm(), } } pub fn default_turbulence(&self, point: &Point3) -> f64 { self.turbulence(point, 7) } pub fn turbulence(&self, point: &Point3, depth: i32) -> f64 { let mut accum = 0.0; let mut temp_p = point.clone(); let mut weight = 1.0; for _i in 0..depth { accum += weight * self.noise(&temp_p); weight *= 0.5; temp_p *= 2.0; } accum.abs() } pub fn noise(&self, point: &Point3) -> f64 { let u = point.x() - point.x().floor(); let v = point.y() - point.y().floor(); let w = point.z() - point.z().floor(); let i = point.x().floor() as i32; let j = point.y().floor() as i32; let k = point.z().floor() as i32; let mut c: [[[Vec3; 2]; 2]; 2] = [[[Vec3::default(); 2]; 2]; 2]; for di in 0..2 { for dj in 0..2 { for dk in 0..2 { let idx = self.perm_x[((i+di) & 255) as usize] ^ self.perm_y[((j+dj) & 255) as usize] ^ self.perm_z[((k+dk) & 255) as usize]; c[di as usize][dj as usize][dk as usize] = self.ranvec[idx]; } } } Perlin::perlin_interp(c, u, v, w) } fn perlin_generate_perm() -> Vec { let mut p = Vec::with_capacity(Perlin::POINT_COUNT); (0..Perlin::POINT_COUNT).for_each(|i| p.push(i)); let mut rng = rand::thread_rng(); (1..Perlin::POINT_COUNT).rev().for_each(|idx| { let target = rng.gen_range(0..=idx); p.swap(idx, target); }); p } fn perlin_interp(c: [[[Vec3; 2]; 2]; 2], u: f64, v: f64, w: f64) -> f64 { let uu = u * u * (3.0 - 2.0 * u); let vv = v * v * (3.0 - 2.0 * v); let ww = w * w * (3.0 - 2.0 * w); let mut accum = 0.0; for i in 0..2 { for j in 0..2 { for k in 0..2 { let ifl = i as f64; let jfl = j as f64; let kfl = k as f64; let weight_v = Vec3::new(u - ifl, v - jfl, w - kfl); accum += (ifl*uu + (1.0-ifl)*(1.0-uu)) * (jfl*vv + (1.0-jfl)*(1.0-vv)) * (kfl*ww + (1.0-kfl)*(1.0-ww)) * (c[i][j][k]).dot(&weight_v); } } } accum } }