rapier3d_f64/dynamics/
rigid_body_components.rs

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