diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs index bf0a8f47310d8..a755465dc1470 100644 --- a/crates/bevy_math/src/ray.rs +++ b/crates/bevy_math/src/ray.rs @@ -6,6 +6,59 @@ use crate::Vec3; pub struct Ray { /// The origin of the ray. pub origin: Vec3, - /// The direction of the ray. + /// A normalized vector representing the direction of the ray. pub direction: Vec3, } + +impl Ray { + /// Returns the distance to the plane if the ray intersects it. + #[inline] + pub fn intersect_plane(&self, plane_origin: Vec3, plane_normal: Vec3) -> Option { + let denominator = plane_normal.dot(self.direction); + if denominator.abs() > f32::EPSILON { + let distance = (plane_origin - self.origin).dot(plane_normal) / denominator; + if distance >= f32::EPSILON { + return Some(distance); + } + } + None + } + + /// Retrieve a point at the given distance along the ray. + #[inline] + pub fn get_point(&self, distance: f32) -> Vec3 { + self.origin + self.direction * distance + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn intersects_plane() { + let ray = Ray { + origin: Vec3::ZERO, + direction: Vec3::Z, + }; + + // Orthogonal, and test that plane_normal direction doesn't matter + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::Z)); + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::NEG_Z)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::Z)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::NEG_Z)); + + // Diagonal + assert_eq!(Some(1.), ray.intersect_plane(Vec3::Z, Vec3::ONE)); + assert_eq!(None, ray.intersect_plane(Vec3::NEG_Z, Vec3::ONE)); + + // Parralel + assert_eq!(None, ray.intersect_plane(Vec3::X, Vec3::X)); + + // Parralel with simulated rounding error + assert_eq!( + None, + ray.intersect_plane(Vec3::X, Vec3::X + Vec3::Z * f32::EPSILON) + ); + } +}