use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike}; use crate::core::medium::Medium; use crate::core::pbrt::Float; use crate::utils::math::{next_float_down, next_float_up}; use crate::utils::ptr::Ptr; #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct Ray { pub o: Point3f, pub d: Vector3f, pub time: Float, pub medium: Ptr, // We do this instead of creating a trait for Rayable or some gnarly thing like that pub has_differentials: bool, pub differential: RayDifferential, } impl Default for Ray { fn default() -> Self { Self { o: Point3f::new(0.0, 0.0, 0.0), d: Vector3f::new(0.0, 0.0, 0.0), medium: Ptr::null(), time: 0.0, has_differentials: false, differential: RayDifferential::default(), } } } impl Ray { pub fn new(o: Point3f, d: Vector3f, time: Option, medium: Ptr) -> Self { Self { o, d, time: time.unwrap_or_else(|| Self::default().time), medium, ..Self::default() } } pub fn at(&self, t: Float) -> Point3f { self.o + self.d * t } pub fn offset_origin(p: &Point3fi, n: &Normal3f, w: &Vector3f) -> Point3f { let d: Float = Vector3f::from(n.abs()).dot(p.error()); let normal: Vector3f = Vector3f::from(*n); let mut offset = p.midpoint(); if w.dot(normal) < 0.0 { offset -= normal * d; } else { offset += normal * d; } for i in 0..3 { if n[i] > 0.0 { offset[i] = next_float_up(offset[i]); } else if n[i] < 0.0 { offset[i] = next_float_down(offset[i]); } } offset } pub fn spawn(pi: &Point3fi, n: &Normal3f, time: Float, d: Vector3f) -> Ray { let origin = Self::offset_origin(pi, n, &d); Ray { o: origin, d, time, medium: Ptr::null(), has_differentials: false, differential: RayDifferential::default(), } } pub fn spawn_to_point(p_from: &Point3fi, n: &Normal3f, time: Float, p_to: Point3f) -> Ray { let d = p_to - p_from.midpoint(); Self::spawn(p_from, n, time, d) } pub fn spawn_to_interaction( p_from: &Point3fi, n_from: &Normal3f, time: Float, p_to: &Point3fi, n_to: &Normal3f, ) -> Ray { let dir_for_offset = p_to.midpoint() - p_from.midpoint(); let pf = Self::offset_origin(p_from, n_from, &dir_for_offset); let pt = Self::offset_origin(p_to, n_to, &(pf - p_to.midpoint())); let d = pt - pf; Ray { o: pf, d, time, medium: Ptr::null(), has_differentials: false, differential: RayDifferential::default(), } } pub fn scale_differentials(&mut self, s: Float) { if self.has_differentials { let differential = &mut self.differential; differential.rx_origin = self.o + (differential.rx_origin - self.o) * s; differential.ry_origin = self.o + (differential.ry_origin - self.o) * s; differential.rx_direction = self.d + (differential.rx_direction - self.d) * s; differential.ry_direction = self.d + (differential.ry_direction - self.d) * s; } } } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct RayDifferential { pub rx_origin: Point3f, pub ry_origin: Point3f, pub rx_direction: Vector3f, pub ry_direction: Vector3f, }