use crate::image::prelude::Colour;
use crate::linalg;
use crate::trace::prelude::Vec3;
use rand::RngCore;
use std::fmt::Debug;
pub trait Material: Debug + Send + Sync {
fn colour(&self) -> Colour;
fn bounce(
&self,
rng: &mut Box<dyn RngCore>,
incident_direction: Vec3,
surface_normal: Vec3,
) -> Option<Vec3>;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Lambertian {
pub colour: Colour,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Metal {
pub colour: Colour,
pub fuzz: f64,
}
impl Lambertian {
pub const fn new(colour: Colour) -> Self {
Self { colour }
}
}
impl Material for Lambertian {
fn colour(&self) -> Colour {
self.colour
}
fn bounce(
&self,
rng: &mut Box<dyn RngCore>,
incident_direction: Vec3,
surface_normal: Vec3,
) -> Option<Vec3> {
loop {
let bounce_direction = linalg::rand::random_unit3(rng) + surface_normal;
if bounce_direction.mag() < 1.0e-9 {
continue;
}
break Some(bounce_direction.normalised());
}
}
}
impl Metal {
pub const fn new(colour: Colour, fuzz: f64) -> Self {
Self { colour, fuzz }
}
}
impl Material for Metal {
fn colour(&self) -> Colour {
self.colour
}
fn bounce(
&self,
rng: &mut Box<dyn RngCore>,
incident_direction: Vec3,
surface_normal: Vec3,
) -> Option<Vec3> {
let b_height = incident_direction.dot(surface_normal);
let pure_bounce = incident_direction - surface_normal * 2.0 * b_height;
let fuzz_displacement = linalg::rand::random_unit3(rng) * self.fuzz;
let bounce = pure_bounce + fuzz_displacement;
if bounce.dot(surface_normal) < 0.0 {
None
} else {
Some(bounce)
}
}
}