/// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html use std::f32::consts::PI; use rand; use vec3::Vec3; const NOISE_SIZE: usize = 128; pub struct Noise { // Using fixed array causes stack overflow. noise: Vec>>, //[[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE], } impl Noise { pub fn new(rng: &mut R) -> Noise where R: rand::Rng, { let mut noise = vec![vec![vec![0.; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE]; // Squelch warning about only being used to index, as this way reads more consistently. #[cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))] for x in 0..NOISE_SIZE { for y in 0..NOISE_SIZE { for z in 0..NOISE_SIZE { noise[x][y][z] = rng.gen_range::(0., 1.); } } } Noise { noise } } pub fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize) -> f32 { let xyz_value = p.x * period.x / NOISE_SIZE as f32 + p.y * period.y / NOISE_SIZE as f32 + p.z * period.z / NOISE_SIZE as f32 + power * self.turbulence(p, size); //info!("p {} xyz_value {}", p, xyz_value); (xyz_value * PI).sin().abs() } pub fn turbulence(&self, p: Vec3, factor: usize) -> f32 { let mut value = 0.; let initial_factor = factor; let mut factor = factor; while factor >= 1 { value += self.value(p / factor as f32, 1.) * factor as f32; factor /= 2; } 0.5 * value / initial_factor as f32 } pub fn value(&self, p: Vec3, scale: f32) -> f32 { let p = p / scale; // Fractional part of vector components let x = p.x - p.x.floor(); let y = p.y - p.y.floor(); let z = p.z - p.z.floor(); // Wrap around the arrays let x1 = (p.x.floor() as usize + NOISE_SIZE) % NOISE_SIZE; let y1 = (p.y.floor() as usize + NOISE_SIZE) % NOISE_SIZE; let z1 = (p.z.floor() as usize + NOISE_SIZE) % NOISE_SIZE; // Neighbor values let x2 = (x1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; let y2 = (y1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; let z2 = (z1 as usize + NOISE_SIZE - 1) % NOISE_SIZE; trace!(target: "noise", "p {}", p); trace!(target: "noise", "x {:.2} y {:.2} z {:.2}", x, y, z); trace!(target: "noise", "x1 {:.2} y1 {:.2} z1 {:.2}", x1, y1, z1); trace!(target: "noise", "x2 {:.2} y2 {:.2} z2 {:.2}", x2, y2, z2); let mut value = 0.; // Smooth the noise with bilinear interpolation. Completely untested value += x * y * z * self.noise[x1][y1][z1]; value += (1. - x) * y * z * self.noise[x2][y1][z1]; value += x * (1. - y) * z * self.noise[x1][y2][z1]; value += x * y * (1. - z) * self.noise[x1][y1][z2]; value += (1. - x) * (1. - y) * z * self.noise[x2][y2][z1]; value += x * (1. - y) * (1. - z) * self.noise[x1][y2][z2]; value += (1. - x) * y * (1. - z) * self.noise[x2][y1][z2]; value += (1. - x) * (1. - y) * (1. - z) * self.noise[x2][y2][z2]; trace!(target: "noise", "luma {}", value); value } }