1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! Traits and structure needed to cast rays.
use crate::math::{Isometry, Point, Real, Vector};
use crate::shape::FeatureId;
#[cfg(feature = "rkyv")]
use rkyv::{bytecheck, CheckBytes};
/// A Ray.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
archive(as = "Self")
)]
#[repr(C)]
pub struct Ray {
/// Starting point of the ray.
pub origin: Point<Real>,
/// Direction of the ray.
pub dir: Vector<Real>,
}
impl Ray {
/// Creates a new ray starting from `origin` and with the direction `dir`.
pub fn new(origin: Point<Real>, dir: Vector<Real>) -> Ray {
Ray { origin, dir }
}
/// Transforms this ray by the given isometry.
#[inline]
pub fn transform_by(&self, m: &Isometry<Real>) -> Self {
Self::new(m * self.origin, m * self.dir)
}
/// Transforms this ray by the inverse of the given isometry.
#[inline]
pub fn inverse_transform_by(&self, m: &Isometry<Real>) -> Self {
Self::new(
m.inverse_transform_point(&self.origin),
m.inverse_transform_vector(&self.dir),
)
}
/// Translates this ray by the given vector. Its direction is left unchanged.
#[inline]
pub fn translate_by(&self, v: Vector<Real>) -> Self {
Self::new(self.origin + v, self.dir)
}
/// Computes the point at the given parameter on this line.
///
/// This computes `self.origin + self.dir * t`.
#[inline]
pub fn point_at(&self, t: Real) -> Point<Real> {
self.origin + self.dir * t
}
}
/// Structure containing the result of a successful ray cast.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
archive(as = "Self")
)]
pub struct RayIntersection {
/// The time of impact of the ray with the object. The exact contact point can be computed
/// with: `ray.point_at(time_of_impact)` or equivalently `origin + dir * time_of_impact` where `origin` is the origin of the ray;
/// `dir` is its direction and `time_of_impact` is the value of this field.
pub time_of_impact: Real,
/// The normal at the intersection point.
///
/// If the origin of the ray is inside the shape and the shape is not solid,
/// the normal will point towards the interior of the shape.
/// Otherwise, the normal points outward.
///
/// If the `time_of_impact` is exactly zero, the normal might not be reliable.
// TODO: use a Unit<Vector> instead.
pub normal: Vector<Real>,
/// Feature at the intersection point.
pub feature: FeatureId,
}
impl RayIntersection {
#[inline]
/// Creates a new `RayIntersection`.
#[cfg(feature = "dim3")]
pub fn new(time_of_impact: Real, normal: Vector<Real>, feature: FeatureId) -> RayIntersection {
RayIntersection {
time_of_impact,
normal,
feature,
}
}
#[inline]
/// Creates a new `RayIntersection`.
#[cfg(feature = "dim2")]
pub fn new(time_of_impact: Real, normal: Vector<Real>, feature: FeatureId) -> RayIntersection {
RayIntersection {
time_of_impact,
normal,
feature,
}
}
#[inline]
pub fn transform_by(&self, transform: &Isometry<Real>) -> Self {
RayIntersection {
time_of_impact: self.time_of_impact,
normal: transform * self.normal,
feature: self.feature,
}
}
}
/// Traits of objects which can be transformed and tested for intersection with a ray.
pub trait RayCast {
/// Computes the time of impact between this transform shape and a ray.
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)
}
/// Computes the time of impact, and normal between this transformed shape and a ray.
fn cast_local_ray_and_get_normal(
&self,
ray: &Ray,
max_time_of_impact: Real,
solid: bool,
) -> Option<RayIntersection>;
/// Tests whether a ray intersects this transformed shape.
#[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()
}
/// Computes the time of impact between this transform shape and a ray.
fn cast_ray(
&self,
m: &Isometry<Real>,
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)
}
/// Computes the time of impact, and normal between this transformed shape and a ray.
fn cast_ray_and_get_normal(
&self,
m: &Isometry<Real>,
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))
}
/// Tests whether a ray intersects this transformed shape.
#[inline]
fn intersects_ray(&self, m: &Isometry<Real>, 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)
}
}