Skip to main content

bevy_rapier2d/geometry/
mod.rs

1pub use self::collider::*;
2pub use self::shape_views::ColliderView;
3pub use rapier::geometry::SolverFlags;
4pub use rapier::geometry::{MeshConverter, MeshConverterError};
5pub use rapier::parry::query::{ShapeCastOptions, ShapeCastStatus, Unsupported};
6#[cfg(feature = "dim3")]
7pub use rapier::parry::shape::HeightFieldFlags;
8pub use rapier::parry::shape::{CompoundFlags, PolylineFlags, TriMeshFlags};
9pub use rapier::parry::transformation::{vhacd::VHACDParameters, voxelization::FillMode};
10
11use crate::math::{Real, Rot, Vect};
12use rapier::prelude::FeatureId;
13
14mod collider;
15mod collider_impl;
16/// Wrappers around Rapier shapes to access their properties.
17pub mod shape_views;
18#[cfg(feature = "to-bevy-mesh")]
19pub mod to_bevy_mesh;
20
21/// Result of the projection of a point on a shape.
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct PointProjection {
24    /// Whether or not the point to project was inside of the shape.
25    pub is_inside: bool,
26    /// The projection result.
27    pub point: Vect,
28    /// The index of the part of the shape the point was projected on (for compound shapes,
29    /// triangle meshes, polylines, voxels, etc.), or `0` for a shape with no sub-shape.
30    pub subshape: u32,
31}
32
33impl PointProjection {
34    pub(crate) fn from_rapier(raw: rapier::parry::query::PointProjection) -> Self {
35        Self {
36            is_inside: raw.is_inside,
37            point: raw.point,
38            subshape: raw.subshape,
39        }
40    }
41}
42impl From<rapier::parry::query::PointProjection> for PointProjection {
43    fn from(projection: rapier::parry::query::PointProjection) -> PointProjection {
44        PointProjection::from_rapier(projection)
45    }
46}
47
48/// Structure containing the result of a successful ray cast.
49#[derive(Copy, Clone, Debug, PartialEq)]
50pub struct RayIntersection {
51    /// The time of impact of the ray with the object.  The exact contact point can be computed
52    /// with `origin + dir * time_of_impact` where `origin` is the origin of the ray;
53    /// `dir` is its direction and `time_of_impact` is the value of this field.
54    pub time_of_impact: Real,
55
56    /// The intersection point between the ray and the object.
57    pub point: Vect,
58
59    /// The normal at the intersection point.
60    ///
61    /// If the `toi` is exactly zero, the normal might not be reliable.
62    pub normal: Vect,
63
64    /// Feature at the intersection point.
65    pub feature: FeatureId,
66
67    /// The index of the part of the shape hit by the ray (for compound shapes, triangle meshes,
68    /// polylines, voxels, etc.), or `0` for a shape with no sub-shape.
69    pub subshape: u32,
70}
71
72impl RayIntersection {
73    pub(crate) fn from_rapier(
74        inter: rapier::parry::query::RayIntersection,
75        unscaled_origin: Vect,
76        unscaled_dir: Vect,
77    ) -> Self {
78        Self {
79            time_of_impact: inter.time_of_impact,
80            point: unscaled_origin + unscaled_dir * inter.time_of_impact,
81            normal: inter.normal,
82            feature: inter.feature,
83            subshape: inter.subshape,
84        }
85    }
86}
87
88/// The result of a shape cast.
89#[derive(Copy, Clone, Debug, PartialEq)]
90pub struct ShapeCastHit {
91    /// The time at which the objects touch.
92    pub time_of_impact: Real,
93    /// Detail about the impact points.
94    ///
95    /// `None` if `status` is `PenetratingOrWithinTargetDist` and
96    /// [`ShapeCastOptions::compute_impact_geometry_on_penetration`] was `false`.
97    pub details: Option<ShapeCastHitDetails>,
98    /// The way the time-of-impact computation algorithm terminated.
99    pub status: ShapeCastStatus,
100    /// The index of the part of the first shape that was hit (for compound shapes, triangle
101    /// meshes, polylines, voxels, etc.), or `0` for a shape with no sub-shape.
102    ///
103    /// For scene queries and character collisions, the first shape is the collider being hit.
104    pub subshape1: u32,
105    /// The index of the part of the second shape that was hit (for compound shapes, triangle
106    /// meshes, polylines, voxels, etc.), or `0` for a shape with no sub-shape.
107    ///
108    /// For scene queries and character collisions, the second shape is the shape being cast.
109    pub subshape2: u32,
110}
111
112/// In depth information about a shape-cast hit.
113///
114/// The frame of each field depends on the query:
115/// - For scene queries (e.g. [`RapierContext::cast_shape`],
116///   [`RapierContext::cast_shape_nonlinear`]) and [`CharacterCollision::hit`], the first shape is
117///   the collider hit: `witness1` and `normal1` are in world-space. The second shape is the cast
118///   shape (or the character): `witness2` and `normal2` are in its local-space, at its pose at the
119///   time of impact.
120/// - For [`Collider::cast_shape`] and [`Collider::cast_shape_nonlinear`], the first shape is `self`
121///   and the second one `other`: every field is in the local-space of its shape.
122///
123/// [`RapierContext::cast_shape`]: crate::plugin::context::systemparams::RapierContext::cast_shape
124/// [`RapierContext::cast_shape_nonlinear`]: crate::plugin::context::systemparams::RapierContext::cast_shape_nonlinear
125/// [`CharacterCollision::hit`]: crate::control::CharacterCollision::hit
126#[derive(Copy, Clone, Debug, PartialEq)]
127pub struct ShapeCastHitDetails {
128    /// The closest point on the first shape at the time of impact (world-space for scene
129    /// queries, local-space otherwise).
130    pub witness1: Vect,
131    /// The closest point on the second shape at the time of impact, in its local-space.
132    pub witness2: Vect,
133    /// The outward normal on the first shape at the time of impact (world-space for scene
134    /// queries, local-space otherwise).
135    pub normal1: Vect,
136    /// The outward normal on the second shape at the time of impact, in its local-space.
137    pub normal2: Vect,
138}
139
140impl ShapeCastHit {
141    /// Convert from internal `rapier::query::ShapeCastHit`.
142    pub fn from_rapier(
143        hit: rapier::parry::query::ShapeCastHit,
144        details_always_computed: bool,
145    ) -> Self {
146        let details = match (details_always_computed, hit.status) {
147            (_, ShapeCastStatus::Failed) => None,
148            (false, ShapeCastStatus::PenetratingOrWithinTargetDist) => None,
149            _ => Some(ShapeCastHitDetails {
150                witness1: hit.witness1,
151                witness2: hit.witness2,
152                normal1: hit.normal1,
153                normal2: hit.normal2,
154            }),
155        };
156        Self {
157            time_of_impact: hit.time_of_impact,
158            status: hit.status,
159            details,
160            subshape1: hit.subshape1,
161            subshape2: hit.subshape2,
162        }
163    }
164}
165
166/// The result of a contact query between two colliders.
167#[derive(Copy, Clone, Debug, PartialEq)]
168pub struct ShapeContact {
169    /// The contact point on the first collider, in world-space.
170    pub point1: Vect,
171    /// The contact point on the second collider, in world-space.
172    pub point2: Vect,
173    /// The contact normal, pointing outward from the first collider, in world-space.
174    pub normal1: Vect,
175    /// The contact normal, pointing outward from the second collider, in world-space.
176    ///
177    /// This is always equal to `-normal1`.
178    pub normal2: Vect,
179    /// The signed distance between the two contact points.
180    ///
181    /// It is negative if the colliders are penetrating.
182    pub distance: Real,
183    /// The index of the part of the first collider involved in the contact (for compound
184    /// shapes, triangle meshes, polylines, etc.), or `0` for a shape with no sub-shape.
185    pub subshape1: u32,
186    /// The index of the part of the second collider involved in the contact (for compound
187    /// shapes, triangle meshes, polylines, etc.), or `0` for a shape with no sub-shape.
188    pub subshape2: u32,
189}
190
191impl ShapeContact {
192    pub(crate) fn from_rapier(contact: rapier::parry::query::Contact) -> Self {
193        Self {
194            point1: contact.point1,
195            point2: contact.point2,
196            normal1: contact.normal1,
197            normal2: contact.normal2,
198            distance: contact.dist,
199            subshape1: contact.subshape1,
200            subshape2: contact.subshape2,
201        }
202    }
203}
204
205/// The result of a closest-points query between two colliders.
206#[derive(Copy, Clone, Debug, PartialEq)]
207pub enum ShapeClosestPoints {
208    /// The two colliders are intersecting.
209    Intersecting,
210    /// The two colliders are disjoint but closer than the queried maximum distance.
211    ///
212    /// Contains the closest point on the first collider, then the closest point on the second
213    /// collider, both in world-space.
214    WithinMargin(Vect, Vect),
215    /// The two colliders are further apart than the queried maximum distance.
216    Disjoint,
217}
218
219impl ShapeClosestPoints {
220    pub(crate) fn from_rapier(points: rapier::parry::query::ClosestPoints) -> Self {
221        match points {
222            rapier::parry::query::ClosestPoints::Intersecting => Self::Intersecting,
223            rapier::parry::query::ClosestPoints::WithinMargin(p1, p2) => Self::WithinMargin(p1, p2),
224            rapier::parry::query::ClosestPoints::Disjoint => Self::Disjoint,
225        }
226    }
227}
228
229/// A rigid motion combining a constant linear and a constant angular velocity, used for
230/// nonlinear shape casting with [`Collider::cast_shape_nonlinear`].
231///
232/// At time `t`, the collider is rotated by `angular_velocity * t` around the point
233/// `local_center` (expressed in the collider's local-space), and translated by
234/// `linear_velocity * t`, starting from the pose given by `translation` and `rotation`.
235#[derive(Copy, Clone, Debug, PartialEq)]
236pub struct NonlinearMotion {
237    /// The translation of the collider at time `0.0`.
238    pub translation: Vect,
239    /// The rotation of the collider at time `0.0`.
240    pub rotation: Rot,
241    /// The local-space point around which the collider rotates.
242    pub local_center: Vect,
243    /// The linear velocity of the collider.
244    pub linear_velocity: Vect,
245    /// The angular velocity of the collider.
246    #[cfg(feature = "dim2")]
247    pub angular_velocity: Real,
248    /// The angular velocity of the collider.
249    #[cfg(feature = "dim3")]
250    pub angular_velocity: Vect,
251}
252
253impl NonlinearMotion {
254    /// A motion that keeps the collider at the given position.
255    pub fn constant_position(translation: Vect, rotation: Rot) -> Self {
256        Self {
257            translation,
258            rotation,
259            local_center: Vect::ZERO,
260            linear_velocity: Vect::ZERO,
261            #[cfg(feature = "dim2")]
262            angular_velocity: 0.0,
263            #[cfg(feature = "dim3")]
264            angular_velocity: Vect::ZERO,
265        }
266    }
267
268    /// Converts this motion to Rapier's `NonlinearRigidMotion`.
269    pub fn into_rapier(self) -> rapier::parry::query::NonlinearRigidMotion {
270        rapier::parry::query::NonlinearRigidMotion::new(
271            crate::utils::pose_from(self.translation, self.rotation),
272            self.local_center,
273            self.linear_velocity,
274            self.angular_velocity,
275        )
276    }
277}