use std::f32::consts::PI; use crate::{ aabb::AABB, hitable::{Hit, HitRecord}, material::Material, ray::Ray, vec3::{dot, Vec3}, }; #[derive(Debug)] pub struct Sphere where M: Material, { center: Vec3, radius: f32, material: M, } pub fn get_sphere_uv(p: Vec3) -> (f32, f32) { let phi = p.z.atan2(p.x); let theta = p.y.asin(); let u = 1. - (phi + PI) / (2. * PI); let v = (theta + PI / 2.) / PI; (u, v) } impl Sphere where M: Material, { pub fn new(center: V, radius: f32, material: M) -> Sphere where V: Into, { Sphere { center: center.into(), radius, material, } } } impl Hit for Sphere where M: Material, { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { let oc = r.origin - self.center; let a = dot(r.direction, r.direction); let b = dot(oc, r.direction); let c = dot(oc, oc) - self.radius * self.radius; let discriminant = b * b - a * c; if discriminant > 0. { let temp = (-b - (b * b - a * c).sqrt()) / a; if temp < t_max && temp > t_min { let point = r.point_at_parameter(temp); let uv = get_sphere_uv((point - self.center) / self.radius); return Some(HitRecord { t: temp, uv, p: point, normal: (point - self.center) / self.radius, material: &self.material, }); } let temp = (-b + (b * b - a * c).sqrt()) / a; if temp < t_max && temp > t_min { let point = r.point_at_parameter(temp); let uv = get_sphere_uv((point - self.center) / self.radius); return Some(HitRecord { t: temp, uv, p: point, normal: (point - self.center) / self.radius, material: &self.material, }); } } None } fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option { Some(AABB::new( self.center - Vec3::new(self.radius, self.radius, self.radius), self.center + Vec3::new(self.radius, self.radius, self.radius), )) } }