Skip to main content

avian3d/dynamics/rigid_body/mass_properties/components/
mod.rs

1//! Mass property components.
2
3use crate::prelude::*;
4use bevy::{
5    ecs::{lifecycle::HookContext, world::DeferredWorld},
6    prelude::*,
7};
8#[cfg(feature = "3d")]
9use bevy_heavy::AngularInertiaTensor;
10use derive_more::From;
11#[cfg(feature = "3d")]
12use glam_matrix_extras::{MatConversionError, SymmetricMat3};
13
14mod collider;
15pub use collider::*;
16
17mod computed;
18pub use computed::*;
19
20/// An error returned for an invalid mass.
21#[derive(Clone, Copy, Debug, PartialEq)]
22pub enum MassError {
23    /// The mass is negative.
24    Negative,
25    /// The mass is NaN.
26    NaN,
27}
28
29/// The [mass] of an entity, representing resistance to linear acceleration.
30/// A higher mass requires more force for the same acceleration.
31///
32/// If [`Mass`] is not present, but the entity has a [`Collider`], its [`ColliderMassProperties`]
33/// computed based on the shape and [`ColliderDensity`] will be used instead.
34///
35/// The [`Mass`] component does *not* take into account the masses of child entities, and it is never modified
36/// by the engine. The total mass of a dynamic [rigid body] that *does* consider child entities and colliders
37/// is stored in the [`ComputedMass`] component. It is updated automatically when mass properties are changed,
38/// or when colliders are added or removed.
39///
40/// A total mass of zero is a special case, and is interpreted as infinite mass, meaning the rigid body
41/// will not be affected by any forces.
42///
43/// [mass]: https://en.wikipedia.org/wiki/Mass
44/// [rigid body]: RigidBody
45///
46/// # Usage
47///
48/// The [`Mass`] component can be used to define the mass of a [rigid body] entity or its descendants:
49///
50/// ```
51#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
52#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
53/// # use bevy::prelude::*;
54/// #
55/// # fn setup(mut commands: Commands) {
56/// commands.spawn((
57///     RigidBody::Dynamic,
58///     Collider::capsule(0.5, 1.5),
59///     Mass(5.0),
60/// ));
61/// # }
62/// ```
63///
64/// If no [`Mass`] is present, the [`ComputedMass`] will be computed from the collider
65/// based on its shape and [`ColliderDensity`].
66///
67/// ```
68#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
69#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
70/// # use bevy::prelude::*;
71/// #
72/// # fn setup(mut commands: Commands) {
73/// // Note: `ColliderDensity` is optional, and defaults to `1.0` if not present.
74/// commands.spawn((
75///     RigidBody::Dynamic,
76///     Collider::capsule(0.5, 1.5),
77///     ColliderDensity(2.0),
78/// ));
79/// # }
80/// ```
81///
82/// If the rigid body has child colliders, their masses will be added to the total [`ComputedMass`].
83///
84/// ```
85#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
86#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
87/// # use bevy::prelude::*;
88/// #
89/// # fn setup(mut commands: Commands) {
90/// // Total mass: 10.0 + 5.0 = 15.0
91/// commands.spawn((
92///     RigidBody::Dynamic,
93///     Collider::capsule(0.5, 1.5),
94///     Mass(10.0),
95/// ))
96#[cfg_attr(
97    feature = "2d",
98    doc = ".with_child((Collider::circle(1.0), Mass(5.0)));"
99)]
100#[cfg_attr(
101    feature = "3d",
102    doc = ".with_child((Collider::sphere(1.0), Mass(5.0)));"
103)]
104/// # }
105/// ```
106///
107/// To prevent masses of child entities from contributing to the total [`ComputedMass`],
108/// add the [`NoAutoMass`] component. This can be useful when full control over mass is desired.
109///
110/// ```
111#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
112#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
113/// # use bevy::prelude::*;
114/// #
115/// # fn setup(mut commands: Commands) {
116/// // Total mass: 10.0
117/// commands.spawn((
118///     RigidBody::Dynamic,
119///     Collider::capsule(0.5, 1.5),
120///     Mass(10.0),
121///     NoAutoMass,
122/// ))
123#[cfg_attr(
124    feature = "2d",
125    doc = ".with_child((Collider::circle(1.0), Mass(5.0)));"
126)]
127#[cfg_attr(
128    feature = "3d",
129    doc = ".with_child((Collider::sphere(1.0), Mass(5.0)));"
130)]
131/// # }
132/// ```
133///
134/// # Mass Updates
135///
136/// The [`Mass`] component is never modified by the engine, so you can safely update it at any time.
137///
138/// The total [`ComputedMass`] is updated automatically whenever the mass properties of a [rigid body]
139/// or its descendants change, or when colliders are added or removed. This update is triggered by adding
140/// the [`RecomputeMassProperties`] component, which is removed after the update is performed
141/// in [`MassPropertySystems::UpdateComputedMassProperties`](super::MassPropertySystems::UpdateComputedMassProperties).
142///
143/// To immediately perform a manual update of the total mass properties for a specific rigid body entity,
144/// you can call [`MassPropertyHelper::update_mass_properties`] in a system.
145///
146/// # Related Types
147///
148/// - [`ComputedMass`] stores the total mass of a dynamic [rigid body] that considers child entities and colliders.
149/// - [`NoAutoMass`] disables masses of child entities being taken into account for the [`ComputedMass`].
150/// - [`AngularInertia`] is the rotational equivalent of mass, representing resistance to angular acceleration.
151/// - [`CenterOfMass`] is the local point where the mass is concentrated. Applying forces at this point produces no torque.
152/// - [`MassPropertiesBundle`] is a bundle containing mass properties.
153/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
154///
155/// [`SystemParam`]: bevy::ecs::system::SystemParam
156#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
157#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
158#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
159#[reflect(Debug, Component, Default, PartialEq)]
160pub struct Mass(pub f32);
161
162impl Mass {
163    /// A mass of `0.0`.
164    pub const ZERO: Self = Self(0.0);
165
166    /// Computes the [`Mass`] of the given shape using the given density.
167    ///
168    /// ```
169    #[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
170    #[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
171    /// # use bevy::prelude::*;
172    /// #
173    /// // Compute the mass from a collider with a density of `2.0`.
174    #[cfg_attr(
175        feature = "2d",
176        doc = "let mass = Mass::from_shape(&Collider::circle(1.0), 2.0);"
177    )]
178    #[cfg_attr(
179        feature = "3d",
180        doc = "let mass = Mass::from_shape(&Collider::sphere(1.0), 2.0);"
181    )]
182    ///
183    /// // Bevy's primitive shapes can also be used.
184    #[cfg_attr(
185        feature = "2d",
186        doc = "let mass = Mass::from_shape(&Circle::new(1.0), 2.0);"
187    )]
188    #[cfg_attr(
189        feature = "3d",
190        doc = "let mass = Mass::from_shape(&Sphere::new(1.0), 2.0);"
191    )]
192    /// ```
193    #[inline]
194    pub fn from_shape<T: ComputeMassProperties>(shape: &T, density: f32) -> Self {
195        Self(shape.mass(density))
196    }
197}
198
199// TODO: Add errors for asymmetric and non-positive definite matrices in 3D.
200/// An error returned for an invalid angular inertia.
201#[derive(Clone, Copy, Debug, PartialEq)]
202pub enum AngularInertiaError {
203    /// The angular inertia is negative.
204    Negative,
205    /// The angular inertia is NaN.
206    NaN,
207}
208
209/// The [angular inertia] of an entity, representing resistance to angular acceleration.
210/// A higher angular inertia requires more torque for the same acceleration.
211///
212/// If [`AngularInertia`] is not present, but the entity has a [`Collider`], its [`ColliderMassProperties`]
213/// computed based on the shape and will be used instead. Angular inertia scales with mass,
214/// so a higher [`Mass`] or [`ColliderDensity`] will result in higher inertia.
215///
216/// The [`AngularInertia`] component does *not* take into account the inertia of child entities, and it is never modified
217/// by the engine. The total angular inertia of a dynamic [rigid body] that *does* consider child entities and colliders
218/// is stored in the [`ComputedAngularInertia`] component. It is updated automatically when mass properties are changed,
219/// or when colliders are added or removed.
220///
221/// A total angular inertia of zero is a special case, and is interpreted as infinite inertia, meaning the rigid body
222/// will not be affected by any torque.
223///
224/// [angular inertia]: https://en.wikipedia.org/wiki/Moment_of_inertia
225/// [rigid body]: RigidBody
226///
227/// # Usage
228///
229/// The [`AngularInertia`] component can be used to define the angular inertia of a [rigid body]
230/// entity or its descendants:
231///
232/// ```
233/// # use avian2d::prelude::*;
234/// # use bevy::prelude::*;
235/// #
236/// # fn setup(mut commands: Commands) {
237/// commands.spawn((
238///     RigidBody::Dynamic,
239///     Collider::capsule(0.5, 1.5),
240///     AngularInertia(2.0),
241/// ));
242/// # }
243/// ```
244///
245/// If no [`AngularInertia`] is present, the [`ComputedAngularInertia`] will be computed from the collider
246/// based on its mass and shape.
247///
248/// ```
249/// # use avian2d::prelude::*;
250/// # use bevy::prelude::*;
251/// #
252/// # fn setup(mut commands: Commands) {
253/// // Note: `ColliderDensity` is optional, and defaults to `1.0` if not present.
254/// commands.spawn((
255///     RigidBody::Dynamic,
256///     Collider::capsule(0.5, 1.5),
257///     ColliderDensity(2.0),
258/// ));
259/// # }
260/// ```
261///
262/// If the rigid body has child colliders, their angular inertia will be added to the total [`ComputedAngularInertia`].
263///
264/// ```
265/// # use avian2d::prelude::*;
266/// # use bevy::prelude::*;
267/// #
268/// # fn setup(mut commands: Commands) {
269/// // Total angular inertia: 3.0 + 2.0 = 5.0
270/// commands.spawn((
271///     RigidBody::Dynamic,
272///     Collider::capsule(0.5, 1.5),
273///     AngularInertia(3.0),
274/// ))
275/// .with_child((Collider::circle(1.0), AngularInertia(2.0)));
276/// # }
277/// ```
278///
279/// To prevent angular inertia of child entities from contributing to the total [`ComputedAngularInertia`],
280/// add the [`NoAutoAngularInertia`] component. This can be useful when full control over inertia is desired.
281///
282/// ```
283/// # use avian2d::prelude::*;
284/// # use bevy::prelude::*;
285/// #
286/// # fn setup(mut commands: Commands) {
287/// // Total angular inertia: 3.0
288/// commands.spawn((
289///     RigidBody::Dynamic,
290///     Collider::capsule(0.5, 1.5),
291///     AngularInertia(3.0),
292///     NoAutoAngularInertia,
293/// ))
294/// .with_child((Collider::circle(1.0), AngularInertia(2.0)));
295/// # }
296/// ```
297///
298/// # Angular Inertia Updates
299///
300/// The [`AngularInertia`] component is never modified by the engine, so you can safely update it at any time.
301///
302/// The total [`ComputedAngularInertia`] is updated automatically whenever the mass properties of a [rigid body]
303/// or its descendants change, or when colliders are added or removed. This update is triggered by adding
304/// the [`RecomputeMassProperties`] component, which is removed after the update is performed
305/// in [`MassPropertySystems::UpdateComputedMassProperties`](super::MassPropertySystems::UpdateComputedMassProperties).
306///
307/// To immediately perform a manual update of the total mass properties for a specific rigid body entity,
308/// you can call [`MassPropertyHelper::update_mass_properties`] in a system.
309///
310/// # Related Types
311///
312/// - [`ComputedAngularInertia`] stores the total angular inertia of a dynamic [rigid body] that considers child entities and colliders.
313/// - [`NoAutoAngularInertia`] disables the mass properties of child entities being taken into account for the [`ComputedAngularInertia`].
314/// - [`Mass`] is the linear equivalent of angular inertia, representing resistance to linear acceleration.
315/// - [`CenterOfMass`] is the local point where the mass is concentrated. Applying forces at this point produces no torque.
316/// - [`MassPropertiesBundle`] is a bundle containing mass properties.
317/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
318///
319/// [`SystemParam`]: bevy::ecs::system::SystemParam
320#[cfg(feature = "2d")]
321#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
322#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
323#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
324#[reflect(Debug, Component, Default, PartialEq)]
325#[doc(alias = "MomentOfInertia")]
326pub struct AngularInertia(pub f32);
327
328#[cfg(feature = "2d")]
329impl AngularInertia {
330    /// An angular inertia of `0.0`.
331    pub const ZERO: Self = Self(0.0);
332
333    /// Computes the [`AngularInertia`] of the given shape using the given mass.
334    ///
335    /// ```
336    /// # use avian2d::prelude::*;
337    /// # use bevy::prelude::*;
338    /// #
339    /// // Compute the angular inertia from a collider with a mass of `2.0`.
340    /// let inertia = AngularInertia::from_shape(&Collider::circle(1.0), 2.0);
341    ///
342    /// // Bevy's primitive shapes can also be used.
343    /// let inertia = AngularInertia::from_shape(&Circle::new(1.0), 2.0);
344    /// ```
345    #[inline]
346    pub fn from_shape<T: ComputeMassProperties>(shape: &T, mass: f32) -> Self {
347        Self(shape.angular_inertia(mass))
348    }
349
350    /// Computes the angular inertia shifted by the given offset, taking into account the given mass.
351    #[inline]
352    pub fn shifted(&self, mass: f32, offset: Vec2) -> f32 {
353        if mass > 0.0 && mass.is_finite() && offset != Vec2::ZERO {
354            self.0 + offset.length_squared() * mass
355        } else {
356            self.0
357        }
358    }
359}
360
361/// The local [angular inertia] of an entity, representing resistance to angular acceleration.
362/// A higher angular inertia requires more torque for the same acceleration.
363///
364/// If [`AngularInertia`] is not present, but the entity has a [`Collider`], its [`ColliderMassProperties`]
365/// computed based on the shape and [mass] will be used instead. Angular inertia scales with mass,
366/// so a higher [`Mass`] or [`ColliderDensity`] will result in higher inertia.
367///
368/// The [`AngularInertia`] component does *not* take into account the inertia of child entities, and it is never modified
369/// by the engine. The total angular inertia of a dynamic [rigid body] that *does* consider child entities and colliders
370/// is stored in the [`ComputedAngularInertia`] component. It is updated automatically when mass properties are changed,
371/// or when colliders are added or removed.
372///
373/// A total angular inertia of zero is a special case, and is interpreted as infinite inertia, meaning the rigid body
374/// will not be affected by any torques.
375///
376/// See the [module-level documentation] for more general information on angular inertia.
377///
378/// [angular inertia]: https://en.wikipedia.org/wiki/Moment_of_inertia
379/// [rigid body]: RigidBody
380/// [mass]: super#mass
381/// [module-level documentation]: super#angular-inertia
382///
383/// # Usage
384///
385/// [`AngularInertia`] is defined by the moment of inertia along the *principal axes* (a [`Vec3`])
386/// defined by the *local inertial frame* (a [`Quat`]). Most entities will have a local inertial frame
387/// of [`Quat::IDENTITY`], which means that the principal axes are aligned with the entity's local axes.
388///
389/// ```
390/// # use avian3d::prelude::*;
391/// # use bevy::prelude::*;
392/// # use core::f32::consts::PI;
393/// #
394/// # fn setup(mut commands: Commands) {
395/// // Principal angular inertia: `2.0` for the local X and Z axes, and `5.0` for the Y axis.
396/// let inertia1 = AngularInertia::new(Vec3::new(2.0, 5.0, 2.0));
397///
398/// // A local inertial frame rotated by 90 degrees about the X axis.
399/// let inertia2 = AngularInertia::new_with_local_frame(Vec3::new(2.0, 5.0, 2.0), Quat::from_rotation_x(PI / 2.0));
400/// # }
401///
402/// ```
403///
404/// [`AngularInertia`] can also be created from a symmetric 3x3 matrix known as the *[angular inertia tensor]*.
405/// It summarizes all moments of inertia of an object in a single matrix, and can be used to perform
406/// computations with angular inertia, but is often not constructed manually.
407///
408/// [angular inertia tensor]: AngularInertiaTensor
409///
410/// ```
411/// # use avian3d::prelude::*;
412/// # use bevy::prelude::*;
413/// #
414/// # fn setup(mut commands: Commands) {
415/// // For simplicity, we use the same principal angular inertia as before, with an identity local frame.
416/// let inertia1 = AngularInertia::from_tensor(AngularInertiaTensor::new(Vec3::new(2.0, 5.0, 2.0)));
417///
418/// // The angular inertia tensor can be retrieved back from the principal angular inertia.
419/// let tensor = inertia1.tensor();
420/// # }
421/// ```
422///
423/// The [`AngularInertia`] component can be used to define the angular inertia of a [rigid body]
424/// entity or its descendants:
425///
426/// ```
427/// # use avian3d::prelude::*;
428/// # use bevy::prelude::*;
429/// #
430/// # fn setup(mut commands: Commands) {
431/// commands.spawn((
432///     RigidBody::Dynamic,
433///     Collider::capsule(0.5, 1.5),
434///     AngularInertia::new(Vec3::new(2.0, 5.0, 2.0)),
435/// ));
436/// # }
437/// ```
438///
439/// If no [`AngularInertia`] is present, the [`ComputedAngularInertia`] will be computed from the collider
440/// based on its mass and shape.
441///
442/// ```
443/// # use avian3d::prelude::*;
444/// # use bevy::prelude::*;
445/// #
446/// # fn setup(mut commands: Commands) {
447/// // Note: `ColliderDensity` is optional, and defaults to `1.0` if not present.
448/// commands.spawn((
449///     RigidBody::Dynamic,
450///     Collider::capsule(0.5, 1.5),
451///     ColliderDensity(2.0),
452/// ));
453/// # }
454/// ```
455///
456/// If the rigid body has child colliders, their angular inertia will be added to the total [`ComputedAngularInertia`].
457/// Here both entities are at the origin, but offsets relative to the center of mass would also impact the total inertia.
458///
459/// ```
460/// # use avian3d::prelude::*;
461/// # use bevy::prelude::*;
462/// #
463/// # fn setup(mut commands: Commands) {
464/// // Total angular inertia: [2.0, 5.0, 2.0] + [1.0, 2.0, 1.0] = [3.0, 7.0, 3.0]
465/// commands.spawn((
466///     RigidBody::Dynamic,
467///     Collider::capsule(0.5, 1.5),
468///     AngularInertia::new(Vec3::new(2.0, 5.0, 2.0)),
469/// ))
470/// .with_child((Collider::sphere(1.0), AngularInertia::new(Vec3::new(1.0, 2.0, 1.0))));
471/// # }
472/// ```
473///
474/// To prevent angular inertia of child entities from contributing to the total [`ComputedAngularInertia`],
475/// add the [`NoAutoAngularInertia`] component. This can be useful when full control over inertia is desired.
476///
477/// ```
478/// # use avian3d::prelude::*;
479/// # use bevy::prelude::*;
480/// #
481/// # fn setup(mut commands: Commands) {
482/// // Total angular inertia: [2.0, 5.0, 2.0]
483/// commands.spawn((
484///     RigidBody::Dynamic,
485///     Collider::capsule(0.5, 1.5),
486///     AngularInertia::new(Vec3::new(2.0, 5.0, 2.0)),
487///     NoAutoAngularInertia,
488/// ))
489/// .with_child((Collider::sphere(1.0), AngularInertia::new(Vec3::new(1.0, 2.0, 1.0))));
490/// # }
491/// ```
492///
493/// # Angular Inertia Updates
494///
495/// The [`AngularInertia`] component is never modified by the engine, so you can safely update it at any time.
496///
497/// The total [`ComputedAngularInertia`] is updated automatically whenever the mass properties of a [rigid body]
498/// or its descendants change, or when colliders are added or removed. This update is triggered by adding
499/// the [`RecomputeMassProperties`] component, which is removed after the update is performed
500/// in [`MassPropertySystems::UpdateComputedMassProperties`](super::MassPropertySystems::UpdateComputedMassProperties).
501///
502/// To immediately perform a manual update of the total mass properties for a specific rigid body entity,
503/// you can call [`MassPropertyHelper::update_mass_properties`] in a system.
504///
505/// # Related Types
506///
507/// - [`ComputedAngularInertia`] stores the total angular inertia of a dynamic [rigid body] that considers child entities and colliders.
508/// - [`NoAutoAngularInertia`] disables the mass properties of child entities being taken into account for the [`ComputedAngularInertia`].
509/// - [`AngularInertiaTensor`] is the symmetric 3x3 matrix representation of angular inertia.
510/// - [`Mass`] is the linear equivalent of angular inertia, representing resistance to linear acceleration.
511/// - [`CenterOfMass`] is the local point where the mass is concentrated. Applying forces at this point produces no torque.
512/// - [`MassPropertiesBundle`] is a bundle containing mass properties.
513/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
514///
515/// [`SystemParam`]: bevy::ecs::system::SystemParam
516#[cfg(feature = "3d")]
517#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
518#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
519#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
520#[reflect(Debug, Component, PartialEq)]
521#[doc(alias = "MomentOfInertia")]
522pub struct AngularInertia {
523    /// The principal angular inertia, representing resistance to angular acceleration
524    /// about the local coordinate axes defined by the `local_frame`.
525    pub principal: Vec3,
526    /// The orientation of the local inertial frame.
527    pub local_frame: Quat,
528}
529
530#[cfg(feature = "3d")]
531impl AngularInertia {
532    /// An angular inertia of `0.0` for all axes, with an identity local frame.
533    pub const ZERO: Self = Self {
534        principal: Vec3::ZERO,
535        local_frame: Quat::IDENTITY,
536    };
537
538    /// Creates a new [`AngularInertia`] from the given principal angular inertia.
539    ///
540    /// The principal angular inertia represents resistance to angular acceleration
541    /// about the local coordinate axes.
542    ///
543    /// To specify the orientation of the local inertial frame, consider using [`AngularInertia::new_with_local_frame`].
544    ///
545    /// # Panics
546    ///
547    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
548    #[inline]
549    #[doc(alias = "from_principal_angular_inertia")]
550    pub fn new(principal_angular_inertia: Vec3) -> Self {
551        debug_assert!(
552            principal_angular_inertia.cmpge(Vec3::ZERO).all()
553                && !principal_angular_inertia.is_nan(),
554            "principal angular inertia must be positive or zero for all axes"
555        );
556
557        Self {
558            principal: principal_angular_inertia,
559            local_frame: Quat::IDENTITY,
560        }
561    }
562
563    /// Tries to create a new [`AngularInertia`] from the given principal angular inertia.
564    ///
565    /// The principal angular inertia represents resistance to angular acceleration
566    /// about the local coordinate axes. To specify the orientation of the local inertial frame,
567    /// consider using [`AngularInertia::try_new_with_local_frame`].
568    ///
569    /// # Errors
570    ///
571    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
572    #[inline]
573    pub fn try_new(principal_angular_inertia: Vec3) -> Result<Self, AngularInertiaError> {
574        if principal_angular_inertia.is_nan() {
575            Err(AngularInertiaError::NaN)
576        } else if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
577            Err(AngularInertiaError::Negative)
578        } else {
579            Ok(Self {
580                principal: principal_angular_inertia,
581                local_frame: Quat::IDENTITY,
582            })
583        }
584    }
585
586    /// Creates a new [`AngularInertia`] from the given principal angular inertia
587    /// and the orientation of the local inertial frame.
588    ///
589    /// The principal angular inertia represents resistance to angular acceleration
590    /// about the local coordinate axes defined by the given `local_frame`.
591    ///
592    /// # Panics
593    ///
594    /// Panics if any component of the principal angular inertia is negative or NaN when `debug_assertions` are enabled.
595    #[inline]
596    #[doc(alias = "from_principal_angular_inertia_with_local_frame")]
597    pub fn new_with_local_frame(principal_angular_inertia: Vec3, local_frame: Quat) -> Self {
598        debug_assert!(
599            principal_angular_inertia.cmpge(Vec3::ZERO).all()
600                && !principal_angular_inertia.is_nan(),
601            "principal angular inertia must be positive or zero for all axes"
602        );
603
604        Self {
605            principal: principal_angular_inertia,
606            local_frame,
607        }
608    }
609
610    /// Tries to create a new [`AngularInertia`] from the given principal angular inertia
611    /// and the orientation of the local inertial frame.
612    ///
613    /// The principal angular inertia represents resistance to angular acceleration
614    /// about the local coordinate axes defined by the given `local_frame`.
615    ///
616    /// # Errors
617    ///
618    /// Returns [`Err(AngularInertiaError)`](AngularInertiaError) if any component of the principal angular inertia is negative or NaN.
619    #[inline]
620    pub fn try_new_with_local_frame(
621        principal_angular_inertia: Vec3,
622        local_frame: Quat,
623    ) -> Result<Self, AngularInertiaError> {
624        if principal_angular_inertia.is_nan() {
625            Err(AngularInertiaError::NaN)
626        } else if !principal_angular_inertia.cmpge(Vec3::ZERO).all() {
627            Err(AngularInertiaError::Negative)
628        } else {
629            Ok(Self {
630                principal: principal_angular_inertia,
631                local_frame,
632            })
633        }
634    }
635
636    /// Creates a new [`AngularInertia`] from the given [angular inertia tensor].
637    ///
638    /// The tensor should be symmetric and positive definite.
639    ///
640    /// [angular inertia tensor]: AngularInertiaTensor
641    #[inline]
642    #[doc(alias = "from_mat3")]
643    pub fn from_tensor(tensor: impl Into<AngularInertiaTensor>) -> Self {
644        let tensor = tensor.into();
645        let (principal, local_frame) = tensor.principal_angular_inertia_with_local_frame();
646
647        Self {
648            principal,
649            local_frame,
650        }
651    }
652
653    /// Creates a new [`AngularInertiaTensor`] from the given angular inertia [tensor]
654    /// represented as a [`SymmetricMat3`].
655    ///
656    /// The tensor should be [positive-semidefinite], but this is *not* checked.
657    ///
658    /// [tensor]: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
659    /// [positive-semidefinite]: https://en.wikipedia.org/wiki/Definite_matrix
660    #[inline]
661    #[must_use]
662    #[doc(alias = "from_tensor")]
663    pub fn from_symmetric_mat3(mat: SymmetricMat3) -> Self {
664        Self::from_tensor(AngularInertiaTensor::from_symmetric_mat3(mat))
665    }
666
667    /// Tries to create a new [`AngularInertiaTensor`] from the given angular inertia [tensor]
668    /// represented as a [`Mat3`].
669    ///
670    /// The tensor should be [positive-semidefinite], but this is *not* checked.
671    ///
672    /// [tensor]: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
673    /// [positive-semidefinite]: https://en.wikipedia.org/wiki/Definite_matrix
674    ///
675    /// # Errors
676    ///
677    /// Returns a [`MatConversionError`] if the given matrix is not symmetric.
678    #[inline]
679    pub fn try_from_mat3(mat: Mat3) -> Result<Self, MatConversionError> {
680        SymmetricMat3::try_from_mat3(mat).map(Self::from_tensor)
681    }
682
683    /// Creates a new [`AngularInertiaTensor`] from the given angular inertia [tensor]
684    /// represented as a [`Mat3`].
685    ///
686    /// Only the lower left triangle of the matrix is used. No check is performed to ensure
687    /// that the given matrix is truly symmetric or [positive-semidefinite].
688    ///
689    /// [tensor]: https://en.wikipedia.org/wiki/Moment_of_inertia#Inertia_tensor
690    /// [positive-semidefinite]: https://en.wikipedia.org/wiki/Definite_matrix
691    #[inline]
692    #[must_use]
693    pub fn from_mat3_unchecked(mat: Mat3) -> Self {
694        Self::from_tensor(SymmetricMat3::from_mat3_unchecked(mat))
695    }
696
697    /// Computes the [`AngularInertia`] of the given shape using the given mass.
698    ///
699    /// ```
700    /// # use avian3d::prelude::*;
701    /// # use bevy::prelude::*;
702    /// #
703    /// // Compute the angular inertia from collider with a mass of `2.0`.
704    /// let inertia = AngularInertia::from_shape(&Collider::sphere(1.0), 2.0);
705    ///
706    /// // Bevy's primitive shapes can also be used.
707    /// let inertia = AngularInertia::from_shape(&Sphere::new(1.0), 2.0);
708    /// ```
709    #[inline]
710    pub fn from_shape<T: ComputeMassProperties>(shape: &T, mass: f32) -> Self {
711        let principal = shape.principal_angular_inertia(mass);
712        let local_frame = shape.local_inertial_frame();
713        Self::new_with_local_frame(principal, local_frame)
714    }
715
716    /// Returns the [`AngularInertiaTensor`] represented by this principal [`AngularInertia`]
717    /// and local inertial frame.
718    #[inline]
719    pub fn tensor(self) -> AngularInertiaTensor {
720        AngularInertiaTensor::new_with_local_frame(self.principal, self.local_frame)
721    }
722
723    /// Returns `true` if the principal angular inertia and inertial local frame are neither infinite nor NaN.
724    #[inline]
725    pub fn is_finite(self) -> bool {
726        self.principal.is_finite() && self.local_frame.is_finite()
727    }
728
729    /// Returns `true` if the principal angular inertia or inertial local frame is NaN.
730    #[inline]
731    pub fn is_nan(self) -> bool {
732        self.principal.is_nan() || self.local_frame.is_nan()
733    }
734}
735
736#[cfg(feature = "3d")]
737impl TryFrom<Mat3> for AngularInertia {
738    type Error = MatConversionError;
739
740    fn try_from(mat: Mat3) -> Result<Self, Self::Error> {
741        Self::try_from_mat3(mat)
742    }
743}
744
745#[cfg(feature = "3d")]
746impl From<SymmetricMat3> for AngularInertia {
747    fn from(tensor: SymmetricMat3) -> Self {
748        Self::from_tensor(tensor)
749    }
750}
751
752#[cfg(feature = "3d")]
753impl From<AngularInertiaTensor> for AngularInertia {
754    fn from(tensor: AngularInertiaTensor) -> Self {
755        Self::from_tensor(tensor)
756    }
757}
758
759#[cfg(feature = "3d")]
760impl From<AngularInertia> for AngularInertiaTensor {
761    fn from(inertia: AngularInertia) -> Self {
762        inertia.tensor()
763    }
764}
765
766/// The local [center of mass] of an entity, representing the average position of mass
767/// in the object. Applying forces at this point produces no torque.
768///
769/// If [`CenterOfMass`] is not present, but the entity has a [`Collider`], its [`ColliderMassProperties`]
770/// computed based on the shape will be used instead.
771///
772/// The [`CenterOfMass`] component does *not* take into account child entities, and it is never modified
773/// by the engine. The total center of mass of a dynamic [rigid body] that *does* consider child entities and colliders
774/// is stored in the [`ComputedCenterOfMass`] component. It is updated automatically when mass properties are changed,
775/// or when colliders are added or removed.
776///
777/// [center of mass]: https://en.wikipedia.org/wiki/Center_of_mass
778/// [rigid body]: RigidBody
779///
780/// # Usage
781///
782/// The [`CenterOfMass`] component can be used to define the center of mass of a [rigid body] entity or its descendants:
783///
784/// ```
785#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
786#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
787/// # use bevy::prelude::*;
788/// #
789/// # fn setup(mut commands: Commands) {
790/// // An offset of `-0.5` along the Y axis.
791/// commands.spawn((
792///     RigidBody::Dynamic,
793///     Collider::capsule(0.5, 1.5),
794#[cfg_attr(feature = "2d", doc = "    CenterOfMass::new(0.0, -0.5),")]
795#[cfg_attr(feature = "3d", doc = "    CenterOfMass::new(0.0, -0.5, 0.0),")]
796/// ));
797/// # }
798/// ```
799///
800/// If no [`CenterOfMass`] is present, the [`ComputedCenterOfMass`] will be computed from the collider
801/// based on its shape.
802///
803/// ```
804#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
805#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
806/// # use bevy::prelude::*;
807/// #
808/// # fn setup(mut commands: Commands) {
809/// // For a capsule, the center of mass is at the local origin.
810/// commands.spawn((RigidBody::Dynamic, Collider::capsule(0.5, 1.5)));
811/// # }
812/// ```
813///
814/// If the rigid body has child colliders, they will contribute to the total [`ComputedCenterOfMass`]
815/// based on weighted average of their global centers of mass.
816///
817/// ```
818#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
819#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
820/// # use bevy::prelude::*;
821/// #
822/// # fn setup(mut commands: Commands) {
823#[cfg_attr(
824    feature = "2d",
825    doc = "// Total center of mass: (10.0 * [0.0, -0.5] + 5.0 * [0.0, 4.0]) / (10.0 + 5.0) = [0.0, 1.0]"
826)]
827#[cfg_attr(
828    feature = "3d",
829    doc = "// Total center of mass: (10.0 * [0.0, -0.5, 0.0] + 5.0 * [0.0, 4.0, 0.0]) / (10.0 + 5.0) = [0.0, 1.0, 0.0]"
830)]
831/// commands.spawn((
832///     RigidBody::Dynamic,
833///     Collider::capsule(0.5, 1.5),
834///     Mass(10.0),
835#[cfg_attr(feature = "2d", doc = "    CenterOfMass::new(0.0, -0.5),")]
836#[cfg_attr(feature = "3d", doc = "    CenterOfMass::new(0.0, -0.5, 0.0),")]
837///     Transform::default(),
838/// ))
839/// .with_child((
840#[cfg_attr(feature = "2d", doc = "    Collider::circle(1.0),")]
841#[cfg_attr(feature = "3d", doc = "    Collider::sphere(1.0),")]
842///     Mass(5.0),
843///     Transform::from_xyz(0.0, 4.0, 0.0),
844/// ));
845/// # }
846/// ```
847///
848/// To prevent the centers of mass of child entities from contributing to the total [`ComputedCenterOfMass`],
849/// add the [`NoAutoCenterOfMass`], component. This can be useful when full control over the center of mass is desired.
850///
851/// ```
852#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
853#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
854/// # use bevy::prelude::*;
855/// #
856/// # fn setup(mut commands: Commands) {
857#[cfg_attr(feature = "2d", doc = "// Total center of mass: [0.0, -0.5]")]
858#[cfg_attr(feature = "3d", doc = "// Total center of mass: [0.0, -0.5, 0.0]")]
859/// commands.spawn((
860///     RigidBody::Dynamic,
861///     Collider::capsule(0.5, 1.5),
862#[cfg_attr(feature = "2d", doc = "    CenterOfMass::new(0.0, -0.5),")]
863#[cfg_attr(feature = "3d", doc = "    CenterOfMass::new(0.0, -0.5, 0.0),")]
864///     Transform::default(),
865/// ))
866/// .with_child((
867#[cfg_attr(feature = "2d", doc = "    Collider::circle(1.0),")]
868#[cfg_attr(feature = "3d", doc = "    Collider::sphere(1.0),")]
869///     Mass(5.0),
870///     Transform::from_xyz(0.0, 4.0, 0.0),
871/// ));
872/// # }
873/// ```
874///
875/// # Center of Mass Updates
876///
877/// The [`CenterOfMass`] component is never modified by the engine, so you can safely update it at any time.
878///
879/// The total [`ComputedCenterOfMass`] is updated automatically whenever the mass properties of a [rigid body]
880/// or its descendants change, or when colliders are added, removed, or transformed. This update is triggered by adding
881/// the [`RecomputeMassProperties`] component, which is removed after the update is performed
882/// in [`MassPropertySystems::UpdateComputedMassProperties`](super::MassPropertySystems::UpdateComputedMassProperties).
883///
884/// To immediately perform a manual update of the total mass properties for a specific rigid body entity,
885/// you can call [`MassPropertyHelper::update_mass_properties`] in a system.
886///
887/// # Related Types
888///
889/// - [`ComputedCenterOfMass`] stores the total center of mass of a dynamic [rigid body] that considers child entities and colliders.
890/// - [`NoAutoCenterOfMass`] disables the centers of mass of child entities being taken into account for the [`ComputedCenterOfMass`].
891/// - [`Mass`] represents resistance to linear acceleration.
892/// - [`AngularInertia`] is the rotational equivalent of mass, representing resistance to angular acceleration.
893/// - [`MassPropertiesBundle`] is a bundle containing mass properties.
894/// - [`MassPropertyHelper`] is a [`SystemParam`] with utilities for computing and updating mass properties.
895///
896/// [`SystemParam`]: bevy::ecs::system::SystemParam
897#[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)]
898#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
899#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
900#[reflect(Debug, Component, Default, PartialEq)]
901pub struct CenterOfMass(pub Vector);
902
903impl CenterOfMass {
904    /// A center of mass set at the local origin.
905    pub const ZERO: Self = Self(Vector::ZERO);
906
907    /// Creates a new [`CenterOfMass`] at the given local position.
908    #[inline]
909    #[cfg(feature = "2d")]
910    pub const fn new(x: f32, y: f32) -> Self {
911        Self(Vec2::new(x, y))
912    }
913
914    /// Creates a new [`CenterOfMass`] at the given local position.
915    #[inline]
916    #[cfg(feature = "3d")]
917    pub const fn new(x: f32, y: f32, z: f32) -> Self {
918        Self(Vec3::new(x, y, z))
919    }
920
921    /// Computes the [`CenterOfMass`] of the given shape.
922    ///
923    /// ```
924    #[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
925    #[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
926    /// # use bevy::prelude::*;
927    /// #
928    /// // Compute the center of mass from a collider.
929    #[cfg_attr(
930        feature = "2d",
931        doc = "let center_of_mass = CenterOfMass::from_shape(&Collider::circle(1.0));"
932    )]
933    #[cfg_attr(
934        feature = "3d",
935        doc = "let center_of_mass = CenterOfMass::from_shape(&Collider::sphere(1.0));"
936    )]
937    ///
938    /// // Bevy's primitive shapes can also be used.
939    #[cfg_attr(
940        feature = "2d",
941        doc = "let center_of_mass = CenterOfMass::from_shape(&Circle::new(1.0));"
942    )]
943    #[cfg_attr(
944        feature = "3d",
945        doc = "let center_of_mass = CenterOfMass::from_shape(&Sphere::new(1.0));"
946    )]
947    /// ```
948    #[inline]
949    pub fn from_shape<T: ComputeMassProperties>(shape: &T) -> Self {
950        Self(shape.center_of_mass())
951    }
952}
953
954/// A marker component that prevents descendants or attached colliders
955/// from contributing to the total [`ComputedMass`] of a [rigid body].
956///
957/// Only the [`Mass`] component of the rigid body entity itself will be considered.
958/// This is useful when full control over mass is desired.
959///
960/// [rigid body]: RigidBody
961#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
962#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
963#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
964#[reflect(Debug, Component, Default, PartialEq)]
965#[require(RecomputeMassProperties)]
966#[component(on_remove = on_remove_no_auto_mass_property)]
967pub struct NoAutoMass;
968
969/// A marker component that prevents descendants or attached colliders
970/// from contributing to the total [`ComputedAngularInertia`] of a [rigid body].
971///
972/// Only the [`AngularInertia`] component of the rigid body entity itself will be considered.
973/// This is useful when full control over inertia is desired.
974///
975/// [rigid body]: RigidBody
976#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
977#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
978#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
979#[reflect(Debug, Component, Default, PartialEq)]
980#[require(RecomputeMassProperties)]
981#[component(on_remove = on_remove_no_auto_mass_property)]
982pub struct NoAutoAngularInertia;
983
984/// A marker component that prevents descendants or attached colliders
985/// from contributing to the total [`ComputedCenterOfMass`] of a [rigid body].
986///
987/// Only the [`CenterOfMass`] component of the rigid body entity itself will be considered.
988/// This is useful when full control over the center of mass is desired.
989///
990/// [rigid body]: RigidBody
991#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
992#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
993#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
994#[reflect(Debug, Component, Default, PartialEq)]
995#[require(RecomputeMassProperties)]
996#[component(on_remove = on_remove_no_auto_mass_property)]
997pub struct NoAutoCenterOfMass;
998
999/// Triggers the recomputation of mass properties for rigid bodies when automatic computation is re-enabled.
1000fn on_remove_no_auto_mass_property(mut world: DeferredWorld, ctx: HookContext) {
1001    if let Ok(mut entity_commands) = world.commands().get_entity(ctx.entity) {
1002        entity_commands.try_insert(RecomputeMassProperties);
1003    }
1004}
1005
1006/// A marker component that forces the recomputation of [`ComputedMass`], [`ComputedAngularInertia`]
1007/// and [`ComputedCenterOfMass`] for a [rigid body].
1008///
1009/// This is added automatically when the mass properties of a [rigid body] change, or when colliders are added or removed,
1010/// and is removed after the recomputation in [`MassPropertySystems::UpdateComputedMassProperties`].
1011///
1012/// [rigid body]: RigidBody
1013/// [`MassPropertySystems::UpdateComputedMassProperties`]: super::MassPropertySystems::UpdateComputedMassProperties
1014#[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq)]
1015#[component(storage = "SparseSet")]
1016pub struct RecomputeMassProperties;
1017
1018/// A bundle containing [mass properties].
1019///
1020/// [mass properties]: super
1021#[allow(missing_docs)]
1022#[derive(Bundle, Debug, Default, Clone, PartialEq)]
1023#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1024pub struct MassPropertiesBundle {
1025    pub mass: Mass,
1026    pub angular_inertia: AngularInertia,
1027    pub center_of_mass: CenterOfMass,
1028}
1029
1030impl MassPropertiesBundle {
1031    /// Computes the mass properties for a [`Collider`] based on its shape and a given density.
1032    ///
1033    /// ```
1034    #[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
1035    #[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
1036    /// # use bevy::prelude::*;
1037    /// #
1038    /// # fn setup(mut commands: Commands) {
1039    /// // Compute mass properties from a collider with a density of `2.0`.
1040    /// commands.spawn((
1041    ///     RigidBody::Dynamic,
1042    #[cfg_attr(
1043        feature = "2d",
1044        doc = "    MassPropertiesBundle::from_shape(&Collider::circle(0.5), 2.0),"
1045    )]
1046    #[cfg_attr(
1047        feature = "3d",
1048        doc = "    MassPropertiesBundle::from_shape(&Collider::sphere(0.5), 2.0),"
1049    )]
1050    /// ));
1051    ///
1052    /// // Bevy's primitive shapes can also be used.
1053    /// commands.spawn((
1054    ///     RigidBody::Dynamic,
1055    #[cfg_attr(
1056        feature = "2d",
1057        doc = "    MassPropertiesBundle::from_shape(&Circle::new(0.5), 2.0),"
1058    )]
1059    #[cfg_attr(
1060        feature = "3d",
1061        doc = "    MassPropertiesBundle::from_shape(&Sphere::new(0.5), 2.0),"
1062    )]
1063    /// ));
1064    /// # }
1065    /// ```
1066    #[inline]
1067    pub fn from_shape<T: ComputeMassProperties>(shape: &T, density: f32) -> Self {
1068        shape.mass_properties(density).to_bundle()
1069    }
1070}
1071
1072#[cfg(test)]
1073mod tests {
1074    #[cfg(feature = "3d")]
1075    use super::*;
1076    #[cfg(feature = "3d")]
1077    use approx::assert_relative_eq;
1078
1079    #[test]
1080    #[cfg(feature = "3d")]
1081    fn angular_inertia_creation() {
1082        use bevy_heavy::AngularInertiaTensor;
1083
1084        let angular_inertia = AngularInertia::new(Vec3::new(10.0, 20.0, 30.0));
1085        assert_eq!(angular_inertia.principal, Vec3::new(10.0, 20.0, 30.0));
1086        assert_eq!(angular_inertia.local_frame, Quat::IDENTITY);
1087        assert_relative_eq!(
1088            angular_inertia.tensor(),
1089            AngularInertiaTensor::new(Vec3::new(10.0, 20.0, 30.0))
1090        );
1091    }
1092
1093    #[test]
1094    #[should_panic]
1095    #[cfg(feature = "3d")]
1096    fn negative_angular_inertia_panics() {
1097        AngularInertia::new(Vec3::new(-1.0, 2.0, 3.0));
1098    }
1099
1100    #[test]
1101    #[cfg(feature = "3d")]
1102    fn negative_angular_inertia_error() {
1103        assert_eq!(
1104            AngularInertia::try_new(Vec3::new(-1.0, 2.0, 3.0)),
1105            Err(AngularInertiaError::Negative),
1106            "negative angular inertia should return an error"
1107        );
1108    }
1109
1110    #[test]
1111    #[cfg(feature = "3d")]
1112    fn nan_angular_inertia_error() {
1113        assert_eq!(
1114            AngularInertia::try_new(Vec3::new(f32::NAN, 2.0, 3.0)),
1115            Err(AngularInertiaError::NaN),
1116            "NaN angular inertia should return an error"
1117        );
1118    }
1119}