From 2dfbd9ae92c139e306afc87994adac82489f30eb Mon Sep 17 00:00:00 2001 From: Crozet Sébastien Date: Fri, 30 Apr 2021 11:37:58 +0200 Subject: Add comments. --- src/data/arena.rs | 12 +-- src/data/component_set.rs | 17 ++++ src/dynamics/island_manager.rs | 6 +- src/dynamics/joint/joint_set.rs | 4 +- src/dynamics/rigid_body.rs | 137 ++++++--------------------- src/dynamics/rigid_body_components.rs | 172 ++++++++++++++++++++++++++++++++-- src/dynamics/rigid_body_set.rs | 10 +- src/geometry/collider.rs | 32 +++---- src/geometry/collider_components.rs | 66 +++++++++++-- src/geometry/collider_set.rs | 4 +- src/lib.rs | 2 +- src/pipeline/physics_pipeline.rs | 6 +- 12 files changed, 308 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/data/arena.rs b/src/data/arena.rs index bc7176d..c7cbf07 100644 --- a/src/data/arena.rs +++ b/src/data/arena.rs @@ -770,13 +770,13 @@ impl Arena { /// other kinds of bit-efficient indexing. /// /// You should use the `get` method instead most of the time. - pub fn get_unknown_gen(&self, i: usize) -> Option<(&T, Index)> { - match self.items.get(i) { + pub fn get_unknown_gen(&self, i: u32) -> Option<(&T, Index)> { + match self.items.get(i as usize) { Some(Entry::Occupied { generation, value }) => Some(( value, Index { generation: *generation, - index: i as u32, + index: i, }, )), _ => None, @@ -793,13 +793,13 @@ impl Arena { /// other kinds of bit-efficient indexing. /// /// You should use the `get_mut` method instead most of the time. - pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(&mut T, Index)> { - match self.items.get_mut(i) { + pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut T, Index)> { + match self.items.get_mut(i as usize) { Some(Entry::Occupied { generation, value }) => Some(( value, Index { generation: *generation, - index: i as u32, + index: i, }, )), _ => None, diff --git a/src/data/component_set.rs b/src/data/component_set.rs index ca7df67..6e0461c 100644 --- a/src/data/component_set.rs +++ b/src/data/component_set.rs @@ -5,30 +5,47 @@ use crate::data::Index; // fn get(&self, handle: Index) -> Option<&T>; // } +/// A set of optional elements of type `T`. pub trait ComponentSetOption: Sync { + /// Get the element associated to the given `handle`, if there is one. fn get(&self, handle: Index) -> Option<&T>; } +/// A set of elements of type `T`. pub trait ComponentSet: ComponentSetOption { + /// The estimated number of elements in this set. + /// + /// This value is typically used for preallocating some arrays for + /// better performances. fn size_hint(&self) -> usize; // TODO ECS: remove this, its only needed by the query pipeline update // which should only take the modified colliders into account. + /// Iterate through all the elements on this set. fn for_each(&self, f: impl FnMut(Index, &T)); + /// Get the element associated to the given `handle`. fn index(&self, handle: Index) -> &T { self.get(handle).unwrap() } } +/// A set of mutable elements of type `T`. pub trait ComponentSetMut: ComponentSet { + /// Applies the given closure to the element associated to the given `handle`. + /// + /// Return `None` if the element doesn't exist. fn map_mut_internal( &mut self, handle: crate::data::Index, f: impl FnOnce(&mut T) -> Result, ) -> Option; + + /// Set the value of this element. fn set_internal(&mut self, handle: crate::data::Index, val: T); } +/// Helper trait to address multiple elements at once. pub trait BundleSet<'a, T> { + /// Access multiple elements from this set. fn index_bundle(&'a self, handle: Index) -> T; } diff --git a/src/dynamics/island_manager.rs b/src/dynamics/island_manager.rs index 551e5a4..b79cdb2 100644 --- a/src/dynamics/island_manager.rs +++ b/src/dynamics/island_manager.rs @@ -6,6 +6,8 @@ use crate::dynamics::{ use crate::geometry::{ColliderParent, InteractionGraph, NarrowPhase}; use crate::math::Real; +/// Structure responsible for maintaining the set of active rigid-bodies, and +/// putting non-moving rigid-bodies to sleep to save computation times. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub struct IslandManager { pub(crate) active_dynamic_set: Vec, @@ -19,6 +21,7 @@ pub struct IslandManager { } impl IslandManager { + /// Creates a new empty island manager. pub fn new() -> Self { Self { active_dynamic_set: vec![], @@ -34,6 +37,7 @@ impl IslandManager { self.active_islands.len() - 1 } + /// Update this data-structure after one or multiple rigid-bodies have been removed for `bodies`. pub fn cleanup_removed_rigid_bodies( &mut self, bodies: &mut impl ComponentSetMut, @@ -59,7 +63,7 @@ impl IslandManager { } } - pub fn rigid_body_removed( + pub(crate) fn rigid_body_removed( &mut self, removed_handle: RigidBodyHandle, removed_ids: &RigidBodyIds, diff --git a/src/dynamics/joint/joint_set.rs b/src/dynamics/joint/joint_set.rs index 8e4d9b8..c316008 100644 --- a/src/dynamics/joint/joint_set.rs +++ b/src/dynamics/joint/joint_set.rs @@ -94,7 +94,7 @@ impl JointSet { /// /// Using this is discouraged in favor of `self.get(handle)` which does not /// suffer form the ABA problem. - pub fn get_unknown_gen(&self, i: usize) -> Option<(&Joint, JointHandle)> { + pub fn get_unknown_gen(&self, i: u32) -> Option<(&Joint, JointHandle)> { let (id, handle) = self.joint_ids.get_unknown_gen(i)?; Some(( self.joint_graph.graph.edge_weight(*id)?, @@ -111,7 +111,7 @@ impl JointSet { /// /// Using this is discouraged in favor of `self.get_mut(handle)` which does not /// suffer form the ABA problem. - pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(&mut Joint, JointHandle)> { + pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut Joint, JointHandle)> { let (id, handle) = self.joint_ids.get_unknown_gen(i)?; Some(( self.joint_graph.graph.edge_weight_mut(*id)?, diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index 1bd1ba5..824ec92 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -7,8 +7,8 @@ use crate::geometry::{ Collider, ColliderHandle, ColliderMassProperties, ColliderParent, ColliderPosition, ColliderShape, }; -use crate::math::{AngVector, Isometry, Point, Real, Rotation, Translation, Vector}; -use crate::utils::{self, WAngularInertia, WCross}; +use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector}; +use crate::utils::{self, WCross}; use na::ComplexField; use num::Zero; @@ -18,21 +18,21 @@ use num::Zero; /// To create a new rigid-body, use the `RigidBodyBuilder` structure. #[derive(Debug, Clone)] pub struct RigidBody { - pub rb_pos: RigidBodyPosition, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_mprops: RigidBodyMassProps, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_vels: RigidBodyVelocity, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_damping: RigidBodyDamping, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_forces: RigidBodyForces, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_ccd: RigidBodyCcd, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_ids: RigidBodyIds, // TODO ECS: public only for initial tests with bevy_rapier. - pub rb_colliders: RigidBodyColliders, // TODO ECS: public only for initial tests with bevy_rapier. + pub(crate) rb_pos: RigidBodyPosition, + pub(crate) rb_mprops: RigidBodyMassProps, + pub(crate) rb_vels: RigidBodyVelocity, + pub(crate) rb_damping: RigidBodyDamping, + pub(crate) rb_forces: RigidBodyForces, + pub(crate) rb_ccd: RigidBodyCcd, + pub(crate) rb_ids: RigidBodyIds, + pub(crate) rb_colliders: RigidBodyColliders, /// Whether or not this rigid-body is sleeping. - pub rb_activation: RigidBodyActivation, // TODO ECS: public only for initial tests with bevy_rapier. - pub changes: RigidBodyChanges, // TODO ECS: public only for initial tests with bevy_rapier. + pub(crate) rb_activation: RigidBodyActivation, + pub(crate) changes: RigidBodyChanges, /// The status of the body, governing how it is affected by external forces. - pub rb_type: RigidBodyType, // TODO ECS: public only for initial tests with bevy_rapier + pub(crate) rb_type: RigidBodyType, /// The dominance group this rigid-body is part of. - pub rb_dominance: RigidBodyDominance, + pub(crate) rb_dominance: RigidBodyDominance, /// User-defined data associated to this rigid-body. pub user_data: u128, } @@ -61,61 +61,48 @@ impl RigidBody { self.rb_ids = Default::default(); } - pub fn user_data(&self) -> u128 { - self.user_data - } - - pub fn set_user_data(&mut self, data: u128) { - self.user_data = data; - } - - #[inline] - pub fn rb_activation(&self) -> &RigidBodyActivation { + /// The activation status of this rigid-body. + pub fn activation(&self) -> &RigidBodyActivation { &self.rb_activation } - #[inline] + /// Mutable reference to the activation status of this rigid-body. pub fn activation_mut(&mut self) -> &mut RigidBodyActivation { + self.changes |= RigidBodyChanges::SLEEP; &mut self.rb_activation } - #[inline] - pub(crate) fn changes(&self) -> RigidBodyChanges { - self.changes - } - - #[inline] - pub(crate) fn changes_mut_internal(&mut self) -> &mut RigidBodyChanges { - &mut self.changes - } - + /// The linear damping coefficient of this rigid-body. #[inline] pub fn linear_damping(&self) -> Real { self.rb_damping.linear_damping } + /// Sets the linear damping coefficient of this rigid-body. #[inline] pub fn set_linear_damping(&mut self, damping: Real) { self.rb_damping.linear_damping = damping; } + /// The angular damping coefficient of this rigid-body. #[inline] pub fn angular_damping(&self) -> Real { self.rb_damping.angular_damping } + /// Sets the angular damping coefficient of this rigid-body. #[inline] pub fn set_angular_damping(&mut self, damping: Real) { self.rb_damping.angular_damping = damping } - /// The status of this rigid-body. - pub fn rb_type(&self) -> RigidBodyType { + /// The type of this rigid-body. + pub fn body_type(&self) -> RigidBodyType { self.rb_type } - /// Sets the status of this rigid-body. - pub fn set_rb_type(&mut self, status: RigidBodyType) { + /// Sets the type of this rigid-body. + pub fn set_body_type(&mut self, status: RigidBodyType) { if status != self.rb_type { self.changes.insert(RigidBodyChanges::TYPE); self.rb_type = status; @@ -336,22 +323,6 @@ impl RigidBody { !self.rb_vels.linvel.is_zero() || !self.rb_vels.angvel.is_zero() } - /// Computes the predict position of this rigid-body after `dt` seconds, taking - /// into account its velocities and external forces applied to it. - pub fn predict_position_using_velocity_and_forces(&self, dt: Real) -> Isometry { - let dlinvel = self.rb_forces.force * (self.rb_mprops.effective_inv_mass * dt); - let dangvel = self - .rb_mprops - .effective_world_inv_inertia_sqrt - .transform_vector(self.rb_forces.torque * dt); - let linvel = self.rb_vels.linvel + dlinvel; - let angvel = self.rb_vels.angvel + dangvel; - - let com = self.rb_pos.position * self.rb_mprops.mass_properties.local_com; - let shift = Translation::from(com.coords); - shift * Isometry::new(linvel * dt, angvel * dt) * shift.inverse() * self.rb_pos.position - } - /// The linear velocity of this rigid-body. pub fn linvel(&self) -> &Vector { &self.rb_vels.linvel @@ -567,30 +538,13 @@ impl RigidBody { impl RigidBody { /// The velocity of the given world-space point on this rigid-body. pub fn velocity_at_point(&self, point: &Point) -> Vector { - let dpt = point - self.rb_mprops.world_com; - self.rb_vels.linvel + self.rb_vels.angvel.gcross(dpt) + self.rb_vels + .velocity_at_point(point, &self.rb_mprops.world_com) } /// The kinetic energy of this body. pub fn kinetic_energy(&self) -> Real { - let mut energy = (self.mass() * self.rb_vels.linvel.norm_squared()) / 2.0; - - #[cfg(feature = "dim2")] - if !self.rb_mprops.effective_world_inv_inertia_sqrt.is_zero() { - let inertia_sqrt = 1.0 / self.rb_mprops.effective_world_inv_inertia_sqrt; - energy += (inertia_sqrt * self.rb_vels.angvel).powi(2) / 2.0; - } - - #[cfg(feature = "dim3")] - if !self.rb_mprops.effective_world_inv_inertia_sqrt.is_zero() { - let inertia_sqrt = self - .rb_mprops - .effective_world_inv_inertia_sqrt - .inverse_unchecked(); - energy += (inertia_sqrt * self.rb_vels.angvel).norm_squared() / 2.0; - } - - energy + self.rb_vels.kinetic_energy(&self.rb_mprops) } /// The potential energy of this body in a gravity field. @@ -894,39 +848,6 @@ impl RigidBodyBuilder { self } - pub fn components( - &self, - ) -> ( - RigidBodyPosition, - RigidBodyMassProps, - RigidBodyVelocity, - RigidBodyDamping, - RigidBodyForces, - RigidBodyCcd, - RigidBodyIds, - RigidBodyColliders, - RigidBodyActivation, - RigidBodyChanges, - RigidBodyType, - RigidBodyDominance, - ) { - let rb = self.build(); - ( - rb.rb_pos, - rb.rb_mprops, - rb.rb_vels, - rb.rb_damping, - rb.rb_forces, - rb.rb_ccd, - rb.rb_ids, - rb.rb_colliders, - rb.rb_activation, - rb.changes, - rb.rb_type, - rb.rb_dominance, - ) - } - /// Build a new rigid-body with the parameters configured with this builder. pub fn build(&self) -> RigidBody { let mut rb = RigidBody::new(); diff --git a/src/dynamics/rigid_body_components.rs b/src/dynamics/rigid_body_components.rs index c87df1f..2a652f7 100644 --- a/src/dynamics/rigid_body_components.rs +++ b/src/dynamics/rigid_body_components.rs @@ -6,7 +6,7 @@ use crate::geometry::{ }; use crate::math::{AngVector, AngularInertia, Isometry, Point, Real, Translation, Vector}; use crate::parry::partitioning::IndexedData; -use crate::utils::WDot; +use crate::utils::{WCross, WDot}; use num::Zero; /// The unique handle of a rigid body added to a `RigidBodySet`. @@ -69,14 +69,17 @@ pub enum RigidBodyType { } impl RigidBodyType { + /// Is this rigid-body static (i.e. cannot move)? pub fn is_static(self) -> bool { self == RigidBodyType::Static } + /// Is this rigid-body dynamic (i.e. can move and be affected by forces)? pub fn is_dynamic(self) -> bool { self == RigidBodyType::Dynamic } + /// Is this rigid-body kinematic (i.e. can move but is unaffected by forces)? pub fn is_kinematic(self) -> bool { self == RigidBodyType::Kinematic } @@ -86,10 +89,15 @@ bitflags::bitflags! { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] /// Flags describing how the rigid-body has been modified by the user. pub struct RigidBodyChanges: u32 { + /// Flag indicating that any component of this rigid-body has been modified. const MODIFIED = 1 << 0; + /// Flag indicating that the `RigidBodyPosition` component of this rigid-body has been modified. const POSITION = 1 << 1; + /// Flag indicating that the `RigidBodyActivation` component of this rigid-body has been modified. const SLEEP = 1 << 2; + /// Flag indicating that the `RigidBodyColliders` component of this rigid-body has been modified. const COLLIDERS = 1 << 3; + /// Flag indicating that the `RigidBodyType` component of this rigid-body has been modified. const TYPE = 1 << 4; } } @@ -102,6 +110,7 @@ impl Default for RigidBodyChanges { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// The position of this rigid-body. pub struct RigidBodyPosition { /// The world-space position of the rigid-body. pub position: Isometry, @@ -127,6 +136,8 @@ impl Default for RigidBodyPosition { } impl RigidBodyPosition { + /// Computes the velocity need to travel from `self.position` to `self.next_position` in + /// a time equal to `1.0 / inv_dt`. #[must_use] pub fn interpolate_velocity(&self, inv_dt: Real) -> RigidBodyVelocity { let dpos = self.next_position * self.position.inverse(); @@ -143,6 +154,9 @@ impl RigidBodyPosition { RigidBodyVelocity { linvel, angvel } } + /// Compute new positions after integrating the given forces and velocities. + /// + /// This uses a symplectic Euler integration scheme. #[must_use] pub fn integrate_forces_and_velocities( &self, @@ -173,16 +187,23 @@ bitflags::bitflags! { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] /// Flags affecting the behavior of the constraints solver for a given contact manifold. pub struct RigidBodyMassPropsFlags: u8 { + /// Flag indicating that the rigid-body cannot translate along any direction. const TRANSLATION_LOCKED = 1 << 0; + /// Flag indicating that the rigid-body cannot rotate along the `X` axis. const ROTATION_LOCKED_X = 1 << 1; + /// Flag indicating that the rigid-body cannot rotate along the `X` axis. const ROTATION_LOCKED_Y = 1 << 2; + /// Flag indicating that the rigid-body cannot rotate along the `X` axis. const ROTATION_LOCKED_Z = 1 << 3; + /// Combination of flags indicating that the rigid-body cannot rotate along any axis. const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits | Self::ROTATION_LOCKED_Y.bits | Self::ROTATION_LOCKED_Z.bits; } } +// TODO: split this into "LocalMassProps" and `WorldMassProps"? #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// The mass properties of this rigid-bodies. pub struct RigidBodyMassProps { /// Flags for locking rotation and translation. pub flags: RigidBodyMassPropsFlags, @@ -219,16 +240,20 @@ impl From for RigidBodyMassProps { } impl RigidBodyMassProps { + /// The mass of the rigid-body. #[must_use] - pub fn with_translations_locked(mut self) -> Self { - self.flags |= RigidBodyMassPropsFlags::TRANSLATION_LOCKED; - self + pub fn mass(&self) -> Real { + crate::utils::inv(self.mass_properties.inv_mass) } + /// The effective mass (that takes the potential translation locking into account) of + /// this rigid-body. + #[must_use] pub fn effective_mass(&self) -> Real { crate::utils::inv(self.effective_inv_mass) } + /// Update the world-space mass properties of `self`, taking into account the new position. pub fn update_world_mass_properties(&mut self, position: &Isometry) { self.world_com = self.mass_properties.world_com(&position); self.effective_inv_mass = self.mass_properties.inv_mass; @@ -286,6 +311,7 @@ impl RigidBodyMassProps { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// The velocities of this rigid-body. pub struct RigidBodyVelocity { /// The linear velocity of the rigid-body. pub linvel: Vector, @@ -300,6 +326,7 @@ impl Default for RigidBodyVelocity { } impl RigidBodyVelocity { + /// Velocities set to zero. #[must_use] pub fn zero() -> Self { Self { @@ -308,11 +335,16 @@ impl RigidBodyVelocity { } } + /// The approximate kinetic energy of this rigid-body. + /// + /// This approximation does not take the rigid-body's mass and angular inertia + /// into account. #[must_use] pub fn pseudo_kinetic_energy(&self) -> Real { self.linvel.norm_squared() + self.angvel.gdot(self.angvel) } + /// Returns the update velocities after applying the given damping. #[must_use] pub fn apply_damping(&self, dt: Real, damping: &RigidBodyDamping) -> Self { RigidBodyVelocity { @@ -321,6 +353,15 @@ impl RigidBodyVelocity { } } + /// The velocity of the given world-space point on this rigid-body. + #[must_use] + pub fn velocity_at_point(&self, point: &Point, world_com: &Point) -> Vector { + let dpt = point - world_com; + self.linvel + self.angvel.gcross(dpt) + } + + /// Integrate the velocities in `self` to compute obtain new positions when moving from the given + /// inital position `init_pos`. #[must_use] pub fn integrate( &self, @@ -336,14 +377,81 @@ impl RigidBodyVelocity { result } + /// Are these velocities exactly equal to zero? #[must_use] pub fn is_zero(&self) -> bool { self.linvel.is_zero() && self.angvel.is_zero() } + + /// The kinetic energy of this rigid-body. + #[must_use] + pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real { + let mut energy = (rb_mprops.mass() * self.linvel.norm_squared()) / 2.0; + + #[cfg(feature = "dim2")] + if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() { + let inertia_sqrt = 1.0 / rb_mprops.effective_world_inv_inertia_sqrt; + energy += (inertia_sqrt * self.angvel).powi(2) / 2.0; + } + + #[cfg(feature = "dim3")] + if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() { + let inertia_sqrt = rb_mprops + .effective_world_inv_inertia_sqrt + .inverse_unchecked(); + energy += (inertia_sqrt * self.angvel).norm_squared() / 2.0; + } + + energy + } + + /// Applies an impulse at the center-of-mass of this rigid-body. + /// The impulse is applied right away, changing the linear velocity. + /// This does nothing on non-dynamic bodies. + pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector) { + self.linvel += impulse * rb_mprops.effective_inv_mass; + } + + /// Applies an angular impulse at the center-of-mass of this rigid-body. + /// The impulse is applied right away, changing the angular velocity. + /// This does nothing on non-dynamic bodies. + #[cfg(feature = "dim2")] + pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) { + self.angvel += rb_mprops.effective_world_inv_inertia_sqrt + * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse); + } + + /// Applies an angular impulse at the center-of-mass of this rigid-body. + /// The impulse is applied right away, changing the angular velocity. + /// This does nothing on non-dynamic bodies. + #[cfg(feature = "dim3")] + pub fn apply_torque_impulse( + &mut self, + rb_mprops: &RigidBodyMassProps, + torque_impulse: Vector, + ) { + self.angvel += rb_mprops.effective_world_inv_inertia_sqrt + * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse); + } + + /// Applies an impulse at the given world-space point of this rigid-body. + /// The impulse is applied right away, changing the linear and/or angular velocities. + /// This does nothing on non-dynamic bodies. + pub fn apply_impulse_at_point( + &mut self, + rb_mprops: &RigidBodyMassProps, + impulse: Vector, + point: Point, + ) { + let torque_impulse = (point - rb_mprops.world_com).gcross(impulse); + self.apply_impulse(rb_mprops, impulse); + self.apply_torque_impulse(rb_mprops, torque_impulse); + } } #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// Damping factors to progressively slow down a rigid-body. pub struct RigidBodyDamping { /// Damping factor for gradually slowing down the translational motion of the rigid-body. pub linear_damping: Real, @@ -362,11 +470,14 @@ impl Default for RigidBodyDamping { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// The user-defined external forces applied to this rigid-body. pub struct RigidBodyForces { /// Accumulation of external forces (only for dynamic bodies). pub force: Vector, /// Accumulation of external torques (only for dynamic bodies). pub torque: AngVector, + /// Gravity is multiplied by this scaling factor before it's + /// applied to this rigid-body. pub gravity_scale: Real, } @@ -381,6 +492,7 @@ impl Default for RigidBodyForces { } impl RigidBodyForces { + /// Integrate these forces to compute new velocities. #[must_use] pub fn integrate( &self, @@ -398,17 +510,41 @@ impl RigidBodyForces { } } - pub fn add_linear_acceleration(&mut self, gravity: &Vector, mass: Real) { + /// Adds to `self` the gravitational force that would result in a gravitational acceleration + /// equal to `gravity`. + pub fn add_gravity_acceleration(&mut self, gravity: &Vector, mass: Real) { self.force += gravity * self.gravity_scale * mass; } + + /// Applies a force at the given world-space point of the rigid-body with the given mass properties. + pub fn apply_force_at_point( + &mut self, + rb_mprops: &RigidBodyMassProps, + force: Vector, + point: Point, + ) { + self.force += force; + self.torque += (point - rb_mprops.world_com).gcross(force); + } } #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// Information used for Continuous-Collision-Detection. pub struct RigidBodyCcd { + /// The distance used by the CCD solver to decide if a movement would + /// result in a tunnelling problem. pub ccd_thickness: Real, + /// The max distance between this rigid-body's center of mass and its + /// furthest collider point. pub ccd_max_dist: Real, + /// Is CCD active for this rigid-body? + /// + /// If `self.ccd_enabled` is `true`, then this is automatically set to + /// `true` when the CCD solver detects that the rigid-body is moving fast + /// enough to potential cause a tunneling problem. pub ccd_active: bool, + /// Is CCD enabled for this rigid-body? pub ccd_enabled: bool, } @@ -424,6 +560,8 @@ impl Default for RigidBodyCcd { } impl RigidBodyCcd { + /// The maximum velocity any point of any collider attached to this rigid-body + /// moving with the given velocity can have. pub fn max_point_velocity(&self, vels: &RigidBodyVelocity) -> Real { #[cfg(feature = "dim2")] return vels.linvel.norm() + vels.angvel.abs() * self.ccd_max_dist; @@ -431,6 +569,7 @@ impl RigidBodyCcd { return vels.linvel.norm() + vels.angvel.norm() * self.ccd_max_dist; } + /// Is this rigid-body moving fast enough so that it may cause a tunneling problem? pub fn is_moving_fast( &self, dt: Real, @@ -463,12 +602,13 @@ impl RigidBodyCcd { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// Internal identifiers used by the physics engine. pub struct RigidBodyIds { - pub joint_graph_index: RigidBodyGraphIndex, - pub active_island_id: usize, - pub active_set_id: usize, - pub active_set_offset: usize, - pub active_set_timestamp: u32, + pub(crate) joint_graph_index: RigidBodyGraphIndex, + pub(crate) active_island_id: usize, + pub(crate) active_set_id: usize, + pub(crate) active_set_offset: usize, + pub(crate) active_set_timestamp: u32, } impl Default for RigidBodyIds { @@ -485,6 +625,11 @@ impl Default for RigidBodyIds { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] +/// The set of colliders attached to this rigid-bodies. +/// +/// This should not be modified manually unless you really know what +/// you are doing (for example if you are trying to integrate Rapier +/// to a game engine using its component-based interface). pub struct RigidBodyColliders(pub Vec); impl Default for RigidBodyColliders { @@ -494,6 +639,7 @@ impl Default for RigidBodyColliders { } impl RigidBodyColliders { + /// Detach a collider from this rigid-body. pub fn detach_collider( &mut self, rb_changes: &mut RigidBodyChanges, @@ -508,6 +654,7 @@ impl RigidBodyColliders { } } + /// Attach a collider to this rigid-body. pub fn attach_collider( &mut self, rb_changes: &mut RigidBodyChanges, @@ -541,6 +688,7 @@ impl RigidBodyColliders { rb_mprops.update_world_mass_properties(&rb_pos.position); } + /// Update the positions of all the colliders attached to this rigid-body. pub fn update_positions( &self, colliders: &mut Colliders, @@ -574,6 +722,7 @@ impl RigidBodyColliders { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Copy)] +/// The dominance groups of a rigid-body. pub struct RigidBodyDominance(pub i8); impl Default for RigidBodyDominance { @@ -583,6 +732,7 @@ impl Default for RigidBodyDominance { } impl RigidBodyDominance { + /// The actually dominance group of this rigid-body, after taking into account its type. pub fn effective_group(&self, status: &RigidBodyType) -> i16 { if status.is_dynamic() { self.0 as i16 @@ -643,6 +793,7 @@ impl RigidBodyActivation { self.energy != 0.0 } + /// Wakes up this rigid-body. #[inline] pub fn wake_up(&mut self, strong: bool) { self.sleeping = false; @@ -651,6 +802,7 @@ impl RigidBodyActivation { } } + /// Put this rigid-body to sleep. #[inline] pub fn sleep(&mut self) { self.energy = 0.0; diff --git a/src/dynamics/rigid_body_set.rs b/src/dynamics/rigid_body_set.rs index b0cf39a..70a5451 100644 --- a/src/dynamics/rigid_body_set.rs +++ b/src/dynamics/rigid_body_set.rs @@ -125,7 +125,7 @@ impl RigidBodySet { // Make sure the internal links are reset, they may not be // if this rigid-body was obtained by cloning another one. rb.reset_internal_references(); - rb.changes_mut_internal().set(RigidBodyChanges::all(), true); + rb.changes.set(RigidBodyChanges::all(), true); let handle = RigidBodyHandle(self.bodies.insert(rb)); self.modified_bodies.push(handle); @@ -170,7 +170,7 @@ impl RigidBodySet { /// /// Using this is discouraged in favor of `self.get(handle)` which does not /// suffer form the ABA problem. - pub fn get_unknown_gen(&self, i: usize) -> Option<(&RigidBody, RigidBodyHandle)> { + pub fn get_unknown_gen(&self, i: u32) -> Option<(&RigidBody, RigidBodyHandle)> { self.bodies .get_unknown_gen(i) .map(|(b, h)| (b, RigidBodyHandle(h))) @@ -186,7 +186,7 @@ impl RigidBodySet { /// Using this is discouraged in favor of `self.get_mut(handle)` which does not /// suffer form the ABA problem. #[cfg(not(feature = "dev-remove-slow-accessors"))] - pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(&mut RigidBody, RigidBodyHandle)> { + pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut RigidBody, RigidBodyHandle)> { let (rb, handle) = self.bodies.get_unknown_gen_mut(i)?; let handle = RigidBodyHandle(handle); Self::mark_as_modified(handle, rb, &mut self.modified_bodies); @@ -203,8 +203,8 @@ impl RigidBodySet { rb: &mut RigidBody, modified_bodies: &mut Vec, ) { - if !rb.changes().contains(RigidBodyChanges::MODIFIED) { - *rb.changes_mut_internal() = RigidBodyChanges::MODIFIED; + if !rb.changes.contains(RigidBodyChanges::MODIFIED) { + rb.changes = RigidBodyChanges::MODIFIED; modified_bodies.push(handle); } } diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index edf1d88..08295c1 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -7,7 +7,7 @@ use crate::geometry::{ use crate::math::{AngVector, Isometry, Point, Real, Rotation, Vector, DIM}; use crate::parry::transformation::vhacd::VHACDParameters; use na::Unit; -use parry::bounding_volume::{BoundingVolume, AABB}; +use parry::bounding_volume::AABB; use parry::shape::Shape; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -16,22 +16,21 @@ use parry::shape::Shape; /// /// To build a new collider, use the `ColliderBuilder` structure. pub struct Collider { - pub co_type: ColliderType, - pub co_shape: ColliderShape, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_mprops: ColliderMassProperties, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_changes: ColliderChanges, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_parent: ColliderParent, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_pos: ColliderPosition, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_material: ColliderMaterial, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_groups: ColliderGroups, // TODO ECS: this is public only for our bevy_rapier experiments. - pub co_bf_data: ColliderBroadPhaseData, // TODO ECS: this is public only for our bevy_rapier experiments. + pub(crate) co_type: ColliderType, + pub(crate) co_shape: ColliderShape, + pub(crate) co_mprops: ColliderMassProperties, + pub(crate) co_changes: ColliderChanges, + pub(crate) co_parent: ColliderParent, + pub(crate) co_pos: ColliderPosition, + pub(crate) co_material: ColliderMaterial, + pub(crate) co_groups: ColliderGroups, + pub(crate) co_bf_data: ColliderBroadPhaseData, /// User-defined data associated to this rigid-body. pub user_data: u128, } impl Collider { - // TODO ECS: exists only for our bevy_ecs tests. - pub fn reset_internal_references(&mut self) { + pub(crate) fn reset_internal_references(&mut self) { self.co_parent.handle = RigidBodyHandle::invalid(); self.co_bf_data.proxy_index = crate::INVALID_U32; self.co_changes = ColliderChanges::all(); @@ -140,6 +139,7 @@ impl Collider { } } + /// The material (friction and restitution properties) of this collider. pub fn material(&self) -> &ColliderMaterial { &self.co_material } @@ -178,11 +178,11 @@ impl Collider { self.co_shape.compute_aabb(&self.co_pos) } - /// Compute the axis-aligned bounding box of this collider. + /// Compute the axis-aligned bounding box of this collider moving from its current position + /// to the given `next_position` pub fn compute_swept_aabb(&self, next_position: &Isometry) -> AABB { - let aabb1 = self.co_shape.compute_aabb(&self.co_pos); - let aabb2 = self.co_shape.compute_aabb(next_position); - aabb1.merged(&aabb2) + self.co_shape + .compute_swept_aabb(&self.co_pos, next_position) } /// Compute the local-space mass properties of this collider. diff --git a/src/geometry/collider_components.rs b/src/geometry/collider_components.rs index 1aa760a..a02c706 100644 --- a/src/geometry/collider_components.rs +++ b/src/geometry/collider_components.rs @@ -44,12 +44,18 @@ bitflags::bitflags! { #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] /// Flags describing how the collider has been modified by the user. pub struct ColliderChanges: u32 { - const MODIFIED = 1 << 0; - const PARENT = 1 << 1; // => BF & NF updates. - const POSITION = 1 << 2; // => BF & NF updates. - const GROUPS = 1 << 3; // => NF update. - const SHAPE = 1 << 4; // => BF & NF update. NF pair workspace invalidation. - const TYPE = 1 << 5; // => NF update. NF pair invalidation. + /// Flag indicating that any component of the collider has been modified. + const MODIFIED = 1 << 0; + /// Flag indicating that the `RigidBodyParent` component of the collider has been modified. + const PARENT = 1 << 1; // => BF & NF updates. + /// Flag indicating that the `RigidBodyPosition` component of the collider has been modified. + const POSITION = 1 << 2; // => BF & NF updates. + /// Flag indicating that the `RigidBodyGroups` component of the collider has been modified. + const GROUPS = 1 << 3; // => NF update. + /// Flag indicating that the `RigidBodyShape` component of the collider has been modified. + const SHAPE = 1 << 4; // => BF & NF update. NF pair workspace invalidation. + /// Flag indicating that the `RigidBodyType` component of the collider has been modified. + const TYPE = 1 << 5; // => NF update. NF pair invalidation. } } @@ -60,12 +66,14 @@ impl Default for ColliderChanges { } impl ColliderChanges { + /// Do these changes justify a broad-phase update? pub fn needs_broad_phase_update(self) -> bool { self.intersects( ColliderChanges::PARENT | ColliderChanges::POSITION | ColliderChanges::SHAPE, ) } + /// Do these changes justify a narrow-phase update? pub fn needs_narrow_phase_update(self) -> bool { self.bits() > 1 } @@ -73,12 +81,16 @@ impl ColliderChanges { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// The type of collider. pub enum ColliderType { + /// A collider that can generate contacts and contact events. Solid, + /// A collider that can generate intersection and intersection events. Sensor, } impl ColliderType { + /// Is this collider a sensor? pub fn is_sensor(self) -> bool { self == ColliderType::Sensor } @@ -86,6 +98,7 @@ impl ColliderType { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// Data associated to a collider that takes part to a broad-phase algorithm. pub struct ColliderBroadPhaseData { pub(crate) proxy_index: SAPProxyIndex, } @@ -98,13 +111,19 @@ impl Default for ColliderBroadPhaseData { } } +/// The shape of a collider. pub type ColliderShape = SharedShape; #[derive(Clone)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// The mass-properties of a collider. pub enum ColliderMassProperties { - /// `MassProperties` are computed with the help of [`SharedShape::mass_properties`]. + /// The collider is given a density. + /// + /// Its actual `MassProperties` are computed automatically with + /// the help of [`SharedShape::mass_properties`]. Density(Real), + /// The collider is given explicit mass-properties. MassProperties(Box), } @@ -115,6 +134,12 @@ impl Default for ColliderMassProperties { } impl ColliderMassProperties { + /// The mass-properties of this collider. + /// + /// If `self` is the `Density` variant, then this computes the mass-properties based + /// on the given shape. + /// + /// If `self` is the `MassProperties` variant, then this returns the stored mass-properties. pub fn mass_properties(&self, shape: &dyn Shape) -> MassProperties { match self { Self::Density(density) => shape.mass_properties(*density), @@ -125,13 +150,17 @@ impl ColliderMassProperties { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// Information about the rigid-body this collider is attached to. pub struct ColliderParent { + /// Handle of the rigid-body this collider is attached to. pub handle: RigidBodyHandle, + /// Const position of this collider relative to its parent rigid-body. pub pos_wrt_parent: Isometry, } #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// The position of a collider. pub struct ColliderPosition(pub Isometry); impl AsRef> for ColliderPosition { @@ -156,8 +185,9 @@ impl Default for ColliderPosition { } impl ColliderPosition { + /// The identity position. #[must_use] - fn identity() -> Self { + pub fn identity() -> Self { ColliderPosition(Isometry::identity()) } } @@ -173,8 +203,13 @@ where #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// The groups of this collider, for filtering contact and solver pairs. pub struct ColliderGroups { + /// The groups controlling the pairs of colliders that can interact (generate + /// interaction events or contacts). pub collision_groups: InteractionGroups, + /// The groups controlling the pairs of collider that have their contact + /// points taken into account for force computation. pub solver_groups: InteractionGroups, } @@ -189,15 +224,30 @@ impl Default for ColliderGroups { #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// The constraints solver-related properties of this collider (friction, restitution, etc.) pub struct ColliderMaterial { + /// The friction coefficient of this collider. + /// + /// The greater the value, the stronger the friction forces will be. + /// Should be `>= 0`. pub friction: Real, + /// The restitution coefficient of this collider. + /// + /// Increase this value to make contacts with this collider more "bouncy". + /// Should be `>= 0` and should generally not be greater than `1` (perfectly elastic + /// collision). pub restitution: Real, + /// The rule applied to combine the friction coefficients of two colliders in contact. pub friction_combine_rule: CoefficientCombineRule, + /// The rule applied to combine the restitution coefficients of two colliders. pub restitution_combine_rule: CoefficientCombineRule, + /// The solver flags attached to this collider in order to customize the way the + /// constraints solver will work with contacts involving this collider. pub solver_flags: SolverFlags, } impl ColliderMaterial { + /// Creates a new collider material with the given friction and restitution coefficients. pub fn new(friction: Real, restitution: Real) -> Self { Self { friction, diff --git a/src/geometry/collider_set.rs b/src/geometry/collider_set.rs index 1becc8c..14eb54c 100644 --- a/src/geometry/collider_set.rs +++ b/src/geometry/collider_set.rs @@ -197,7 +197,7 @@ impl ColliderSet { /// /// Using this is discouraged in favor of `self.get(handle)` which does not /// suffer form the ABA problem. - pub fn get_unknown_gen(&self, i: usize) -> Option<(&Collider, ColliderHandle)> { + pub fn get_unknown_gen(&self, i: u32) -> Option<(&Collider, ColliderHandle)> { self.colliders .get_unknown_gen(i) .map(|(c, h)| (c, ColliderHandle(h))) @@ -213,7 +213,7 @@ impl ColliderSet { /// Using this is discouraged in favor of `self.get_mut(handle)` which does not /// suffer form the ABA problem. #[cfg(not(feature = "dev-remove-slow-accessors"))] - pub fn get_unknown_gen_mut(&mut self, i: usize) -> Option<(&mut Collider, ColliderHandle)> { + pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut Collider, ColliderHandle)> { let (collider, handle) = self.colliders.get_unknown_gen_mut(i)?; let handle = ColliderHandle(handle); Self::mark_as_modified(handle, collider, &mut self.modified_colliders); diff --git a/src/lib.rs b/src/lib.rs index 46f6c6e..79abaa5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ //! are compliant with the IEEE 754-2008 floating point standard. #![deny(bare_trait_objects)] -// #![warn(missing_docs)] // TODO: re-enable this +#![warn(missing_docs)] #[cfg(all(feature = "dim2", feature = "f32"))] pub extern crate parry2d as parry; diff --git a/src/pipeline/physics_pipeline.rs b/src/pipeline/physics_pipeline.rs index a5196ba..2f2a95d 100644 --- a/src/pipeline/physics_pipeline.rs +++ b/src/pipeline/physics_pipeline.rs @@ -271,7 +271,7 @@ impl PhysicsPipeline { }) .unwrap(); bodies.map_mut_internal(handle.0, |forces: &mut RigidBodyForces| { - forces.add_linear_acceleration(&gravity, effective_inv_mass) + forces.add_gravity_acceleration(&gravity, effective_inv_mass) }); } self.counters.stages.update_time.pause(); @@ -442,6 +442,10 @@ impl PhysicsPipeline { } } + /// Executes one timestep of the physics simulation. + /// + /// This is the same as `self.step_generic`, except that it is specialized + /// to work with `RigidBodySet` and `ColliderSet`. #[cfg(feature = "default-sets")] pub fn step( &mut self, -- cgit