rtiow: break project into multiple workspaces.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2019-11-09 11:56:33 -08:00
parent 2541b76ae6
commit d9d183b1e5
62 changed files with 941 additions and 957 deletions

View File

@@ -0,0 +1,68 @@
use log::trace;
/// Implements the concepts from https://lodev.org/cgtutor/randomnoise.html
use rand;
use crate::noise::NoiseSource;
use crate::vec3::Vec3;
const NOISE_SIZE: usize = 128;
pub struct Lode {
// Using fixed array causes stack overflow.
noise: Vec<Vec<Vec<f32>>>, //[[[f32; NOISE_SIZE]; NOISE_SIZE]; NOISE_SIZE],
}
impl Lode {
pub fn new<R>(rng: &mut R) -> Lode
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.
#[allow(clippy::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.);
}
}
}
Lode { noise }
}
}
impl NoiseSource for Lode {
fn value(&self, p: Vec3) -> f32 {
// 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
}
}

View File

@@ -0,0 +1,96 @@
pub mod lode;
pub mod perlin;
use std::f32::consts::PI;
use serde_derive::Deserialize;
use crate::vec3::Vec3;
pub trait NoiseSource: Send + Sync {
/// value returns noise on the interval [0., 1.).
fn value(&self, p: Vec3) -> f32;
fn marble(&self, p: Vec3, period: Vec3, power: f32, size: usize, scale: f32) -> f32 {
let p = p / scale;
// TODO(wathiede): can't understand why 255 works for perlin and lode, maybe it's near 360
// degrees and interacts with the sine function?
let xyz_value = p.x * period.x / 255.
+ p.y * period.y / 255.
+ p.z * period.z / 255.
+ power * self.turbulence(p, size);
(xyz_value * PI).sin().abs()
}
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) * factor as f32;
factor /= 2;
}
0.5 * value / initial_factor as f32
}
fn scaled(&self, p: Vec3, scale: f32) -> f32 {
let p = p / scale;
self.value(p)
}
}
impl NoiseSource for Box<dyn NoiseSource> {
fn value(&self, p: Vec3) -> f32 {
(**self).value(p)
}
}
#[derive(Copy, Clone, Debug, Deserialize)]
#[serde(tag = "type")]
pub enum NoiseType {
Scale(f32),
Turbulence(usize),
Marble {
period: Vec3,
power: f32,
size: usize,
scale: f32,
},
}
impl NoiseType {
pub fn to_url(&self) -> String {
match &self {
NoiseType::Scale(scale) => format!("scale/{}", scale),
NoiseType::Turbulence(turbulence) => format!("turbulence/{}", turbulence),
NoiseType::Marble {
period,
power,
size,
scale,
} => format!(
"marble/period/{},{},{}/power/{}/size/{}/scale/{}",
period.x, period.y, period.z, power, size, scale
),
}
}
pub fn parameters(&self) -> Vec<(&str, String)> {
match &self {
NoiseType::Scale(scale) => vec![("Scale", scale.to_string())],
NoiseType::Turbulence(turbulence) => vec![("Turbulence", turbulence.to_string())],
NoiseType::Marble {
period,
power,
size,
scale,
} => vec![
("Period", period.to_string()),
("Power", power.to_string()),
("Size", size.to_string()),
("Scale", scale.to_string()),
],
}
}
}

View File

@@ -0,0 +1,152 @@
// There are many math functions in this file, so we allow single letter variable names.
#![allow(clippy::many_single_char_names)]
use log::trace;
use rand::Rng;
use crate::noise::NoiseSource;
use crate::vec3::dot;
use crate::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(-1., 1.),
rng.gen_range(-1., 1.),
rng.gen_range(-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),
};
trace!(target: "perlin",
"ran_vec: {}",
p.ran_vec
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
);
trace!(target: "perlin",
"perm_x: {}",
p.perm_x
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
);
trace!(target: "perlin",
"perm_y: {}",
p.perm_y
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(", ")
);
trace!(target: "perlin",
"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.value(temp_p);
weight *= 0.5;
temp_p = temp_p * 0.2;
}
accum.abs()
}
}
impl NoiseSource for Perlin {
fn value(&self, p: Vec3) -> f32 {
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.
#[allow(clippy::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]]
}
}
}
(1. + perlin_interp(c, u, v, w)) * 0.5
}
}