bevy_rapier2d/dynamics/rigid_body.rs
1use crate::math::Vect;
2use bevy::prelude::*;
3use rapier::prelude::{
4 Isometry, LockedAxes as RapierLockedAxes, RigidBodyActivation, RigidBodyHandle, RigidBodyType,
5};
6use std::ops::{Add, AddAssign, Sub, SubAssign};
7
8#[cfg(doc)]
9use rapier::dynamics::IntegrationParameters;
10
11/// The Rapier handle of a [`RigidBody`] that was inserted to the physics scene.
12#[derive(Copy, Clone, Debug, Component)]
13pub struct RapierRigidBodyHandle(pub RigidBodyHandle);
14
15/// A [`RigidBody`].
16///
17/// Related components:
18/// - [`GlobalTransform`]: used as the ground truth for the bodies position.
19/// - [`Velocity`]
20/// - [`ExternalImpulse`]
21/// - [`ExternalForce`]
22/// - [`AdditionalMassProperties`]
23/// - [`ReadMassProperties`]
24/// - [`Damping`]
25/// - [`Dominance`]
26/// - [`Ccd`]: Helps prevent tunneling through thin objects or rigid bodies
27/// moving at high velocities.
28/// - [`LockedAxes`]
29/// - [`RigidBodyDisabled`]
30/// - [`GravityScale`]
31#[derive(Copy, Clone, Debug, PartialEq, Eq, Component, Reflect, Default)]
32#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
33#[reflect(Component, Default, PartialEq)]
34pub enum RigidBody {
35 /// A `RigidBody::Dynamic` body can be affected by all external forces.
36 #[default]
37 Dynamic,
38 /// A `RigidBody::Fixed` body cannot be affected by external forces.
39 Fixed,
40 /// A `RigidBody::KinematicPositionBased` body cannot be affected by any external forces but can be controlled
41 /// by the user at the position level while keeping realistic one-way interaction with dynamic bodies.
42 ///
43 /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
44 /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
45 /// modified by the user and is independent from any contact or joint it is involved in.
46 KinematicPositionBased,
47 /// A `RigidBody::KinematicVelocityBased` body cannot be affected by any external forces but can be controlled
48 /// by the user at the velocity level while keeping realistic one-way interaction with dynamic bodies.
49 ///
50 /// One-way interaction means that a kinematic body can push a dynamic body, but a kinematic body
51 /// cannot be pushed by anything. In other words, the trajectory of a kinematic body can only be
52 /// modified by the user and is independent from any contact or joint it is involved in.
53 KinematicVelocityBased,
54}
55
56impl From<RigidBody> for RigidBodyType {
57 fn from(rigid_body: RigidBody) -> RigidBodyType {
58 match rigid_body {
59 RigidBody::Dynamic => RigidBodyType::Dynamic,
60 RigidBody::Fixed => RigidBodyType::Fixed,
61 RigidBody::KinematicPositionBased => RigidBodyType::KinematicPositionBased,
62 RigidBody::KinematicVelocityBased => RigidBodyType::KinematicVelocityBased,
63 }
64 }
65}
66
67impl From<RigidBodyType> for RigidBody {
68 fn from(rigid_body: RigidBodyType) -> RigidBody {
69 match rigid_body {
70 RigidBodyType::Dynamic => RigidBody::Dynamic,
71 RigidBodyType::Fixed => RigidBody::Fixed,
72 RigidBodyType::KinematicPositionBased => RigidBody::KinematicPositionBased,
73 RigidBodyType::KinematicVelocityBased => RigidBody::KinematicVelocityBased,
74 }
75 }
76}
77
78/// The velocity of a [`RigidBody`].
79///
80/// Use this component to control and/or read the velocity of a dynamic or kinematic [`RigidBody`].
81/// If this component isn’t present, a dynamic [`RigidBody`] will still be able to move (you will just
82/// not be able to read/modify its velocity).
83///
84/// This only affects entities with a [`RigidBody`] component.
85#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
86#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
87#[reflect(Component, Default, PartialEq)]
88pub struct Velocity {
89 /// The linear velocity of the [`RigidBody`].
90 pub linvel: Vect,
91 /// The angular velocity of the [`RigidBody`] in radian per second.
92 #[cfg(feature = "dim2")]
93 pub angvel: f32,
94 /// The angular velocity of the [`RigidBody`].
95 #[cfg(feature = "dim3")]
96 pub angvel: Vect,
97}
98
99impl Velocity {
100 /// Initialize a velocity set to zero.
101 pub const fn zero() -> Self {
102 Self {
103 linvel: Vect::ZERO,
104 #[cfg(feature = "dim2")]
105 angvel: 0.0,
106 #[cfg(feature = "dim3")]
107 angvel: Vect::ZERO,
108 }
109 }
110
111 /// Initialize a velocity with the given linear velocity, and an angular velocity of zero.
112 pub const fn linear(linvel: Vect) -> Self {
113 Self {
114 linvel,
115 #[cfg(feature = "dim2")]
116 angvel: 0.0,
117 #[cfg(feature = "dim3")]
118 angvel: Vect::ZERO,
119 }
120 }
121
122 /// Initialize a velocity with the given angular velocity, and a linear velocity of zero.
123 #[cfg(feature = "dim2")]
124 pub const fn angular(angvel: f32) -> Self {
125 Self {
126 linvel: Vect::ZERO,
127 angvel,
128 }
129 }
130
131 /// Initialize a velocity with the given angular velocity, and a linear velocity of zero.
132 #[cfg(feature = "dim3")]
133 pub const fn angular(angvel: Vect) -> Self {
134 Self {
135 linvel: Vect::ZERO,
136 angvel,
137 }
138 }
139
140 /// Get linear velocity of specific world-space point of a [`RigidBody`].
141 ///
142 /// # Parameters
143 /// - `point`: the point (world-space) to compute the velocity for.
144 /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the velocity belongs to.
145 pub fn linear_velocity_at_point(&self, point: Vect, center_of_mass: Vect) -> Vect {
146 #[cfg(feature = "dim2")]
147 return self.linvel + self.angvel * (point - center_of_mass).perp();
148
149 #[cfg(feature = "dim3")]
150 return self.linvel + self.angvel.cross(point - center_of_mass);
151 }
152}
153
154/// Mass-properties of a [`RigidBody`], added to the contributions of its attached colliders.
155///
156/// This only affects entities with a [`RigidBody`] component.
157#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
158#[reflect(Component, Default, PartialEq)]
159pub enum AdditionalMassProperties {
160 /// This mass will be added to the [`RigidBody`]. The rigid-body’s total
161 /// angular inertia tensor (obtained from its attached colliders) will
162 /// be scaled accordingly.
163 Mass(f32),
164 /// These mass properties will be added to the [`RigidBody`].
165 MassProperties(MassProperties),
166}
167
168impl Default for AdditionalMassProperties {
169 fn default() -> Self {
170 Self::MassProperties(MassProperties::default())
171 }
172}
173
174/// Center-of-mass, mass, and angular inertia.
175///
176/// When this is used as a component, this lets you read the total mass properties of
177/// a [`RigidBody`] (including the colliders contribution). Modifying this component won’t
178/// affect the mass-properties of the [`RigidBody`] (the attached colliders’ `ColliderMassProperties`
179/// and the `AdditionalMassProperties` should be modified instead).
180///
181/// This only reads the mass from entities with a [`RigidBody`] component.
182#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
183#[reflect(Component, Default, PartialEq)]
184pub struct ReadMassProperties(MassProperties);
185
186impl ReadMassProperties {
187 /// Get the [`MassProperties`] of this [`RigidBody`].
188 pub fn get(&self) -> &MassProperties {
189 &self.0
190 }
191
192 pub(crate) fn set(&mut self, mass_props: MassProperties) {
193 self.0 = mass_props;
194 }
195}
196
197impl std::ops::Deref for ReadMassProperties {
198 type Target = MassProperties;
199 fn deref(&self) -> &Self::Target {
200 self.get()
201 }
202}
203
204/// Entity that likely had their mass properties changed this frame.
205#[derive(Deref, Copy, Clone, Debug, PartialEq, Message)]
206pub struct MassModifiedMessage(pub Entity);
207
208impl From<Entity> for MassModifiedMessage {
209 fn from(entity: Entity) -> Self {
210 Self(entity)
211 }
212}
213
214#[deprecated(
215 since = "0.32.0",
216 note = "MassModifiedMessage has been renamed to MassModifiedEvent for consistency with Bevy 0.17 naming conventions. "
217)]
218pub use MassModifiedMessage as MassModifiedEvent;
219
220/// Center-of-mass, mass, and angular inertia.
221///
222/// This cannot be used as a component. Use the components `ReadMassProperties` to read a [`RigidBody`]’s
223/// mass-properties or `AdditionalMassProperties` to set its additional mass-properties.
224#[derive(Copy, Clone, Debug, Default, PartialEq, Reflect)]
225#[reflect(Default, PartialEq)]
226pub struct MassProperties {
227 /// The center of mass of a [`RigidBody`] expressed in its local-space.
228 pub local_center_of_mass: Vect,
229 /// The mass of a [`RigidBody`].
230 pub mass: f32,
231 /// The principal angular inertia of the [`RigidBody`].
232 #[cfg(feature = "dim2")]
233 pub principal_inertia: f32,
234 /// The principal vectors of the local angular inertia tensor of the [`RigidBody`].
235 #[cfg(feature = "dim3")]
236 pub principal_inertia_local_frame: crate::math::Rot,
237 /// The principal angular inertia of the [`RigidBody`].
238 #[cfg(feature = "dim3")]
239 pub principal_inertia: Vect,
240}
241
242impl MassProperties {
243 /// Converts these mass-properties to Rapier’s `MassProperties` structure.
244 #[cfg(feature = "dim2")]
245 pub fn into_rapier(self) -> rapier::dynamics::MassProperties {
246 rapier::dynamics::MassProperties::new(
247 self.local_center_of_mass.into(),
248 self.mass,
249 #[allow(clippy::useless_conversion)] // Need to convert if dim3 enabled
250 self.principal_inertia.into(),
251 )
252 }
253
254 /// Converts these mass-properties to Rapier’s `MassProperties` structure.
255 #[cfg(feature = "dim3")]
256 pub fn into_rapier(self) -> rapier::dynamics::MassProperties {
257 rapier::dynamics::MassProperties::with_principal_inertia_frame(
258 self.local_center_of_mass.into(),
259 self.mass,
260 self.principal_inertia.into(),
261 self.principal_inertia_local_frame.into(),
262 )
263 }
264
265 /// Converts Rapier’s `MassProperties` structure to `Self`.
266 pub fn from_rapier(mprops: rapier::dynamics::MassProperties) -> Self {
267 #[allow(clippy::useless_conversion)] // Need to convert if dim3 enabled
268 Self {
269 mass: mprops.mass(),
270 local_center_of_mass: mprops.local_com.into(),
271 principal_inertia: mprops.principal_inertia().into(),
272 #[cfg(feature = "dim3")]
273 principal_inertia_local_frame: mprops.principal_inertia_local_frame.into(),
274 }
275 }
276}
277
278#[derive(Default, Debug, Component, Reflect, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
279#[reflect(Component, Default, PartialEq)]
280/// Flags affecting the behavior of the constraints solver for a given contact manifold.
281pub struct LockedAxes(u8);
282
283bitflags::bitflags! {
284 impl LockedAxes: u8 {
285 /// Flag indicating that the [`RigidBody`] cannot translate along the `X` axis.
286 const TRANSLATION_LOCKED_X = 1 << 0;
287 /// Flag indicating that the [`RigidBody`] cannot translate along the `Y` axis.
288 const TRANSLATION_LOCKED_Y = 1 << 1;
289 /// Flag indicating that the [`RigidBody`] cannot translate along the `Z` axis.
290 const TRANSLATION_LOCKED_Z = 1 << 2;
291 /// Flag indicating that the [`RigidBody`] cannot translate along any direction.
292 const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
293 /// Flag indicating that the [`RigidBody`] cannot rotate along the `X` axis.
294 const ROTATION_LOCKED_X = 1 << 3;
295 /// Flag indicating that the [`RigidBody`] cannot rotate along the `Y` axis.
296 const ROTATION_LOCKED_Y = 1 << 4;
297 /// Flag indicating that the [`RigidBody`] cannot rotate along the `Z` axis.
298 const ROTATION_LOCKED_Z = 1 << 5;
299 /// Combination of flags indicating that the [`RigidBody`] cannot rotate along any axis.
300 const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
301 }
302}
303
304impl From<LockedAxes> for RapierLockedAxes {
305 fn from(locked_axes: LockedAxes) -> RapierLockedAxes {
306 RapierLockedAxes::from_bits(locked_axes.bits()).expect("Internal conversion error.")
307 }
308}
309
310/// Constant external forces applied continuously to a [`RigidBody`].
311///
312/// This force is applied at each timestep.
313#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
314#[reflect(Component, Default, PartialEq)]
315pub struct ExternalForce {
316 /// The linear force applied to the [`RigidBody`].
317 pub force: Vect,
318 /// The angular torque applied to the [`RigidBody`].
319 #[cfg(feature = "dim2")]
320 pub torque: f32,
321 /// The angular torque applied to the [`RigidBody`].
322 #[cfg(feature = "dim3")]
323 pub torque: Vect,
324}
325
326impl ExternalForce {
327 /// A force applied at a specific world-space point of a [`RigidBody`].
328 ///
329 /// # Parameters
330 /// - `force`: the force to apply.
331 /// - `point`: the point (world-space) where the impulse must be applied.
332 /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the impulse is being
333 /// applied to.
334 pub fn at_point(force: Vect, point: Vect, center_of_mass: Vect) -> Self {
335 Self {
336 force,
337 #[cfg(feature = "dim2")]
338 torque: (point - center_of_mass).perp_dot(force),
339 #[cfg(feature = "dim3")]
340 torque: (point - center_of_mass).cross(force),
341 }
342 }
343}
344
345impl Add for ExternalForce {
346 type Output = Self;
347
348 #[inline]
349 fn add(mut self, rhs: Self) -> Self::Output {
350 self += rhs;
351 self
352 }
353}
354
355impl Sub for ExternalForce {
356 type Output = Self;
357
358 #[inline]
359 fn sub(mut self, rhs: Self) -> Self::Output {
360 self -= rhs;
361 self
362 }
363}
364
365impl AddAssign for ExternalForce {
366 #[inline]
367 fn add_assign(&mut self, rhs: Self) {
368 self.force += rhs.force;
369 self.torque += rhs.torque;
370 }
371}
372
373impl SubAssign for ExternalForce {
374 #[inline]
375 fn sub_assign(&mut self, rhs: Self) {
376 self.force -= rhs.force;
377 self.torque -= rhs.torque;
378 }
379}
380
381/// Instantaneous external impulse applied continuously to a [`RigidBody`].
382///
383/// The impulse is only applied once, and whenever it it modified (based
384/// on Bevy’s change detection).
385#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
386#[reflect(Component, Default, PartialEq)]
387pub struct ExternalImpulse {
388 /// The linear impulse applied to the [`RigidBody`].
389 pub impulse: Vect,
390 /// The angular impulse applied to the [`RigidBody`].
391 #[cfg(feature = "dim2")]
392 pub torque_impulse: f32,
393 /// The angular impulse applied to the [`RigidBody`].
394 #[cfg(feature = "dim3")]
395 pub torque_impulse: Vect,
396}
397
398impl ExternalImpulse {
399 /// An impulse applied at a specific world-space point of a [`RigidBody`].
400 ///
401 /// # Parameters
402 /// - `impulse`: the impulse to apply.
403 /// - `point`: the point (world-space) where the impulse must be applied.
404 /// - `center_of_mass`: the center-of-mass (world-space) of the [`RigidBody`] the impulse is being
405 /// applied to.
406 pub fn at_point(impulse: Vect, point: Vect, center_of_mass: Vect) -> Self {
407 Self {
408 impulse,
409 #[cfg(feature = "dim2")]
410 torque_impulse: (point - center_of_mass).perp_dot(impulse),
411 #[cfg(feature = "dim3")]
412 torque_impulse: (point - center_of_mass).cross(impulse),
413 }
414 }
415
416 /// Reset the external impulses to zero.
417 pub fn reset(&mut self) {
418 *self = Default::default();
419 }
420}
421
422impl Add for ExternalImpulse {
423 type Output = Self;
424
425 #[inline]
426 fn add(mut self, rhs: Self) -> Self::Output {
427 self += rhs;
428 self
429 }
430}
431
432impl Sub for ExternalImpulse {
433 type Output = Self;
434
435 #[inline]
436 fn sub(mut self, rhs: Self) -> Self::Output {
437 self -= rhs;
438 self
439 }
440}
441
442impl AddAssign for ExternalImpulse {
443 #[inline]
444 fn add_assign(&mut self, rhs: Self) {
445 self.impulse += rhs.impulse;
446 self.torque_impulse += rhs.torque_impulse;
447 }
448}
449
450impl SubAssign for ExternalImpulse {
451 #[inline]
452 fn sub_assign(&mut self, rhs: Self) {
453 self.impulse -= rhs.impulse;
454 self.torque_impulse -= rhs.torque_impulse;
455 }
456}
457
458/// Gravity is multiplied by this scaling factor before it's
459/// applied to this [`RigidBody`].
460#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
461#[reflect(Component, Default, PartialEq)]
462pub struct GravityScale(pub f32);
463
464impl Default for GravityScale {
465 fn default() -> Self {
466 Self(1.0)
467 }
468}
469
470/// Information used for Continuous-Collision-Detection.
471#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Component, Reflect)]
472#[reflect(Component, Default, PartialEq)]
473pub struct Ccd {
474 /// Is CCD enabled for this [`RigidBody`]?
475 pub enabled: bool,
476}
477
478impl Ccd {
479 /// Enable CCD for a [`RigidBody`].
480 pub fn enabled() -> Self {
481 Self { enabled: true }
482 }
483
484 /// Disable CCD for a [`RigidBody`].
485 ///
486 /// Note that a [`RigidBody`] without the Ccd component attached
487 /// has CCD disabled by default.
488 pub fn disabled() -> Self {
489 Self { enabled: false }
490 }
491}
492
493/// Sets the maximum prediction distance Soft Continuous Collision-Detection.
494///
495/// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of
496/// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how
497/// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact
498/// performance badly by increasing the work needed from the broad-phase.
499///
500/// It is a generally cheaper variant of regular CCD (that can be enabled with
501/// [`rapier::dynamics::RigidBody::enable_ccd`] since it relies on predictive constraints instead of
502/// shape-cast and substeps.
503#[derive(Copy, Clone, Debug, Default, PartialEq, Component, Reflect)]
504#[reflect(Component, Default, PartialEq)]
505pub struct SoftCcd {
506 /// The soft CCD prediction distance.
507 pub prediction: f32,
508}
509
510/// The dominance groups of a [`RigidBody`].
511#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Component, Reflect)]
512#[reflect(Component, Default, PartialEq)]
513pub struct Dominance {
514 // FIXME: rename this to `group` (no `s`).
515 /// The dominance groups of a [`RigidBody`].
516 pub groups: i8,
517}
518
519impl Dominance {
520 /// Initialize the dominance to the given group.
521 pub fn group(group: i8) -> Self {
522 Self { groups: group }
523 }
524}
525
526/// The activation status of a body.
527///
528/// This controls whether a body is sleeping or not.
529/// If the threshold is negative, the body never sleeps.
530#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
531#[reflect(Component, Default, PartialEq)]
532pub struct Sleeping {
533 /// The linear velocity below which the body can fall asleep.
534 ///
535 /// The effictive threshold is obtained by multpilying this value by the
536 /// [`IntegrationParameters::length_unit`].
537 pub normalized_linear_threshold: f32,
538 /// The angular velocity below which the body can fall asleep.
539 pub angular_threshold: f32,
540 /// Is this body sleeping?
541 pub sleeping: bool,
542}
543
544impl Sleeping {
545 /// Creates a components that disables sleeping for the associated [`RigidBody`].
546 pub fn disabled() -> Self {
547 Self {
548 normalized_linear_threshold: -1.0,
549 angular_threshold: -1.0,
550 sleeping: false,
551 }
552 }
553}
554
555impl Default for Sleeping {
556 fn default() -> Self {
557 Self {
558 normalized_linear_threshold: RigidBodyActivation::default_normalized_linear_threshold(),
559 angular_threshold: RigidBodyActivation::default_angular_threshold(),
560 sleeping: false,
561 }
562 }
563}
564
565/// Damping factors to gradually slow down a [`RigidBody`].
566#[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)]
567#[reflect(Component, Default, PartialEq)]
568pub struct Damping {
569 // TODO: rename these to "linear" and "angular"?
570 /// Damping factor for gradually slowing down the translational motion of the [`RigidBody`].
571 pub linear_damping: f32,
572 /// Damping factor for gradually slowing down the angular motion of the [`RigidBody`].
573 pub angular_damping: f32,
574}
575
576impl Default for Damping {
577 fn default() -> Self {
578 Self {
579 linear_damping: 0.0,
580 angular_damping: 0.0,
581 }
582 }
583}
584
585/// If the `TimestepMode::Interpolated` mode is set and this component is present,
586/// the associated [`RigidBody`] will have its position automatically interpolated
587/// between the last two [`RigidBody`] positions set by the physics engine.
588#[derive(Copy, Clone, Debug, Default, PartialEq, Component)]
589pub struct TransformInterpolation {
590 /// The starting point of the interpolation.
591 pub start: Option<Isometry<f32>>,
592 /// The end point of the interpolation.
593 pub end: Option<Isometry<f32>>,
594}
595
596impl TransformInterpolation {
597 /// Interpolates between the start and end positions with `t` in the range `[0..1]`.
598 pub fn lerp_slerp(&self, t: f32) -> Option<Isometry<f32>> {
599 if let (Some(start), Some(end)) = (self.start, self.end) {
600 Some(start.lerp_slerp(&end, t))
601 } else {
602 None
603 }
604 }
605}
606
607/// Indicates whether or not the [`RigidBody`] is disabled explicitly by the user.
608#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
609#[reflect(Component, Default, PartialEq)]
610pub struct RigidBodyDisabled;
611
612/// Set the additional number of solver iterations run for a rigid-body and
613/// everything interacting with it.
614///
615/// Increasing this number will help improve simulation accuracy on this rigid-body
616/// and every rigid-body interacting directly or indirectly with it (through joints
617/// or contacts). This implies a performance hit.
618///
619/// The default value is 0, meaning exactly [`IntegrationParameters::num_solver_iterations`] will
620/// be used as number of solver iterations for this body.
621#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Component, Reflect)]
622#[reflect(Component, Default, PartialEq)]
623pub struct AdditionalSolverIterations(pub usize);