rapier3d/dynamics/
rigid_body_components.rs

1#[cfg(doc)]
2use super::IntegrationParameters;
3use crate::control::PdErrors;
4use crate::dynamics::MassProperties;
5use crate::geometry::{
6    ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
7    ColliderSet, ColliderShape, ModifiedColliders,
8};
9use crate::math::{
10    AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector,
11};
12use crate::utils::{SimdAngularInertia, SimdCross, SimdDot};
13use num::Zero;
14
15#[cfg(doc)]
16use crate::control::PidController;
17
18/// The unique handle of a rigid body added to a `RigidBodySet`.
19#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
20#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
21#[repr(transparent)]
22pub struct RigidBodyHandle(pub crate::data::arena::Index);
23
24impl RigidBodyHandle {
25    /// Converts this handle into its (index, generation) components.
26    pub fn into_raw_parts(self) -> (u32, u32) {
27        self.0.into_raw_parts()
28    }
29
30    /// Reconstructs an handle from its (index, generation) components.
31    pub fn from_raw_parts(id: u32, generation: u32) -> Self {
32        Self(crate::data::arena::Index::from_raw_parts(id, generation))
33    }
34
35    /// An always-invalid rigid-body handle.
36    pub fn invalid() -> Self {
37        Self(crate::data::arena::Index::from_raw_parts(
38            crate::INVALID_U32,
39            crate::INVALID_U32,
40        ))
41    }
42}
43
44/// The type of a body, governing the way it is affected by external forces.
45#[deprecated(note = "renamed as RigidBodyType")]
46pub type BodyStatus = RigidBodyType;
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq)]
49#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
50/// The status of a body, governing the way it is affected by external forces.
51pub enum RigidBodyType {
52    /// A `RigidBodyType::Dynamic` body can be affected by all external forces.
53    Dynamic = 0,
54    /// A `RigidBodyType::Fixed` body cannot be affected by external forces.
55    Fixed = 1,
56    /// A `RigidBodyType::KinematicPositionBased` body cannot be affected by any external forces but can be controlled
57    /// by the user at the position level while keeping realistic one-way interaction with dynamic bodies.
58    ///
59    /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
60    /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
61    /// modified by the user and is independent from any contact or joint it is involved in.
62    KinematicPositionBased = 2,
63    /// A `RigidBodyType::KinematicVelocityBased` body cannot be affected by any external forces but can be controlled
64    /// by the user at the velocity level while keeping realistic one-way interaction with dynamic bodies.
65    ///
66    /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
67    /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
68    /// modified by the user and is independent from any contact or joint it is involved in.
69    KinematicVelocityBased = 3,
70    // Semikinematic, // A kinematic that performs automatic CCD with the fixed environment to avoid traversing it?
71    // Disabled,
72}
73
74impl RigidBodyType {
75    /// Is this rigid-body fixed (i.e. cannot move)?
76    pub fn is_fixed(self) -> bool {
77        self == RigidBodyType::Fixed
78    }
79
80    /// Is this rigid-body dynamic (i.e. can move and be affected by forces)?
81    pub fn is_dynamic(self) -> bool {
82        self == RigidBodyType::Dynamic
83    }
84
85    /// Is this rigid-body kinematic (i.e. can move but is unaffected by forces)?
86    pub fn is_kinematic(self) -> bool {
87        self == RigidBodyType::KinematicPositionBased
88            || self == RigidBodyType::KinematicVelocityBased
89    }
90}
91
92bitflags::bitflags! {
93    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
94    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
95    /// Flags describing how the rigid-body has been modified by the user.
96    pub struct RigidBodyChanges: u32 {
97        /// Flag indicating that any component of this rigid-body has been modified.
98        const MODIFIED    = 1 << 0;
99        /// Flag indicating that the `RigidBodyPosition` component of this rigid-body has been modified.
100        const POSITION    = 1 << 1;
101        /// Flag indicating that the `RigidBodyActivation` component of this rigid-body has been modified.
102        const SLEEP       = 1 << 2;
103        /// Flag indicating that the `RigidBodyColliders` component of this rigid-body has been modified.
104        const COLLIDERS   = 1 << 3;
105        /// Flag indicating that the `RigidBodyType` component of this rigid-body has been modified.
106        const TYPE        = 1 << 4;
107        /// Flag indicating that the `RigidBodyDominance` component of this rigid-body has been modified.
108        const DOMINANCE   = 1 << 5;
109        /// Flag indicating that the local mass-properties of this rigid-body must be recomputed.
110        const LOCAL_MASS_PROPERTIES = 1 << 6;
111        /// Flag indicating that the rigid-body was enabled or disabled.
112        const ENABLED_OR_DISABLED = 1 << 7;
113    }
114}
115
116impl Default for RigidBodyChanges {
117    fn default() -> Self {
118        RigidBodyChanges::empty()
119    }
120}
121
122#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
123#[derive(Clone, Debug, Copy, PartialEq)]
124/// The position of this rigid-body.
125pub struct RigidBodyPosition {
126    /// The world-space position of the rigid-body.
127    pub position: Isometry<Real>,
128    /// The next position of the rigid-body.
129    ///
130    /// At the beginning of the timestep, and when the
131    /// timestep is complete we must have position == next_position
132    /// except for kinematic bodies.
133    ///
134    /// The next_position is updated after the velocity and position
135    /// resolution. Then it is either validated (ie. we set position := set_position)
136    /// or clamped by CCD.
137    pub next_position: Isometry<Real>,
138}
139
140impl Default for RigidBodyPosition {
141    fn default() -> Self {
142        Self {
143            position: Isometry::identity(),
144            next_position: Isometry::identity(),
145        }
146    }
147}
148
149impl RigidBodyPosition {
150    /// Computes the velocity need to travel from `self.position` to `self.next_position` in
151    /// a time equal to `1.0 / inv_dt`.
152    #[must_use]
153    pub fn interpolate_velocity(&self, inv_dt: Real, local_com: &Point<Real>) -> RigidBodyVelocity {
154        let pose_err = self.pose_errors(local_com);
155        RigidBodyVelocity {
156            linvel: pose_err.linear * inv_dt,
157            angvel: pose_err.angular * inv_dt,
158        }
159    }
160
161    /// Compute new positions after integrating the given forces and velocities.
162    ///
163    /// This uses a symplectic Euler integration scheme.
164    #[must_use]
165    pub fn integrate_forces_and_velocities(
166        &self,
167        dt: Real,
168        forces: &RigidBodyForces,
169        vels: &RigidBodyVelocity,
170        mprops: &RigidBodyMassProps,
171    ) -> Isometry<Real> {
172        let new_vels = forces.integrate(dt, vels, mprops);
173        new_vels.integrate(dt, &self.position, &mprops.local_mprops.local_com)
174    }
175
176    /// Computes the difference between [`Self::next_position`] and [`Self::position`].
177    ///
178    /// This error measure can for example be used for interpolating the velocity between two poses,
179    /// or be given to the [`PidController`].
180    ///
181    /// Note that interpolating the velocity can be done more conveniently with
182    /// [`Self::interpolate_velocity`].
183    pub fn pose_errors(&self, local_com: &Point<Real>) -> PdErrors {
184        let com = self.position * local_com;
185        let shift = Translation::from(com.coords);
186        let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
187
188        let angular;
189        #[cfg(feature = "dim2")]
190        {
191            angular = dpos.rotation.angle();
192        }
193        #[cfg(feature = "dim3")]
194        {
195            angular = dpos.rotation.scaled_axis();
196        }
197        let linear = dpos.translation.vector;
198
199        PdErrors { linear, angular }
200    }
201}
202
203impl<T> From<T> for RigidBodyPosition
204where
205    Isometry<Real>: From<T>,
206{
207    fn from(position: T) -> Self {
208        let position = position.into();
209        Self {
210            position,
211            next_position: position,
212        }
213    }
214}
215
216bitflags::bitflags! {
217    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
218    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
219    /// Flags affecting the behavior of the constraints solver for a given contact manifold.
220    pub struct AxesMask: u8 {
221        /// The translational X axis.
222        const LIN_X = 1 << 0;
223        /// The translational Y axis.
224        const LIN_Y = 1 << 1;
225        /// The translational Z axis.
226        #[cfg(feature = "dim3")]
227        const LIN_Z = 1 << 2;
228        /// The rotational X axis.
229        #[cfg(feature = "dim3")]
230        const ANG_X = 1 << 3;
231        /// The rotational Y axis.
232        #[cfg(feature = "dim3")]
233        const ANG_Y = 1 << 4;
234        /// The rotational Z axis.
235        const ANG_Z = 1 << 5;
236    }
237}
238
239impl Default for AxesMask {
240    fn default() -> Self {
241        AxesMask::empty()
242    }
243}
244
245bitflags::bitflags! {
246    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
247    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
248    /// Flags affecting the behavior of the constraints solver for a given contact manifold.
249    pub struct LockedAxes: u8 {
250        /// Flag indicating that the rigid-body cannot translate along the `X` axis.
251        const TRANSLATION_LOCKED_X = 1 << 0;
252        /// Flag indicating that the rigid-body cannot translate along the `Y` axis.
253        const TRANSLATION_LOCKED_Y = 1 << 1;
254        /// Flag indicating that the rigid-body cannot translate along the `Z` axis.
255        const TRANSLATION_LOCKED_Z = 1 << 2;
256        /// Flag indicating that the rigid-body cannot translate along any direction.
257        const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
258        /// Flag indicating that the rigid-body cannot rotate along the `X` axis.
259        const ROTATION_LOCKED_X = 1 << 3;
260        /// Flag indicating that the rigid-body cannot rotate along the `Y` axis.
261        const ROTATION_LOCKED_Y = 1 << 4;
262        /// Flag indicating that the rigid-body cannot rotate along the `Z` axis.
263        const ROTATION_LOCKED_Z = 1 << 5;
264        /// Combination of flags indicating that the rigid-body cannot rotate along any axis.
265        const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
266    }
267}
268
269/// Mass and angular inertia added to a rigid-body on top of its attached colliders’ contributions.
270#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
271#[derive(Copy, Clone, Debug, PartialEq)]
272pub enum RigidBodyAdditionalMassProps {
273    /// Mass properties to be added as-is.
274    MassProps(MassProperties),
275    /// Mass to be added to the rigid-body. This will also automatically scale
276    /// the attached colliders total angular inertia to account for the added mass.
277    Mass(Real),
278}
279
280impl Default for RigidBodyAdditionalMassProps {
281    fn default() -> Self {
282        RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
283    }
284}
285
286#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
287#[derive(Clone, Debug, PartialEq)]
288/// The mass properties of a rigid-body.
289pub struct RigidBodyMassProps {
290    /// Flags for locking rotation and translation.
291    pub flags: LockedAxes,
292    /// The local mass properties of the rigid-body.
293    pub local_mprops: MassProperties,
294    /// Mass-properties of this rigid-bodies, added to the contributions of its attached colliders.
295    pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
296    /// The world-space center of mass of the rigid-body.
297    pub world_com: Point<Real>,
298    /// The inverse mass taking into account translation locking.
299    pub effective_inv_mass: Vector<Real>,
300    /// The square-root of the world-space inverse angular inertia tensor of the rigid-body,
301    /// taking into account rotation locking.
302    pub effective_world_inv_inertia_sqrt: AngularInertia<Real>,
303}
304
305impl Default for RigidBodyMassProps {
306    fn default() -> Self {
307        Self {
308            flags: LockedAxes::empty(),
309            local_mprops: MassProperties::zero(),
310            additional_local_mprops: None,
311            world_com: Point::origin(),
312            effective_inv_mass: Vector::zero(),
313            effective_world_inv_inertia_sqrt: AngularInertia::zero(),
314        }
315    }
316}
317
318impl From<LockedAxes> for RigidBodyMassProps {
319    fn from(flags: LockedAxes) -> Self {
320        Self {
321            flags,
322            ..Self::default()
323        }
324    }
325}
326
327impl From<MassProperties> for RigidBodyMassProps {
328    fn from(local_mprops: MassProperties) -> Self {
329        Self {
330            local_mprops,
331            ..Default::default()
332        }
333    }
334}
335
336impl RigidBodyMassProps {
337    /// The mass of the rigid-body.
338    #[must_use]
339    pub fn mass(&self) -> Real {
340        crate::utils::inv(self.local_mprops.inv_mass)
341    }
342
343    /// The effective mass (that takes the potential translation locking into account) of
344    /// this rigid-body.
345    #[must_use]
346    pub fn effective_mass(&self) -> Vector<Real> {
347        self.effective_inv_mass.map(crate::utils::inv)
348    }
349
350    /// The square root of the effective world-space angular inertia (that takes the potential rotation locking into account) of
351    /// this rigid-body.
352    #[must_use]
353    pub fn effective_angular_inertia_sqrt(&self) -> AngularInertia<Real> {
354        #[allow(unused_mut)] // mut needed in 3D.
355        let mut ang_inertia = self.effective_world_inv_inertia_sqrt;
356
357        // Make the matrix invertible.
358        #[cfg(feature = "dim3")]
359        {
360            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
361                ang_inertia.m11 = 1.0;
362            }
363            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
364                ang_inertia.m22 = 1.0;
365            }
366            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
367                ang_inertia.m33 = 1.0;
368            }
369        }
370
371        #[allow(unused_mut)] // mut needed in 3D.
372        let mut result = ang_inertia.inverse();
373
374        // Remove the locked axes again.
375        #[cfg(feature = "dim3")]
376        {
377            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
378                result.m11 = 0.0;
379            }
380            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
381                result.m22 = 0.0;
382            }
383            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
384                result.m33 = 0.0;
385            }
386        }
387
388        result
389    }
390
391    /// The effective world-space angular inertia (that takes the potential rotation locking into account) of
392    /// this rigid-body.
393    #[must_use]
394    pub fn effective_angular_inertia(&self) -> AngularInertia<Real> {
395        self.effective_angular_inertia_sqrt().squared()
396    }
397
398    /// Recompute the mass-properties of this rigid-bodies based on its currently attached colliders.
399    pub fn recompute_mass_properties_from_colliders(
400        &mut self,
401        colliders: &ColliderSet,
402        attached_colliders: &RigidBodyColliders,
403        position: &Isometry<Real>,
404    ) {
405        let added_mprops = self
406            .additional_local_mprops
407            .as_ref()
408            .map(|mprops| **mprops)
409            .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
410
411        self.local_mprops = MassProperties::default();
412
413        for handle in &attached_colliders.0 {
414            if let Some(co) = colliders.get(*handle) {
415                if co.is_enabled() {
416                    if let Some(co_parent) = co.parent {
417                        let to_add = co
418                            .mprops
419                            .mass_properties(&*co.shape)
420                            .transform_by(&co_parent.pos_wrt_parent);
421                        self.local_mprops += to_add;
422                    }
423                }
424            }
425        }
426
427        match added_mprops {
428            RigidBodyAdditionalMassProps::MassProps(mprops) => {
429                self.local_mprops += mprops;
430            }
431            RigidBodyAdditionalMassProps::Mass(mass) => {
432                let new_mass = self.local_mprops.mass() + mass;
433                self.local_mprops.set_mass(new_mass, true);
434            }
435        }
436
437        self.update_world_mass_properties(position);
438    }
439
440    /// Update the world-space mass properties of `self`, taking into account the new position.
441    pub fn update_world_mass_properties(&mut self, position: &Isometry<Real>) {
442        self.world_com = self.local_mprops.world_com(position);
443        self.effective_inv_mass = Vector::repeat(self.local_mprops.inv_mass);
444        self.effective_world_inv_inertia_sqrt =
445            self.local_mprops.world_inv_inertia_sqrt(&position.rotation);
446
447        // Take into account translation/rotation locking.
448        if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
449            self.effective_inv_mass.x = 0.0;
450        }
451
452        if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
453            self.effective_inv_mass.y = 0.0;
454        }
455
456        #[cfg(feature = "dim3")]
457        if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
458            self.effective_inv_mass.z = 0.0;
459        }
460
461        #[cfg(feature = "dim2")]
462        {
463            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
464                self.effective_world_inv_inertia_sqrt = 0.0;
465            }
466        }
467        #[cfg(feature = "dim3")]
468        {
469            if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
470                self.effective_world_inv_inertia_sqrt.m11 = 0.0;
471                self.effective_world_inv_inertia_sqrt.m12 = 0.0;
472                self.effective_world_inv_inertia_sqrt.m13 = 0.0;
473            }
474
475            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
476                self.effective_world_inv_inertia_sqrt.m22 = 0.0;
477                self.effective_world_inv_inertia_sqrt.m12 = 0.0;
478                self.effective_world_inv_inertia_sqrt.m23 = 0.0;
479            }
480            if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
481                self.effective_world_inv_inertia_sqrt.m33 = 0.0;
482                self.effective_world_inv_inertia_sqrt.m13 = 0.0;
483                self.effective_world_inv_inertia_sqrt.m23 = 0.0;
484            }
485        }
486    }
487}
488
489#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
490#[derive(Clone, Debug, Copy, PartialEq)]
491/// The velocities of this rigid-body.
492pub struct RigidBodyVelocity {
493    /// The linear velocity of the rigid-body.
494    pub linvel: Vector<Real>,
495    /// The angular velocity of the rigid-body.
496    pub angvel: AngVector<Real>,
497}
498
499impl Default for RigidBodyVelocity {
500    fn default() -> Self {
501        Self::zero()
502    }
503}
504
505impl RigidBodyVelocity {
506    /// Create a new rigid-body velocity component.
507    #[must_use]
508    pub fn new(linvel: Vector<Real>, angvel: AngVector<Real>) -> Self {
509        Self { linvel, angvel }
510    }
511
512    /// Converts a slice to a rigid-body velocity.
513    ///
514    /// The slice must contain at least 3 elements: the `slice[0..2]` contains
515    /// the linear velocity and the `slice[2]` contains the angular velocity.
516    #[must_use]
517    #[cfg(feature = "dim2")]
518    pub fn from_slice(slice: &[Real]) -> Self {
519        Self {
520            linvel: Vector::new(slice[0], slice[1]),
521            angvel: slice[2],
522        }
523    }
524
525    /// Converts a slice to a rigid-body velocity.
526    ///
527    /// The slice must contain at least 6 elements: the `slice[0..3]` contains
528    /// the linear velocity and the `slice[3..6]` contains the angular velocity.
529    #[must_use]
530    #[cfg(feature = "dim3")]
531    pub fn from_slice(slice: &[Real]) -> Self {
532        Self {
533            linvel: Vector::new(slice[0], slice[1], slice[2]),
534            angvel: AngVector::new(slice[3], slice[4], slice[5]),
535        }
536    }
537
538    /// Velocities set to zero.
539    #[must_use]
540    pub fn zero() -> Self {
541        Self {
542            linvel: na::zero(),
543            angvel: na::zero(),
544        }
545    }
546
547    /// This velocity seen as a slice.
548    ///
549    /// The linear part is stored first.
550    #[inline]
551    pub fn as_slice(&self) -> &[Real] {
552        self.as_vector().as_slice()
553    }
554
555    /// This velocity seen as a mutable slice.
556    ///
557    /// The linear part is stored first.
558    #[inline]
559    pub fn as_mut_slice(&mut self) -> &mut [Real] {
560        self.as_vector_mut().as_mut_slice()
561    }
562
563    /// This velocity seen as a vector.
564    ///
565    /// The linear part is stored first.
566    #[inline]
567    #[cfg(feature = "dim2")]
568    pub fn as_vector(&self) -> &na::Vector3<Real> {
569        unsafe { std::mem::transmute(self) }
570    }
571
572    /// This velocity seen as a mutable vector.
573    ///
574    /// The linear part is stored first.
575    #[inline]
576    #[cfg(feature = "dim2")]
577    pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
578        unsafe { std::mem::transmute(self) }
579    }
580
581    /// This velocity seen as a vector.
582    ///
583    /// The linear part is stored first.
584    #[inline]
585    #[cfg(feature = "dim3")]
586    pub fn as_vector(&self) -> &na::Vector6<Real> {
587        unsafe { std::mem::transmute(self) }
588    }
589
590    /// This velocity seen as a mutable vector.
591    ///
592    /// The linear part is stored first.
593    #[inline]
594    #[cfg(feature = "dim3")]
595    pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
596        unsafe { std::mem::transmute(self) }
597    }
598
599    /// Return `self` rotated by `rotation`.
600    #[must_use]
601    pub fn transformed(self, rotation: &Rotation<Real>) -> Self {
602        Self {
603            linvel: rotation * self.linvel,
604            #[cfg(feature = "dim2")]
605            angvel: self.angvel,
606            #[cfg(feature = "dim3")]
607            angvel: rotation * self.angvel,
608        }
609    }
610
611    /// The approximate kinetic energy of this rigid-body.
612    ///
613    /// This approximation does not take the rigid-body's mass and angular inertia
614    /// into account. Some physics engines call this the "mass-normalized kinetic
615    /// energy".
616    #[must_use]
617    pub fn pseudo_kinetic_energy(&self) -> Real {
618        0.5 * (self.linvel.norm_squared() + self.angvel.gdot(self.angvel))
619    }
620
621    /// Returns the update velocities after applying the given damping.
622    #[must_use]
623    pub fn apply_damping(&self, dt: Real, damping: &RigidBodyDamping) -> Self {
624        RigidBodyVelocity {
625            linvel: self.linvel * (1.0 / (1.0 + dt * damping.linear_damping)),
626            angvel: self.angvel * (1.0 / (1.0 + dt * damping.angular_damping)),
627        }
628    }
629
630    /// The velocity of the given world-space point on this rigid-body.
631    #[must_use]
632    pub fn velocity_at_point(&self, point: &Point<Real>, world_com: &Point<Real>) -> Vector<Real> {
633        let dpt = point - world_com;
634        self.linvel + self.angvel.gcross(dpt)
635    }
636
637    /// Integrate the velocities in `self` to compute obtain new positions when moving from the given
638    /// initial position `init_pos`.
639    #[must_use]
640    pub fn integrate(
641        &self,
642        dt: Real,
643        init_pos: &Isometry<Real>,
644        local_com: &Point<Real>,
645    ) -> Isometry<Real> {
646        let com = init_pos * local_com;
647        let shift = Translation::from(com.coords);
648        let mut result =
649            shift * Isometry::new(self.linvel * dt, self.angvel * dt) * shift.inverse() * init_pos;
650        result.rotation.renormalize_fast();
651        result
652    }
653
654    /// Are these velocities exactly equal to zero?
655    #[must_use]
656    pub fn is_zero(&self) -> bool {
657        self.linvel.is_zero() && self.angvel.is_zero()
658    }
659
660    /// The kinetic energy of this rigid-body.
661    #[must_use]
662    #[profiling::function]
663    pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
664        let mut energy = (rb_mprops.mass() * self.linvel.norm_squared()) / 2.0;
665
666        #[cfg(feature = "dim2")]
667        if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() {
668            let inertia_sqrt = 1.0 / rb_mprops.effective_world_inv_inertia_sqrt;
669            energy += (inertia_sqrt * self.angvel).powi(2) / 2.0;
670        }
671
672        #[cfg(feature = "dim3")]
673        if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() {
674            let inertia_sqrt = rb_mprops
675                .effective_world_inv_inertia_sqrt
676                .inverse_unchecked();
677            energy += (inertia_sqrt * self.angvel).norm_squared() / 2.0;
678        }
679
680        energy
681    }
682
683    /// Applies an impulse at the center-of-mass of this rigid-body.
684    /// The impulse is applied right away, changing the linear velocity.
685    /// This does nothing on non-dynamic bodies.
686    pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector<Real>) {
687        self.linvel += impulse.component_mul(&rb_mprops.effective_inv_mass);
688    }
689
690    /// Applies an angular impulse at the center-of-mass of this rigid-body.
691    /// The impulse is applied right away, changing the angular velocity.
692    /// This does nothing on non-dynamic bodies.
693    #[cfg(feature = "dim2")]
694    pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
695        self.angvel += rb_mprops.effective_world_inv_inertia_sqrt
696            * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse);
697    }
698
699    /// Applies an angular impulse at the center-of-mass of this rigid-body.
700    /// The impulse is applied right away, changing the angular velocity.
701    /// This does nothing on non-dynamic bodies.
702    #[cfg(feature = "dim3")]
703    pub fn apply_torque_impulse(
704        &mut self,
705        rb_mprops: &RigidBodyMassProps,
706        torque_impulse: Vector<Real>,
707    ) {
708        self.angvel += rb_mprops.effective_world_inv_inertia_sqrt
709            * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse);
710    }
711
712    /// Applies an impulse at the given world-space point of this rigid-body.
713    /// The impulse is applied right away, changing the linear and/or angular velocities.
714    /// This does nothing on non-dynamic bodies.
715    pub fn apply_impulse_at_point(
716        &mut self,
717        rb_mprops: &RigidBodyMassProps,
718        impulse: Vector<Real>,
719        point: Point<Real>,
720    ) {
721        let torque_impulse = (point - rb_mprops.world_com).gcross(impulse);
722        self.apply_impulse(rb_mprops, impulse);
723        self.apply_torque_impulse(rb_mprops, torque_impulse);
724    }
725}
726
727impl std::ops::Mul<Real> for RigidBodyVelocity {
728    type Output = Self;
729
730    fn mul(self, rhs: Real) -> Self {
731        RigidBodyVelocity {
732            linvel: self.linvel * rhs,
733            angvel: self.angvel * rhs,
734        }
735    }
736}
737
738impl std::ops::Add<RigidBodyVelocity> for RigidBodyVelocity {
739    type Output = Self;
740
741    fn add(self, rhs: Self) -> Self {
742        RigidBodyVelocity {
743            linvel: self.linvel + rhs.linvel,
744            angvel: self.angvel + rhs.angvel,
745        }
746    }
747}
748
749impl std::ops::AddAssign<RigidBodyVelocity> for RigidBodyVelocity {
750    fn add_assign(&mut self, rhs: Self) {
751        self.linvel += rhs.linvel;
752        self.angvel += rhs.angvel;
753    }
754}
755
756impl std::ops::Sub<RigidBodyVelocity> for RigidBodyVelocity {
757    type Output = Self;
758
759    fn sub(self, rhs: Self) -> Self {
760        RigidBodyVelocity {
761            linvel: self.linvel - rhs.linvel,
762            angvel: self.angvel - rhs.angvel,
763        }
764    }
765}
766
767impl std::ops::SubAssign<RigidBodyVelocity> for RigidBodyVelocity {
768    fn sub_assign(&mut self, rhs: Self) {
769        self.linvel -= rhs.linvel;
770        self.angvel -= rhs.angvel;
771    }
772}
773
774#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
775#[derive(Clone, Debug, Copy, PartialEq)]
776/// Damping factors to progressively slow down a rigid-body.
777pub struct RigidBodyDamping {
778    /// Damping factor for gradually slowing down the translational motion of the rigid-body.
779    pub linear_damping: Real,
780    /// Damping factor for gradually slowing down the angular motion of the rigid-body.
781    pub angular_damping: Real,
782}
783
784impl Default for RigidBodyDamping {
785    fn default() -> Self {
786        Self {
787            linear_damping: 0.0,
788            angular_damping: 0.0,
789        }
790    }
791}
792
793#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
794#[derive(Clone, Debug, Copy, PartialEq)]
795/// The user-defined external forces applied to this rigid-body.
796pub struct RigidBodyForces {
797    /// Accumulation of external forces (only for dynamic bodies).
798    pub force: Vector<Real>,
799    /// Accumulation of external torques (only for dynamic bodies).
800    pub torque: AngVector<Real>,
801    /// Gravity is multiplied by this scaling factor before it's
802    /// applied to this rigid-body.
803    pub gravity_scale: Real,
804    /// Forces applied by the user.
805    pub user_force: Vector<Real>,
806    /// Torque applied by the user.
807    pub user_torque: AngVector<Real>,
808}
809
810impl Default for RigidBodyForces {
811    fn default() -> Self {
812        Self {
813            force: na::zero(),
814            torque: na::zero(),
815            gravity_scale: 1.0,
816            user_force: na::zero(),
817            user_torque: na::zero(),
818        }
819    }
820}
821
822impl RigidBodyForces {
823    /// Integrate these forces to compute new velocities.
824    #[must_use]
825    pub fn integrate(
826        &self,
827        dt: Real,
828        init_vels: &RigidBodyVelocity,
829        mprops: &RigidBodyMassProps,
830    ) -> RigidBodyVelocity {
831        let linear_acc = self.force.component_mul(&mprops.effective_inv_mass);
832        let angular_acc = mprops.effective_world_inv_inertia_sqrt
833            * (mprops.effective_world_inv_inertia_sqrt * self.torque);
834
835        RigidBodyVelocity {
836            linvel: init_vels.linvel + linear_acc * dt,
837            angvel: init_vels.angvel + angular_acc * dt,
838        }
839    }
840
841    /// Adds to `self` the gravitational force that would result in a gravitational acceleration
842    /// equal to `gravity`.
843    pub fn compute_effective_force_and_torque(
844        &mut self,
845        gravity: &Vector<Real>,
846        mass: &Vector<Real>,
847    ) {
848        self.force = self.user_force + gravity.component_mul(mass) * self.gravity_scale;
849        self.torque = self.user_torque;
850    }
851
852    /// Applies a force at the given world-space point of the rigid-body with the given mass properties.
853    pub fn apply_force_at_point(
854        &mut self,
855        rb_mprops: &RigidBodyMassProps,
856        force: Vector<Real>,
857        point: Point<Real>,
858    ) {
859        self.user_force += force;
860        self.user_torque += (point - rb_mprops.world_com).gcross(force);
861    }
862}
863
864#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
865#[derive(Clone, Debug, Copy, PartialEq)]
866/// Information used for Continuous-Collision-Detection.
867pub struct RigidBodyCcd {
868    /// The distance used by the CCD solver to decide if a movement would
869    /// result in a tunnelling problem.
870    pub ccd_thickness: Real,
871    /// The max distance between this rigid-body's center of mass and its
872    /// furthest collider point.
873    pub ccd_max_dist: Real,
874    /// Is CCD active for this rigid-body?
875    ///
876    /// If `self.ccd_enabled` is `true`, then this is automatically set to
877    /// `true` when the CCD solver detects that the rigid-body is moving fast
878    /// enough to potential cause a tunneling problem.
879    pub ccd_active: bool,
880    /// Is CCD enabled for this rigid-body?
881    pub ccd_enabled: bool,
882    /// The soft-CCD prediction distance for this rigid-body.
883    pub soft_ccd_prediction: Real,
884}
885
886impl Default for RigidBodyCcd {
887    fn default() -> Self {
888        Self {
889            ccd_thickness: Real::MAX,
890            ccd_max_dist: 0.0,
891            ccd_active: false,
892            ccd_enabled: false,
893            soft_ccd_prediction: 0.0,
894        }
895    }
896}
897
898impl RigidBodyCcd {
899    /// The maximum velocity any point of any collider attached to this rigid-body
900    /// moving with the given velocity can have.
901    pub fn max_point_velocity(&self, vels: &RigidBodyVelocity) -> Real {
902        #[cfg(feature = "dim2")]
903        return vels.linvel.norm() + vels.angvel.abs() * self.ccd_max_dist;
904        #[cfg(feature = "dim3")]
905        return vels.linvel.norm() + vels.angvel.norm() * self.ccd_max_dist;
906    }
907
908    /// Is this rigid-body moving fast enough so that it may cause a tunneling problem?
909    pub fn is_moving_fast(
910        &self,
911        dt: Real,
912        vels: &RigidBodyVelocity,
913        forces: Option<&RigidBodyForces>,
914    ) -> bool {
915        // NOTE: for the threshold we don't use the exact CCD thickness. Theoretically, we
916        //       should use `self.rb_ccd.ccd_thickness - smallest_contact_dist` where `smallest_contact_dist`
917        //       is the deepest contact (the contact with the largest penetration depth, i.e., the
918        //       negative `dist` with the largest absolute value.
919        //       However, getting this penetration depth assumes querying the contact graph from
920        //       the narrow-phase, which can be pretty expensive. So we use the CCD thickness
921        //       divided by 10 right now. We will see in practice if this value is OK or if we
922        //       should use a smaller (to be less conservative) or larger divisor (to be more conservative).
923        let threshold = self.ccd_thickness / 10.0;
924
925        if let Some(forces) = forces {
926            let linear_part = (vels.linvel + forces.force * dt).norm();
927            #[cfg(feature = "dim2")]
928            let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
929            #[cfg(feature = "dim3")]
930            let angular_part = (vels.angvel + forces.torque * dt).norm() * self.ccd_max_dist;
931            let vel_with_forces = linear_part + angular_part;
932            vel_with_forces > threshold
933        } else {
934            self.max_point_velocity(vels) * dt > threshold
935        }
936    }
937}
938
939#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
940#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, Hash)]
941/// Internal identifiers used by the physics engine.
942pub struct RigidBodyIds {
943    pub(crate) active_island_id: usize,
944    pub(crate) active_set_id: usize,
945    pub(crate) active_set_offset: usize,
946    pub(crate) active_set_timestamp: u32,
947}
948
949#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
950#[derive(Default, Clone, Debug, PartialEq, Eq)]
951/// The set of colliders attached to this rigid-bodies.
952///
953/// This should not be modified manually unless you really know what
954/// you are doing (for example if you are trying to integrate Rapier
955/// to a game engine using its component-based interface).
956pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
957
958impl RigidBodyColliders {
959    /// Detach a collider from this rigid-body.
960    pub fn detach_collider(
961        &mut self,
962        rb_changes: &mut RigidBodyChanges,
963        co_handle: ColliderHandle,
964    ) {
965        if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
966            rb_changes.set(
967                RigidBodyChanges::MODIFIED | RigidBodyChanges::COLLIDERS,
968                true,
969            );
970            self.0.swap_remove(i);
971        }
972    }
973
974    /// Attach a collider to this rigid-body.
975    pub fn attach_collider(
976        &mut self,
977        rb_changes: &mut RigidBodyChanges,
978        rb_ccd: &mut RigidBodyCcd,
979        rb_mprops: &mut RigidBodyMassProps,
980        rb_pos: &RigidBodyPosition,
981        co_handle: ColliderHandle,
982        co_pos: &mut ColliderPosition,
983        co_parent: &ColliderParent,
984        co_shape: &ColliderShape,
985        co_mprops: &ColliderMassProps,
986    ) {
987        rb_changes.set(
988            RigidBodyChanges::MODIFIED | RigidBodyChanges::COLLIDERS,
989            true,
990        );
991
992        co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
993        rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
994
995        let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
996        rb_ccd.ccd_max_dist = rb_ccd
997            .ccd_max_dist
998            .max(shape_bsphere.center.coords.norm() + shape_bsphere.radius);
999
1000        let mass_properties = co_mprops
1001            .mass_properties(&**co_shape)
1002            .transform_by(&co_parent.pos_wrt_parent);
1003        self.0.push(co_handle);
1004        rb_mprops.local_mprops += mass_properties;
1005        rb_mprops.update_world_mass_properties(&rb_pos.position);
1006    }
1007
1008    /// Update the positions of all the colliders attached to this rigid-body.
1009    pub(crate) fn update_positions(
1010        &self,
1011        colliders: &mut ColliderSet,
1012        modified_colliders: &mut ModifiedColliders,
1013        parent_pos: &Isometry<Real>,
1014    ) {
1015        for handle in &self.0 {
1016            // NOTE: the ColliderParent component must exist if we enter this method.
1017            let co = colliders.index_mut_internal(*handle);
1018            let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1019
1020            // Set the modification flag so we can benefit from the modification-tracking
1021            // when updating the narrow-phase/broad-phase afterwards.
1022            modified_colliders.push_once(*handle, co);
1023
1024            co.changes |= ColliderChanges::POSITION;
1025            co.pos = ColliderPosition(new_pos);
1026        }
1027    }
1028}
1029
1030#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1031#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1032/// The dominance groups of a rigid-body.
1033pub struct RigidBodyDominance(pub i8);
1034
1035impl RigidBodyDominance {
1036    /// The actual dominance group of this rigid-body, after taking into account its type.
1037    pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1038        if status.is_dynamic() {
1039            self.0 as i16
1040        } else {
1041            i8::MAX as i16 + 1
1042        }
1043    }
1044}
1045
1046/// The rb_activation status of a body.
1047///
1048/// This controls whether a body is sleeping or not.
1049/// If the threshold is negative, the body never sleeps.
1050#[derive(Copy, Clone, Debug, PartialEq)]
1051#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1052pub struct RigidBodyActivation {
1053    /// The threshold linear velocity below which the body can fall asleep.
1054    ///
1055    /// The value is "normalized", i.e., the actual threshold applied by the physics engine
1056    /// is equal to this value multiplied by [`IntegrationParameters::length_unit`].
1057    pub normalized_linear_threshold: Real,
1058    /// The angular linear velocity below which the body can fall asleep.
1059    pub angular_threshold: Real,
1060    /// The amount of time the rigid-body must remain below the thresholds to be put to sleep.
1061    pub time_until_sleep: Real,
1062    /// Since how much time can this body sleep?
1063    pub time_since_can_sleep: Real,
1064    /// Is this body sleeping?
1065    pub sleeping: bool,
1066}
1067
1068impl Default for RigidBodyActivation {
1069    fn default() -> Self {
1070        Self::active()
1071    }
1072}
1073
1074impl RigidBodyActivation {
1075    /// The default linear velocity below which a body can be put to sleep.
1076    pub fn default_normalized_linear_threshold() -> Real {
1077        0.4
1078    }
1079
1080    /// The default angular velocity below which a body can be put to sleep.
1081    pub fn default_angular_threshold() -> Real {
1082        0.5
1083    }
1084
1085    /// The amount of time the rigid-body must remain below it’s linear and angular velocity
1086    /// threshold before falling to sleep.
1087    pub fn default_time_until_sleep() -> Real {
1088        2.0
1089    }
1090
1091    /// Create a new rb_activation status initialised with the default rb_activation threshold and is active.
1092    pub fn active() -> Self {
1093        RigidBodyActivation {
1094            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1095            angular_threshold: Self::default_angular_threshold(),
1096            time_until_sleep: Self::default_time_until_sleep(),
1097            time_since_can_sleep: 0.0,
1098            sleeping: false,
1099        }
1100    }
1101
1102    /// Create a new rb_activation status initialised with the default rb_activation threshold and is inactive.
1103    pub fn inactive() -> Self {
1104        RigidBodyActivation {
1105            normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1106            angular_threshold: Self::default_angular_threshold(),
1107            time_until_sleep: Self::default_time_until_sleep(),
1108            time_since_can_sleep: Self::default_time_until_sleep(),
1109            sleeping: true,
1110        }
1111    }
1112
1113    /// Create a new activation status that prevents the rigid-body from sleeping.
1114    pub fn cannot_sleep() -> Self {
1115        RigidBodyActivation {
1116            normalized_linear_threshold: -1.0,
1117            angular_threshold: -1.0,
1118            ..Self::active()
1119        }
1120    }
1121
1122    /// Returns `true` if the body is not asleep.
1123    #[inline]
1124    pub fn is_active(&self) -> bool {
1125        !self.sleeping
1126    }
1127
1128    /// Wakes up this rigid-body.
1129    #[inline]
1130    pub fn wake_up(&mut self, strong: bool) {
1131        self.sleeping = false;
1132        if strong {
1133            self.time_since_can_sleep = 0.0;
1134        }
1135    }
1136
1137    /// Put this rigid-body to sleep.
1138    #[inline]
1139    pub fn sleep(&mut self) {
1140        self.sleeping = true;
1141        self.time_since_can_sleep = self.time_until_sleep;
1142    }
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147    use super::*;
1148    use crate::math::Real;
1149
1150    #[test]
1151    fn test_interpolate_velocity() {
1152        // Interpolate and then integrate the velocity to see if
1153        // the end positions match.
1154        #[cfg(feature = "f32")]
1155        let mut rng = oorandom::Rand32::new(0);
1156        #[cfg(feature = "f64")]
1157        let mut rng = oorandom::Rand64::new(0);
1158
1159        for i in -10..=10 {
1160            let mult = i as Real;
1161            let (local_com, curr_pos, next_pos);
1162            #[cfg(feature = "dim2")]
1163            {
1164                local_com = Point::new(rng.rand_float(), rng.rand_float());
1165                curr_pos = Isometry::new(
1166                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1167                    rng.rand_float(),
1168                );
1169                next_pos = Isometry::new(
1170                    Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1171                    rng.rand_float(),
1172                );
1173            }
1174            #[cfg(feature = "dim3")]
1175            {
1176                local_com = Point::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1177                curr_pos = Isometry::new(
1178                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1179                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1180                );
1181                next_pos = Isometry::new(
1182                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1183                    Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1184                );
1185            }
1186
1187            let dt = 0.016;
1188            let rb_pos = RigidBodyPosition {
1189                position: curr_pos,
1190                next_position: next_pos,
1191            };
1192            let vel = rb_pos.interpolate_velocity(1.0 / dt, &local_com);
1193            let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1194            approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1195        }
1196    }
1197}