stocking

git clone https://git.bradleygannon.com/stocking.git
Log | Files | Refs

hittable.rs (2906B)


      1 use std::rc::Rc;
      2 
      3 use crate::{interval::Interval, material::Material, ray::Ray, vec3::Vec3};
      4 
      5 pub struct HitRecord {
      6     point: Vec3,
      7     normal: Vec3,
      8     t: f64,
      9     material: Rc<dyn Material>,
     10     front_face: bool,
     11 }
     12 
     13 impl HitRecord {
     14     fn new(t: f64, r: &Ray, outward_normal: &Vec3, material: Rc<dyn Material>) -> HitRecord {
     15         let point = r.at(t);
     16         let outward_normal_unit = outward_normal.unit();
     17         let front_face = r.direction().dot(&outward_normal_unit) < 0.;
     18         let normal = if front_face {
     19             outward_normal_unit
     20         } else {
     21             -outward_normal_unit
     22         };
     23         HitRecord {
     24             point,
     25             normal,
     26             t,
     27             material,
     28             front_face,
     29         }
     30     }
     31     pub fn normal(&self) -> Vec3 {
     32         self.normal
     33     }
     34     pub fn point(&self) -> Vec3 {
     35         self.point
     36     }
     37     pub fn front_face(&self) -> bool {
     38         self.front_face
     39     }
     40     pub fn material(&self) -> Rc<dyn Material> {
     41         self.material.clone()
     42     }
     43 }
     44 
     45 pub trait Hittable {
     46     fn hit(&self, r: &Ray, ray_t: Interval) -> Option<HitRecord>;
     47 }
     48 
     49 pub struct Sphere {
     50     center: Vec3,
     51     radius: f64,
     52     material: Rc<dyn Material>,
     53 }
     54 
     55 impl Sphere {
     56     pub const fn new(center: Vec3, radius: f64, material: Rc<dyn Material>) -> Self {
     57         Sphere {
     58             center,
     59             radius: radius.max(0.),
     60             material,
     61         }
     62     }
     63 }
     64 
     65 impl Hittable for Sphere {
     66     fn hit(&self, r: &Ray, ray_t: Interval) -> Option<HitRecord> {
     67         let oc = self.center - r.origin();
     68         let a = r.direction().length_squared();
     69         let h = r.direction().dot(&oc);
     70         let c = oc.length_squared() - self.radius.powi(2);
     71         let discriminant = h.powi(2) - a * c;
     72         if discriminant < 0. {
     73             return None;
     74         }
     75         let sqrt_d = discriminant.sqrt();
     76         let root = {
     77             let root_negative = (h - sqrt_d) / a;
     78             if !ray_t.surrounds(root_negative) {
     79                 let root_positive = (h + sqrt_d) / a;
     80                 if !ray_t.surrounds(root_positive) {
     81                     return None;
     82                 }
     83                 root_positive
     84             } else {
     85                 root_negative
     86             }
     87         };
     88         let outward_normal = (r.at(root) - self.center) / self.radius;
     89         Some(HitRecord::new(
     90             root,
     91             r,
     92             &outward_normal,
     93             self.material.clone(),
     94         ))
     95     }
     96 }
     97 
     98 impl Hittable for &[Box<dyn Hittable>] {
     99     fn hit(&self, r: &Ray, ray_t: Interval) -> Option<HitRecord> {
    100         let mut best_hit = None;
    101         let mut closest_so_far = ray_t.max();
    102         for object in self.iter() {
    103             if let Some(hit) = object.hit(r, Interval::new(ray_t.min(), closest_so_far)) {
    104                 closest_so_far = hit.t;
    105                 best_hit = Some(hit);
    106             }
    107         }
    108         best_hit
    109     }
    110 }