parry3d/query/ray/
ray_composite_shape.rs

1use crate::math::Real;
2use crate::partitioning::BvhNode;
3use crate::query::{Ray, RayCast, RayIntersection};
4use crate::shape::{CompositeShapeRef, Compound, Polyline, TypedCompositeShape};
5
6impl<S: TypedCompositeShape> CompositeShapeRef<'_, S> {
7    /// Casts a ray on this composite shape.
8    ///
9    /// The ray is effectively limited to a segment that starts at `Ray::origin` and ends at
10    /// `Ray::origin + Ray::direction * max_time_of_impact`. Set `max_time_of_impact` to `Real::MAX`
11    /// for an unbounded ray.
12    ///
13    /// If `solid` is `false`, then the sub-shapes of `self` are seen as hollow and the ray won’t
14    /// immediately stop until it reaches a boundary even if it started inside a shape.
15    ///
16    /// Returns the ray’s time of impact and the index of the sub-shape of `self` that was hit.
17    /// The hit point can be retrieved with `ray.point_at(t)` where `t` is the value returned
18    /// by this function.
19    #[inline]
20    pub fn cast_local_ray(
21        &self,
22        ray: &Ray,
23        max_time_of_impact: Real,
24        solid: bool,
25    ) -> Option<(u32, Real)> {
26        let hit = self
27            .0
28            .bvh()
29            .cast_ray(ray, max_time_of_impact, |primitive, best_so_far| {
30                self.0.map_typed_part_at(primitive, |pose, part, _| {
31                    if let Some(pose) = pose {
32                        part.cast_ray(pose, ray, best_so_far, solid)
33                    } else {
34                        part.cast_local_ray(ray, best_so_far, solid)
35                    }
36                })?
37            })?;
38        (hit.1 < max_time_of_impact).then_some(hit)
39    }
40
41    /// Same as [`Self::cast_local_ray`] but also computes the normal at the hit location.
42    #[inline]
43    pub fn cast_local_ray_and_get_normal(
44        &self,
45        ray: &Ray,
46        max_time_of_impact: Real,
47        solid: bool,
48    ) -> Option<(u32, RayIntersection)> {
49        self.0.bvh().find_best(
50            max_time_of_impact,
51            |node: &BvhNode, best_so_far| node.cast_ray(ray, best_so_far),
52            |primitive, best_so_far| {
53                self.0.map_typed_part_at(primitive, |pose, part, _| {
54                    if let Some(pose) = pose {
55                        part.cast_ray_and_get_normal(pose, ray, best_so_far, solid)
56                    } else {
57                        part.cast_local_ray_and_get_normal(ray, best_so_far, solid)
58                    }
59                })?
60            },
61        )
62    }
63}
64
65impl RayCast for Polyline {
66    #[inline]
67    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
68        CompositeShapeRef(self)
69            .cast_local_ray(ray, max_time_of_impact, solid)
70            .map(|hit| hit.1)
71    }
72
73    #[inline]
74    fn cast_local_ray_and_get_normal(
75        &self,
76        ray: &Ray,
77        max_time_of_impact: Real,
78        solid: bool,
79    ) -> Option<RayIntersection> {
80        CompositeShapeRef(self)
81            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
82            .map(|hit| hit.1)
83    }
84}
85
86impl RayCast for Compound {
87    #[inline]
88    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
89        CompositeShapeRef(self)
90            .cast_local_ray(ray, max_time_of_impact, solid)
91            .map(|hit| hit.1)
92    }
93
94    #[inline]
95    fn cast_local_ray_and_get_normal(
96        &self,
97        ray: &Ray,
98        max_time_of_impact: Real,
99        solid: bool,
100    ) -> Option<RayIntersection> {
101        CompositeShapeRef(self)
102            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
103            .map(|hit| hit.1)
104    }
105}