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}