Skip to main content

rapier2d/dynamics/
rigid_body_components.rs

1#[cfg(doc)]
2use super::IntegrationParameters;
3use crate::control::PdErrors;
4#[cfg(doc)]
5use crate::control::PidController;
6use crate::dynamics::MassProperties;
7use crate::geometry::{
8    ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
9    ColliderSet, ColliderShape, ModifiedColliders,
10};
11use crate::math::{AngVector, AngularInertia, Pose, Real, Rotation, Vector};
12use crate::utils::{
13    AngularInertiaOps, CrossProduct, DotProduct, PoseOps, ScalarType, SimdRealCopy,
14};
15use num::Zero;
16#[cfg(feature = "dim2")]
17use parry::math::Rot2;
18
19/// The unique handle of a rigid body added to a `RigidBodySet`.
20#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
21#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
22#[repr(transparent)]
23pub struct RigidBodyHandle(pub crate::data::arena::Index);
24
25impl RigidBodyHandle {
26    /// Converts this handle into its (index, generation) components.
27    pub fn into_raw_parts(self) -> (u32, u32) {
28        self.0.into_raw_parts()
29    }
30
31    /// Reconstructs an handle from its (index, generation) components.
32    pub fn from_raw_parts(id: u32, generation: u32) -> Self {
33        Self(crate::data::arena::Index::from_raw_parts(id, generation))
34    }
35
36    /// An always-invalid rigid-body handle.
37    pub fn invalid() -> Self {
38        Self(crate::data::arena::Index::from_raw_parts(
39            crate::INVALID_U32,
40            crate::INVALID_U32,
41        ))
42    }
43}
44
45/// The type of a body, governing the way it is affected by external forces.
46#[deprecated(note = "renamed as RigidBodyType")]
47pub type BodyStatus = RigidBodyType;
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
51/// The type of a rigid body, determining how it responds to forces and movement.
52pub enum RigidBodyType {
53    /// Fully simulated - responds to forces, gravity, and collisions.
54    ///
55    /// Use for: Falling objects, projectiles, physics-based characters, anything that should
56    /// behave realistically under physics simulation.
57    Dynamic = 0,
58
59    /// Never moves - has infinite mass and is unaffected by anything.
60    ///
61    /// Use for: Static level geometry, walls, floors, terrain, buildings.
62    Fixed = 1,
63
64    /// Controlled by setting next position - pushes but isn't pushed.
65    ///
66    /// You control this by setting where it should be next frame. Rapier computes the
67    /// velocity needed to get there. The body can push dynamic bodies but nothing can
68    /// push it back (one-way interaction).
69    ///
70    /// Use for: Animated platforms, objects controlled by external animation systems.
71    KinematicPositionBased = 2,
72
73    /// Controlled by setting velocity - pushes but isn't pushed.
74    ///
75    /// You control this by setting its velocity directly. It moves predictably regardless
76    /// of what it hits. Can push dynamic bodies but nothing can push it back (one-way interaction).
77    ///
78    /// Use for: Moving platforms, elevators, doors, player-controlled characters (when you want
79    /// direct control rather than physics-based movement).
80    KinematicVelocityBased = 3,
81    // Semikinematic, // A kinematic that performs automatic CCD with the fixed environment to avoid traversing it?
82    // Disabled,
83}
84
85impl RigidBodyType {
86    /// Is this rigid-body fixed (i.e. cannot move)?
87    pub fn is_fixed(self) -> bool {
88        self == RigidBodyType::Fixed
89    }
90
91    /// Is this rigid-body dynamic (i.e. can move and be affected by forces)?
92    pub fn is_dynamic(self) -> bool {
93        self == RigidBodyType::Dynamic
94    }
95
96    /// Is this rigid-body kinematic (i.e. can move but is unaffected by forces)?
97    pub fn is_kinematic(self) -> bool {
98        self == RigidBodyType::KinematicPositionBased
99            || self == RigidBodyType::KinematicVelocityBased
100    }
101
102    /// Is this rigid-body a dynamic rigid-body or a kinematic rigid-body?
103    ///
104    /// This method is mostly convenient internally where kinematic and dynamic rigid-body
105    /// are subject to the same behavior.
106    pub fn is_dynamic_or_kinematic(self) -> bool {
107        self != RigidBodyType::Fixed
108    }
109}
110
111bitflags::bitflags! {
112    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
113    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
114    /// Flags describing how the rigid-body has been modified by the user.
115    pub struct RigidBodyChanges: u32 {
116        /// Flag indicating that this rigid-body is in the modified rigid-body set.
117        const IN_MODIFIED_SET = 1 << 0;
118        /// Flag indicating that the `RigidBodyPosition` component of this rigid-body has been modified.
119        const POSITION    = 1 << 1;
120        /// Flag indicating that the `RigidBodyActivation` component of this rigid-body has been modified.
121        const SLEEP       = 1 << 2;
122        /// Flag indicating that the `RigidBodyColliders` component of this rigid-body has been modified.
123        const COLLIDERS   = 1 << 3;
124        /// Flag indicating that the `RigidBodyType` component of this rigid-body has been modified.
125        const TYPE        = 1 << 4;
126        /// Flag indicating that the `RigidBodyDominance` component of this rigid-body has been modified.
127        const DOMINANCE   = 1 << 5;
128        /// Flag indicating that the local mass-properties of this rigid-body must be recomputed.
129        const LOCAL_MASS_PROPERTIES = 1 << 6;
130        /// Flag indicating that the rigid-body was enabled or disabled.
131        const ENABLED_OR_DISABLED = 1 << 7;
132    }
133}
134
135impl Default for RigidBodyChanges {
136    fn default() -> Self {
137        RigidBodyChanges::empty()
138    }
139}
140
141#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
142#[derive(Clone, Debug, Copy, PartialEq)]
143/// The position of this rigid-body.
144pub struct RigidBodyPosition {
145    /// The world-space position of the rigid-body.
146    pub position: Pose,
147    /// The next position of the rigid-body.
148    ///
149    /// At the beginning of the timestep, and when the
150    /// timestep is complete we must have position == next_position
151    /// except for position-based kinematic bodies.
152    ///
153    /// The next_position is updated after the velocity and position
154    /// resolution. Then it is either validated (ie. we set position := set_position)
155    /// or clamped by CCD.
156    pub next_position: Pose,
157}
158
159impl Default for RigidBodyPosition {
160    fn default() -> Self {
161        Self {
162            position: Pose::IDENTITY,
163            next_position: Pose::IDENTITY,
164        }
165    }
166}
167
168impl RigidBodyPosition {
169    /// Computes the velocity need to travel from `self.position` to `self.next_position` in
170    /// a time equal to `1.0 / inv_dt`.
171    #[must_use]
172    pub fn interpolate_velocity(&self, inv_dt: Real, local_com: Vector) -> RigidBodyVelocity<Real> {
173        let pose_err = self.pose_errors(local_com);
174        RigidBodyVelocity {
175            linvel: pose_err.linear * inv_dt,
176            angvel: pose_err.angular * inv_dt,
177        }
178    }
179
180    /// Compute new positions after integrating the given forces and velocities.
181    ///
182    /// This uses a symplectic Euler integration scheme.
183    #[must_use]
184    pub fn integrate_forces_and_velocities(
185        &self,
186        dt: Real,
187        forces: &RigidBodyForces,
188        vels: &RigidBodyVelocity<Real>,
189        mprops: &RigidBodyMassProps,
190    ) -> Pose {
191        let new_vels = forces.integrate(dt, vels, mprops);
192        let local_com = mprops.local_mprops.local_com;
193        new_vels.integrate(dt, &self.position, &local_com)
194    }
195
196    /// Computes the difference between [`Self::next_position`] and [`Self::position`].
197    ///
198    /// This error measure can for example be used for interpolating the velocity between two poses,
199    /// or be given to the [`PidController`].
200    ///
201    /// Note that interpolating the velocity can be done more conveniently with
202    /// [`Self::interpolate_velocity`].
203    pub fn pose_errors(&self, local_com: Vector) -> PdErrors {
204        let com = self.position * local_com;
205        let shift = Pose::from_translation(com);
206        let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
207
208        let angular;
209        #[cfg(feature = "dim2")]
210        {
211            angular = dpos.rotation.angle();
212        }
213        #[cfg(feature = "dim3")]
214        {
215            angular = dpos.rotation.to_scaled_axis();
216        }
217        let linear = dpos.translation;
218
219        PdErrors { linear, angular }
220    }
221}
222
223impl<T> From<T> for RigidBodyPosition
224where
225    Pose: From<T>,
226{
227    fn from(position: T) -> Self {
228        let position = position.into();
229        Self {
230            position,
231            next_position: position,
232        }
233    }
234}
235
236bitflags::bitflags! {
237    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
238    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
239    /// Flags affecting the behavior of the constraints solver for a given contact manifold.
240    pub struct AxesMask: u8 {
241        /// The translational X axis.
242        const LIN_X = 1 << 0;
243        /// The translational Y axis.
244        const LIN_Y = 1 << 1;
245        /// The translational Z axis.
246        #[cfg(feature = "dim3")]
247        const LIN_Z = 1 << 2;
248        /// The rotational X axis.
249        #[cfg(feature = "dim3")]
250        const ANG_X = 1 << 3;
251        /// The rotational Y axis.
252        #[cfg(feature = "dim3")]
253        const ANG_Y = 1 << 4;
254        /// The rotational Z axis.
255        const ANG_Z = 1 << 5;
256    }
257}
258
259impl Default for AxesMask {
260    fn default() -> Self {
261        AxesMask::empty()
262    }
263}
264
265bitflags::bitflags! {
266    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
267    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
268    /// Flags that lock specific movement axes to prevent translation or rotation.
269    ///
270    /// Use this to constrain body movement to specific directions/axes. Common uses:
271    /// - **2D games in 3D**: Lock Z translation and X/Y rotation to keep everything in the XY plane
272    /// - **Upright characters**: Lock rotations to prevent tipping over
273    /// - **Sliding objects**: Lock rotation while allowing translation
274    /// - **Spinning objects**: Lock translation while allowing rotation
275    ///
276    /// # Example
277    /// ```
278    /// # use rapier3d::prelude::*;
279    /// # let mut bodies = RigidBodySet::new();
280    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
281    /// # let body = bodies.get_mut(body_handle).unwrap();
282    /// // Character that can't tip over (rotation locked, but can move)
283    /// body.set_locked_axes(LockedAxes::ROTATION_LOCKED, true);
284    ///
285    /// // Object that slides but doesn't rotate
286    /// body.set_locked_axes(LockedAxes::ROTATION_LOCKED, true);
287    ///
288    /// // 2D game in 3D engine (lock Z movement and X/Y rotation)
289    /// body.set_locked_axes(
290    ///     LockedAxes::TRANSLATION_LOCKED_Z |
291    ///     LockedAxes::ROTATION_LOCKED_X |
292    ///     LockedAxes::ROTATION_LOCKED_Y,
293    ///     true
294    /// );
295    /// ```
296    pub struct LockedAxes: u8 {
297        /// Prevents movement along the X axis.
298        const TRANSLATION_LOCKED_X = 1 << 0;
299        /// Prevents movement along the Y axis.
300        const TRANSLATION_LOCKED_Y = 1 << 1;
301        /// Prevents movement along the Z axis.
302        const TRANSLATION_LOCKED_Z = 1 << 2;
303        /// Prevents all translational movement.
304        const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
305        /// Prevents rotation around the X axis.
306        const ROTATION_LOCKED_X = 1 << 3;
307        /// Prevents rotation around the Y axis.
308        const ROTATION_LOCKED_Y = 1 << 4;
309        /// Prevents rotation around the Z axis.
310        const ROTATION_LOCKED_Z = 1 << 5;
311        /// Prevents all rotational movement.
312        const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
313    }
314}
315
316/// Mass and angular inertia added to a rigid-body on top of its attached colliders’ contributions.
317#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
318#[derive(Copy, Clone, Debug, PartialEq)]
319pub enum RigidBodyAdditionalMassProps {
320    /// Mass properties to be added as-is.
321    MassProps(MassProperties),
322    /// Mass to be added to the rigid-body. This will also automatically scale
323    /// the attached colliders total angular inertia to account for the added mass.
324    Mass(Real),
325}
326
327impl Default for RigidBodyAdditionalMassProps {
328    fn default() -> Self {
329        RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
330    }
331}
332
333#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
334#[derive(Clone, Debug, PartialEq)]
335// #[repr(C)]
336/// The mass properties of a rigid-body.
337pub struct RigidBodyMassProps {
338    /// The world-space center of mass of the rigid-body.
339    pub world_com: Vector,
340    /// The inverse mass taking into account translation locking.
341    pub effective_inv_mass: Vector,
342    /// The square-root of the world-space inverse angular inertia tensor of the rigid-body,
343    /// taking into account rotation locking.
344    pub effective_world_inv_inertia: AngularInertia,
345    /// The local mass properties of the rigid-body.
346    pub local_mprops: MassProperties,
347    /// Flags for locking rotation and translation.
348    pub flags: LockedAxes,
349    /// Mass-properties of this rigid-bodies, added to the contributions of its attached colliders.
350    pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
351}
352
353impl Default for RigidBodyMassProps {
354    fn default() -> Self {
355        Self {
356            flags: LockedAxes::empty(),
357            local_mprops: MassProperties::zero(),
358            additional_local_mprops: None,
359            world_com: Vector::ZERO,
360            effective_inv_mass: Vector::ZERO,
361            effective_world_inv_inertia: AngularInertia::zero(),
362        }
363    }
364}
365
366impl From<LockedAxes> for RigidBodyMassProps {
367    fn from(flags: LockedAxes) -> Self {
368        Self {
369            flags,
370            ..Self::default()
371        }
372    }
373}
374
375impl From<MassProperties> for RigidBodyMassProps {
376    fn from(local_mprops: MassProperties) -> Self {
377        Self {
378            local_mprops,
379            ..Default::default()
380        }
381    }
382}
383
384impl RigidBodyMassProps {
385    /// The mass of the rigid-body.
386    #[must_use]
387    pub fn mass(&self) -> Real {
388        crate::utils::inv(self.local_mprops.inv_mass)
389    }
390
391    /// The effective mass (that takes the potential translation locking into account) of
392    /// this rigid-body.
393    #[must_use]
394    pub fn effective_mass(&self) -> Vector {
395        self.effective_inv_mass.map(crate::utils::inv)
396    }
397
398    /// The square root of the effective world-space angular inertia (that takes the potential rotation locking into account) of
399    /// this rigid-body.
400    #[must_use]
401    pub fn effective_angular_inertia(&self) -> AngularInertia {
402        #[allow(unused_mut)] // mut needed in 3D.
403        let mut ang_inertia = self.effective_world_inv_inertia;
404
405        // Make the matrix invertible.
406        #[cfg(feature = "dim3")]
407        {
408            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
409                ang_inertia.m11 = 1.0;
410            }
411            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
412                ang_inertia.m22 = 1.0;
413            }
414            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
415                ang_inertia.m33 = 1.0;
416            }
417        }
418
419        #[allow(unused_mut)] // mut needed in 3D.
420        let mut result = ang_inertia.inverse();
421
422        // Remove the locked axes again.
423        #[cfg(feature = "dim3")]
424        {
425            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
426                result.m11 = 0.0;
427            }
428            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
429                result.m22 = 0.0;
430            }
431            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
432                result.m33 = 0.0;
433            }
434        }
435
436        result
437    }
438
439    /// Recompute the mass-properties of this rigid-bodies based on its currently attached colliders.
440    pub fn recompute_mass_properties_from_colliders(
441        &mut self,
442        colliders: &ColliderSet,
443        attached_colliders: &RigidBodyColliders,
444        body_type: RigidBodyType,
445        position: &Pose,
446    ) {
447        let added_mprops = self
448            .additional_local_mprops
449            .as_ref()
450            .map(|mprops| **mprops)
451            .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
452
453        self.local_mprops = MassProperties::default();
454
455        for handle in &attached_colliders.0 {
456            if let Some(co) = colliders.get(*handle) {
457                if co.is_enabled() {
458                    if let Some(co_parent) = co.parent {
459                        let to_add = co
460                            .mprops
461                            .mass_properties(&*co.shape)
462                            .transform_by(&co_parent.pos_wrt_parent);
463                        self.local_mprops += to_add;
464                    }
465                }
466            }
467        }
468
469        match added_mprops {
470            RigidBodyAdditionalMassProps::MassProps(mprops) => {
471                self.local_mprops += mprops;
472            }
473            RigidBodyAdditionalMassProps::Mass(mass) => {
474                let new_mass = self.local_mprops.mass() + mass;
475                self.local_mprops.set_mass(new_mass, true);
476            }
477        }
478
479        self.update_world_mass_properties(body_type, position);
480    }
481
482    /// Update the world-space mass properties of `self`, taking into account the new position.
483    pub fn update_world_mass_properties(&mut self, body_type: RigidBodyType, position: &Pose) {
484        self.world_com = self.local_mprops.world_com(position);
485        self.effective_inv_mass = Vector::splat(self.local_mprops.inv_mass);
486        self.effective_world_inv_inertia = self.local_mprops.world_inv_inertia(&position.rotation);
487
488        // Take into account translation/rotation locking.
489        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
490            self.effective_inv_mass.x = 0.0;
491        }
492
493        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
494            self.effective_inv_mass.y = 0.0;
495        }
496
497        #[cfg(feature = "dim3")]
498        if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
499            self.effective_inv_mass.z = 0.0;
500        }
501
502        #[cfg(feature = "dim2")]
503        {
504            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
505                self.effective_world_inv_inertia = 0.0;
506            }
507        }
508        #[cfg(feature = "dim3")]
509        {
510            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
511                self.effective_world_inv_inertia.m11 = 0.0;
512                self.effective_world_inv_inertia.m12 = 0.0;
513                self.effective_world_inv_inertia.m13 = 0.0;
514            }
515
516            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
517                self.effective_world_inv_inertia.m22 = 0.0;
518                self.effective_world_inv_inertia.m12 = 0.0;
519                self.effective_world_inv_inertia.m23 = 0.0;
520            }
521            if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
522                self.effective_world_inv_inertia.m33 = 0.0;
523                self.effective_world_inv_inertia.m13 = 0.0;
524                self.effective_world_inv_inertia.m23 = 0.0;
525            }
526        }
527    }
528}
529
530#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
531#[derive(Clone, Debug, Copy, PartialEq)]
532/// The velocities of this rigid-body.
533pub struct RigidBodyVelocity<T: ScalarType> {
534    /// The linear velocity of the rigid-body.
535    pub linvel: T::Vector,
536    /// The angular velocity of the rigid-body.
537    pub angvel: T::AngVector,
538}
539
540impl Default for RigidBodyVelocity<Real> {
541    fn default() -> Self {
542        Self::zero()
543    }
544}
545
546impl RigidBodyVelocity<Real> {
547    /// Create a new rigid-body velocity component.
548    #[must_use]
549    #[cfg(feature = "dim2")]
550    pub fn new(linvel: Vector, angvel: AngVector) -> Self {
551        Self { linvel, angvel }
552    }
553
554    /// Create a new rigid-body velocity component.
555    #[must_use]
556    #[cfg(feature = "dim3")]
557    pub fn new(linvel: Vector, angvel: AngVector) -> Self {
558        Self {
559            linvel: Vector::new(linvel.x, linvel.y, linvel.z),
560            angvel: AngVector::new(angvel.x, angvel.y, angvel.z),
561        }
562    }
563
564    /// Converts a slice to a rigid-body velocity.
565    ///
566    /// The slice must contain at least 3 elements: the `slice[0..2]` contains
567    /// the linear velocity and the `slice[2]` contains the angular velocity.
568    #[must_use]
569    #[cfg(feature = "dim2")]
570    pub fn from_slice(slice: &[Real]) -> Self {
571        Self {
572            linvel: Vector::new(slice[0], slice[1]),
573            angvel: slice[2],
574        }
575    }
576
577    /// Converts a slice to a rigid-body velocity.
578    ///
579    /// The slice must contain at least 6 elements: the `slice[0..3]` contains
580    /// the linear velocity and the `slice[3..6]` contains the angular velocity.
581    #[must_use]
582    #[cfg(feature = "dim3")]
583    pub fn from_slice(slice: &[Real]) -> Self {
584        Self {
585            linvel: Vector::new(slice[0], slice[1], slice[2]),
586            angvel: AngVector::new(slice[3], slice[4], slice[5]),
587        }
588    }
589
590    /// Velocities set to zero.
591    #[must_use]
592    pub fn zero() -> Self {
593        Self {
594            linvel: Default::default(),
595            angvel: Default::default(),
596        }
597    }
598
599    /// This velocity seen as a slice.
600    ///
601    /// The linear part is stored first.
602    #[inline]
603    pub fn as_slice(&self) -> &[Real] {
604        self.as_vector().as_slice()
605    }
606
607    /// This velocity seen as a mutable slice.
608    ///
609    /// The linear part is stored first.
610    #[inline]
611    pub fn as_mut_slice(&mut self) -> &mut [Real] {
612        self.as_vector_mut().as_mut_slice()
613    }
614
615    /// This velocity seen as a vector.
616    ///
617    /// The linear part is stored first.
618    #[inline]
619    #[cfg(feature = "dim2")]
620    pub fn as_vector(&self) -> &na::Vector3<Real> {
621        unsafe { std::mem::transmute(self) }
622    }
623
624    /// This velocity seen as a mutable vector.
625    ///
626    /// The linear part is stored first.
627    #[inline]
628    #[cfg(feature = "dim2")]
629    pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
630        unsafe { std::mem::transmute(self) }
631    }
632
633    /// This velocity seen as a vector.
634    ///
635    /// The linear part is stored first.
636    #[inline]
637    #[cfg(feature = "dim3")]
638    pub fn as_vector(&self) -> &na::Vector6<Real> {
639        unsafe { std::mem::transmute(self) }
640    }
641
642    /// This velocity seen as a mutable vector.
643    ///
644    /// The linear part is stored first.
645    #[inline]
646    #[cfg(feature = "dim3")]
647    pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
648        unsafe { std::mem::transmute(self) }
649    }
650
651    /// Return `self` rotated by `rotation`.
652    #[must_use]
653    #[cfg(feature = "dim2")]
654    pub fn transformed(self, rotation: &Rotation) -> Self {
655        Self {
656            linvel: *rotation * self.linvel,
657            angvel: self.angvel,
658        }
659    }
660
661    /// Return `self` rotated by `rotation`.
662    #[must_use]
663    #[cfg(feature = "dim3")]
664    pub fn transformed(self, rotation: &Rotation) -> Self {
665        Self {
666            linvel: *rotation * self.linvel,
667            angvel: *rotation * self.angvel,
668        }
669    }
670
671    /// The approximate kinetic energy of this rigid-body.
672    ///
673    /// This approximation does not take the rigid-body's mass and angular inertia
674    /// into account. Some physics engines call this the "mass-normalized kinetic
675    /// energy".
676    #[must_use]
677    pub fn pseudo_kinetic_energy(&self) -> Real {
678        0.5 * (self.linvel.length_squared() + self.angvel.gdot(self.angvel))
679    }
680
681    /// The velocity of the given world-space point on this rigid-body.
682    #[must_use]
683    #[cfg(feature = "dim2")]
684    pub fn velocity_at_point(&self, point: Vector, world_com: Vector) -> Vector {
685        let dpt = point - world_com;
686        self.linvel + self.angvel.gcross(dpt)
687    }
688
689    /// The velocity of the given world-space point on this rigid-body.
690    #[must_use]
691    #[cfg(feature = "dim3")]
692    pub fn velocity_at_point(&self, point: Vector, world_com: Vector) -> Vector {
693        let dpt = point - world_com;
694        self.linvel + self.angvel.gcross(dpt)
695    }
696
697    /// Are these velocities exactly equal to zero?
698    #[must_use]
699    pub fn is_zero(&self) -> bool {
700        self.linvel == Vector::ZERO && self.angvel == AngVector::default()
701    }
702
703    /// The kinetic energy of this rigid-body.
704    #[must_use]
705    #[profiling::function]
706    pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
707        let mut energy = (rb_mprops.mass() * self.linvel.length_squared()) / 2.0;
708
709        #[cfg(feature = "dim2")]
710        if !num::Zero::is_zero(&rb_mprops.effective_world_inv_inertia) {
711            let inertia = 1.0 / rb_mprops.effective_world_inv_inertia;
712            energy += inertia * self.angvel * self.angvel / 2.0;
713        }
714
715        #[cfg(feature = "dim3")]
716        if !rb_mprops.effective_world_inv_inertia.is_zero() {
717            let inertia = rb_mprops.effective_world_inv_inertia.inverse_unchecked();
718            energy += self.angvel.gdot(inertia * self.angvel) / 2.0;
719        }
720
721        energy
722    }
723
724    /// Applies an impulse at the center-of-mass of this rigid-body.
725    /// The impulse is applied right away, changing the linear velocity.
726    /// This does nothing on non-dynamic bodies.
727    pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector) {
728        self.linvel += impulse * rb_mprops.effective_inv_mass;
729    }
730
731    /// Applies an angular impulse at the center-of-mass of this rigid-body.
732    /// The impulse is applied right away, changing the angular velocity.
733    /// This does nothing on non-dynamic bodies.
734    #[cfg(feature = "dim2")]
735    pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
736        self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
737    }
738
739    /// Applies an angular impulse at the center-of-mass of this rigid-body.
740    /// The impulse is applied right away, changing the angular velocity.
741    /// This does nothing on non-dynamic bodies.
742    #[cfg(feature = "dim3")]
743    pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Vector) {
744        self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
745    }
746
747    /// Applies an impulse at the given world-space point of this rigid-body.
748    /// The impulse is applied right away, changing the linear and/or angular velocities.
749    /// This does nothing on non-dynamic bodies.
750    #[cfg(feature = "dim2")]
751    pub fn apply_impulse_at_point(
752        &mut self,
753        rb_mprops: &RigidBodyMassProps,
754        impulse: Vector,
755        point: Vector,
756    ) {
757        let torque_impulse = (point - rb_mprops.world_com).perp_dot(impulse);
758        self.apply_impulse(rb_mprops, impulse);
759        self.apply_torque_impulse(rb_mprops, torque_impulse);
760    }
761
762    /// Applies an impulse at the given world-space point of this rigid-body.
763    /// The impulse is applied right away, changing the linear and/or angular velocities.
764    /// This does nothing on non-dynamic bodies.
765    #[cfg(feature = "dim3")]
766    pub fn apply_impulse_at_point(
767        &mut self,
768        rb_mprops: &RigidBodyMassProps,
769        impulse: Vector,
770        point: Vector,
771    ) {
772        let torque_impulse = (point - rb_mprops.world_com).cross(impulse);
773        self.apply_impulse(rb_mprops, impulse);
774        self.apply_torque_impulse(rb_mprops, torque_impulse);
775    }
776}
777
778impl<T: ScalarType> RigidBodyVelocity<T> {
779    /// Returns the update velocities after applying the given damping.
780    #[must_use]
781    pub fn apply_damping(&self, dt: T, damping: &RigidBodyDamping<T>) -> Self {
782        let one = T::one();
783        RigidBodyVelocity {
784            linvel: self.linvel * (one / (one + dt * damping.linear_damping)),
785            angvel: self.angvel * (one / (one + dt * damping.angular_damping)),
786        }
787    }
788
789    /// Integrate the velocities in `self` to compute obtain new positions when moving from the given
790    /// initial position `init_pos`.
791    #[must_use]
792    #[inline]
793    #[allow(clippy::let_and_return)] // Keeping `result` binding for potential renormalization
794    pub fn integrate(&self, dt: T, init_pos: &T::Pose, local_com: &T::Vector) -> T::Pose {
795        let com = *init_pos * *local_com;
796        let result = init_pos
797            .append_translation(-com)
798            .append_rotation(self.angvel * dt)
799            .append_translation(com + self.linvel * dt);
800        // TODO: is renormalization really useful?
801        // result.rotation.renormalize_fast();
802        result
803    }
804}
805
806impl RigidBodyVelocity<Real> {
807    /// Same as [`Self::integrate`] but with the angular part linearized and the local
808    /// center-of-mass assumed to be zero.
809    #[inline]
810    #[cfg(feature = "dim2")]
811    pub(crate) fn integrate_linearized(
812        &self,
813        dt: Real,
814        translation: &mut Vector,
815        rotation: &mut Rotation,
816    ) {
817        let dang = self.angvel * dt;
818        let new_cos = rotation.re - dang * rotation.im;
819        let new_sin = rotation.im + dang * rotation.re;
820        *rotation = Rot2::from_cos_sin_unchecked(new_cos, new_sin);
821        // NOTE: don't use renormalize_fast since the linearization might cause more drift.
822        rotation.normalize_mut();
823        *translation += self.linvel * dt;
824    }
825
826    /// Same as [`Self::integrate`] but with the angular part linearized and the local
827    /// center-of-mass assumed to be zero.
828    #[inline]
829    #[cfg(feature = "dim3")]
830    pub(crate) fn integrate_linearized(
831        &self,
832        dt: Real,
833        translation: &mut Vector,
834        rotation: &mut Rotation,
835    ) {
836        // Rotations linearization is inspired from
837        // https://ahrs.readthedocs.io/en/latest/filters/angular.html (not using the matrix form).
838        let hang = self.angvel * (dt * 0.5);
839        // Quaternion identity + `hang` seen as a quaternion.
840        let id_plus_hang = Rotation::from_xyzw(hang.x, hang.y, hang.z, 1.0);
841        *rotation = id_plus_hang * *rotation;
842        *rotation = rotation.normalize();
843        *translation += self.linvel * dt;
844    }
845}
846
847impl std::ops::Mul<Real> for RigidBodyVelocity<Real> {
848    type Output = Self;
849
850    fn mul(self, rhs: Real) -> Self {
851        RigidBodyVelocity {
852            linvel: self.linvel * rhs,
853            angvel: self.angvel * rhs,
854        }
855    }
856}
857
858impl std::ops::Add<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
859    type Output = Self;
860
861    fn add(self, rhs: Self) -> Self {
862        RigidBodyVelocity {
863            linvel: self.linvel + rhs.linvel,
864            angvel: self.angvel + rhs.angvel,
865        }
866    }
867}
868
869impl std::ops::AddAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
870    fn add_assign(&mut self, rhs: Self) {
871        self.linvel += rhs.linvel;
872        self.angvel += rhs.angvel;
873    }
874}
875
876impl std::ops::Sub<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
877    type Output = Self;
878
879    fn sub(self, rhs: Self) -> Self {
880        RigidBodyVelocity {
881            linvel: self.linvel - rhs.linvel,
882            angvel: self.angvel - rhs.angvel,
883        }
884    }
885}
886
887impl std::ops::SubAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
888    fn sub_assign(&mut self, rhs: Self) {
889        self.linvel -= rhs.linvel;
890        self.angvel -= rhs.angvel;
891    }
892}
893
894#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
895#[derive(Clone, Debug, Copy, PartialEq)]
896/// Damping factors to progressively slow down a rigid-body.
897pub struct RigidBodyDamping<T> {
898    /// Damping factor for gradually slowing down the translational motion of the rigid-body.
899    pub linear_damping: T,
900    /// Damping factor for gradually slowing down the angular motion of the rigid-body.
901    pub angular_damping: T,
902}
903
904impl<T: SimdRealCopy> Default for RigidBodyDamping<T> {
905    fn default() -> Self {
906        Self {
907            linear_damping: T::zero(),
908            angular_damping: T::zero(),
909        }
910    }
911}
912
913#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
914#[derive(Clone, Debug, Copy, PartialEq)]
915/// The user-defined external forces applied to this rigid-body.
916pub struct RigidBodyForces {
917    /// Accumulation of external forces (only for dynamic bodies).
918    pub force: Vector,
919    /// Accumulation of external torques (only for dynamic bodies).
920    pub torque: AngVector,
921    /// Gravity is multiplied by this scaling factor before it's
922    /// applied to this rigid-body.
923    pub gravity_scale: Real,
924    /// Forces applied by the user.
925    pub user_force: Vector,
926    /// Torque applied by the user.
927    pub user_torque: AngVector,
928    /// Are gyroscopic forces enabled for this rigid-body?
929    #[cfg(feature = "dim3")]
930    pub gyroscopic_forces_enabled: bool,
931}
932
933impl Default for RigidBodyForces {
934    fn default() -> Self {
935        #[cfg(feature = "dim2")]
936        return Self {
937            force: Vector::ZERO,
938            torque: 0.0,
939            gravity_scale: 1.0,
940            user_force: Vector::ZERO,
941            user_torque: 0.0,
942        };
943
944        #[cfg(feature = "dim3")]
945        return Self {
946            force: Vector::ZERO,
947            torque: AngVector::ZERO,
948            gravity_scale: 1.0,
949            user_force: Vector::ZERO,
950            user_torque: AngVector::ZERO,
951            gyroscopic_forces_enabled: false,
952        };
953    }
954}
955
956impl RigidBodyForces {
957    /// Integrate these forces to compute new velocities.
958    #[must_use]
959    pub fn integrate(
960        &self,
961        dt: Real,
962        init_vels: &RigidBodyVelocity<Real>,
963        mprops: &RigidBodyMassProps,
964    ) -> RigidBodyVelocity<Real> {
965        let linear_acc = self.force * mprops.effective_inv_mass;
966        let angular_acc = mprops.effective_world_inv_inertia * self.torque;
967
968        RigidBodyVelocity {
969            linvel: init_vels.linvel + linear_acc * dt,
970            angvel: init_vels.angvel + angular_acc * dt,
971        }
972    }
973
974    /// Adds to `self` the gravitational force that would result in a gravitational acceleration
975    /// equal to `gravity`.
976    pub fn compute_effective_force_and_torque(&mut self, gravity: Vector, mass: Vector) {
977        self.force = self.user_force + gravity * mass * self.gravity_scale;
978        self.torque = self.user_torque;
979    }
980
981    /// Applies a force at the given world-space point of the rigid-body with the given mass properties.
982    pub fn apply_force_at_point(
983        &mut self,
984        rb_mprops: &RigidBodyMassProps,
985        force: Vector,
986        point: Vector,
987    ) {
988        self.user_force += force;
989        self.user_torque += (point - rb_mprops.world_com).gcross(force);
990    }
991}
992
993#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
994#[derive(Clone, Debug, Copy, PartialEq)]
995/// Information used for Continuous-Collision-Detection.
996pub struct RigidBodyCcd {
997    /// The distance used by the CCD solver to decide if a movement would
998    /// result in a tunnelling problem.
999    pub ccd_thickness: Real,
1000    /// The max distance between this rigid-body's center of mass and its
1001    /// furthest collider point.
1002    pub ccd_max_dist: Real,
1003    /// Is CCD active for this rigid-body?
1004    ///
1005    /// If `self.ccd_enabled` is `true`, then this is automatically set to
1006    /// `true` when the CCD solver detects that the rigid-body is moving fast
1007    /// enough to potential cause a tunneling problem.
1008    pub ccd_active: bool,
1009    /// Is CCD enabled for this rigid-body?
1010    pub ccd_enabled: bool,
1011    /// The soft-CCD prediction distance for this rigid-body.
1012    pub soft_ccd_prediction: Real,
1013}
1014
1015impl Default for RigidBodyCcd {
1016    fn default() -> Self {
1017        Self {
1018            ccd_thickness: Real::MAX,
1019            ccd_max_dist: 0.0,
1020            ccd_active: false,
1021            ccd_enabled: false,
1022            soft_ccd_prediction: 0.0,
1023        }
1024    }
1025}
1026
1027impl RigidBodyCcd {
1028    /// The maximum velocity any point of any collider attached to this rigid-body
1029    /// moving with the given velocity can have.
1030    pub fn max_point_velocity(&self, vels: &RigidBodyVelocity<Real>) -> Real {
1031        #[cfg(feature = "dim2")]
1032        return vels.linvel.length() + vels.angvel.abs() * self.ccd_max_dist;
1033        #[cfg(feature = "dim3")]
1034        return vels.linvel.length() + vels.angvel.length() * self.ccd_max_dist;
1035    }
1036
1037    /// Is this rigid-body moving fast enough so that it may cause a tunneling problem?
1038    pub fn is_moving_fast(
1039        &self,
1040        dt: Real,
1041        vels: &RigidBodyVelocity<Real>,
1042        forces: Option<&RigidBodyForces>,
1043    ) -> bool {
1044        // NOTE: for the threshold we don't use the exact CCD thickness. Theoretically, we
1045        //       should use `self.rb_ccd.ccd_thickness - smallest_contact_dist` where `smallest_contact_dist`
1046        //       is the deepest contact (the contact with the largest penetration depth, i.e., the
1047        //       negative `dist` with the largest absolute value.
1048        //       However, getting this penetration depth assumes querying the contact graph from
1049        //       the narrow-phase, which can be pretty expensive. So we use the CCD thickness
1050        //       divided by 10 right now. We will see in practice if this value is OK or if we
1051        //       should use a smaller (to be less conservative) or larger divisor (to be more conservative).
1052        let threshold = self.ccd_thickness / 10.0;
1053
1054        if let Some(forces) = forces {
1055            let linear_part = (vels.linvel + forces.force * dt).length();
1056            #[cfg(feature = "dim2")]
1057            let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
1058            #[cfg(feature = "dim3")]
1059            let angular_part = (vels.angvel + forces.torque * dt).length() * self.ccd_max_dist;
1060            let vel_with_forces = linear_part + angular_part;
1061            vel_with_forces > threshold
1062        } else {
1063            self.max_point_velocity(vels) * dt > threshold
1064        }
1065    }
1066}
1067
1068#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1069#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
1070/// Internal identifiers used by the physics engine.
1071pub struct RigidBodyIds {
1072    pub(crate) active_island_id: usize,
1073    pub(crate) active_set_id: usize,
1074    pub(crate) active_set_timestamp: u32,
1075}
1076
1077impl Default for RigidBodyIds {
1078    fn default() -> Self {
1079        Self {
1080            active_island_id: usize::MAX,
1081            active_set_id: usize::MAX,
1082            active_set_timestamp: 0,
1083        }
1084    }
1085}
1086
1087#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1088#[derive(Default, Clone, Debug, PartialEq, Eq)]
1089/// The set of colliders attached to this rigid-bodies.
1090///
1091/// This should not be modified manually unless you really know what
1092/// you are doing (for example if you are trying to integrate Rapier
1093/// to a game engine using its component-based interface).
1094pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
1095
1096impl RigidBodyColliders {
1097    /// Detach a collider from this rigid-body.
1098    pub fn detach_collider(
1099        &mut self,
1100        rb_changes: &mut RigidBodyChanges,
1101        co_handle: ColliderHandle,
1102    ) {
1103        if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
1104            rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1105            self.0.swap_remove(i);
1106        }
1107    }
1108
1109    /// Attach a collider to this rigid-body.
1110    pub fn attach_collider(
1111        &mut self,
1112        rb_type: RigidBodyType,
1113        rb_changes: &mut RigidBodyChanges,
1114        rb_ccd: &mut RigidBodyCcd,
1115        rb_mprops: &mut RigidBodyMassProps,
1116        rb_pos: &RigidBodyPosition,
1117        co_handle: ColliderHandle,
1118        co_pos: &mut ColliderPosition,
1119        co_parent: &ColliderParent,
1120        co_shape: &ColliderShape,
1121        co_mprops: &ColliderMassProps,
1122    ) {
1123        rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1124
1125        co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
1126        rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
1127
1128        let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
1129        rb_ccd.ccd_max_dist = rb_ccd
1130            .ccd_max_dist
1131            .max(shape_bsphere.center.length() + shape_bsphere.radius);
1132
1133        let mass_properties = co_mprops
1134            .mass_properties(&**co_shape)
1135            .transform_by(&co_parent.pos_wrt_parent);
1136        self.0.push(co_handle);
1137        rb_mprops.local_mprops += mass_properties;
1138        rb_mprops.update_world_mass_properties(rb_type, &rb_pos.position);
1139    }
1140
1141    /// Update the positions of all the colliders attached to this rigid-body.
1142    pub(crate) fn update_positions(
1143        &self,
1144        colliders: &mut ColliderSet,
1145        modified_colliders: &mut ModifiedColliders,
1146        parent_pos: &Pose,
1147    ) {
1148        for handle in &self.0 {
1149            // NOTE: the ColliderParent component must exist if we enter this method.
1150            // NOTE: currently, we are propagating the position even if the collider is disabled.
1151            //       Is that the best behavior?
1152            let co = colliders.index_mut_internal(*handle);
1153            let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1154
1155            // Set the modification flag so we can benefit from the modification-tracking
1156            // when updating the narrow-phase/broad-phase afterwards.
1157            modified_colliders.push_once(*handle, co);
1158
1159            co.changes |= ColliderChanges::POSITION;
1160            co.pos = ColliderPosition(new_pos);
1161        }
1162    }
1163}
1164
1165#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1166#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1167/// The dominance groups of a rigid-body.
1168pub struct RigidBodyDominance(pub i8);
1169
1170impl RigidBodyDominance {
1171    /// The actual dominance group of this rigid-body, after taking into account its type.
1172    pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1173        if status.is_dynamic_or_kinematic() {
1174            self.0 as i16
1175        } else {
1176            i8::MAX as i16 + 1
1177        }
1178    }
1179}
1180
1181#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1182#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1183pub(crate) enum SleepRootState {
1184    /// This sleep root has already been traversed. No need to traverse
1185    /// again until the rigid-body either gets awaken by an event.
1186    Traversed,
1187    /// This sleep root has not been traversed yet.
1188    TraversalPending,
1189    /// This body can become a sleep root once it falls asleep.
1190    #[default]
1191    Unknown,
1192}
1193
1194/// Controls when a body goes to sleep (becomes inactive to save CPU).
1195///
1196/// ## Sleeping System
1197///
1198/// Bodies automatically sleep when they're at rest, dramatically improving performance
1199/// in scenes with many inactive objects. Sleeping bodies are:
1200/// - Excluded from simulation (no collision detection, no velocity integration)
1201/// - Automatically woken when disturbed (hit by moving object, connected via joint)
1202/// - Woken manually with `body.wake_up()` or `islands.wake_up()`
1203///
1204/// ## How sleeping works
1205///
1206/// A body sleeps after its linear AND angular velocities stay below thresholds for
1207/// `time_until_sleep` seconds (default: 2 seconds). Set thresholds to negative to disable sleeping.
1208///
1209/// ## When to disable sleeping
1210///
1211/// Most bodies should sleep! Only disable if the body needs to stay active despite being still:
1212/// - Bodies you frequently query for raycasts/contacts
1213/// - Bodies with time-based behaviors while stationary
1214///
1215/// Use `RigidBodyBuilder::can_sleep(false)` or `RigidBodyActivation::cannot_sleep()`.
1216#[derive(Copy, Clone, Debug, PartialEq)]
1217#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1218pub struct RigidBodyActivation {
1219    /// Linear velocity threshold for sleeping (scaled by `length_unit`).
1220    ///
1221    /// If negative, body never sleeps. Default: 0.4 (in length units/second).
1222    pub normalized_linear_threshold: Real,
1223
1224    /// Angular velocity threshold for sleeping (radians/second).
1225    ///
1226    /// If negative, body never sleeps. Default: 0.5 rad/s.
1227    pub angular_threshold: Real,
1228
1229    /// How long the body must be still before sleeping (seconds).
1230    ///
1231    /// Default: 2.0 seconds. Must be below both velocity thresholds for this duration.
1232    pub time_until_sleep: Real,
1233
1234    /// Internal timer tracking how long body has been still.
1235    pub time_since_can_sleep: Real,
1236
1237    /// Is this body currently sleeping?
1238    pub sleeping: bool,
1239
1240    pub(crate) sleep_root_state: SleepRootState,
1241}
1242
1243impl Default for RigidBodyActivation {
1244    fn default() -> Self {
1245        Self::active()
1246    }
1247}
1248
1249impl RigidBodyActivation {
1250    /// The default linear velocity below which a body can be put to sleep.
1251    pub fn default_normalized_linear_threshold() -> Real {
1252        0.4
1253    }
1254
1255    /// The default angular velocity below which a body can be put to sleep.
1256    pub fn default_angular_threshold() -> Real {
1257        0.5
1258    }
1259
1260    /// The amount of time the rigid-body must remain below it’s linear and angular velocity
1261    /// threshold before falling to sleep.
1262    pub fn default_time_until_sleep() -> Real {
1263        2.0
1264    }
1265
1266    /// Create a new rb_activation status initialised with the default rb_activation threshold and is active.
1267    pub fn active() -> Self {
1268        RigidBodyActivation {
1269            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1270            angular_threshold: Self::default_angular_threshold(),
1271            time_until_sleep: Self::default_time_until_sleep(),
1272            time_since_can_sleep: 0.0,
1273            sleeping: false,
1274            sleep_root_state: SleepRootState::Unknown,
1275        }
1276    }
1277
1278    /// Create a new rb_activation status initialised with the default rb_activation threshold and is inactive.
1279    pub fn inactive() -> Self {
1280        RigidBodyActivation {
1281            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1282            angular_threshold: Self::default_angular_threshold(),
1283            time_until_sleep: Self::default_time_until_sleep(),
1284            time_since_can_sleep: Self::default_time_until_sleep(),
1285            sleeping: true,
1286            sleep_root_state: SleepRootState::Unknown,
1287        }
1288    }
1289
1290    /// Create a new activation status that prevents the rigid-body from sleeping.
1291    pub fn cannot_sleep() -> Self {
1292        RigidBodyActivation {
1293            normalized_linear_threshold: -1.0,
1294            angular_threshold: -1.0,
1295            ..Self::active()
1296        }
1297    }
1298
1299    /// Returns `true` if the body is not asleep.
1300    #[inline]
1301    pub fn is_active(&self) -> bool {
1302        !self.sleeping
1303    }
1304
1305    /// Wakes up this rigid-body.
1306    #[inline]
1307    pub fn wake_up(&mut self, strong: bool) {
1308        self.sleeping = false;
1309
1310        // Make this body eligible as a sleep root again.
1311        if self.sleep_root_state != SleepRootState::TraversalPending {
1312            self.sleep_root_state = SleepRootState::Unknown;
1313        }
1314
1315        if strong {
1316            self.time_since_can_sleep = 0.0;
1317        }
1318    }
1319
1320    /// Put this rigid-body to sleep.
1321    #[inline]
1322    pub fn sleep(&mut self) {
1323        self.sleeping = true;
1324        self.time_since_can_sleep = self.time_until_sleep;
1325    }
1326
1327    /// Does this body have a sufficiently low kinetic energy for a long enough
1328    /// duration to be eligible for sleeping?
1329    pub fn is_eligible_for_sleep(&self) -> bool {
1330        self.time_since_can_sleep >= self.time_until_sleep
1331    }
1332
1333    pub(crate) fn update_energy(
1334        &mut self,
1335        body_type: RigidBodyType,
1336        length_unit: Real,
1337        sq_linvel: Real,
1338        sq_angvel: Real,
1339        dt: Real,
1340    ) {
1341        let can_sleep = match body_type {
1342            RigidBodyType::Dynamic => {
1343                let linear_threshold = self.normalized_linear_threshold * length_unit;
1344                sq_linvel < linear_threshold * linear_threshold.abs()
1345                    && sq_angvel < self.angular_threshold * self.angular_threshold.abs()
1346            }
1347            RigidBodyType::KinematicPositionBased | RigidBodyType::KinematicVelocityBased => {
1348                // Platforms only sleep if both velocities are exactly zero. If it’s not exactly
1349                // zero, then the user really wants them to move.
1350                sq_linvel == 0.0 && sq_angvel == 0.0
1351            }
1352            RigidBodyType::Fixed => true,
1353        };
1354
1355        if can_sleep {
1356            self.time_since_can_sleep += dt;
1357        } else {
1358            self.time_since_can_sleep = 0.0;
1359        }
1360    }
1361}
1362
1363#[cfg(test)]
1364mod tests {
1365    use super::*;
1366    use crate::math::Real;
1367
1368    #[test]
1369    fn test_interpolate_velocity() {
1370        // Interpolate and then integrate the velocity to see if
1371        // the end positions match.
1372        #[cfg(feature = "f32")]
1373        let mut rng = oorandom::Rand32::new(0);
1374        #[cfg(feature = "f64")]
1375        let mut rng = oorandom::Rand64::new(0);
1376
1377        for i in -10..=10 {
1378            let mult = i as Real;
1379            let (local_com, curr_pos, next_pos);
1380            #[cfg(feature = "dim2")]
1381            {
1382                local_com = Vector::new(rng.rand_float(), rng.rand_float());
1383                curr_pos = Pose::new(
1384                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1385                    rng.rand_float(),
1386                );
1387                next_pos = Pose::new(
1388                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1389                    rng.rand_float(),
1390                );
1391            }
1392            #[cfg(feature = "dim3")]
1393            {
1394                local_com = Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1395                curr_pos = Pose::new(
1396                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1397                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1398                );
1399                next_pos = Pose::new(
1400                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1401                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1402                );
1403            }
1404
1405            let dt = 0.016;
1406            let rb_pos = RigidBodyPosition {
1407                position: curr_pos,
1408                next_position: next_pos,
1409            };
1410            let vel = rb_pos.interpolate_velocity(1.0 / dt, local_com);
1411            let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1412            approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1413        }
1414    }
1415}