use crate::core::geometry::{ Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f, Vector3fi, VectorLike, ray, }; use crate::core::interaction::{ Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction, }; use crate::core::{MaterialIdx, LightIdx}; use crate::core::light::Light; use crate::core::material::Material; use crate::core::medium::{Medium, MediumInterface}; use crate::shapes::*; use crate::utils::math::{next_float_down, next_float_up}; use crate::utils::{Ptr, Transform}; use crate::{Float, PI}; use enum_dispatch::enum_dispatch; // Define Intersection objects. This only varies for #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct ShapeIntersection { pub intr: SurfaceInteraction, pub t_hit: Float, } impl ShapeIntersection { pub fn new(intr: SurfaceInteraction, t_hit: Float) -> Self { Self { intr, t_hit } } pub fn t_hit(&self) -> Float { self.t_hit } pub fn set_t_hit(&mut self, new_t: Float) { self.t_hit = new_t; } pub fn set_intersection_properties( &mut self, mtl: MaterialIdx, area: LightIdx, prim_medium_interface: MediumInterface, ray_medium: Ptr, ) { self.intr .set_intersection_properties(mtl, area, ray_medium, prim_medium_interface); } } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct QuadricIntersection { pub t_hit: Float, pub p_obj: Point3f, pub phi: Float, } impl QuadricIntersection { pub fn new(t_hit: Float, p_obj: Point3f, phi: Float) -> Self { Self { t_hit, p_obj, phi } } } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct ShapeSample { pub intr: Interaction, pub pdf: Float, } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct ShapeSampleContext { pub pi: Point3fi, pub n: Normal3f, pub ns: Normal3f, pub time: Float, } impl ShapeSampleContext { pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f, time: Float) -> Self { Self { pi, n, ns, time } } pub fn new_from_interaction(si: &SurfaceInteraction) -> Self { Self { pi: si.pi(), n: si.n(), ns: si.shading.n, time: si.time(), } } pub fn p(&self) -> Point3f { Point3f::from(self.pi) } pub fn offset_ray_origin(&self, w: Vector3f) -> Point3f { let d = self.n.abs().dot(self.pi.error().into()); let mut offset = d * Vector3f::from(self.n); if w.dot(self.n.into()) < 0.0 { offset = -offset; } let mut po = Point3f::from(self.pi) + offset; for i in 0..3 { if offset[i] > 0.0 { po[i] = next_float_up(po[i]); } else { po[i] = next_float_down(po[i]); } } po } pub fn offset_ray_origin_from_point(&self, pt: Point3f) -> Point3f { self.offset_ray_origin(pt - self.p()) } pub fn spawn_ray(&self, w: Vector3f) -> Ray { Ray::new(self.offset_ray_origin(w), w, Some(self.time), Ptr::null()) } } #[enum_dispatch] pub trait ShapeTrait { fn bounds(&self) -> Bounds3f; fn normal_bounds(&self) -> DirectionCone; fn area(&self) -> Float; fn sample(&self, u: Point2f) -> Option; fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option; fn intersect(&self, ray: &Ray, t_max: Option) -> Option; fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool; fn pdf(&self, interaction: &Interaction) -> Float; fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float; } #[repr(C)] #[derive(Debug, Clone, Copy)] #[enum_dispatch(ShapeTrait)] pub enum Shape { Sphere(SphereShape), Cylinder(CylinderShape), Disk(DiskShape), Triangle(TriangleShape), BilinearPatch(BilinearPatchShape), Curve(CurveShape), } impl Default for Shape { fn default() -> Self { Shape::Sphere(SphereShape::default()) } }