151 lines
4.0 KiB
Rust
151 lines
4.0 KiB
Rust
// There are many math functions in this file, so we allow single letter variable names.
|
|
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
|
|
use rand::Rng;
|
|
|
|
use vec3::dot;
|
|
use vec3::Vec3;
|
|
|
|
pub struct Perlin {
|
|
ran_vec: Vec<Vec3>,
|
|
perm_x: Vec<usize>,
|
|
perm_y: Vec<usize>,
|
|
perm_z: Vec<usize>,
|
|
}
|
|
|
|
fn perlin_generate<R>(rng: &mut R) -> Vec<Vec3>
|
|
where
|
|
R: Rng,
|
|
{
|
|
(0..256)
|
|
.map(|_| {
|
|
Vec3::new(
|
|
rng.gen_range::<f32>(-1., 1.),
|
|
rng.gen_range::<f32>(-1., 1.),
|
|
rng.gen_range::<f32>(-1., 1.),
|
|
).unit_vector()
|
|
}).collect()
|
|
}
|
|
|
|
fn perlin_generate_perm<R>(rng: &mut R) -> Vec<usize>
|
|
where
|
|
R: Rng,
|
|
{
|
|
let mut p: Vec<usize> = (0..256).map(|i| i).collect();
|
|
rng.shuffle(&mut p);
|
|
p
|
|
}
|
|
|
|
fn perlin_interp(c: [[[Vec3; 2]; 2]; 2], u: f32, v: f32, w: f32) -> f32 {
|
|
// Hermite cubic to round off interpolation and attempt to address 'mach bands'.
|
|
let uu = u * u * (3. - 2. * u);
|
|
let vv = v * v * (3. - 2. * v);
|
|
let ww = w * w * (3. - 2. * w);
|
|
|
|
let mut accum = 0.;
|
|
for i in 0..2 {
|
|
for j in 0..2 {
|
|
for k in 0..2 {
|
|
let weight_v = Vec3::new(u - i as f32, v - j as f32, w - k as f32);
|
|
|
|
let i = i as f32;
|
|
let j = j as f32;
|
|
let k = k as f32;
|
|
accum += (i * uu + (1. - i) * (1. - uu))
|
|
* (j * vv + (1. - j) * (1. - vv))
|
|
* (k * ww + (1. - k) * (1. - ww))
|
|
* dot(c[i as usize][j as usize][k as usize], weight_v);
|
|
}
|
|
}
|
|
}
|
|
//info!("u {} v {} accum {}", u, v, accum);
|
|
accum
|
|
}
|
|
|
|
impl Perlin {
|
|
pub fn new<R>(rng: &mut R) -> Perlin
|
|
where
|
|
R: Rng,
|
|
{
|
|
let p = Perlin {
|
|
ran_vec: perlin_generate(rng),
|
|
perm_x: perlin_generate_perm(rng),
|
|
perm_y: perlin_generate_perm(rng),
|
|
perm_z: perlin_generate_perm(rng),
|
|
};
|
|
info!(
|
|
"ran_vec: {}",
|
|
p.ran_vec
|
|
.iter()
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<String>>()
|
|
.join(", ")
|
|
);
|
|
info!(
|
|
"perm_x: {}",
|
|
p.perm_x
|
|
.iter()
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<String>>()
|
|
.join(", ")
|
|
);
|
|
info!(
|
|
"perm_y: {}",
|
|
p.perm_y
|
|
.iter()
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<String>>()
|
|
.join(", ")
|
|
);
|
|
info!(
|
|
"perm_z: {}",
|
|
p.perm_z
|
|
.iter()
|
|
.map(|v| v.to_string())
|
|
.collect::<Vec<String>>()
|
|
.join(", ")
|
|
);
|
|
p
|
|
}
|
|
|
|
pub fn turb(&self, p: Vec3, depth: usize) -> f32 {
|
|
let mut accum = 0.;
|
|
let mut temp_p = p;
|
|
let mut weight = 1.;
|
|
for _ in 0..depth {
|
|
accum += weight * self.noise(temp_p);
|
|
weight *= 0.5;
|
|
temp_p = temp_p * 0.2;
|
|
}
|
|
accum.abs()
|
|
}
|
|
|
|
pub fn noise<V>(&self, p: V) -> f32
|
|
where
|
|
V: Into<Vec3>,
|
|
{
|
|
let p = p.into();
|
|
let u = p.x - p.x.floor();
|
|
let v = p.y - p.y.floor();
|
|
let w = p.z - p.z.floor();
|
|
|
|
let i = p.x.floor() as usize;
|
|
let j = p.y.floor() as usize;
|
|
let k = p.z.floor() as usize;
|
|
|
|
let mut c: [[[Vec3; 2]; 2]; 2] = Default::default();
|
|
|
|
// Squelch warning about di only being used to index, as this way reads more consistently.
|
|
#[cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))]
|
|
for di in 0..2 {
|
|
for dj in 0..2 {
|
|
for dk in 0..2 {
|
|
c[di][dj][dk] = self.ran_vec[self.perm_x[(i + di) & 255]
|
|
^ self.perm_y[(j + dj) & 255]
|
|
^ self.perm_z[(k + dk) & 255]]
|
|
}
|
|
}
|
|
}
|
|
perlin_interp(c, u, v, w)
|
|
}
|
|
}
|