use crate::calculus::calculus::{Point3, Ray, Vec3}; use crate::common::Color; use crate::interval::Interval; use crate::material::MaterialType; #[derive(Clone)] pub struct HitRecord { pub position: Point3, pub normal: Vec3, pub t: f32, pub front_face: bool, pub material: MaterialType, pub color: Color, pub fuzz: f32, } 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, material: MaterialType::Diffuse, color:Color::new(0.0, 0.0, 0.0), fuzz: 1.0, } } } 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_t: Interval, rec: &mut HitRecord) -> bool; } pub struct Sphere { center: Point3, radius: f32, material: MaterialType, color: Color, fuzz: f32, } impl Sphere { pub fn new(center: Vec3, radius: f32, material: MaterialType, color: Color, fuzz: f32) -> Self { let radius = f32::max(0.0, radius); Self { center, radius, material, color, fuzz } } } impl Hittable for Sphere { fn hit(&self, r: &Ray, ray_t: Interval, 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_t.min || ray_t.max <= root { root = (h + sqrtd) / a; if root <= ray_t.min || ray_t.max <= root { return false; } } rec.t = root; rec.position = r.at(rec.t); let outward_normal = rec.position .sub(&self.center) .scalar_mul(1.0 / self.radius); rec.set_face_normal(r, outward_normal); rec.material = self.material.clone(); rec.color = self.color.clone(); rec.fuzz = self.fuzz; true } } pub struct HittableList { objects: Vec>, } impl HittableList { pub fn new() -> Self { Self { objects: vec![] } } pub fn push(&mut self, object: T) { self.objects.push(Box::new(object)); } } impl Hittable for HittableList { fn hit(&self, r: &Ray, ray_t: Interval, rec: &mut HitRecord) -> bool { let mut temp_rec = HitRecord::default(); let mut hit_anything = false; let mut closest_so_far = ray_t.max; self.objects.iter().for_each( |object| { let interval = Interval::new(ray_t.min, closest_so_far); if object.hit(r, interval, &mut temp_rec) { hit_anything = true; closest_so_far = temp_rec.t; *rec = temp_rec.clone(); } } ); hit_anything } }