diff options
Diffstat (limited to 'src/object.rs')
-rw-r--r-- | src/object.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/object.rs b/src/object.rs new file mode 100644 index 0000000..ff965f0 --- /dev/null +++ b/src/object.rs @@ -0,0 +1,109 @@ +use crate::calculus::calculus::{Point3, Ray, Vec3}; + +#[derive(Clone)] +pub struct HitRecord { + pub position: Point3, + pub normal: Vec3, + pub t: f32, + pub front_face: bool, +} + +impl Default for HitRecord { + fn default() -> Self { + Self { + position: Point3 {x:0.0, y:0.0, z:0.0}, + normal: Vec3 {x:0.0, y:0.0, z:0.0}, + t: 0.0, + front_face: false, + } + } +} + +impl HitRecord { + pub fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) { + self.front_face = r.direction.dot_prod(&outward_normal) < 0.0; + self.normal = if self.front_face { outward_normal } else { outward_normal.scalar_mul(-1.0) }; + } +} + +pub trait Hittable { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool; +} + +pub struct Sphere { + center: Point3, + radius: f32, +} + +impl Sphere { + pub fn new(center: Vec3, radius: f32) -> Self { + let r = f32::max(0.0, radius); + Self { center, radius } + } +} + +impl Hittable for Sphere { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool { + let oc = self.center.sub(&r.origin); + let a = r.direction.mag_sqr(); + let h = r.direction.dot_prod(&oc); + let c = oc.mag_sqr() - self.radius * self.radius; + let discriminant = h * h - a * c; + if discriminant < 0.0 { + return false; + } + + let sqrtd = discriminant.sqrt(); + + let mut root = (h - sqrtd) / a; + if root <= ray_tmin || ray_tmax <= root { + root = (h + sqrtd) / a; + if root <= ray_tmin || ray_tmax <= root { + return false; + } + } + + rec.t = root; + rec.position = r.at(rec.t); + let outward_normal = rec.normal + .sub(&self.center) + .scalar_mul(1.0 / self.radius); + rec.set_face_normal(r, outward_normal); + + true + } +} + +pub struct HittableList { + objects: Vec<Box<dyn Hittable>>, +} + +impl HittableList { + pub fn new() -> Self { + Self { objects: vec![] } + } + + pub fn push<T: Hittable + 'static>(&mut self, object: T) { + self.objects.push(Box::new(object)); + } +} + +impl Hittable for HittableList { + fn hit(&self, r: &Ray, ray_tmin: f32, ray_tmax: f32, rec: &mut HitRecord) -> bool { + let mut temp_rec = HitRecord::default(); + let mut hit_anything = false; + let mut closest_so_far = ray_tmax; + + self.objects.iter().for_each( + |object| { + if object.hit(r, ray_tmin, closest_so_far, &mut temp_rec) { + hit_anything = true; + closest_so_far = temp_rec.t; + *rec = temp_rec.clone(); + } + } + ); + + hit_anything + } +}
\ No newline at end of file |