use crate::core::bxdf::BSDF; use crate::core::material::MaterialTrait; use crate::core::medium::{Medium, MediumInterface}; use crate::core::pbrt::Float; use crate::geometry::{Normal3f, Point2f, Point3f, Point3fi, Ray, Vector3f, VectorLike}; use crate::lights::Light; use crate::shapes::ShapeTrait; use std::any::Any; use std::sync::Arc; #[derive(Default, Clone, Debug)] pub struct InteractionData { pub pi: Point3fi, pub n: Normal3f, pub time: Float, pub wo: Vector3f, pub medium_interface: Option>, pub medium: Option>, } pub trait Interaction: Send + Sync { fn get_common(&self) -> &InteractionData; fn get_common_mut(&mut self) -> &mut InteractionData; fn as_any(&self) -> &dyn Any; fn p(&self) -> Point3f { self.get_common().pi.into() } fn pi(&self) -> Point3fi { self.get_common().pi } fn time(&self) -> Float { self.get_common().time } fn wo(&self) -> Vector3f { self.get_common().wo } fn n(&self) -> Normal3f { self.get_common().n } fn is_surface_interaction(&self) -> bool { self.as_any().is::() } fn is_medium_interaction(&self) -> bool { self.as_any().is::() } fn get_medium(&self, w: Vector3f) -> Option>; fn spawn_ray(&self, d: Vector3f) -> Ray; fn spawn_ray_to_point(&self, p2: Point3f) -> Ray { let origin = self.p(); let direction = p2 - origin; Ray { o: origin, d: direction, time: self.time(), medium: self.get_medium(direction), differential: None, } } fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray { self.spawn_ray_to_point(other.p()) } fn offset_ray_vector(&self, w: Vector3f) -> Point3f { Ray::offset_origin(&self.pi(), &self.n(), &w) } fn offset_ray_point(&self, pt: Point3f) -> Point3f { self.offset_ray_vector(pt - self.p()) } } #[derive(Default, Clone, Debug)] pub struct ShadingGeometry { pub n: Normal3f, pub dpdu: Vector3f, pub dpdv: Vector3f, pub dndu: Normal3f, pub dndv: Normal3f, } #[derive(Default, Debug, Clone)] pub struct SurfaceInteraction { pub common: InteractionData, pub uv: Point2f, pub dpdu: Vector3f, pub dpdv: Vector3f, pub dndu: Normal3f, pub dndv: Normal3f, pub shading: ShadingGeometry, pub medium_interface: Option>, pub face_index: usize, pub area_light: Option>, pub material: Option>, pub dpdx: Vector3f, pub dpdy: Vector3f, pub dudx: Float, pub dvdx: Float, pub dudy: Float, pub dvdy: Float, pub shape: Option>, pub bsdf: Option, } impl SurfaceInteraction { pub fn set_intersection_properties( &mut self, mtl: Arc, area: Arc, prim_medium_interface: Option>, ray_medium: Arc, ) { self.material = Some(mtl); self.area_light = Some(area); if prim_medium_interface.as_ref().map_or(false, |mi| mi.is_medium_transition()) { self.common.medium_interface = prim_medium_interface; } else { self.common.medium = Some(ray_medium); } } } pub struct PhaseFunction; pub struct MediumInteraction { pub common: InteractionData, pub medium: Arc, pub phase: PhaseFunction, } impl Interaction for SurfaceInteraction { fn get_common(&self) -> &InteractionData { &self.common } fn get_common_mut(&mut self) -> &mut InteractionData { &mut self.common } fn as_any(&self) -> &dyn Any { self } fn get_medium(&self, w: Vector3f) -> Option> { self.medium_interface.as_ref().and_then(|interface| { if self.n().dot(w.into()) > 0.0 { interface.outside.clone() } else { interface.inside.clone() } }) } fn spawn_ray(&self, d: Vector3f) -> Ray { let mut ray = Ray::spawn(&self.pi(), &self.n(), self.time(), d); ray.medium = self.get_medium(d); ray } fn spawn_ray_to_point(&self, p2: Point3f) -> Ray { let mut ray = Ray::spawn_to_point(&self.pi(), &self.n(), self.time(), p2); ray.medium = self.get_medium(ray.d); ray } fn spawn_ray_to_interaction(&self, other: &dyn Interaction) -> Ray { // Check if the other interaction is a surface to use the robust spawn method if let Some(si_to) = other.as_any().downcast_ref::() { let mut ray = Ray::spawn_to_interaction( &self.pi(), &self.n(), self.time(), &si_to.pi(), &si_to.n(), ); ray.medium = self.get_medium(ray.d); ray } else { // Fallback for non-surface interactions self.spawn_ray_to_point(other.p()) } } } impl SurfaceInteraction { pub fn new( pi: Point3fi, uv: Point2f, wo: Vector3f, dpdu: Vector3f, dpdv: Vector3f, dndu: Normal3f, dndv: Normal3f, time: Float, flip: bool, ) -> Self { let mut n = Normal3f::from(dpdu.cross(dpdv).normalize()); let mut shading_n = n; if flip { n *= -1.0; shading_n *= -1.0; } Self { common: InteractionData { pi, n, time, wo, medium_interface: None, medium: None }, uv, dpdu, dpdv, dndu, dndv, shading: ShadingGeometry { n: shading_n, dpdu, dpdv, dndu, dndv, }, medium_interface: None, material: None, face_index: 0, area_light: None, dpdx: Vector3f::zero(), dpdy: Vector3f::zero(), dudx: 0.0, dudy: 0.0, dvdx: 0.0, dvdy: 0.0, shape: None, bsdf: None, } } pub fn new_with_face( pi: Point3fi, uv: Point2f, wo: Vector3f, dpdu: Vector3f, dpdv: Vector3f, dndu: Normal3f, dndv: Normal3f, time: Float, flip: bool, face_index: usize, ) -> Self { let mut si = Self::new(pi, uv, wo, dpdu, dpdv, dndu, dndv, time, flip); si.face_index = face_index; si } pub fn set_shading_geometry( &mut self, ns: Normal3f, dpdus: Vector3f, dpdvs: Vector3f, dndus: Normal3f, dndvs: Normal3f, orientation: bool, ) { self.shading.n = ns; if orientation { self.common.n = self.n().face_forward(self.shading.n.into()); } self.shading.dpdu = dpdus; self.shading.dpdv = dpdvs; self.shading.dndu = dndus.into(); self.shading.dndv = dndvs.into(); } pub fn new_simple(pi: Point3fi, n: Normal3f, uv: Point2f) -> Self { let mut si = Self::default(); si.common = InteractionData { pi, n, time: 0., wo: Vector3f::zero(), medium_interface: None, medium: None, }; si.uv = uv; si } } impl Interaction for MediumInteraction { fn get_common(&self) -> &InteractionData { &self.common } fn get_common_mut(&mut self) -> &mut InteractionData { &mut self.common } fn as_any(&self) -> &dyn Any { self } fn get_medium(&self, _w: Vector3f) -> Option> { Some(self.medium.clone()) } fn spawn_ray(&self, d: Vector3f) -> Ray { Ray { o: self.p(), d, time: self.time(), medium: Some(self.medium.clone()), differential: None, } } }