Skip to main content

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, SubShapeId, 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<(SubShapeId, 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    ///
43    /// Returns the index of the sub-shape of `self` that was hit alongside the hit, which is
44    /// left as that sub-shape reported it (its `subshape` is the sub-shape's own, when it is a
45    /// composite too).
46    #[inline]
47    pub fn cast_local_ray_and_get_normal(
48        &self,
49        ray: &Ray,
50        max_time_of_impact: Real,
51        solid: bool,
52    ) -> Option<(SubShapeId, RayIntersection)> {
53        self.0.bvh().find_best(
54            max_time_of_impact,
55            |node: &BvhNode, best_so_far| node.cast_ray(ray, best_so_far),
56            |primitive, best_so_far| {
57                self.0.map_typed_part_at(primitive, |pose, part, _| {
58                    if let Some(pose) = pose {
59                        part.cast_ray_and_get_normal(pose, ray, best_so_far, solid)
60                    } else {
61                        part.cast_local_ray_and_get_normal(ray, best_so_far, solid)
62                    }
63                })?
64            },
65        )
66    }
67}
68
69impl RayCast for Polyline {
70    #[inline]
71    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
72        CompositeShapeRef(self)
73            .cast_local_ray(ray, max_time_of_impact, solid)
74            .map(|hit| hit.1)
75    }
76
77    #[inline]
78    fn cast_local_ray_and_get_normal(
79        &self,
80        ray: &Ray,
81        max_time_of_impact: Real,
82        solid: bool,
83    ) -> Option<RayIntersection> {
84        CompositeShapeRef(self)
85            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
86            .map(|(segment_id, hit)| hit.with_subshape(segment_id))
87    }
88}
89
90impl RayCast for Compound {
91    #[inline]
92    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
93        CompositeShapeRef(self)
94            .cast_local_ray(ray, max_time_of_impact, solid)
95            .map(|hit| hit.1)
96    }
97
98    #[inline]
99    fn cast_local_ray_and_get_normal(
100        &self,
101        ray: &Ray,
102        max_time_of_impact: Real,
103        solid: bool,
104    ) -> Option<RayIntersection> {
105        CompositeShapeRef(self)
106            .cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
107            .map(|(part_id, hit)| hit.with_subshape(part_id))
108    }
109}