use std::eprintln; use crate::vec3::{Vec3, Point3}; const POINT_COUNT: usize = 256; // Mix-in images are necessarily monochrome, so each data entry is a pixel. pub struct MixInImage { width: usize, height: usize, data: Vec, } impl MixInImage { pub fn from_bmp(bmp_data: &Vec) -> Self { let data_position = u32::from_le_bytes([ bmp_data[0x0A], bmp_data[0x0B], bmp_data[0x0C], bmp_data[0x0D], ]); // assuming windows BITMAPINFOHEADER, these are i32 let width = i32::from_le_bytes([ bmp_data[0x12], bmp_data[0x13], bmp_data[0x14], bmp_data[0x15], ]) as usize; let height = i32::from_le_bytes([ bmp_data[0x16], bmp_data[0x17], bmp_data[0x18], bmp_data[0x19], ]) as usize; // assuming 3 channels, all of which are equal let data = bmp_data[(data_position as usize)..bmp_data.len()].iter().skip(2).step_by(3).map(|x| *x as f64 / 255.0).collect(); Self { width, height, data, } } pub fn get(&self, point: &Point3) -> &f64 { let disc_x = ((point.x * self.width as f64) % self.width as f64) as usize; let disc_y = ((point.y * self.height as f64) % self.height as f64) as usize; self.data.get((disc_y * self.width) + disc_x).unwrap() } } pub struct Perlin { ranvec: Vec, perm_x: Vec, perm_y: Vec, perm_z: Vec, } impl Perlin { pub fn new() -> Self { let mut ranvec = Vec::with_capacity(POINT_COUNT); for _ in 0..POINT_COUNT { ranvec.push(Vec3::random_in_range(-1.0, 1.0).unit_vector()); } Self { ranvec, perm_x: Self::generate_perm(), perm_y: Self::generate_perm(), perm_z: Self::generate_perm(), } } pub fn turb(&self, point: &Point3, depth: u32) -> f64 { let mut accum = 0.0; let mut temp_point = point.clone(); let mut weight = 1.0; for _ in 0..depth { accum += weight * self.noise(&temp_point); weight *= 0.5; temp_point *= 2.0; } accum.abs() } pub fn turb_dsw(&self, point: &Point3, depth: u32, factor: f64) -> f64 { let mut accum = 0.0; let mut temp_point = point.clone(); let mut weight = 1.0; for _ in 0..depth { accum += weight * self.noise(&temp_point); temp_point *= 2.0; temp_point += temp_point.xy_diff(&|p: &Vec3| -> f64 {weight * self.noise(p)}) * factor; weight *= 0.5; } accum.abs() } pub fn turb_dsw_mixin(&self, point: &Point3, depth: u32, factor: f64, mixin: &MixInImage, mixin_factor: f64, mixin_strength: f64) -> f64 { let mut accum = 0.0; let mut temp_point = point.clone(); let mut mixin_point = point.clone(); let mut weight = 1.0; for _ in 0..depth { accum += weight * (1.0 - mixin_strength) * self.noise(&temp_point); temp_point *= 2.0; let diff = temp_point.xy_diff(&|p: &Vec3| -> f64 {weight * self.noise(p)}); temp_point += &diff * factor; mixin_point += &diff * mixin_factor; accum += weight * mixin_strength * mixin.get(&mixin_point); weight *= 0.5; } 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] = Default::default(); for di in 0..2 { for dj in 0..2 { for dk in 0..2 { c[di][dj][dk] = self .ranvec .get( (self.perm_x.get(((i + di as i32) & 255) as usize).unwrap() ^ self.perm_y.get(((j + dj as i32) & 255) as usize).unwrap() ^ self.perm_z.get(((k + dk as i32) & 255) as usize).unwrap()) as usize, ) .unwrap().clone(); } } } Self::trilinear_interpolate(c, u, v, w) } fn trilinear_interpolate(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: f64 = 0.0; for i in 0..2 { for j in 0..2 { for k in 0..2 { let i_f = i as f64; let j_f = j as f64; let k_f = k as f64; let weight_v = Vec3 { x: u - i_f, y: v - j_f, z: w - k_f }; accum += (i_f * uu + (1.0 - i_f) * (1.0 - uu)) * (j_f * vv + (1.0 - j_f) * (1.0 - vv)) * (k_f * ww + (1.0 - k_f) * (1.0 - ww)) * c[i][j][k].dot(&weight_v); } } } accum } fn generate_perm() -> Vec { let mut p = (0..POINT_COUNT).collect(); Self::permute(&mut p, POINT_COUNT); p } fn permute(p: &mut Vec, n: usize) { for i in (1..n).rev() { p.swap(i, rand::random::() % i); } } }