bevy_rapier2d/dynamics/
rigid_body.rs

1use crate::math::Vect;
2use bevy::prelude::*;
3use rapier::prelude::{
4    Isometry, LockedAxes as RapierLockedAxes, RigidBodyActivation, RigidBodyHandle, RigidBodyType,
5};
6use std::ops::{Add, AddAssign, Sub, SubAssign};
7
8#[cfg(doc)]
9use rapier::dynamics::IntegrationParameters;
10
11/// The Rapier handle of a [`RigidBody`] that was inserted to the physics scene.
12#[derive(Copy, Clone, Debug, Component)]
13pub struct RapierRigidBodyHandle(pub RigidBodyHandle);
14
15/// A [`RigidBody`].
16///
17/// Related components:
18/// - [`GlobalTransform`]: used as the ground truth for the bodies position.
19/// - [`Velocity`]
20/// - [`ExternalImpulse`]
21/// - [`ExternalForce`]
22/// - [`AdditionalMassProperties`]
23/// - [`ReadMassProperties`]
24/// - [`Damping`]
25/// - [`Dominance`]
26/// - [`Ccd`]: Helps prevent tunneling through thin objects or rigid bodies
27///   moving at high velocities.
28/// - [`LockedAxes`]
29/// - [`RigidBodyDisabled`]
30/// - [`GravityScale`]
31#[derive(Copy, Clone, Debug, PartialEq, Eq, Component, Reflect, Default)]
32#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
33#[reflect(Component, Default, PartialEq)]
34pub enum RigidBody {
35    /// A `RigidBody::Dynamic` body can be affected by all external forces.
36    #[default]
37    Dynamic,
38    /// A `RigidBody::Fixed` body cannot be affected by external forces.
39    Fixed,
40    /// A `RigidBody::KinematicPositionBased` body cannot be affected by any external forces but can be controlled
41    /// by the user at the position level while keeping realistic one-way interaction with dynamic bodies.
42    ///
43    /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
44    /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
45    /// modified by the user and is independent from any contact or joint it is involved in.
46    KinematicPositionBased,
47    /// A `RigidBody::KinematicVelocityBased` body cannot be affected by any external forces but can be controlled
48    /// by the user at the velocity level while keeping realistic one-way interaction with dynamic bodies.
49    ///
50    /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
51    /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
52    /// modified by the user and is independent from any contact or joint it is involved in.
53    KinematicVelocityBased,
54}
55
56impl From<RigidBody> for RigidBodyType {
57    fn from(rigid_body: RigidBody) -> RigidBodyType {
58        match rigid_body {
59            RigidBody::Dynamic => RigidBodyType::Dynamic,
60            RigidBody::Fixed => RigidBodyType::Fixed,
61            RigidBody::KinematicPositionBased => RigidBodyType::KinematicPositionBased,
62            RigidBody::KinematicVelocityBased => RigidBodyType::KinematicVelocityBased,
63        }
64    }
65}
66
67impl From<RigidBodyType> for RigidBody {
68    fn from(rigid_body: RigidBodyType) -> RigidBody {
69        match rigid_body {
70            RigidBodyType::Dynamic => RigidBody::Dynamic,
71            RigidBodyType::Fixed => RigidBody::Fixed,
72            RigidBodyType::KinematicPositionBased => RigidBody::KinematicPositionBased,
73            RigidBodyType::KinematicVelocityBased => RigidBody::KinematicVelocityBased,
74        }
75    }
76}
77
78/// The velocity of a [`RigidBody`].
79///
80/// Use this component to control and/or read the velocity of a dynamic or kinematic [`RigidBody`].
81/// If this component isn’t present, a dynamic [`RigidBody`] will still be able to move (you will just
82/// not be able to read/modify its velocity).
83///
84/// This only affects entities with a [`RigidBody`] component.
85#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
86#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
87#[reflect(Component, Default, PartialEq)]
88pub struct Velocity {
89    /// The linear velocity of the [`RigidBody`].
90    pub linvel: Vect,
91    /// The angular velocity of the [`RigidBody`] in radian per second.
92    #[cfg(feature = "dim2")]
93    pub angvel: f32,
94    /// The angular velocity of the [`RigidBody`].
95    #[cfg(feature = "dim3")]
96    pub angvel: Vect,
97}
98
99impl Velocity {
100    /// Initialize a velocity set to zero.
101    pub const fn zero() -> Self {
102        Self {
103            linvel: Vect::ZERO,
104            #[cfg(feature = "dim2")]
105            angvel: 0.0,
106            #[cfg(feature = "dim3")]
107            angvel: Vect::ZERO,
108        }
109    }
110
111    /// Initialize a velocity with the given linear velocity, and an angular velocity of zero.
112    pub const fn linear(linvel: Vect) -> Self {
113        Self {
114            linvel,
115            #[cfg(feature = "dim2")]
116            angvel: 0.0,
117            #[cfg(feature = "dim3")]
118            angvel: Vect::ZERO,
119        }
120    }
121
122    /// Initialize a velocity with the given angular velocity, and a linear velocity of zero.
123    #[cfg(feature = "dim2")]
124    pub const fn angular(angvel: f32) -> Self {
125        Self {
126            linvel: Vect::ZERO,
127            angvel,
128        }
129    }
130
131    /// Initialize a velocity with the given angular velocity, and a linear velocity of zero.
132    #[cfg(feature = "dim3")]
133    pub const fn angular(angvel: Vect) -> Self {
134        Self {
135            linvel: Vect::ZERO,
136            angvel,
137        }
138    }
139
140    /// Get linear velocity of specific world-space point of a [`RigidBody`].
141    ///
142    /// # Parameters
143    /// - `point`: the point (world-space) to compute the velocity for.
144    /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the velocity belongs to.
145    pub fn linear_velocity_at_point(&self, point: Vect, center_of_mass: Vect) -> Vect {
146        #[cfg(feature = "dim2")]
147        return self.linvel + self.angvel * (point - center_of_mass).perp();
148
149        #[cfg(feature = "dim3")]
150        return self.linvel + self.angvel.cross(point - center_of_mass);
151    }
152}
153
154/// Mass-properties of a [`RigidBody`], added to the contributions of its attached colliders.
155///
156/// This only affects entities with a [`RigidBody`] component.
157#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
158#[reflect(Component, Default, PartialEq)]
159pub enum AdditionalMassProperties {
160    /// This mass will be added to the [`RigidBody`]. The rigid-body’s total
161    /// angular inertia tensor (obtained from its attached colliders) will
162    /// be scaled accordingly.
163    Mass(f32),
164    /// These mass properties will be added to the [`RigidBody`].
165    MassProperties(MassProperties),
166}
167
168impl Default for AdditionalMassProperties {
169    fn default() -> Self {
170        Self::MassProperties(MassProperties::default())
171    }
172}
173
174/// Center-of-mass, mass, and angular inertia.
175///
176/// When this is used as a component, this lets you read the total mass properties of
177/// a [`RigidBody`] (including the colliders contribution). Modifying this component won’t
178/// affect the mass-properties of the [`RigidBody`] (the attached colliders’ `ColliderMassProperties`
179/// and the `AdditionalMassProperties` should be modified instead).
180///
181/// This only reads the mass from entities with a [`RigidBody`] component.
182#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
183#[reflect(Component, Default, PartialEq)]
184pub struct ReadMassProperties(MassProperties);
185
186impl ReadMassProperties {
187    /// Get the [`MassProperties`] of this [`RigidBody`].
188    pub fn get(&self) -> &MassProperties {
189        &self.0
190    }
191
192    pub(crate) fn set(&mut self, mass_props: MassProperties) {
193        self.0 = mass_props;
194    }
195}
196
197impl std::ops::Deref for ReadMassProperties {
198    type Target = MassProperties;
199    fn deref(&self) -> &Self::Target {
200        self.get()
201    }
202}
203
204/// Entity that likely had their mass properties changed this frame.
205#[derive(Deref, Copy, Clone, Debug, PartialEq, Message)]
206pub struct MassModifiedMessage(pub Entity);
207
208impl From<Entity> for MassModifiedMessage {
209    fn from(entity: Entity) -> Self {
210        Self(entity)
211    }
212}
213
214#[deprecated(
215    since = "0.32.0",
216    note = "MassModifiedMessage has been renamed to MassModifiedEvent for consistency with Bevy 0.17 naming conventions. "
217)]
218pub use MassModifiedMessage as MassModifiedEvent;
219
220/// Center-of-mass, mass, and angular inertia.
221///
222/// This cannot be used as a component. Use the components `ReadMassProperties` to read a [`RigidBody`]’s
223/// mass-properties or `AdditionalMassProperties` to set its additional mass-properties.
224#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
225#[reflect(Default, PartialEq)]
226pub struct MassProperties {
227    /// The center of mass of a [`RigidBody`] expressed in its local-space.
228    pub local_center_of_mass: Vect,
229    /// The mass of a [`RigidBody`].
230    pub mass: f32,
231    /// The principal angular inertia of the [`RigidBody`].
232    #[cfg(feature = "dim2")]
233    pub principal_inertia: f32,
234    /// The principal vectors of the local angular inertia tensor of the [`RigidBody`].
235    #[cfg(feature = "dim3")]
236    pub principal_inertia_local_frame: crate::math::Rot,
237    /// The principal angular inertia of the [`RigidBody`].
238    #[cfg(feature = "dim3")]
239    pub principal_inertia: Vect,
240}
241
242impl MassProperties {
243    /// Converts these mass-properties to Rapier’s `MassProperties` structure.
244    #[cfg(feature = "dim2")]
245    pub fn into_rapier(self) -> rapier::dynamics::MassProperties {
246        rapier::dynamics::MassProperties::new(
247            self.local_center_of_mass.into(),
248            self.mass,
249            #[allow(clippy::useless_conversion)] // Need to convert if dim3 enabled
250            self.principal_inertia.into(),
251        )
252    }
253
254    /// Converts these mass-properties to Rapier’s `MassProperties` structure.
255    #[cfg(feature = "dim3")]
256    pub fn into_rapier(self) -> rapier::dynamics::MassProperties {
257        rapier::dynamics::MassProperties::with_principal_inertia_frame(
258            self.local_center_of_mass.into(),
259            self.mass,
260            self.principal_inertia.into(),
261            self.principal_inertia_local_frame.into(),
262        )
263    }
264
265    /// Converts Rapier’s `MassProperties` structure to `Self`.
266    pub fn from_rapier(mprops: rapier::dynamics::MassProperties) -> Self {
267        #[allow(clippy::useless_conversion)] // Need to convert if dim3 enabled
268        Self {
269            mass: mprops.mass(),
270            local_center_of_mass: mprops.local_com.into(),
271            principal_inertia: mprops.principal_inertia().into(),
272            #[cfg(feature = "dim3")]
273            principal_inertia_local_frame: mprops.principal_inertia_local_frame.into(),
274        }
275    }
276}
277
278#[derive(Default, Debug, Component, Reflect, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
279#[reflect(Component, Default, PartialEq)]
280/// Flags affecting the behavior of the constraints solver for a given contact manifold.
281pub struct LockedAxes(u8);
282
283bitflags::bitflags! {
284    impl LockedAxes: u8 {
285        /// Flag indicating that the [`RigidBody`] cannot translate along the `X` axis.
286        const TRANSLATION_LOCKED_X = 1 << 0;
287        /// Flag indicating that the [`RigidBody`] cannot translate along the `Y` axis.
288        const TRANSLATION_LOCKED_Y = 1 << 1;
289        /// Flag indicating that the [`RigidBody`] cannot translate along the `Z` axis.
290        const TRANSLATION_LOCKED_Z = 1 << 2;
291        /// Flag indicating that the [`RigidBody`] cannot translate along any direction.
292        const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
293        /// Flag indicating that the [`RigidBody`] cannot rotate along the `X` axis.
294        const ROTATION_LOCKED_X = 1 << 3;
295        /// Flag indicating that the [`RigidBody`] cannot rotate along the `Y` axis.
296        const ROTATION_LOCKED_Y = 1 << 4;
297        /// Flag indicating that the [`RigidBody`] cannot rotate along the `Z` axis.
298        const ROTATION_LOCKED_Z = 1 << 5;
299        /// Combination of flags indicating that the [`RigidBody`] cannot rotate along any axis.
300        const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
301    }
302}
303
304impl From<LockedAxes> for RapierLockedAxes {
305    fn from(locked_axes: LockedAxes) -> RapierLockedAxes {
306        RapierLockedAxes::from_bits(locked_axes.bits()).expect("Internal conversion error.")
307    }
308}
309
310/// Constant external forces applied continuously to a [`RigidBody`].
311///
312/// This force is applied at each timestep.
313#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
314#[reflect(Component, Default, PartialEq)]
315pub struct ExternalForce {
316    /// The linear force applied to the [`RigidBody`].
317    pub force: Vect,
318    /// The angular torque applied to the [`RigidBody`].
319    #[cfg(feature = "dim2")]
320    pub torque: f32,
321    /// The angular torque applied to the [`RigidBody`].
322    #[cfg(feature = "dim3")]
323    pub torque: Vect,
324}
325
326impl ExternalForce {
327    /// A force applied at a specific world-space point of a [`RigidBody`].
328    ///
329    /// # Parameters
330    /// - `force`: the force to apply.
331    /// - `point`: the point (world-space) where the impulse must be applied.
332    /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the impulse is being
333    ///   applied to.
334    pub fn at_point(force: Vect, point: Vect, center_of_mass: Vect) -> Self {
335        Self {
336            force,
337            #[cfg(feature = "dim2")]
338            torque: (point - center_of_mass).perp_dot(force),
339            #[cfg(feature = "dim3")]
340            torque: (point - center_of_mass).cross(force),
341        }
342    }
343}
344
345impl Add for ExternalForce {
346    type Output = Self;
347
348    #[inline]
349    fn add(mut self, rhs: Self) -> Self::Output {
350        self += rhs;
351        self
352    }
353}
354
355impl Sub for ExternalForce {
356    type Output = Self;
357
358    #[inline]
359    fn sub(mut self, rhs: Self) -> Self::Output {
360        self -= rhs;
361        self
362    }
363}
364
365impl AddAssign for ExternalForce {
366    #[inline]
367    fn add_assign(&mut self, rhs: Self) {
368        self.force += rhs.force;
369        self.torque += rhs.torque;
370    }
371}
372
373impl SubAssign for ExternalForce {
374    #[inline]
375    fn sub_assign(&mut self, rhs: Self) {
376        self.force -= rhs.force;
377        self.torque -= rhs.torque;
378    }
379}
380
381/// Instantaneous external impulse applied continuously to a [`RigidBody`].
382///
383/// The impulse is only applied once, and whenever it it modified (based
384/// on Bevy’s change detection).
385#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
386#[reflect(Component, Default, PartialEq)]
387pub struct ExternalImpulse {
388    /// The linear impulse applied to the [`RigidBody`].
389    pub impulse: Vect,
390    /// The angular impulse applied to the [`RigidBody`].
391    #[cfg(feature = "dim2")]
392    pub torque_impulse: f32,
393    /// The angular impulse applied to the [`RigidBody`].
394    #[cfg(feature = "dim3")]
395    pub torque_impulse: Vect,
396}
397
398impl ExternalImpulse {
399    /// An impulse applied at a specific world-space point of a [`RigidBody`].
400    ///
401    /// # Parameters
402    /// - `impulse`: the impulse to apply.
403    /// - `point`: the point (world-space) where the impulse must be applied.
404    /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the impulse is being
405    ///   applied to.
406    pub fn at_point(impulse: Vect, point: Vect, center_of_mass: Vect) -> Self {
407        Self {
408            impulse,
409            #[cfg(feature = "dim2")]
410            torque_impulse: (point - center_of_mass).perp_dot(impulse),
411            #[cfg(feature = "dim3")]
412            torque_impulse: (point - center_of_mass).cross(impulse),
413        }
414    }
415
416    /// Reset the external impulses to zero.
417    pub fn reset(&mut self) {
418        *self = Default::default();
419    }
420}
421
422impl Add for ExternalImpulse {
423    type Output = Self;
424
425    #[inline]
426    fn add(mut self, rhs: Self) -> Self::Output {
427        self += rhs;
428        self
429    }
430}
431
432impl Sub for ExternalImpulse {
433    type Output = Self;
434
435    #[inline]
436    fn sub(mut self, rhs: Self) -> Self::Output {
437        self -= rhs;
438        self
439    }
440}
441
442impl AddAssign for ExternalImpulse {
443    #[inline]
444    fn add_assign(&mut self, rhs: Self) {
445        self.impulse += rhs.impulse;
446        self.torque_impulse += rhs.torque_impulse;
447    }
448}
449
450impl SubAssign for ExternalImpulse {
451    #[inline]
452    fn sub_assign(&mut self, rhs: Self) {
453        self.impulse -= rhs.impulse;
454        self.torque_impulse -= rhs.torque_impulse;
455    }
456}
457
458/// Gravity is multiplied by this scaling factor before it's
459/// applied to this [`RigidBody`].
460#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
461#[reflect(Component, Default, PartialEq)]
462pub struct GravityScale(pub f32);
463
464impl Default for GravityScale {
465    fn default() -> Self {
466        Self(1.0)
467    }
468}
469
470/// Information used for Continuous-Collision-Detection.
471#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Component, Reflect)]
472#[reflect(Component, Default, PartialEq)]
473pub struct Ccd {
474    /// Is CCD enabled for this [`RigidBody`]?
475    pub enabled: bool,
476}
477
478impl Ccd {
479    /// Enable CCD for a [`RigidBody`].
480    pub fn enabled() -> Self {
481        Self { enabled: true }
482    }
483
484    /// Disable CCD for a [`RigidBody`].
485    ///
486    /// Note that a [`RigidBody`] without the Ccd component attached
487    /// has CCD disabled by default.
488    pub fn disabled() -> Self {
489        Self { enabled: false }
490    }
491}
492
493/// Sets the maximum prediction distance Soft Continuous Collision-Detection.
494///
495/// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of
496/// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how
497/// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact
498/// performance badly by increasing the work needed from the broad-phase.
499///
500/// It is a generally cheaper variant of regular CCD (that can be enabled with
501/// [`rapier::dynamics::RigidBody::enable_ccd`] since it relies on predictive constraints instead of
502/// shape-cast and substeps.
503#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
504#[reflect(Component, Default, PartialEq)]
505pub struct SoftCcd {
506    /// The soft CCD prediction distance.
507    pub prediction: f32,
508}
509
510/// The dominance groups of a [`RigidBody`].
511#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Component, Reflect)]
512#[reflect(Component, Default, PartialEq)]
513pub struct Dominance {
514    // FIXME: rename this to `group` (no `s`).
515    /// The dominance groups of a [`RigidBody`].
516    pub groups: i8,
517}
518
519impl Dominance {
520    /// Initialize the dominance to the given group.
521    pub fn group(group: i8) -> Self {
522        Self { groups: group }
523    }
524}
525
526/// The activation status of a body.
527///
528/// This controls whether a body is sleeping or not.
529/// If the threshold is negative, the body never sleeps.
530#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
531#[reflect(Component, Default, PartialEq)]
532pub struct Sleeping {
533    /// The linear velocity below which the body can fall asleep.
534    ///
535    /// The effictive threshold is obtained by multpilying this value by the
536    /// [`IntegrationParameters::length_unit`].
537    pub normalized_linear_threshold: f32,
538    /// The angular velocity below which the body can fall asleep.
539    pub angular_threshold: f32,
540    /// Is this body sleeping?
541    pub sleeping: bool,
542}
543
544impl Sleeping {
545    /// Creates a components that disables sleeping for the associated [`RigidBody`].
546    pub fn disabled() -> Self {
547        Self {
548            normalized_linear_threshold: -1.0,
549            angular_threshold: -1.0,
550            sleeping: false,
551        }
552    }
553}
554
555impl Default for Sleeping {
556    fn default() -> Self {
557        Self {
558            normalized_linear_threshold: RigidBodyActivation::default_normalized_linear_threshold(),
559            angular_threshold: RigidBodyActivation::default_angular_threshold(),
560            sleeping: false,
561        }
562    }
563}
564
565/// Damping factors to gradually slow down a [`RigidBody`].
566#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
567#[reflect(Component, Default, PartialEq)]
568pub struct Damping {
569    // TODO: rename these to "linear" and "angular"?
570    /// Damping factor for gradually slowing down the translational motion of the [`RigidBody`].
571    pub linear_damping: f32,
572    /// Damping factor for gradually slowing down the angular motion of the [`RigidBody`].
573    pub angular_damping: f32,
574}
575
576impl Default for Damping {
577    fn default() -> Self {
578        Self {
579            linear_damping: 0.0,
580            angular_damping: 0.0,
581        }
582    }
583}
584
585/// If the `TimestepMode::Interpolated` mode is set and this component is present,
586/// the associated [`RigidBody`] will have its position automatically interpolated
587/// between the last two [`RigidBody`] positions set by the physics engine.
588#[derive(Copy, Clone, Debug, Default, PartialEq, Component)]
589pub struct TransformInterpolation {
590    /// The starting point of the interpolation.
591    pub start: Option<Isometry<f32>>,
592    /// The end point of the interpolation.
593    pub end: Option<Isometry<f32>>,
594}
595
596impl TransformInterpolation {
597    /// Interpolates between the start and end positions with `t` in the range `[0..1]`.
598    pub fn lerp_slerp(&self, t: f32) -> Option<Isometry<f32>> {
599        if let (Some(start), Some(end)) = (self.start, self.end) {
600            Some(start.lerp_slerp(&end, t))
601        } else {
602            None
603        }
604    }
605}
606
607/// Indicates whether or not the [`RigidBody`] is disabled explicitly by the user.
608#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
609#[reflect(Component, Default, PartialEq)]
610pub struct RigidBodyDisabled;
611
612/// Set the additional number of solver iterations run for a rigid-body and
613/// everything interacting with it.
614///
615/// Increasing this number will help improve simulation accuracy on this rigid-body
616/// and every rigid-body interacting directly or indirectly with it (through joints
617/// or contacts). This implies a performance hit.
618///
619/// The default value is 0, meaning exactly [`IntegrationParameters::num_solver_iterations`] will
620/// be used as number of solver iterations for this body.
621#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
622#[reflect(Component, Default, PartialEq)]
623pub struct AdditionalSolverIterations(pub usize);