use core::f32::math::sqrt; use std::fmt::{self, Debug}; use std::sync::Arc; use crate::objects::hit::Hit; use crate::objects::materials::traits::Material; use crate::objects::traits::Hittable; use crate::ray::Ray; use crate::Vec3; pub struct Sphere { center: Vec3, radius: f32, material: Arc, } impl Debug for Sphere { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Sphere") .field("center", &self.center) .field("radius", &self.radius) .field("material", &self.material) .finish() } } impl Sphere { pub fn new(center: Vec3, r: f32, mat: Arc) -> Self { Self { center, radius: r, material: mat, } } pub fn xyz(x: f32, y: f32, z: f32, r: f32, mat: Arc) -> Self { Self { center: Vec3::new(x, y, z), radius: r, material: mat, } } } impl Hittable for Sphere { fn hit(&self, r: &Ray) -> Option { let oc = self.center - r.origin(); let a = r.dir().length_squared(); let h = r.dir().dot(&oc); let c = oc.length_squared() - self.radius * self.radius; let d = h * h - a * c; if d < 0. { None } else { let tl = (h - sqrt(d)) / a; let tr = (h + sqrt(d)) / a; let t = if tl > 0.001 { tl } else { tr }; let p = r.at(t); let out_n = (p - self.center) / self.radius; Some(Hit::new( t, p, self.normal_at(&p), self.material.clone(), out_n.dot(r.dir()) < 0., )) } } fn normal_at(&self, p: &Vec3) -> Vec3 { (*p - self.center).get_unit() } }