use crate::math::{Pose, Real, Vector};
use crate::shape::FeatureId;
#[cfg(feature = "alloc")]
use crate::partitioning::BvhLeafCost;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
)]
#[repr(C)]
pub struct Ray {
pub origin: Vector,
pub dir: Vector,
}
impl Ray {
pub fn new(origin: Vector, dir: Vector) -> Ray {
Ray { origin, dir }
}
#[inline]
pub fn transform_by(&self, m: &Pose) -> Self {
Self::new(m * self.origin, m.rotation * self.dir)
}
#[inline]
pub fn inverse_transform_by(&self, m: &Pose) -> Self {
Self::new(
m.inverse_transform_point(self.origin),
m.rotation.inverse() * self.dir,
)
}
#[inline]
pub fn translate_by(&self, v: Vector) -> Self {
Self::new(self.origin + v, self.dir)
}
#[inline]
pub fn point_at(&self, t: Real) -> Vector {
self.origin + self.dir * t
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
)]
pub struct RayIntersection {
pub time_of_impact: Real,
pub normal: Vector,
pub feature: FeatureId,
}
impl RayIntersection {
#[inline]
#[cfg(feature = "dim3")]
pub fn new(time_of_impact: Real, normal: Vector, feature: FeatureId) -> RayIntersection {
RayIntersection {
time_of_impact,
normal,
feature,
}
}
#[inline]
#[cfg(feature = "dim2")]
pub fn new(time_of_impact: Real, normal: Vector, feature: FeatureId) -> RayIntersection {
RayIntersection {
time_of_impact,
normal,
feature,
}
}
#[inline]
pub fn transform_by(&self, transform: &Pose) -> Self {
RayIntersection {
time_of_impact: self.time_of_impact,
normal: transform.rotation * self.normal,
feature: self.feature,
}
}
}
#[cfg(feature = "alloc")]
impl BvhLeafCost for RayIntersection {
#[inline]
fn cost(&self) -> Real {
self.time_of_impact
}
}
pub trait RayCast {
fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
self.cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
.map(|inter| inter.time_of_impact)
}
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_time_of_impact: Real,
solid: bool,
) -> Option<RayIntersection>;
#[inline]
fn intersects_local_ray(&self, ray: &Ray, max_time_of_impact: Real) -> bool {
self.cast_local_ray(ray, max_time_of_impact, true).is_some()
}
fn cast_ray(&self, m: &Pose, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
let ls_ray = ray.inverse_transform_by(m);
self.cast_local_ray(&ls_ray, max_time_of_impact, solid)
}
fn cast_ray_and_get_normal(
&self,
m: &Pose,
ray: &Ray,
max_time_of_impact: Real,
solid: bool,
) -> Option<RayIntersection> {
let ls_ray = ray.inverse_transform_by(m);
self.cast_local_ray_and_get_normal(&ls_ray, max_time_of_impact, solid)
.map(|inter| inter.transform_by(m))
}
#[inline]
fn intersects_ray(&self, m: &Pose, ray: &Ray, max_time_of_impact: Real) -> bool {
let ls_ray = ray.inverse_transform_by(m);
self.intersects_local_ray(&ls_ray, max_time_of_impact)
}
}