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 }