203 lines
6.2 KiB
Rust
203 lines
6.2 KiB
Rust
use crate::{Point3, Ray, Vec3};
|
|
use crate::material::{Material};
|
|
use crate::aabb::Aabb;
|
|
use std::f64::consts::PI;
|
|
use std::sync::Arc;
|
|
|
|
pub type HittableObject = Arc<dyn Hittable + Sync + Send>;
|
|
pub type HittableList = Vec<HittableObject>;
|
|
|
|
//#[derive(Debug, Clone, Copy)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct HitRecord<'material> {
|
|
pub point: Point3,
|
|
pub normal: Vec3,
|
|
pub t: f64,
|
|
pub u: f64,
|
|
pub v: f64,
|
|
pub front_face: bool,
|
|
pub material: &'material Arc<Material>
|
|
}
|
|
|
|
impl<'material> HitRecord<'material> {
|
|
pub fn normalized(&mut self, ray: &Ray) {
|
|
let dot = ray.direction().dot(&self.normal);
|
|
let front_face = dot < 0.0;
|
|
let normal = if front_face { self.normal } else { -self.normal };
|
|
self.normal = normal;
|
|
self.front_face = front_face;
|
|
}
|
|
/*pub fn normalized(& self) -> Self {
|
|
let dot = ray.direction().dot(self.normal);
|
|
let front_face = dot < 0.0;
|
|
let normal = if front_face { self.normal } else { -self.normal };
|
|
HitRecord {
|
|
point: self.point,
|
|
normal,
|
|
t: self.t,
|
|
u: self.u,
|
|
v: self.v,
|
|
front_face,
|
|
material: self.material
|
|
}
|
|
}*/
|
|
}
|
|
|
|
pub trait Hittable {
|
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord>;
|
|
fn bounding_box(&self, time0: f64, time1: f64) -> Option<Aabb>;
|
|
}
|
|
|
|
//#[derive(Clone)]
|
|
pub struct Sphere {
|
|
pub center: Point3,
|
|
pub radius: f64,
|
|
pub material: Arc<Material>
|
|
}
|
|
impl Sphere {
|
|
pub fn new(center: Point3, radius: f64, material: Arc<Material>) -> Sphere {
|
|
Sphere { center, radius, material }
|
|
}
|
|
pub fn uv(point: &Point3) -> (f64, f64) {
|
|
// p: a given point on the sphere of radius one, centered at the origin.
|
|
// u: returned value [0,1] of angle around the Y axis from X=-1.
|
|
// v: returned value [0,1] of angle from Y=-1 to Y=+1.
|
|
// <1 0 0> yields <0.50 0.50> <-1 0 0> yields <0.00 0.50>
|
|
// <0 1 0> yields <0.50 1.00> < 0 -1 0> yields <0.50 0.00>
|
|
// <0 0 1> yields <0.25 0.50> < 0 0 -1> yields <0.75 0.50>
|
|
|
|
let theta = -(point.y()).acos();
|
|
let phi = -(point.z()).atan2(point.x()) + PI;
|
|
|
|
(phi / (2.0 * PI), theta / PI)
|
|
}
|
|
}
|
|
impl Default for Sphere {
|
|
fn default() -> Self {
|
|
Sphere {
|
|
center: Point3::default(),
|
|
radius: 0.0,
|
|
material: Arc::new(Material::default())
|
|
}
|
|
}
|
|
}
|
|
impl Hittable for Sphere {
|
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
|
let oc = &ray.origin() - &self.center;
|
|
let a = ray.direction().length_squared();
|
|
let half_b = oc.dot(&ray.direction());
|
|
let c = oc.length_squared() - &self.radius * &self.radius;
|
|
let discriminant = half_b * half_b - a * c;
|
|
if discriminant < 0.0 { return None; }
|
|
|
|
let sqrtd = discriminant.sqrt();
|
|
let mut root = (-half_b - sqrtd) / a;
|
|
if root < t_min || t_max < root {
|
|
root = (-half_b + sqrtd) / a;
|
|
if root < t_min || t_max < root { return None; }
|
|
}
|
|
let point = ray.at(root);
|
|
let normal = (point - self.center) / self.radius;
|
|
let (u, v) = Sphere::uv(&normal);
|
|
let mut rec = HitRecord {
|
|
point,
|
|
normal,
|
|
t: root,
|
|
u,
|
|
v,
|
|
front_face: false, // Will be set during normalied()
|
|
material: &self.material
|
|
};
|
|
rec.normalized(ray);
|
|
Some(rec)
|
|
}
|
|
fn bounding_box(&self, _time0: f64, _time1: f64) -> Option<Aabb> {
|
|
let s = Vec3::new(self.radius, self.radius, self.radius);
|
|
Some(Aabb {
|
|
minimum: self.center - s,
|
|
maximum: self.center + s,
|
|
})
|
|
}
|
|
}
|
|
|
|
//#[derive(Clone)]
|
|
pub struct MovableSphere {
|
|
pub center0: Point3,
|
|
pub center1: Point3,
|
|
pub radius: f64,
|
|
pub material: Arc<Material>,
|
|
pub time0: f64,
|
|
pub time1: f64,
|
|
}
|
|
impl MovableSphere {
|
|
pub fn new(
|
|
center0: Point3,
|
|
center1: Point3,
|
|
radius: f64,
|
|
material: Arc<Material>,
|
|
time0: f64,
|
|
time1: f64) -> Self {
|
|
MovableSphere { center0, center1, radius, material, time0, time1 }
|
|
}
|
|
pub fn center(&self, time: f64) -> Point3 {
|
|
self.center0 + ((time - self.time0) / (self.time1 - self.time0))*(self.center1 - self.center0)
|
|
}
|
|
}
|
|
impl Default for MovableSphere {
|
|
fn default() -> Self {
|
|
MovableSphere {
|
|
center0: Point3::default(),
|
|
center1: Point3::default(),
|
|
radius: 0.0,
|
|
material: Arc::new(Material::default()),
|
|
time0: 0.0,
|
|
time1: 0.0
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Hittable for MovableSphere {
|
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
|
let oc = &ray.origin() - &self.center(ray.time());
|
|
let a = ray.direction().length_squared();
|
|
let half_b = oc.dot(&ray.direction());
|
|
let c = oc.length_squared() - &self.radius * &self.radius;
|
|
let discriminant = half_b * half_b - a * c;
|
|
if discriminant < 0.0 { return None; }
|
|
|
|
let sqrtd = discriminant.sqrt();
|
|
let mut root = (-half_b - sqrtd) / a;
|
|
if root < t_min || t_max < root {
|
|
root = (-half_b + sqrtd) / a;
|
|
if root < t_min || t_max < root { return None; }
|
|
}
|
|
let point = ray.at(root);
|
|
let normal = (point - self.center(ray.time())) / self.radius;
|
|
let dot = ray.direction().dot(&normal);
|
|
let front_face = dot < 0.0;
|
|
let (u, v) = Sphere::uv(&normal);
|
|
let normal = if front_face { normal } else { -normal };
|
|
Some(HitRecord {
|
|
point,
|
|
normal,
|
|
t: root,
|
|
u,
|
|
v,
|
|
front_face,
|
|
material: &self.material,
|
|
})
|
|
}
|
|
fn bounding_box(&self, time0: f64, time1: f64) -> Option<Aabb> {
|
|
let s = Vec3::new(self.radius, self.radius, self.radius);
|
|
let center0 = self.center(time0);
|
|
let center1 = self.center(time1);
|
|
Some(Aabb {
|
|
minimum: center0 - s,
|
|
maximum: center0 + s,
|
|
}.surrounding(&Aabb {
|
|
minimum: center1 - s,
|
|
maximum: center1 + s
|
|
}))
|
|
}
|
|
}
|