Improve the API for initializing/setting mass-properties

This commit is contained in:
Sébastien Crozet
2022-07-05 14:59:14 +02:00
parent b8d46a6b1d
commit ba081fb6f5
7 changed files with 294 additions and 175 deletions

View File

@@ -1,3 +1,26 @@
## Unreleased
### Fixed
### Modified
- The `RigidBodyBuilder::additional_mass` method will now result in the additional angular inertia
being automatically computed based on the shapes of the colliders attached to the rigid-body.
- Remove the deprecated methods `RigidBodyBuilder::mass`, `::principal_angular_inertia`, `::principal_inertia`.
- Remove the methods `RigidBodyBuilder::additional_principal_angular_inertia`. Use
`RigidBodyBuilder::additional_mass_properties` instead.
### Added
- Add `RigidBody::recompute_mass_properties_from_colliders` to force the immediate computation
of a rigid-bodys mass properties (instead of waiting for them to be recomputed during the next
timestep). This is useful to be able to read immediately the result of a change of a rigid-body
additional mass-properties or a change of one of its colliders mass-properties.
- Add `RigidBody::set_additional_mass` to set the additional mass for the collider. The additional
angular inertia is automatically computed based on the attached colliders shapes.
- Add `Collider::set_density`, `::set_mass`, `set_mass_properties`, to alter a colliders mass
properties. Note that `::set_mass` will result in the colliders angular inertia being automatically
computed based on this mass and on its shape.
- Add `ColliderBuilder::mass` to set the mass of the collider instead of its density. Its angular
inertia tensor will be automatically computed based on this mass and its shape.
## v0.13.0 (31 May 2022) ## v0.13.0 (31 May 2022)
### Fixed ### Fixed
- Fix incorrect sensor events being generated after collider removal. - Fix incorrect sensor events being generated after collider removal.

View File

@@ -1,14 +1,14 @@
use crate::dynamics::{ use crate::dynamics::{
LockedAxes, MassProperties, RigidBodyActivation, RigidBodyCcd, RigidBodyChanges, LockedAxes, MassProperties, RigidBodyActivation, RigidBodyAdditionalMassProps, RigidBodyCcd,
RigidBodyColliders, RigidBodyDamping, RigidBodyDominance, RigidBodyForces, RigidBodyIds, RigidBodyChanges, RigidBodyColliders, RigidBodyDamping, RigidBodyDominance, RigidBodyForces,
RigidBodyMassProps, RigidBodyPosition, RigidBodyType, RigidBodyVelocity, RigidBodyIds, RigidBodyMassProps, RigidBodyPosition, RigidBodyType, RigidBodyVelocity,
}; };
use crate::geometry::{ use crate::geometry::{
Collider, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition, ColliderShape, Collider, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition, ColliderSet,
ColliderShape,
}; };
use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector}; use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector};
use crate::utils::{self, WCross}; use crate::utils::WCross;
use na::ComplexField;
use num::Zero; use num::Zero;
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
@@ -123,7 +123,7 @@ impl RigidBody {
} }
} }
/// The mass properties of this rigid-body. /// The mass-properties of this rigid-body.
#[inline] #[inline]
pub fn mass_properties(&self) -> &MassProperties { pub fn mass_properties(&self) -> &MassProperties {
&self.mprops.local_mprops &self.mprops.local_mprops
@@ -310,26 +310,82 @@ impl RigidBody {
self.ccd.ccd_active self.ccd.ccd_active
} }
/// Sets the rigid-body's additional mass properties. /// Recompute the mass-properties of this rigid-bodies based on its currently attached colliders.
pub fn recompute_mass_properties_from_colliders(&mut self, colliders: &ColliderSet) {
self.mprops.recompute_mass_properties_from_colliders(
colliders,
&self.colliders,
&self.pos.position,
);
}
/// Sets the rigid-body's additional mass.
///
/// The total angular inertia of the rigid-body will be scaled automatically based on this
/// additional mass. If this scaling effect isnt desired, use [`Self::additional_mass_properties`]
/// instead of this method.
///
/// This is only the "additional" mass because the total mass of the rigid-body is
/// equal to the sum of this additional mass and the mass computed from the colliders
/// (with non-zero densities) attached to this rigid-body.
///
/// That total mass (which includes the attached colliders contributions)
/// will be updated at the name physics step, or can be updated manually with
/// [`Self::recompute_mass_properties_from_colliders`].
///
/// This will override any previous mass-properties set by [`Self::set_additional_mass`],
/// [`Self::set_additional_mass_properties`], [`RigidBodyBuilder::additional_mass`], or
/// [`RigidBodyBuilder::additional_mass_properties`] for this rigid-body.
///
/// If `wake_up` is `true` then the rigid-body will be woken up if it was
/// put to sleep because it did not move for a while.
#[inline]
pub fn set_additional_mass(&mut self, additional_mass: Real, wake_up: bool) {
self.do_set_additional_mass_properties(
RigidBodyAdditionalMassProps::Mass(additional_mass),
wake_up,
)
}
/// Sets the rigid-body's additional mass-properties.
///
/// This is only the "additional" mass-properties because the total mass-properties of the
/// rigid-body is equal to the sum of this additional mass-properties and the mass computed from
/// the colliders (with non-zero densities) attached to this rigid-body.
///
/// That total mass-properties (which include the attached colliders contributions)
/// will be updated at the name physics step, or can be updated manually with
/// [`Self::recompute_mass_properties_from_colliders`].
///
/// This will override any previous mass-properties set by [`Self::set_additional_mass`],
/// [`Self::set_additional_mass_properties`], [`RigidBodyBuilder::additional_mass`], or
/// [`RigidBodyBuilder::additional_mass_properties`] for this rigid-body.
/// ///
/// If `wake_up` is `true` then the rigid-body will be woken up if it was /// If `wake_up` is `true` then the rigid-body will be woken up if it was
/// put to sleep because it did not move for a while. /// put to sleep because it did not move for a while.
#[inline] #[inline]
pub fn set_additional_mass_properties(&mut self, props: MassProperties, wake_up: bool) { pub fn set_additional_mass_properties(&mut self, props: MassProperties, wake_up: bool) {
if let Some(add_mprops) = &mut self.mprops.additional_local_mprops { self.do_set_additional_mass_properties(
self.mprops.local_mprops += props; RigidBodyAdditionalMassProps::MassProps(props),
self.mprops.local_mprops -= **add_mprops; wake_up,
**add_mprops = props; )
} else { }
self.mprops.additional_local_mprops = Some(Box::new(props));
self.mprops.local_mprops += props;
}
if self.is_dynamic() && wake_up { fn do_set_additional_mass_properties(
self.wake_up(true); &mut self,
} props: RigidBodyAdditionalMassProps,
wake_up: bool,
) {
let new_mprops = Some(Box::new(props));
self.update_world_mass_properties(); if self.mprops.additional_local_mprops != new_mprops {
self.changes.insert(RigidBodyChanges::LOCAL_MASS_PROPERTIES);
self.mprops.additional_local_mprops = new_mprops;
if self.is_dynamic() && wake_up {
self.wake_up(true);
}
}
} }
/// The handles of colliders attached to this rigid body. /// The handles of colliders attached to this rigid body.
@@ -432,12 +488,6 @@ impl RigidBody {
if let Some(i) = self.colliders.0.iter().position(|e| *e == handle) { if let Some(i) = self.colliders.0.iter().position(|e| *e == handle) {
self.changes.set(RigidBodyChanges::COLLIDERS, true); self.changes.set(RigidBodyChanges::COLLIDERS, true);
self.colliders.0.swap_remove(i); self.colliders.0.swap_remove(i);
let mass_properties = coll
.mass_properties()
.transform_by(coll.position_wrt_parent().unwrap());
self.mprops.local_mprops -= mass_properties;
self.update_world_mass_properties();
} }
} }
@@ -859,8 +909,8 @@ pub struct RigidBodyBuilder {
pub angular_damping: Real, pub angular_damping: Real,
body_type: RigidBodyType, body_type: RigidBodyType,
mprops_flags: LockedAxes, mprops_flags: LockedAxes,
/// The additional mass properties of the rigid-body being built. See [`RigidBodyBuilder::additional_mass_properties`] for more information. /// The additional mass-properties of the rigid-body being built. See [`RigidBodyBuilder::additional_mass_properties`] for more information.
pub additional_mass_properties: MassProperties, additional_mass_properties: RigidBodyAdditionalMassProps,
/// Whether or not the rigid-body to be created can sleep if it reaches a dynamic equilibrium. /// Whether or not the rigid-body to be created can sleep if it reaches a dynamic equilibrium.
pub can_sleep: bool, pub can_sleep: bool,
/// Whether or not the rigid-body is to be created asleep. /// Whether or not the rigid-body is to be created asleep.
@@ -887,7 +937,7 @@ impl RigidBodyBuilder {
angular_damping: 0.0, angular_damping: 0.0,
body_type, body_type,
mprops_flags: LockedAxes::empty(), mprops_flags: LockedAxes::empty(),
additional_mass_properties: MassProperties::zero(), additional_mass_properties: RigidBodyAdditionalMassProps::default(),
can_sleep: true, can_sleep: true,
sleeping: false, sleeping: false,
ccd_enabled: false, ccd_enabled: false,
@@ -968,18 +1018,41 @@ impl RigidBodyBuilder {
self self
} }
/// Sets the additional mass properties of the rigid-body being built. /// Sets the additional mass-properties of the rigid-body being built.
/// ///
/// Note that "additional" means that the final mass properties of the rigid-bodies depends /// This will be overridden by a call to [`Self::additional_mass`] so it only makes sense to call
/// either [`Self::additional_mass`] or [`Self::additional_mass_properties`].
///
/// Note that "additional" means that the final mass-properties of the rigid-bodies depends
/// on the initial mass-properties of the rigid-body (set by this method) /// on the initial mass-properties of the rigid-body (set by this method)
/// to which is added the contributions of all the colliders with non-zero density /// to which is added the contributions of all the colliders with non-zero density
/// attached to this rigid-body. /// attached to this rigid-body.
/// ///
/// Therefore, if you want your provided mass properties to be the final /// Therefore, if you want your provided mass-properties to be the final
/// mass properties of your rigid-body, don't attach colliders to it, or /// mass-properties of your rigid-body, don't attach colliders to it, or
/// only attach colliders with densities equal to zero. /// only attach colliders with densities equal to zero.
pub fn additional_mass_properties(mut self, props: MassProperties) -> Self { pub fn additional_mass_properties(mut self, mprops: MassProperties) -> Self {
self.additional_mass_properties = props; self.additional_mass_properties = RigidBodyAdditionalMassProps::MassProps(mprops);
self
}
/// Sets the additional mass of the rigid-body being built.
///
/// This will be overridden by a call to [`Self::additional_mass_properties`] so it only makes
/// sense to call either [`Self::additional_mass`] or [`Self::additional_mass_properties`].
///
/// This is only the "additional" mass because the total mass of the rigid-body is
/// equal to the sum of this additional mass and the mass computed from the colliders
/// (with non-zero densities) attached to this rigid-body.
///
/// The total angular inertia of the rigid-body will be scaled automatically based on this
/// additional mass. If this scaling effect isnt desired, use [`Self::additional_mass_properties`]
/// instead of this method.
///
/// # Parameters
/// * `mass`- The mass that will be added to the created rigid-body.
pub fn additional_mass(mut self, mass: Real) -> Self {
self.additional_mass_properties = RigidBodyAdditionalMassProps::Mass(mass);
self self
} }
@@ -1037,78 +1110,6 @@ impl RigidBodyBuilder {
self self
} }
/// Sets the additional mass of the rigid-body being built.
///
/// This is only the "additional" mass because the total mass of the rigid-body is
/// equal to the sum of this additional mass and the mass computed from the colliders
/// (with non-zero densities) attached to this rigid-body.
pub fn additional_mass(mut self, mass: Real) -> Self {
self.additional_mass_properties.set_mass(mass, false);
self
}
/// Sets the additional mass of the rigid-body being built.
///
/// This is only the "additional" mass because the total mass of the rigid-body is
/// equal to the sum of this additional mass and the mass computed from the colliders
/// (with non-zero densities) attached to this rigid-body.
#[deprecated(note = "renamed to `additional_mass`.")]
pub fn mass(self, mass: Real) -> Self {
self.additional_mass(mass)
}
/// Sets the additional angular inertia of this rigid-body.
///
/// This is only the "additional" angular inertia because the total angular inertia of
/// the rigid-body is equal to the sum of this additional value and the angular inertia
/// computed from the colliders (with non-zero densities) attached to this rigid-body.
#[cfg(feature = "dim2")]
pub fn additional_principal_angular_inertia(mut self, inertia: Real) -> Self {
self.additional_mass_properties.inv_principal_inertia_sqrt =
utils::inv(ComplexField::sqrt(inertia.max(0.0)));
self
}
/// Sets the angular inertia of this rigid-body.
#[cfg(feature = "dim2")]
#[deprecated(note = "renamed to `additional_principal_angular_inertia`.")]
pub fn principal_angular_inertia(self, inertia: Real) -> Self {
self.additional_principal_angular_inertia(inertia)
}
/// Use `self.principal_angular_inertia` instead.
#[cfg(feature = "dim2")]
#[deprecated(note = "renamed to `additional_principal_angular_inertia`.")]
pub fn principal_inertia(self, inertia: Real) -> Self {
self.additional_principal_angular_inertia(inertia)
}
/// Sets the additional principal angular inertia of this rigid-body.
///
/// This is only the "additional" angular inertia because the total angular inertia of
/// the rigid-body is equal to the sum of this additional value and the angular inertia
/// computed from the colliders (with non-zero densities) attached to this rigid-body.
#[cfg(feature = "dim3")]
pub fn additional_principal_angular_inertia(mut self, inertia: AngVector<Real>) -> Self {
self.additional_mass_properties.inv_principal_inertia_sqrt =
inertia.map(|e| utils::inv(ComplexField::sqrt(e.max(0.0))));
self
}
/// Sets the principal angular inertia of this rigid-body.
#[cfg(feature = "dim3")]
#[deprecated(note = "renamed to `additional_principal_angular_inertia`.")]
pub fn principal_angular_inertia(self, inertia: AngVector<Real>) -> Self {
self.additional_principal_angular_inertia(inertia)
}
/// Use `self.principal_angular_inertia` instead.
#[cfg(feature = "dim3")]
#[deprecated(note = "renamed to `additional_principal_angular_inertia`.")]
pub fn principal_inertia(self, inertia: AngVector<Real>) -> Self {
self.additional_principal_angular_inertia(inertia)
}
/// Sets the damping factor for the linear part of the rigid-body motion. /// Sets the damping factor for the linear part of the rigid-body motion.
/// ///
/// The higher the linear damping factor is, the more quickly the rigid-body /// The higher the linear damping factor is, the more quickly the rigid-body
@@ -1169,9 +1170,11 @@ impl RigidBodyBuilder {
rb.body_type = self.body_type; rb.body_type = self.body_type;
rb.user_data = self.user_data; rb.user_data = self.user_data;
if self.additional_mass_properties != MassProperties::default() { if self.additional_mass_properties
!= RigidBodyAdditionalMassProps::MassProps(MassProperties::zero())
&& self.additional_mass_properties != RigidBodyAdditionalMassProps::Mass(0.0)
{
rb.mprops.additional_local_mprops = Some(Box::new(self.additional_mass_properties)); rb.mprops.additional_local_mprops = Some(Box::new(self.additional_mass_properties));
rb.mprops.local_mprops = self.additional_mass_properties;
} }
rb.mprops.flags = self.mprops_flags; rb.mprops.flags = self.mprops_flags;

View File

@@ -110,6 +110,8 @@ bitflags::bitflags! {
const TYPE = 1 << 4; const TYPE = 1 << 4;
/// Flag indicating that the `RigidBodyDominance` component of this rigid-body has been modified. /// Flag indicating that the `RigidBodyDominance` component of this rigid-body has been modified.
const DOMINANCE = 1 << 5; const DOMINANCE = 1 << 5;
/// Flag indicating that the local mass-properties of this rigid-body must be recomputed.
const LOCAL_MASS_PROPERTIES = 1 << 6;
} }
} }
@@ -222,7 +224,23 @@ bitflags::bitflags! {
} }
} }
// TODO: split this into "LocalMassProps" and `WorldMassProps"? /// Mass and angular inertia added to a rigid-body on top of its attached colliders contributions.
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RigidBodyAdditionalMassProps {
/// Mass properties to be added as-is.
MassProps(MassProperties),
/// Mass to be added to the rigid-body. This will also automatically scale
/// the attached colliders total angular inertia to account for the added mass.
Mass(Real),
}
impl Default for RigidBodyAdditionalMassProps {
fn default() -> Self {
RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
}
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
/// The mass properties of this rigid-bodies. /// The mass properties of this rigid-bodies.
@@ -232,7 +250,7 @@ pub struct RigidBodyMassProps {
/// The local mass properties of the rigid-body. /// The local mass properties of the rigid-body.
pub local_mprops: MassProperties, pub local_mprops: MassProperties,
/// Mass-properties of this rigid-bodies, added to the contributions of its attached colliders. /// Mass-properties of this rigid-bodies, added to the contributions of its attached colliders.
pub additional_local_mprops: Option<Box<MassProperties>>, pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
/// The world-space center of mass of the rigid-body. /// The world-space center of mass of the rigid-body.
pub world_com: Point<Real>, pub world_com: Point<Real>,
/// The inverse mass taking into account translation locking. /// The inverse mass taking into account translation locking.
@@ -294,18 +312,20 @@ impl RigidBodyMassProps {
self.effective_world_inv_inertia_sqrt.squared().inverse() self.effective_world_inv_inertia_sqrt.squared().inverse()
} }
/// Recompute the mass-properties of this rigid-bodies based on its currentely attached colliders. /// Recompute the mass-properties of this rigid-bodies based on its currently attached colliders.
pub fn recompute_mass_properties_from_colliders( pub fn recompute_mass_properties_from_colliders(
&mut self, &mut self,
colliders: &ColliderSet, colliders: &ColliderSet,
attached_colliders: &RigidBodyColliders, attached_colliders: &RigidBodyColliders,
position: &Isometry<Real>, position: &Isometry<Real>,
) { ) {
self.local_mprops = self let added_mprops = self
.additional_local_mprops .additional_local_mprops
.as_ref() .as_ref()
.map(|mprops| **mprops) .map(|mprops| **mprops)
.unwrap_or_else(MassProperties::default); .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
self.local_mprops = MassProperties::default();
for handle in &attached_colliders.0 { for handle in &attached_colliders.0 {
if let Some(co) = colliders.get(*handle) { if let Some(co) = colliders.get(*handle) {
@@ -319,6 +339,16 @@ impl RigidBodyMassProps {
} }
} }
match added_mprops {
RigidBodyAdditionalMassProps::MassProps(mprops) => {
self.local_mprops += mprops;
}
RigidBodyAdditionalMassProps::Mass(mass) => {
let new_mass = self.local_mprops.mass() + mass;
self.local_mprops.set_mass(new_mass, true);
}
}
self.update_world_mass_properties(position); self.update_world_mass_properties(position);
} }

View File

@@ -241,10 +241,54 @@ impl Collider {
pub fn density(&self) -> Option<Real> { pub fn density(&self) -> Option<Real> {
match &self.mprops { match &self.mprops {
ColliderMassProps::Density(density) => Some(*density), ColliderMassProps::Density(density) => Some(*density),
ColliderMassProps::Mass(_) => None,
ColliderMassProps::MassProperties(_) => None, ColliderMassProps::MassProperties(_) => None,
} }
} }
/// Sets the uniform density of this collider.
///
/// This will override any previous mass-properties set by [`Self::set_density`],
/// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
/// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
/// for this collider.
///
/// The mass and angular inertia of this collider will be computed automatically based on its
/// shape.
pub fn set_density(&mut self, density: Real) {
self.do_set_mass_properties(ColliderMassProps::Density(density));
}
/// Sets the mass of this collider.
///
/// This will override any previous mass-properties set by [`Self::set_density`],
/// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
/// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
/// for this collider.
///
/// The angular inertia of this collider will be computed automatically based on its shape
/// and this mass value.
pub fn set_mass(&mut self, mass: Real) {
self.do_set_mass_properties(ColliderMassProps::Mass(mass));
}
/// Sets the mass properties of this collider.
///
/// This will override any previous mass-properties set by [`Self::set_density`],
/// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
/// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
/// for this collider.
pub fn set_mass_properties(&mut self, mass_properties: MassProperties) {
self.do_set_mass_properties(ColliderMassProps::MassProperties(Box::new(mass_properties)))
}
fn do_set_mass_properties(&mut self, mprops: ColliderMassProps) {
if mprops != self.mprops {
self.changes |= ColliderChanges::LOCAL_MASS_PROPERTIES;
self.mprops = mprops;
}
}
/// The geometric shape of this collider. /// The geometric shape of this collider.
pub fn shape(&self) -> &dyn Shape { pub fn shape(&self) -> &dyn Shape {
self.shape.as_ref() self.shape.as_ref()
@@ -284,10 +328,7 @@ impl Collider {
/// Compute the local-space mass properties of this collider. /// Compute the local-space mass properties of this collider.
pub fn mass_properties(&self) -> MassProperties { pub fn mass_properties(&self) -> MassProperties {
match &self.mprops { self.mprops.mass_properties(&*self.shape)
ColliderMassProps::Density(density) => self.shape.mass_properties(*density),
ColliderMassProps::MassProperties(mass_properties) => **mass_properties,
}
} }
/// The total force magnitude beyond which a contact force event can be emitted. /// The total force magnitude beyond which a contact force event can be emitted.
@@ -303,11 +344,8 @@ impl Collider {
pub struct ColliderBuilder { pub struct ColliderBuilder {
/// The shape of the collider to be built. /// The shape of the collider to be built.
pub shape: SharedShape, pub shape: SharedShape,
/// The uniform density of the collider to be built. /// Controls the way the colliders mass-properties are computed.
pub density: Option<Real>, pub mass_properties: ColliderMassProps,
/// Overrides automatic computation of `MassProperties`.
/// If None, it will be computed based on shape and density.
pub mass_properties: Option<MassProperties>,
/// The friction coefficient of the collider to be built. /// The friction coefficient of the collider to be built.
pub friction: Real, pub friction: Real,
/// The rule used to combine two friction coefficients. /// The rule used to combine two friction coefficients.
@@ -341,8 +379,7 @@ impl ColliderBuilder {
pub fn new(shape: SharedShape) -> Self { pub fn new(shape: SharedShape) -> Self {
Self { Self {
shape, shape,
density: None, mass_properties: ColliderMassProps::default(),
mass_properties: None,
friction: Self::default_friction(), friction: Self::default_friction(),
restitution: 0.0, restitution: 0.0,
position: Isometry::identity(), position: Isometry::identity(),
@@ -627,9 +664,6 @@ impl ColliderBuilder {
} }
/// Sets whether or not the collider built by this builder is a sensor. /// Sets whether or not the collider built by this builder is a sensor.
///
/// Sensors will have a default density of zero,
/// but if you call [`Self::mass_properties`] you can assign a mass to a sensor.
pub fn sensor(mut self, is_sensor: bool) -> Self { pub fn sensor(mut self, is_sensor: bool) -> Self {
self.is_sensor = is_sensor; self.is_sensor = is_sensor;
self self
@@ -679,19 +713,34 @@ impl ColliderBuilder {
/// Sets the uniform density of the collider this builder will build. /// Sets the uniform density of the collider this builder will build.
/// ///
/// This will be overridden by a call to [`Self::mass_properties`] so it only makes sense to call /// This will be overridden by a call to [`Self::mass`] or [`Self::mass_properties`] so it only
/// either [`Self::density`] or [`Self::mass_properties`]. /// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
///
/// The mass and angular inertia of this collider will be computed automatically based on its
/// shape.
pub fn density(mut self, density: Real) -> Self { pub fn density(mut self, density: Real) -> Self {
self.density = Some(density); self.mass_properties = ColliderMassProps::Density(density);
self
}
/// Sets the mass of the collider this builder will build.
///
/// This will be overridden by a call to [`Self::density`] or [`Self::mass_properties`] so it only
/// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
///
/// The angular inertia of this collider will be computed automatically based on its shape
/// and this mass value.
pub fn mass(mut self, mass: Real) -> Self {
self.mass_properties = ColliderMassProps::Mass(mass);
self self
} }
/// Sets the mass properties of the collider this builder will build. /// Sets the mass properties of the collider this builder will build.
/// ///
/// If this is set, [`Self::density`] will be ignored, so it only makes sense to call /// This will be overridden by a call to [`Self::density`] or [`Self::mass`] so it only
/// either [`Self::density`] or [`Self::mass_properties`]. /// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
pub fn mass_properties(mut self, mass_properties: MassProperties) -> Self { pub fn mass_properties(mut self, mass_properties: MassProperties) -> Self {
self.mass_properties = Some(mass_properties); self.mass_properties = ColliderMassProps::MassProperties(Box::new(mass_properties));
self self
} }
@@ -745,16 +794,7 @@ impl ColliderBuilder {
/// Builds a new collider attached to the given rigid-body. /// Builds a new collider attached to the given rigid-body.
pub fn build(&self) -> Collider { pub fn build(&self) -> Collider {
let mass_info = if let Some(mp) = self.mass_properties {
ColliderMassProps::MassProperties(Box::new(mp))
} else {
let default_density = Self::default_density();
let density = self.density.unwrap_or(default_density);
ColliderMassProps::Density(density)
};
let shape = self.shape.clone(); let shape = self.shape.clone();
let mprops = mass_info;
let material = ColliderMaterial { let material = ColliderMaterial {
friction: self.friction, friction: self.friction,
restitution: self.restitution, restitution: self.restitution,
@@ -779,7 +819,7 @@ impl ColliderBuilder {
Collider { Collider {
shape, shape,
mprops, mprops: self.mass_properties.clone(),
material, material,
parent: None, parent: None,
changes, changes,

View File

@@ -47,21 +47,23 @@ bitflags::bitflags! {
pub struct ColliderChanges: u32 { pub struct ColliderChanges: u32 {
/// Flag indicating that any component of the collider has been modified. /// Flag indicating that any component of the collider has been modified.
const MODIFIED = 1 << 0; const MODIFIED = 1 << 0;
/// Flag indicating that the density or mass-properties of this collider was changed.
const LOCAL_MASS_PROPERTIES = 1 << 1; // => RigidBody local mass-properties update.
/// Flag indicating that the `ColliderParent` component of the collider has been modified. /// Flag indicating that the `ColliderParent` component of the collider has been modified.
const PARENT = 1 << 1; // => BF & NF updates. const PARENT = 1 << 2; // => BF & NF updates.
/// Flag indicating that the `ColliderPosition` component of the collider has been modified. /// Flag indicating that the `ColliderPosition` component of the collider has been modified.
const POSITION = 1 << 2; // => BF & NF updates. const POSITION = 1 << 3; // => BF & NF updates.
/// Flag indicating that the collision groups of the collider have been modified. /// Flag indicating that the collision groups of the collider have been modified.
const GROUPS = 1 << 3; // => NF update. const GROUPS = 1 << 4; // => NF update.
/// Flag indicating that the `ColliderShape` component of the collider has been modified. /// Flag indicating that the `ColliderShape` component of the collider has been modified.
const SHAPE = 1 << 4; // => BF & NF update. NF pair workspace invalidation. const SHAPE = 1 << 5; // => BF & NF update. NF pair workspace invalidation.
/// Flag indicating that the `ColliderType` component of the collider has been modified. /// Flag indicating that the `ColliderType` component of the collider has been modified.
const TYPE = 1 << 5; // => NF update. NF pair invalidation. const TYPE = 1 << 6; // => NF update. NF pair invalidation.
/// Flag indicating that the dominance groups of the parent of this collider have been modified. /// Flag indicating that the dominance groups of the parent of this collider have been modified.
/// ///
/// This flags is automatically set by the `PhysicsPipeline` when the `RigidBodyChanges::DOMINANCE` /// This flags is automatically set by the `PhysicsPipeline` when the `RigidBodyChanges::DOMINANCE`
/// or `RigidBodyChanges::TYPE` of the parent rigid-body of this collider is detected. /// or `RigidBodyChanges::TYPE` of the parent rigid-body of this collider is detected.
const PARENT_EFFECTIVE_DOMINANCE = 1 << 6; // NF update. const PARENT_EFFECTIVE_DOMINANCE = 1 << 7; // NF update.
} }
} }
@@ -86,7 +88,7 @@ impl ColliderChanges {
// bottleneck at some point in the future (which is very unlikely) // bottleneck at some point in the future (which is very unlikely)
// we could do a special-case for dominance-only change (so that // we could do a special-case for dominance-only change (so that
// we only update the relative_dominance of the pre-existing contact. // we only update the relative_dominance of the pre-existing contact.
self.bits() > 1 self.bits() > 2
} }
} }
@@ -134,6 +136,10 @@ pub enum ColliderMassProps {
/// Its actual `MassProperties` are computed automatically with /// Its actual `MassProperties` are computed automatically with
/// the help of [`SharedShape::mass_properties`]. /// the help of [`SharedShape::mass_properties`].
Density(Real), Density(Real),
/// The collider is given a mass.
///
/// Its angular inertia will be computed automatically based on this mass.
Mass(Real),
/// The collider is given explicit mass-properties. /// The collider is given explicit mass-properties.
MassProperties(Box<MassProperties>), MassProperties(Box<MassProperties>),
} }
@@ -159,8 +165,23 @@ impl ColliderMassProps {
/// If `self` is the `MassProperties` variant, then this returns the stored mass-properties. /// If `self` is the `MassProperties` variant, then this returns the stored mass-properties.
pub fn mass_properties(&self, shape: &dyn Shape) -> MassProperties { pub fn mass_properties(&self, shape: &dyn Shape) -> MassProperties {
match self { match self {
Self::Density(density) => shape.mass_properties(*density), ColliderMassProps::Density(density) => {
Self::MassProperties(mprops) => **mprops, if *density != 0.0 {
shape.mass_properties(*density)
} else {
MassProperties::default()
}
}
ColliderMassProps::Mass(mass) => {
if *mass != 0.0 {
let mut mprops = shape.mass_properties(1.0);
mprops.set_mass(*mass, true);
mprops
} else {
MassProperties::default()
}
}
ColliderMassProps::MassProperties(mass_properties) => **mass_properties,
} }
} }
} }

View File

@@ -407,6 +407,9 @@ impl PhysicsPipeline {
hooks: &dyn PhysicsHooks, hooks: &dyn PhysicsHooks,
events: &dyn EventHandler, events: &dyn EventHandler,
) { ) {
self.counters.reset();
self.counters.step_started();
// Apply some of delayed wake-ups. // Apply some of delayed wake-ups.
for handle in impulse_joints for handle in impulse_joints
.to_wake_up .to_wake_up
@@ -417,18 +420,15 @@ impl PhysicsPipeline {
} }
// Apply modifications. // Apply modifications.
let modified_bodies = bodies.take_modified();
let mut modified_colliders = colliders.take_modified(); let mut modified_colliders = colliders.take_modified();
let mut removed_colliders = colliders.take_removed(); let mut removed_colliders = colliders.take_removed();
self.counters.reset();
self.counters.step_started();
super::user_changes::handle_user_changes_to_colliders( super::user_changes::handle_user_changes_to_colliders(
bodies, bodies,
colliders, colliders,
&modified_colliders[..], &modified_colliders[..],
); );
let modified_bodies = bodies.take_modified();
super::user_changes::handle_user_changes_to_rigid_bodies( super::user_changes::handle_user_changes_to_rigid_bodies(
Some(islands), Some(islands),
bodies, bodies,

View File

@@ -2,17 +2,12 @@ use crate::dynamics::{
IslandManager, RigidBodyChanges, RigidBodyHandle, RigidBodySet, RigidBodyType, IslandManager, RigidBodyChanges, RigidBodyHandle, RigidBodySet, RigidBodyType,
}; };
use crate::geometry::{ColliderChanges, ColliderHandle, ColliderPosition, ColliderSet}; use crate::geometry::{ColliderChanges, ColliderHandle, ColliderPosition, ColliderSet};
use parry::utils::hashmap::HashMap;
pub(crate) fn handle_user_changes_to_colliders( pub(crate) fn handle_user_changes_to_colliders(
bodies: &mut RigidBodySet, bodies: &mut RigidBodySet,
colliders: &mut ColliderSet, colliders: &mut ColliderSet,
modified_colliders: &[ColliderHandle], modified_colliders: &[ColliderHandle],
) { ) {
// TODO: avoid this hashmap? We could perhaps add a new flag to RigidBodyChanges to
// indicated that the mass properties need to be recomputed?
let mut mprops_to_update = HashMap::default();
for handle in modified_colliders { for handle in modified_colliders {
// NOTE: we use `get` because the collider may no longer // NOTE: we use `get` because the collider may no longer
// exist if it has been removed. // exist if it has been removed.
@@ -26,22 +21,19 @@ pub(crate) fn handle_user_changes_to_colliders(
} }
} }
if co.changes.contains(ColliderChanges::SHAPE) { if co
if let Some(co_parent) = co.parent { .changes
mprops_to_update.insert(co_parent.handle, ()); .intersects(ColliderChanges::SHAPE | ColliderChanges::LOCAL_MASS_PROPERTIES)
{
if let Some(rb) = co
.parent
.and_then(|p| bodies.get_mut_internal_with_modification_tracking(p.handle))
{
rb.changes |= RigidBodyChanges::LOCAL_MASS_PROPERTIES;
} }
} }
} }
} }
for (to_update, _) in mprops_to_update {
let rb = bodies.index_mut_internal(to_update);
rb.mprops.recompute_mass_properties_from_colliders(
colliders,
&rb.colliders,
&rb.pos.position,
);
}
} }
pub(crate) fn handle_user_changes_to_rigid_bodies( pub(crate) fn handle_user_changes_to_rigid_bodies(
@@ -163,6 +155,16 @@ pub(crate) fn handle_user_changes_to_rigid_bodies(
} }
} }
if changes
.intersects(RigidBodyChanges::LOCAL_MASS_PROPERTIES | RigidBodyChanges::COLLIDERS)
{
rb.mprops.recompute_mass_properties_from_colliders(
colliders,
&rb.colliders,
&rb.pos.position,
);
}
rb.changes = RigidBodyChanges::empty(); rb.changes = RigidBodyChanges::empty();
rb.ids = ids; rb.ids = ids;
rb.activation = activation; rb.activation = activation;