rapier2d/dynamics/rigid_body.rs
1use crate::alloc_prelude::*;
2#[cfg(all(not(feature = "std"), feature = "dim3"))]
3use simba::scalar::ComplexField;
4
5#[cfg(doc)]
6use super::IntegrationParameters;
7use crate::dynamics::{
8 LockedAxes, MassProperties, RigidBodyActivation, RigidBodyAdditionalMassProps, RigidBodyCcd,
9 RigidBodyChanges, RigidBodyColliders, RigidBodyDamping, RigidBodyDominance, RigidBodyForces,
10 RigidBodyIds, RigidBodyMassProps, RigidBodyPosition, RigidBodyType, RigidBodyVelocity,
11};
12use crate::geometry::{
13 ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition, ColliderSet, ColliderShape,
14};
15use crate::math::{AngVector, Pose, Real, Rotation, Vector, rotation_from_angle};
16use crate::utils::CrossProduct;
17
18#[cfg(feature = "dim2")]
19use crate::num::Zero;
20
21#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
22/// A physical object that can move, rotate, and collide with other objects in your simulation.
23///
24/// Rigid bodies are the fundamental moving objects in physics simulations. Think of them as
25/// the "physical representation" of your game objects - a character, a crate, a vehicle, etc.
26///
27/// ## Body types
28///
29/// - **Dynamic**: Affected by forces, gravity, and collisions. Use for objects that should move realistically (falling boxes, projectiles, etc.)
30/// - **Fixed**: Never moves. Use for static geometry like walls, floors, and terrain
31/// - **Kinematic**: Moved by setting velocity or position directly, not by forces. Use for moving platforms, doors, or player-controlled characters
32///
33/// ## Creating bodies
34///
35/// Always use [`RigidBodyBuilder`] to create new rigid bodies:
36///
37/// ```
38/// # use rapier3d::prelude::*;
39/// # let mut bodies = RigidBodySet::new();
40/// let body = RigidBodyBuilder::dynamic()
41/// .translation(Vector::new(0.0, 10.0, 0.0))
42/// .build();
43/// let handle = bodies.insert(body);
44/// ```
45#[derive(Debug, Clone)]
46// #[repr(C)]
47// #[repr(align(64))]
48pub struct RigidBody {
49 pub(crate) ids: RigidBodyIds,
50 pub(crate) pos: RigidBodyPosition,
51 pub(crate) damping: RigidBodyDamping<Real>,
52 pub(crate) vels: RigidBodyVelocity<Real>,
53 pub(crate) forces: RigidBodyForces,
54 pub(crate) mprops: RigidBodyMassProps,
55
56 pub(crate) ccd_vels: RigidBodyVelocity<Real>,
57 pub(crate) ccd: RigidBodyCcd,
58 pub(crate) colliders: RigidBodyColliders,
59 /// Whether or not this rigid-body is sleeping.
60 pub(crate) activation: RigidBodyActivation,
61 pub(crate) changes: RigidBodyChanges,
62 /// The status of the body, governing how it is affected by external forces.
63 pub(crate) body_type: RigidBodyType,
64 /// The dominance group this rigid-body is part of.
65 pub(crate) dominance: RigidBodyDominance,
66 pub(crate) enabled: bool,
67 pub(crate) additional_solver_iterations: usize,
68 /// User-defined data associated to this rigid-body.
69 pub user_data: u128,
70}
71
72impl Default for RigidBody {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl RigidBody {
79 fn new() -> Self {
80 Self {
81 pos: RigidBodyPosition::default(),
82 mprops: RigidBodyMassProps::default(),
83 ccd_vels: RigidBodyVelocity::default(),
84 vels: RigidBodyVelocity::default(),
85 damping: RigidBodyDamping::default(),
86 forces: RigidBodyForces::default(),
87 ccd: RigidBodyCcd::default(),
88 ids: RigidBodyIds::default(),
89 colliders: RigidBodyColliders::default(),
90 activation: RigidBodyActivation::active(),
91 changes: RigidBodyChanges::all(),
92 body_type: RigidBodyType::Dynamic,
93 dominance: RigidBodyDominance::default(),
94 enabled: true,
95 user_data: 0,
96 additional_solver_iterations: 0,
97 }
98 }
99
100 pub(crate) fn reset_internal_references(&mut self) {
101 self.colliders.0 = Vec::new();
102 self.ids = Default::default();
103 }
104
105 /// Copy all the characteristics from `other` to `self`.
106 ///
107 /// If you have a mutable reference to a rigid-body `rigid_body: &mut RigidBody`, attempting to
108 /// assign it a whole new rigid-body instance, e.g., `*rigid_body = RigidBodyBuilder::dynamic().build()`,
109 /// will crash due to some internal indices being overwritten. Instead, use
110 /// `rigid_body.copy_from(&RigidBodyBuilder::dynamic().build())`.
111 ///
112 /// This method will allow you to set most characteristics of this rigid-body from another
113 /// rigid-body instance without causing any breakage.
114 ///
115 /// This method **cannot** be used for editing the list of colliders attached to this rigid-body.
116 /// Therefore, the list of colliders attached to `self` won’t be replaced by the one attached
117 /// to `other`.
118 ///
119 /// The pose of `other` will only copied into `self` if `self` doesn’t have a parent (if it has
120 /// a parent, its position is directly controlled by the parent rigid-body).
121 pub fn copy_from(&mut self, other: &RigidBody) {
122 // NOTE: we deconstruct the rigid-body struct to be sure we don’t forget to
123 // add some copies here if we add more field to RigidBody in the future.
124 let RigidBody {
125 pos,
126 mprops,
127 ccd_vels: integrated_vels,
128 vels,
129 damping,
130 forces,
131 ccd,
132 ids: _ids, // Internal ids must not be overwritten.
133 colliders: _colliders, // This function cannot be used to edit collider sets.
134 activation,
135 changes: _changes, // Will be set to ALL.
136 body_type,
137 dominance,
138 enabled,
139 additional_solver_iterations,
140 user_data,
141 } = other;
142
143 self.pos = *pos;
144 self.mprops = mprops.clone();
145 self.ccd_vels = *integrated_vels;
146 self.vels = *vels;
147 self.damping = *damping;
148 self.forces = *forces;
149 self.ccd = *ccd;
150 self.activation = *activation;
151 self.body_type = *body_type;
152 self.dominance = *dominance;
153 self.enabled = *enabled;
154 self.additional_solver_iterations = *additional_solver_iterations;
155 self.user_data = *user_data;
156
157 self.changes = RigidBodyChanges::all();
158 }
159
160 /// The additional number of solver iterations run for the constraints directly
161 /// involving this rigid-body.
162 ///
163 /// See [`Self::set_additional_solver_iterations`] for additional information.
164 pub fn additional_solver_iterations(&self) -> usize {
165 self.additional_solver_iterations
166 }
167
168 /// Set the additional number of solver substeps run for the simulation island containing this
169 /// rigid-body (default: 0). Each extra substep re-derives the soft constraint bias at a smaller
170 /// timestep, improving accuracy for stiff couplings (joint chains, high mass-ratio stacks). The
171 /// whole connected component (contacts + joints) runs `num_solver_iterations +
172 /// max(additional_solver_iterations)` substeps, so the cost scales with component size —
173 /// attaching an elevated body to a large pile substeps the pile too.
174 pub fn set_additional_solver_iterations(&mut self, additional_iterations: usize) {
175 self.additional_solver_iterations = additional_iterations;
176 }
177
178 /// The activation status of this rigid-body.
179 pub fn activation(&self) -> &RigidBodyActivation {
180 &self.activation
181 }
182
183 /// Mutable reference to the activation status of this rigid-body.
184 pub fn activation_mut(&mut self) -> &mut RigidBodyActivation {
185 self.changes |= RigidBodyChanges::SLEEP;
186 &mut self.activation
187 }
188
189 /// Is this rigid-body enabled?
190 pub fn is_enabled(&self) -> bool {
191 self.enabled
192 }
193
194 /// Sets whether this rigid-body is enabled or not.
195 pub fn set_enabled(&mut self, enabled: bool) {
196 if enabled != self.enabled {
197 if enabled {
198 // NOTE: this is probably overkill, but it makes sure we don’t
199 // forget anything that needs to be updated because the rigid-body
200 // was basically interpreted as if it was removed while it was
201 // disabled.
202 self.changes = RigidBodyChanges::all();
203 } else {
204 self.changes |= RigidBodyChanges::ENABLED_OR_DISABLED;
205 }
206
207 self.enabled = enabled;
208 }
209 }
210
211 /// The linear damping coefficient (velocity reduction over time).
212 ///
213 /// Damping gradually slows down moving objects. `0.0` = no damping (infinite momentum),
214 /// higher values = faster slowdown. Use for air resistance, friction, etc.
215 #[inline]
216 pub fn linear_damping(&self) -> Real {
217 self.damping.linear_damping
218 }
219
220 /// Sets how quickly linear velocity decreases over time.
221 ///
222 /// - `0.0` = no slowdown (space/frictionless)
223 /// - `0.1` = gradual slowdown (air resistance)
224 /// - `1.0+` = rapid slowdown (thick fluid)
225 #[inline]
226 pub fn set_linear_damping(&mut self, damping: Real) {
227 self.damping.linear_damping = damping;
228 }
229
230 /// The angular damping coefficient (rotation slowdown over time).
231 ///
232 /// Like linear damping but for rotation. Higher values make spinning objects stop faster.
233 #[inline]
234 pub fn angular_damping(&self) -> Real {
235 self.damping.angular_damping
236 }
237
238 /// Sets how quickly angular velocity decreases over time.
239 ///
240 /// Controls how fast spinning objects slow down.
241 #[inline]
242 pub fn set_angular_damping(&mut self, damping: Real) {
243 self.damping.angular_damping = damping
244 }
245
246 /// The type of this rigid-body.
247 pub fn body_type(&self) -> RigidBodyType {
248 self.body_type
249 }
250
251 /// Sets the type of this rigid-body.
252 pub fn set_body_type(&mut self, status: RigidBodyType, wake_up: bool) {
253 if status != self.body_type {
254 self.changes.insert(RigidBodyChanges::TYPE);
255 self.body_type = status;
256
257 if status == RigidBodyType::Fixed {
258 self.vels = RigidBodyVelocity::zero();
259 }
260
261 // The effective mass-properties depend on the body type (kinematic and
262 // fixed bodies have zero effective inverse masses).
263 self.update_world_mass_properties();
264
265 if self.is_dynamic_or_kinematic() && wake_up {
266 self.wake_up(true);
267 }
268 }
269 }
270
271 /// The center of mass position in world coordinates.
272 ///
273 /// This is the "balance point" where the body's mass is centered. Forces applied here
274 /// produce no rotation, only translation.
275 #[inline]
276 pub fn center_of_mass(&self) -> Vector {
277 self.mprops.world_com
278 }
279
280 /// The center of mass in the body's local coordinate system.
281 ///
282 /// This is relative to the body's position, computed from attached colliders.
283 #[inline]
284 pub fn local_center_of_mass(&self) -> Vector {
285 self.mprops.local_mprops.local_com
286 }
287
288 /// The mass-properties of this rigid-body.
289 #[inline]
290 pub fn mass_properties(&self) -> &RigidBodyMassProps {
291 &self.mprops
292 }
293
294 /// The dominance group of this rigid-body.
295 ///
296 /// This method always returns `i8::MAX + 1` for non-dynamic
297 /// rigid-bodies.
298 #[inline]
299 pub fn effective_dominance_group(&self) -> i16 {
300 self.dominance.effective_group(&self.body_type)
301 }
302
303 /// Sets the axes along which this rigid-body cannot translate or rotate.
304 #[inline]
305 pub fn set_locked_axes(&mut self, locked_axes: LockedAxes, wake_up: bool) {
306 if locked_axes != self.mprops.flags {
307 if self.is_dynamic_or_kinematic() && wake_up {
308 self.wake_up(true);
309 }
310
311 self.mprops.flags = locked_axes;
312 self.update_world_mass_properties();
313 }
314 }
315
316 /// The axes along which this rigid-body cannot translate or rotate.
317 #[inline]
318 pub fn locked_axes(&self) -> LockedAxes {
319 self.mprops.flags
320 }
321
322 /// Locks or unlocks all rotational movement for this body.
323 ///
324 /// When locked, the body cannot rotate at all (useful for keeping objects upright).
325 /// Use for characters that shouldn't tip over, or objects that should only slide.
326 #[inline]
327 pub fn lock_rotations(&mut self, locked: bool, wake_up: bool) {
328 if locked != self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED) {
329 if self.is_dynamic_or_kinematic() && wake_up {
330 self.wake_up(true);
331 }
332
333 self.mprops.flags.set(LockedAxes::ROTATION_LOCKED_X, locked);
334 self.mprops.flags.set(LockedAxes::ROTATION_LOCKED_Y, locked);
335 self.mprops.flags.set(LockedAxes::ROTATION_LOCKED_Z, locked);
336 self.update_world_mass_properties();
337 }
338 }
339
340 #[inline]
341 /// Locks or unlocks rotations of this rigid-body along each cartesian axes.
342 pub fn set_enabled_rotations(
343 &mut self,
344 allow_rotations_x: bool,
345 allow_rotations_y: bool,
346 allow_rotations_z: bool,
347 wake_up: bool,
348 ) {
349 if self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_X) == allow_rotations_x
350 || self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_Y) == allow_rotations_y
351 || self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_Z) == allow_rotations_z
352 {
353 if self.is_dynamic_or_kinematic() && wake_up {
354 self.wake_up(true);
355 }
356
357 self.mprops
358 .flags
359 .set(LockedAxes::ROTATION_LOCKED_X, !allow_rotations_x);
360 self.mprops
361 .flags
362 .set(LockedAxes::ROTATION_LOCKED_Y, !allow_rotations_y);
363 self.mprops
364 .flags
365 .set(LockedAxes::ROTATION_LOCKED_Z, !allow_rotations_z);
366 self.update_world_mass_properties();
367 }
368 }
369
370 /// Locks or unlocks rotations of this rigid-body along each cartesian axes.
371 #[deprecated(note = "Use `set_enabled_rotations` instead")]
372 pub fn restrict_rotations(
373 &mut self,
374 allow_rotations_x: bool,
375 allow_rotations_y: bool,
376 allow_rotations_z: bool,
377 wake_up: bool,
378 ) {
379 self.set_enabled_rotations(
380 allow_rotations_x,
381 allow_rotations_y,
382 allow_rotations_z,
383 wake_up,
384 );
385 }
386
387 /// Locks or unlocks all translational movement for this body.
388 ///
389 /// When locked, the body cannot move from its position (but can still rotate).
390 /// Use for rotating platforms, turrets, or objects fixed in space.
391 #[inline]
392 pub fn lock_translations(&mut self, locked: bool, wake_up: bool) {
393 if locked != self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED) {
394 if self.is_dynamic_or_kinematic() && wake_up {
395 self.wake_up(true);
396 }
397
398 self.mprops
399 .flags
400 .set(LockedAxes::TRANSLATION_LOCKED, locked);
401 self.update_world_mass_properties();
402 }
403 }
404
405 #[inline]
406 /// Locks or unlocks rotations of this rigid-body along each cartesian axes.
407 pub fn set_enabled_translations(
408 &mut self,
409 allow_translation_x: bool,
410 allow_translation_y: bool,
411 #[cfg(feature = "dim3")] allow_translation_z: bool,
412 wake_up: bool,
413 ) {
414 #[cfg(feature = "dim2")]
415 if self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) != allow_translation_x
416 && self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) != allow_translation_y
417 {
418 // Nothing to change.
419 return;
420 }
421 #[cfg(feature = "dim3")]
422 if self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) != allow_translation_x
423 && self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) != allow_translation_y
424 && self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) != allow_translation_z
425 {
426 // Nothing to change.
427 return;
428 }
429
430 if self.is_dynamic_or_kinematic() && wake_up {
431 self.wake_up(true);
432 }
433
434 self.mprops
435 .flags
436 .set(LockedAxes::TRANSLATION_LOCKED_X, !allow_translation_x);
437 self.mprops
438 .flags
439 .set(LockedAxes::TRANSLATION_LOCKED_Y, !allow_translation_y);
440 #[cfg(feature = "dim3")]
441 self.mprops
442 .flags
443 .set(LockedAxes::TRANSLATION_LOCKED_Z, !allow_translation_z);
444 self.update_world_mass_properties();
445 }
446
447 #[inline]
448 #[deprecated(note = "Use `set_enabled_translations` instead")]
449 /// Locks or unlocks rotations of this rigid-body along each cartesian axes.
450 pub fn restrict_translations(
451 &mut self,
452 allow_translation_x: bool,
453 allow_translation_y: bool,
454 #[cfg(feature = "dim3")] allow_translation_z: bool,
455 wake_up: bool,
456 ) {
457 self.set_enabled_translations(
458 allow_translation_x,
459 allow_translation_y,
460 #[cfg(feature = "dim3")]
461 allow_translation_z,
462 wake_up,
463 )
464 }
465
466 /// Are the translations of this rigid-body locked?
467 #[cfg(feature = "dim2")]
468 pub fn is_translation_locked(&self) -> bool {
469 self.mprops
470 .flags
471 .contains(LockedAxes::TRANSLATION_LOCKED_X | LockedAxes::TRANSLATION_LOCKED_Y)
472 }
473
474 /// Are the translations of this rigid-body locked?
475 #[cfg(feature = "dim3")]
476 pub fn is_translation_locked(&self) -> bool {
477 self.mprops.flags.contains(LockedAxes::TRANSLATION_LOCKED)
478 }
479
480 /// Are the rotations of this rigid-body locked?
481 #[cfg(feature = "dim2")]
482 pub fn is_rotation_locked(&self) -> bool {
483 self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_Z)
484 }
485
486 /// Returns `true` for each rotational degrees of freedom locked on this rigid-body.
487 #[cfg(feature = "dim3")]
488 pub fn is_rotation_locked(&self) -> [bool; 3] {
489 [
490 self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_X),
491 self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_Y),
492 self.mprops.flags.contains(LockedAxes::ROTATION_LOCKED_Z),
493 ]
494 }
495
496 /// Enables or disables full ("bullet") CCD: fast dynamic bodies already sweep **fixed**
497 /// colliders automatically (unless [`IntegrationParameters::max_ccd_substeps`] is `0`); this
498 /// upgrades the body to also sweep **kinematic and dynamic** bodies at extra CPU cost —
499 /// for projectiles that must not tunnel through other moving bodies. A bullet never
500 /// sweeps another bullet, so two bullets can still tunnel through each other.
501 pub fn enable_ccd(&mut self, enabled: bool) {
502 self.ccd.ccd_enabled = enabled;
503 }
504
505 /// Checks if full ("bullet") CCD is enabled: whether this body sweeps against all bodies
506 /// rather than only fixed colliders. Independent from whether CCD is *active* this frame
507 /// ([`RigidBody::is_ccd_active`]) and from the automatic fixed-collider CCD of fast dynamic bodies.
508 pub fn is_ccd_enabled(&self) -> bool {
509 self.ccd.ccd_enabled
510 }
511
512 /// Sets the maximum prediction distance Soft Continuous Collision-Detection.
513 ///
514 /// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of
515 /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how
516 /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact
517 /// performance badly by increasing the work needed from the broad-phase.
518 ///
519 /// It is a generally cheaper variant of regular CCD (that can be enabled with
520 /// [`RigidBody::enable_ccd`] since it relies on predictive constraints instead of
521 /// shape-cast and substeps.
522 pub fn set_soft_ccd_prediction(&mut self, prediction_distance: Real) {
523 self.ccd.soft_ccd_prediction = prediction_distance;
524 }
525
526 /// The soft-CCD prediction distance for this rigid-body.
527 ///
528 /// See the documentation of [`RigidBody::set_soft_ccd_prediction`] for additional details on
529 /// soft-CCD.
530 pub fn soft_ccd_prediction(&self) -> Real {
531 self.ccd.soft_ccd_prediction
532 }
533
534 /// Allow (or disallow) this body to exceed the angular speed cap.
535 ///
536 /// By default angular velocity is clamped each substep to ~45°/step to keep CCD reliable;
537 /// pass `true` for bodies that must spin fast, e.g. wheels.
538 pub fn set_allow_fast_rotation(&mut self, allow: bool) {
539 self.ccd.allow_fast_rotation = allow;
540 }
541
542 /// Is this body allowed to exceed the angular speed cap?
543 ///
544 /// See [`RigidBody::set_allow_fast_rotation`].
545 pub fn is_fast_rotation_allowed(&self) -> bool {
546 self.ccd.allow_fast_rotation
547 }
548
549 // This is different from `is_ccd_enabled`. This checks that CCD
550 // is active for this rigid-body, i.e., if it was seen to move fast
551 // enough to justify a CCD run.
552 /// Is CCD active for this rigid-body?
553 ///
554 /// Set for *any* dynamic body moving faster than an automatically-computed threshold (which
555 /// then sweeps fixed colliders), not only bodies with [`RigidBody::is_ccd_enabled`] — which
556 /// only says whether the body is upgraded to sweep all bodies, independently of its velocity.
557 pub fn is_ccd_active(&self) -> bool {
558 self.ccd.ccd_active
559 }
560
561 /// Recalculates mass, center of mass, and inertia from attached colliders.
562 ///
563 /// Normally automatic, but call this if you modify collider shapes/masses at runtime.
564 /// Only needed after directly modifying colliders without going through the builder.
565 pub fn recompute_mass_properties_from_colliders(&mut self, colliders: &ColliderSet) {
566 self.mprops.recompute_mass_properties_from_colliders(
567 colliders,
568 &self.colliders,
569 self.body_type,
570 &self.pos.position,
571 );
572 }
573
574 /// Adds extra mass on top of collider-computed mass.
575 ///
576 /// Total mass = collider masses + this additional mass. Use when you want to make
577 /// a body heavier without changing collider densities.
578 ///
579 /// # Example
580 /// ```
581 /// # use rapier3d::prelude::*;
582 /// # let mut bodies = RigidBodySet::new();
583 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
584 /// // Add 50kg to make this body heavier
585 /// bodies[body].set_additional_mass(50.0, true);
586 /// ```
587 ///
588 /// Angular inertia is automatically scaled to match the mass increase.
589 /// Updated automatically at next physics step or call `recompute_mass_properties_from_colliders()`.
590 #[inline]
591 pub fn set_additional_mass(&mut self, additional_mass: Real, wake_up: bool) {
592 self.do_set_additional_mass_properties(
593 RigidBodyAdditionalMassProps::Mass(additional_mass),
594 wake_up,
595 )
596 }
597
598 /// Sets the rigid-body's additional mass-properties.
599 ///
600 /// This is only the "additional" mass-properties because the total mass-properties of the
601 /// rigid-body is equal to the sum of this additional mass-properties and the mass computed from
602 /// the colliders (with non-zero densities) attached to this rigid-body.
603 ///
604 /// That total mass-properties (which include the attached colliders’ contributions)
605 /// will be updated at the name physics step, or can be updated manually with
606 /// [`Self::recompute_mass_properties_from_colliders`].
607 ///
608 /// This will override any previous mass-properties set by [`Self::set_additional_mass`],
609 /// [`Self::set_additional_mass_properties`], [`RigidBodyBuilder::additional_mass`], or
610 /// [`RigidBodyBuilder::additional_mass_properties`] for this rigid-body.
611 ///
612 /// If `wake_up` is `true` then the rigid-body will be woken up if it was
613 /// put to sleep because it did not move for a while.
614 #[inline]
615 pub fn set_additional_mass_properties(&mut self, props: MassProperties, wake_up: bool) {
616 self.do_set_additional_mass_properties(
617 RigidBodyAdditionalMassProps::MassProps(props),
618 wake_up,
619 )
620 }
621
622 fn do_set_additional_mass_properties(
623 &mut self,
624 props: RigidBodyAdditionalMassProps,
625 wake_up: bool,
626 ) {
627 let new_mprops = Some(Box::new(props));
628
629 if self.mprops.additional_local_mprops != new_mprops {
630 self.changes.insert(RigidBodyChanges::LOCAL_MASS_PROPERTIES);
631 self.mprops.additional_local_mprops = new_mprops;
632
633 if self.is_dynamic_or_kinematic() && wake_up {
634 self.wake_up(true);
635 }
636 }
637 }
638
639 /// Returns handles of all colliders attached to this body.
640 ///
641 /// Use to iterate over a body's collision shapes or to modify them.
642 ///
643 /// # Example
644 /// ```
645 /// # use rapier3d::prelude::*;
646 /// # let mut bodies = RigidBodySet::new();
647 /// # let mut colliders = ColliderSet::new();
648 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
649 /// # colliders.insert_with_parent(ColliderBuilder::ball(0.5), body, &mut bodies);
650 /// for collider_handle in bodies[body].colliders() {
651 /// if let Some(collider) = colliders.get_mut(*collider_handle) {
652 /// collider.set_friction(0.5);
653 /// }
654 /// }
655 /// ```
656 pub fn colliders(&self) -> &[ColliderHandle] {
657 &self.colliders.0[..]
658 }
659
660 /// Checks if this is a dynamic body (moves via forces and collisions).
661 ///
662 /// Dynamic bodies are fully simulated and respond to gravity, forces, and collisions.
663 pub fn is_dynamic(&self) -> bool {
664 self.body_type == RigidBodyType::Dynamic
665 }
666
667 /// Checks if this is a kinematic body (moves via direct velocity/position control).
668 ///
669 /// Kinematic bodies move by setting velocity directly, not by applying forces.
670 pub fn is_kinematic(&self) -> bool {
671 self.body_type.is_kinematic()
672 }
673
674 /// Is this rigid-body a dynamic rigid-body or a kinematic rigid-body?
675 ///
676 /// This method is mostly convenient internally where kinematic and dynamic rigid-body
677 /// are subject to the same behavior.
678 pub fn is_dynamic_or_kinematic(&self) -> bool {
679 self.body_type.is_dynamic_or_kinematic()
680 }
681
682 /// The offset index in the solver’s active set, or `u32::MAX` if
683 /// the rigid-body isn’t dynamic or kinematic.
684 // TODO: is this really necessary? Could we just always assign u32::MAX
685 // to all the fixed bodies active set offsets?
686 pub fn effective_active_set_offset(&self) -> u32 {
687 if self.is_dynamic_or_kinematic() {
688 self.ids.active_set_id
689 } else {
690 u32::MAX
691 }
692 }
693
694 /// Checks if this is a fixed body (never moves, infinite mass).
695 ///
696 /// Fixed bodies are static geometry: walls, floors, terrain. They never move
697 /// and are not affected by any forces or collisions.
698 pub fn is_fixed(&self) -> bool {
699 self.body_type == RigidBodyType::Fixed
700 }
701
702 /// The mass of this rigid body in kilograms.
703 ///
704 /// Returns zero for fixed bodies (which technically have infinite mass).
705 /// Mass is computed from attached colliders' shapes and densities.
706 pub fn mass(&self) -> Real {
707 self.mprops.local_mprops.mass()
708 }
709
710 /// The predicted position of this rigid-body.
711 ///
712 /// If this rigid-body is kinematic this value is set by the `set_next_kinematic_position`
713 /// method and is used for estimating the kinematic body velocity at the next timestep.
714 /// For non-kinematic bodies, this value is currently unspecified.
715 pub fn next_position(&self) -> &Pose {
716 &self.pos.next_position
717 }
718
719 /// The gravity scale multiplier for this body.
720 ///
721 /// - `1.0` (default) = normal gravity
722 /// - `0.0` = no gravity (floating)
723 /// - `2.0` = double gravity (heavy/fast falling)
724 /// - Negative values = reverse gravity (objects fall upward!)
725 pub fn gravity_scale(&self) -> Real {
726 self.forces.gravity_scale
727 }
728
729 /// Sets how much gravity affects this body (multiplier).
730 ///
731 /// # Examples
732 /// ```
733 /// # use rapier3d::prelude::*;
734 /// # let mut bodies = RigidBodySet::new();
735 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
736 /// bodies[body].set_gravity_scale(0.0, true); // Zero-G (space)
737 /// bodies[body].set_gravity_scale(0.1, true); // Moon gravity
738 /// bodies[body].set_gravity_scale(2.0, true); // Extra heavy
739 /// ```
740 pub fn set_gravity_scale(&mut self, scale: Real, wake_up: bool) {
741 if self.forces.gravity_scale != scale {
742 if wake_up && self.activation.sleeping {
743 self.changes.insert(RigidBodyChanges::SLEEP);
744 self.activation.sleeping = false;
745 }
746
747 self.forces.gravity_scale = scale;
748 }
749 }
750
751 /// The dominance group of this rigid-body.
752 pub fn dominance_group(&self) -> i8 {
753 self.dominance.0
754 }
755
756 /// The dominance group of this rigid-body.
757 pub fn set_dominance_group(&mut self, dominance: i8) {
758 if self.dominance.0 != dominance {
759 self.changes.insert(RigidBodyChanges::DOMINANCE);
760 self.dominance.0 = dominance
761 }
762 }
763
764 /// Adds a collider to this rigid-body.
765 pub(crate) fn add_collider_internal(
766 &mut self,
767 co_handle: ColliderHandle,
768 co_parent: &ColliderParent,
769 co_pos: &mut ColliderPosition,
770 co_shape: &ColliderShape,
771 co_mprops: &ColliderMassProps,
772 ) {
773 self.colliders.attach_collider(
774 self.body_type,
775 &mut self.changes,
776 &mut self.ccd,
777 &mut self.mprops,
778 &self.pos,
779 co_handle,
780 co_pos,
781 co_parent,
782 co_shape,
783 co_mprops,
784 )
785 }
786
787 /// Removes a collider from this rigid-body.
788 pub(crate) fn remove_collider_internal(&mut self, handle: ColliderHandle) {
789 if let Some(i) = self.colliders.0.iter().position(|e| *e == handle) {
790 self.changes.set(RigidBodyChanges::COLLIDERS, true);
791 self.colliders.0.swap_remove(i);
792 }
793 }
794
795 /// Forces this body to sleep immediately (stop simulating it).
796 ///
797 /// Sleeping bodies are excluded from physics simulation until disturbed. Use to manually
798 /// deactivate bodies you know won't move for a while.
799 ///
800 /// The body will auto-wake if:
801 /// - Hit by a moving object
802 /// - Connected via joint to a moving body
803 /// - Manually woken with `wake_up()`
804 pub fn sleep(&mut self) {
805 self.activation.sleep();
806 self.vels = RigidBodyVelocity::zero();
807 }
808
809 /// Wakes up this body if it's sleeping, making it active in the simulation.
810 ///
811 /// # Parameters
812 /// * `strong` - If `true`, guarantees the body stays awake for multiple frames.
813 /// If `false`, it might sleep again immediately if conditions are met.
814 ///
815 /// Use after manually moving a sleeping body or to keep it active temporarily.
816 pub fn wake_up(&mut self, strong: bool) {
817 if self.activation.sleeping {
818 self.changes.insert(RigidBodyChanges::SLEEP);
819 }
820
821 self.activation.wake_up(strong);
822 }
823
824 /// Is this rigid body sleeping?
825 pub fn is_sleeping(&self) -> bool {
826 // TODO: should we:
827 // - return false for fixed bodies.
828 // - return true for non-sleeping dynamic bodies.
829 // - return true only for kinematic bodies with non-zero velocity?
830 self.activation.sleeping
831 }
832
833 /// Returns `true` if the body has non-zero linear or angular velocity.
834 ///
835 /// Useful for checking if an object is actually moving vs sitting still.
836 pub fn is_moving(&self) -> bool {
837 #[cfg(feature = "dim2")]
838 let angvel_is_nonzero = self.vels.angvel != 0.0;
839 #[cfg(feature = "dim3")]
840 let angvel_is_nonzero = self.vels.angvel != Default::default();
841 self.vels.linvel != Default::default() || angvel_is_nonzero
842 }
843
844 /// Returns both linear and angular velocity as a combined structure.
845 ///
846 /// Most users should use `linvel()` and `angvel()` separately instead.
847 pub fn vels(&self) -> &RigidBodyVelocity<Real> {
848 &self.vels
849 }
850
851 /// The current linear velocity (speed and direction of movement).
852 ///
853 /// This is how fast the body is moving in units per second. Use with [`set_linvel()`](Self::set_linvel)
854 /// to directly control the body's movement speed.
855 pub fn linvel(&self) -> Vector {
856 self.vels.linvel
857 }
858
859 /// The current angular velocity (rotation speed) in 2D.
860 ///
861 /// Returns radians per second. Positive = counter-clockwise, negative = clockwise.
862 #[cfg(feature = "dim2")]
863 pub fn angvel(&self) -> Real {
864 self.vels.angvel
865 }
866
867 /// The current angular velocity (rotation speed) in 3D.
868 ///
869 /// Returns a vector in radians per second around each axis (X, Y, Z).
870 #[cfg(feature = "dim3")]
871 pub fn angvel(&self) -> AngVector {
872 self.vels.angvel
873 }
874
875 /// Set both the angular and linear velocity of this rigid-body.
876 ///
877 /// If `wake_up` is `true` then the rigid-body will be woken up if it was
878 /// put to sleep because it did not move for a while.
879 pub fn set_vels(&mut self, vels: RigidBodyVelocity<Real>, wake_up: bool) {
880 self.set_linvel(vels.linvel, wake_up);
881 #[cfg(feature = "dim2")]
882 self.set_angvel(vels.angvel, wake_up);
883 #[cfg(feature = "dim3")]
884 self.set_angvel(vels.angvel, wake_up);
885 }
886
887 /// Sets how fast this body is moving (linear velocity).
888 ///
889 /// This directly sets the body's velocity without applying forces. Use for:
890 /// - Player character movement
891 /// - Kinematic object control
892 /// - Instantly changing an object's speed
893 ///
894 /// For physics-based movement, consider using [`apply_impulse()`](Self::apply_impulse) or
895 /// [`add_force()`](Self::add_force) instead for more realistic behavior.
896 ///
897 /// # Example
898 /// ```
899 /// # use rapier3d::prelude::*;
900 /// # let mut bodies = RigidBodySet::new();
901 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
902 /// // Make the body move to the right at 5 units/second
903 /// bodies[body].set_linvel(Vector::new(5.0, 0.0, 0.0), true);
904 /// ```
905 pub fn set_linvel(&mut self, linvel: Vector, wake_up: bool) {
906 if self.vels.linvel != linvel {
907 match self.body_type {
908 RigidBodyType::Dynamic | RigidBodyType::KinematicVelocityBased => {
909 self.vels.linvel = linvel;
910 if wake_up {
911 self.wake_up(true)
912 }
913 }
914 RigidBodyType::Fixed | RigidBodyType::KinematicPositionBased => {}
915 }
916 }
917 }
918
919 /// The angular velocity of this rigid-body.
920 ///
921 /// If `wake_up` is `true` then the rigid-body will be woken up if it was
922 /// put to sleep because it did not move for a while.
923 #[cfg(feature = "dim2")]
924 pub fn set_angvel(&mut self, angvel: Real, wake_up: bool) {
925 if self.vels.angvel != angvel {
926 match self.body_type {
927 RigidBodyType::Dynamic | RigidBodyType::KinematicVelocityBased => {
928 self.vels.angvel = angvel;
929 if wake_up {
930 self.wake_up(true)
931 }
932 }
933 RigidBodyType::Fixed | RigidBodyType::KinematicPositionBased => {}
934 }
935 }
936 }
937
938 /// The angular velocity of this rigid-body.
939 ///
940 /// If `wake_up` is `true` then the rigid-body will be woken up if it was
941 /// put to sleep because it did not move for a while.
942 #[cfg(feature = "dim3")]
943 pub fn set_angvel(&mut self, angvel: AngVector, wake_up: bool) {
944 if self.vels.angvel != angvel {
945 match self.body_type {
946 RigidBodyType::Dynamic | RigidBodyType::KinematicVelocityBased => {
947 self.vels.angvel = angvel;
948 if wake_up {
949 self.wake_up(true)
950 }
951 }
952 RigidBodyType::Fixed | RigidBodyType::KinematicPositionBased => {}
953 }
954 }
955 }
956
957 /// The current position (translation + rotation) of this rigid body in world space.
958 ///
959 /// Returns an `SimdPose` which combines both translation and rotation.
960 /// For just the position vector, use [`translation()`](Self::translation) instead.
961 #[inline]
962 pub fn position(&self) -> &Pose {
963 &self.pos.position
964 }
965
966 /// The current position vector of this rigid body (world coordinates).
967 ///
968 /// This is just the XYZ location, without rotation. For the full pose (position + rotation),
969 /// use [`position()`](Self::position).
970 #[inline]
971 pub fn translation(&self) -> Vector {
972 self.pos.position.translation
973 }
974
975 /// Teleports this rigid body to a new position (world coordinates).
976 ///
977 /// ⚠️ **Warning**: This instantly moves the body, ignoring physics! The body will "teleport"
978 /// without checking for collisions in between. Use this for:
979 /// - Respawning objects
980 /// - Level transitions
981 /// - Resetting positions
982 ///
983 /// For smooth physics-based movement, use velocities or forces instead.
984 ///
985 /// # Parameters
986 /// * `wake_up` - If `true`, prevents the body from immediately going back to sleep
987 #[inline]
988 pub fn set_translation(&mut self, translation: Vector, wake_up: bool) {
989 if self.pos.position.translation != translation
990 || self.pos.next_position.translation != translation
991 {
992 self.changes.insert(RigidBodyChanges::POSITION);
993 self.pos.position.translation = translation;
994 self.pos.next_position.translation = translation;
995
996 // Update the world mass-properties so torque application remains valid.
997 self.update_world_mass_properties();
998
999 // TODO: Do we really need to check that the body isn't dynamic?
1000 if wake_up && self.is_dynamic_or_kinematic() {
1001 self.wake_up(true)
1002 }
1003 }
1004 }
1005
1006 /// The current rotation/orientation of this rigid body.
1007 #[inline]
1008 pub fn rotation(&self) -> &Rotation {
1009 &self.pos.position.rotation
1010 }
1011
1012 /// Instantly rotates this rigid body to a new orientation.
1013 ///
1014 /// ⚠️ **Warning**: This teleports the rotation, ignoring physics! See [`set_translation()`](Self::set_translation) for details.
1015 #[inline]
1016 pub fn set_rotation(&mut self, rotation: Rotation, wake_up: bool) {
1017 if self.pos.position.rotation != rotation || self.pos.next_position.rotation != rotation {
1018 self.changes.insert(RigidBodyChanges::POSITION);
1019 self.pos.position.rotation = rotation;
1020 self.pos.next_position.rotation = rotation;
1021
1022 // Update the world mass-properties so torque application remains valid.
1023 self.update_world_mass_properties();
1024
1025 // TODO: Do we really need to check that the body isn't dynamic?
1026 if wake_up && self.is_dynamic_or_kinematic() {
1027 self.wake_up(true)
1028 }
1029 }
1030 }
1031
1032 /// Teleports this body to a new position and rotation (ignoring physics).
1033 ///
1034 /// ⚠️ **Warning**: Instantly moves the body without checking for collisions!
1035 /// For position-based kinematic bodies, this also resets their interpolated velocity to zero.
1036 ///
1037 /// Use for respawning, level transitions, or resetting positions.
1038 pub fn set_position(&mut self, pos: Pose, wake_up: bool) {
1039 if self.pos.position != pos || self.pos.next_position != pos {
1040 self.changes.insert(RigidBodyChanges::POSITION);
1041 self.pos.position = pos;
1042 self.pos.next_position = pos;
1043
1044 // Update the world mass-properties so torque application remains valid.
1045 self.update_world_mass_properties();
1046
1047 // TODO: Do we really need to check that the body isn't dynamic?
1048 if wake_up && self.is_dynamic_or_kinematic() {
1049 self.wake_up(true)
1050 }
1051 }
1052 }
1053
1054 /// For position-based kinematic bodies: sets where the body should rotate to by next frame.
1055 ///
1056 /// Only works for `KinematicPositionBased` bodies. Rapier computes the angular velocity
1057 /// needed to reach this rotation smoothly.
1058 pub fn set_next_kinematic_rotation(&mut self, rotation: Rotation) {
1059 if self.is_kinematic() {
1060 self.pos.next_position.rotation = rotation;
1061
1062 if self.pos.position.rotation != rotation {
1063 self.wake_up(true);
1064 }
1065 }
1066 }
1067
1068 /// For position-based kinematic bodies: sets where the body should move to by next frame.
1069 ///
1070 /// Only works for `KinematicPositionBased` bodies. Rapier computes the velocity
1071 /// needed to reach this position smoothly.
1072 pub fn set_next_kinematic_translation(&mut self, translation: Vector) {
1073 if self.is_kinematic() {
1074 self.pos.next_position.translation = translation;
1075
1076 if self.pos.position.translation != translation {
1077 self.wake_up(true);
1078 }
1079 }
1080 }
1081
1082 /// For position-based kinematic bodies: sets the target pose (position + rotation) for next frame.
1083 ///
1084 /// Only works for `KinematicPositionBased` bodies. Combines translation and rotation control.
1085 pub fn set_next_kinematic_position(&mut self, pos: Pose) {
1086 if self.is_kinematic() {
1087 self.pos.next_position = pos;
1088
1089 if self.pos.position != pos {
1090 self.wake_up(true);
1091 }
1092 }
1093 }
1094
1095 /// Predicts the next position of this rigid-body, by integrating its velocity and forces
1096 /// by a time of `dt`.
1097 pub(crate) fn predict_position_using_velocity_and_forces_with_max_dist(
1098 &self,
1099 dt: Real,
1100 max_dist: Real,
1101 ) -> Pose {
1102 let new_vels = self.forces.integrate(dt, &self.vels, &self.mprops);
1103 // Compute the clamped dt such that the body doesn't travel more than `max_dist`.
1104 let linvel_norm = new_vels.linvel.length();
1105 let clamped_linvel = linvel_norm.min(max_dist * crate::utils::inv(dt));
1106 let clamped_dt = dt * clamped_linvel * crate::utils::inv(linvel_norm);
1107 new_vels.integrate(
1108 clamped_dt,
1109 &self.pos.position,
1110 &self.mprops.local_mprops.local_com,
1111 )
1112 }
1113
1114 /// Calculates where this body will be after `dt` seconds, considering current velocity AND forces.
1115 ///
1116 /// Useful for predicting future positions or implementing custom integration.
1117 /// Accounts for gravity and applied forces.
1118 pub fn predict_position_using_velocity_and_forces(&self, dt: Real) -> Pose {
1119 self.pos
1120 .integrate_forces_and_velocities(dt, &self.forces, &self.vels, &self.mprops)
1121 }
1122
1123 /// Calculates where this body will be after `dt` seconds, considering only current velocity (not forces).
1124 ///
1125 /// Like `predict_position_using_velocity_and_forces()` but ignores applied forces.
1126 /// Useful when you only care about inertial motion without acceleration.
1127 pub fn predict_position_using_velocity(&self, dt: Real) -> Pose {
1128 self.vels
1129 .integrate(dt, &self.pos.position, &self.mprops.local_mprops.local_com)
1130 }
1131
1132 pub(crate) fn update_world_mass_properties(&mut self) {
1133 self.mprops
1134 .update_world_mass_properties(self.body_type, &self.pos.position);
1135 }
1136}
1137
1138/// ## Applying forces and torques
1139impl RigidBody {
1140 /// Clears all forces that were added with `add_force()`.
1141 ///
1142 /// User-defined forces are **not** cleared automatically: once added, a force keeps being
1143 /// applied at every physics step until you change it or clear it with this method. Call this
1144 /// when you want a previously-added force to stop being applied.
1145 pub fn reset_forces(&mut self, wake_up: bool) {
1146 if self.forces.user_force != Vector::ZERO {
1147 self.forces.user_force = Vector::ZERO;
1148
1149 if wake_up {
1150 self.wake_up(true);
1151 }
1152 }
1153 }
1154
1155 /// Clears all torques that were added with `add_torque()`.
1156 ///
1157 /// User-defined torques are **not** cleared automatically: once added, a torque keeps being
1158 /// applied at every physics step until you change it or clear it with this method.
1159 #[cfg(feature = "dim2")]
1160 pub fn reset_torques(&mut self, wake_up: bool) {
1161 if self.forces.user_torque != 0.0 {
1162 self.forces.user_torque = 0.0;
1163
1164 if wake_up {
1165 self.wake_up(true);
1166 }
1167 }
1168 }
1169
1170 /// Clears all torques that were added with `add_torque()`.
1171 ///
1172 /// User-defined torques are **not** cleared automatically: once added, a torque keeps being
1173 /// applied at every physics step until you change it or clear it with this method.
1174 #[cfg(feature = "dim3")]
1175 pub fn reset_torques(&mut self, wake_up: bool) {
1176 if self.forces.user_torque != AngVector::ZERO {
1177 self.forces.user_torque = AngVector::ZERO;
1178
1179 if wake_up {
1180 self.wake_up(true);
1181 }
1182 }
1183 }
1184
1185 /// Applies a continuous force to this body (like thrust, wind, or magnets).
1186 ///
1187 /// Unlike [`apply_impulse()`](Self::apply_impulse) which is instant, a force is applied
1188 /// continuously over time. Successive calls to `add_force()` accumulate. Use for:
1189 /// - Rocket/jet thrust
1190 /// - Wind or water currents
1191 /// - Magnetic/gravity fields
1192 /// - Continuous pushing/pulling
1193 ///
1194 /// User-defined forces are **not** cleared automatically: once added, a force keeps being
1195 /// applied at every physics step until you change it or clear it with
1196 /// [`reset_forces()`](Self::reset_forces). To apply a force for a single step only, call
1197 /// `reset_forces()` after stepping the simulation.
1198 ///
1199 /// # Example
1200 /// ```
1201 /// # use rapier3d::prelude::*;
1202 /// # let mut bodies = RigidBodySet::new();
1203 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
1204 /// // Apply thrust every frame
1205 /// bodies[body].add_force(Vector::new(0.0, 100.0, 0.0), true);
1206 /// ```
1207 ///
1208 /// Only affects dynamic bodies (does nothing for kinematic/fixed bodies).
1209 pub fn add_force(&mut self, force: Vector, wake_up: bool) {
1210 if force != Vector::ZERO && self.body_type == RigidBodyType::Dynamic {
1211 self.forces.user_force += force;
1212
1213 if wake_up {
1214 self.wake_up(true);
1215 }
1216 }
1217 }
1218
1219 /// Applies a continuous rotational force (torque) to spin this body.
1220 ///
1221 /// Like `add_force()` but for rotation. Successive calls accumulate, and the torque is **not**
1222 /// cleared automatically (see [`reset_torques()`](Self::reset_torques)).
1223 /// In 2D: positive = counter-clockwise, negative = clockwise.
1224 ///
1225 /// Only affects dynamic bodies.
1226 #[cfg(feature = "dim2")]
1227 pub fn add_torque(&mut self, torque: Real, wake_up: bool) {
1228 if !torque.is_zero() && self.body_type == RigidBodyType::Dynamic {
1229 self.forces.user_torque += torque;
1230
1231 if wake_up {
1232 self.wake_up(true);
1233 }
1234 }
1235 }
1236
1237 /// Applies a continuous rotational force (torque) to spin this body.
1238 ///
1239 /// Like `add_force()` but for rotation. In 3D, the torque vector direction
1240 /// determines the rotation axis (right-hand rule).
1241 ///
1242 /// Only affects dynamic bodies.
1243 #[cfg(feature = "dim3")]
1244 pub fn add_torque(&mut self, torque: Vector, wake_up: bool) {
1245 if torque != Vector::ZERO && self.body_type == RigidBodyType::Dynamic {
1246 self.forces.user_torque += torque;
1247
1248 if wake_up {
1249 self.wake_up(true);
1250 }
1251 }
1252 }
1253
1254 /// Applies force at a specific point on the body (creates both force and torque).
1255 ///
1256 /// When you push an object off-center, it both moves AND spins. This method handles both effects.
1257 /// The force creates linear acceleration, and the offset from center-of-mass creates torque.
1258 ///
1259 /// Use for: Forces applied at contact points, explosions at specific locations, pushing objects.
1260 ///
1261 /// # Parameters
1262 /// * `force` - The force vector to apply
1263 /// * `point` - Where to apply the force (world coordinates)
1264 ///
1265 /// Only affects dynamic bodies.
1266 pub fn add_force_at_point(&mut self, force: Vector, point: Vector, wake_up: bool) {
1267 if force != Vector::ZERO && self.body_type == RigidBodyType::Dynamic {
1268 self.forces.user_force += force;
1269 self.forces.user_torque += (point - self.mprops.world_com).gcross(force);
1270
1271 if wake_up {
1272 self.wake_up(true);
1273 }
1274 }
1275 }
1276}
1277
1278/// ## Applying impulses and angular impulses
1279impl RigidBody {
1280 /// Instantly changes the velocity by applying an impulse (like a kick or explosion).
1281 ///
1282 /// An impulse is an instant change in momentum. Think of it as a "one-time push" that
1283 /// immediately affects velocity. Use for:
1284 /// - Jumping (apply upward impulse)
1285 /// - Explosions pushing objects away
1286 /// - Getting hit by something
1287 /// - Launching projectiles
1288 ///
1289 /// The effect depends on the body's mass - heavier objects will be affected less by the same impulse.
1290 ///
1291 /// **For continuous forces** (like rocket thrust or wind), use [`add_force()`](Self::add_force) instead.
1292 ///
1293 /// # Example
1294 /// ```
1295 /// # use rapier3d::prelude::*;
1296 /// # let mut bodies = RigidBodySet::new();
1297 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
1298 /// // Make a character jump
1299 /// bodies[body].apply_impulse(Vector::new(0.0, 300.0, 0.0), true);
1300 /// ```
1301 ///
1302 /// Only affects dynamic bodies (does nothing for kinematic/fixed bodies).
1303 #[profiling::function]
1304 pub fn apply_impulse(&mut self, impulse: Vector, wake_up: bool) {
1305 if impulse != Vector::ZERO && self.body_type == RigidBodyType::Dynamic {
1306 self.vels.linvel += impulse * self.mprops.effective_inv_mass;
1307
1308 if wake_up {
1309 self.wake_up(true);
1310 }
1311 }
1312 }
1313
1314 /// Applies an angular impulse at the center-of-mass of this rigid-body.
1315 /// The impulse is applied right away, changing the angular velocity.
1316 /// This does nothing on non-dynamic bodies.
1317 #[cfg(feature = "dim2")]
1318 #[profiling::function]
1319 pub fn apply_torque_impulse(&mut self, torque_impulse: Real, wake_up: bool) {
1320 if !torque_impulse.is_zero() && self.body_type == RigidBodyType::Dynamic {
1321 self.vels.angvel += self.mprops.effective_world_inv_inertia * torque_impulse;
1322
1323 if wake_up {
1324 self.wake_up(true);
1325 }
1326 }
1327 }
1328
1329 /// Instantly changes rotation speed by applying angular impulse (like a sudden spin).
1330 ///
1331 /// In 3D, the impulse vector direction determines the spin axis (right-hand rule).
1332 /// Like `apply_impulse()` but for rotation. Only affects dynamic bodies.
1333 #[cfg(feature = "dim3")]
1334 #[profiling::function]
1335 pub fn apply_torque_impulse(&mut self, torque_impulse: Vector, wake_up: bool) {
1336 if torque_impulse != Vector::ZERO && self.body_type == RigidBodyType::Dynamic {
1337 self.vels.angvel += self.mprops.effective_world_inv_inertia * torque_impulse;
1338
1339 if wake_up {
1340 self.wake_up(true);
1341 }
1342 }
1343 }
1344
1345 /// Applies impulse at a specific point on the body (creates both linear and angular effects).
1346 ///
1347 /// Like `add_force_at_point()` but instant instead of continuous. When you hit an object
1348 /// off-center, it both flies away AND spins - this method handles both.
1349 ///
1350 /// # Example
1351 /// ```
1352 /// # use rapier3d::prelude::*;
1353 /// # let mut bodies = RigidBodySet::new();
1354 /// # let body = bodies.insert(RigidBodyBuilder::dynamic());
1355 /// // Hit the top-left corner of a box
1356 /// bodies[body].apply_impulse_at_point(
1357 /// Vector::new(100.0, 0.0, 0.0),
1358 /// Vector::new(-0.5, 0.5, 0.0), // Top-left of a 1x1 box
1359 /// true
1360 /// );
1361 /// // Box will move right AND spin
1362 /// ```
1363 ///
1364 /// Only affects dynamic bodies.
1365 pub fn apply_impulse_at_point(&mut self, impulse: Vector, point: Vector, wake_up: bool) {
1366 let torque_impulse = (point - self.mprops.world_com).gcross(impulse);
1367 self.apply_impulse(impulse, wake_up);
1368 self.apply_torque_impulse(torque_impulse, wake_up);
1369 }
1370
1371 /// Returns the total force currently queued to be applied this frame.
1372 ///
1373 /// This is the sum of all `add_force()` calls since the last physics step.
1374 /// Returns zero for non-dynamic bodies.
1375 pub fn user_force(&self) -> Vector {
1376 if self.body_type == RigidBodyType::Dynamic {
1377 self.forces.user_force
1378 } else {
1379 Vector::ZERO
1380 }
1381 }
1382
1383 /// Returns the total torque currently queued to be applied this frame.
1384 ///
1385 /// This is the sum of all `add_torque()` calls since the last physics step.
1386 /// Returns zero for non-dynamic bodies.
1387 pub fn user_torque(&self) -> AngVector {
1388 if self.body_type == RigidBodyType::Dynamic {
1389 self.forces.user_torque
1390 } else {
1391 #[cfg(feature = "dim2")]
1392 {
1393 0.0
1394 }
1395 #[cfg(feature = "dim3")]
1396 {
1397 AngVector::ZERO
1398 }
1399 }
1400 }
1401
1402 /// Checks if gyroscopic forces are enabled (3D only).
1403 ///
1404 /// Gyroscopic forces cause spinning objects to resist changes in rotation axis
1405 /// (like how spinning tops stay upright). Adds slight CPU cost.
1406 #[cfg(feature = "dim3")]
1407 pub fn gyroscopic_forces_enabled(&self) -> bool {
1408 self.forces.gyroscopic_forces_enabled
1409 }
1410
1411 /// Enables/disables gyroscopic forces for more realistic spinning behavior.
1412 ///
1413 /// When enabled, rapidly spinning objects resist rotation axis changes (like gyroscopes).
1414 /// Examples: spinning tops, flywheels, rotating spacecraft.
1415 ///
1416 /// **Default**: Disabled (costs performance, rarely needed in games).
1417 #[cfg(feature = "dim3")]
1418 pub fn enable_gyroscopic_forces(&mut self, enabled: bool) {
1419 self.forces.gyroscopic_forces_enabled = enabled;
1420 }
1421}
1422
1423impl RigidBody {
1424 /// Calculates the velocity at a specific point on this body.
1425 ///
1426 /// Due to rotation, different points on a rigid body move at different speeds.
1427 /// This computes the linear velocity at any world-space point.
1428 ///
1429 /// Useful for: impact calculations, particle effects, sound volume based on impact speed.
1430 pub fn velocity_at_point(&self, point: Vector) -> Vector {
1431 self.vels.velocity_at_point(point, self.mprops.world_com)
1432 }
1433
1434 /// Calculates the kinetic energy of this body (energy from motion).
1435 ///
1436 /// Returns `0.5 * mass * velocity² + 0.5 * inertia * angular_velocity²`
1437 /// Useful for physics-based gameplay (energy tracking, damage based on impact energy).
1438 pub fn kinetic_energy(&self) -> Real {
1439 self.vels.kinetic_energy(&self.mprops)
1440 }
1441
1442 /// Calculates the gravitational potential energy of this body.
1443 ///
1444 /// Returns `mass * gravity * height`. Useful for energy conservation checks.
1445 pub fn gravitational_potential_energy(&self, dt: Real, gravity: Vector) -> Real {
1446 let world_com = self.mprops.local_mprops.world_com(&self.pos.position);
1447
1448 // Project position back along velocity vector one half-step (leap-frog)
1449 // to sync up the potential energy with the kinetic energy:
1450 let world_com = world_com - self.vels.linvel * (dt / 2.0);
1451
1452 -self.mass() * self.forces.gravity_scale * gravity.dot(world_com)
1453 }
1454
1455 /// Computes the angular velocity of this rigid-body after application of gyroscopic forces.
1456 #[cfg(feature = "dim3")]
1457 pub fn angvel_with_gyroscopic_forces(&self, dt: Real) -> AngVector {
1458 let mprops = &self.mprops.local_mprops;
1459 // World-space principal axes = body rotation ∘ principal frame.
1460 let principal_axes = self.pos.position.rotation * mprops.principal_inertia_local_frame;
1461 gyroscopic_corrected_angvel(
1462 self.angvel(),
1463 principal_axes,
1464 mprops.principal_inertia(),
1465 mprops.inv_principal_inertia,
1466 dt,
1467 )
1468 }
1469}
1470
1471/// A builder for creating rigid bodies with custom properties.
1472///
1473/// This builder lets you configure all properties of a rigid body before adding it to your world.
1474/// Start with one of the type constructors ([`dynamic()`](Self::dynamic), [`fixed()`](Self::fixed),
1475/// [`kinematic_position_based()`](Self::kinematic_position_based), or
1476/// [`kinematic_velocity_based()`](Self::kinematic_velocity_based)), then chain property setters,
1477/// and finally call [`build()`](Self::build).
1478///
1479/// # Example
1480///
1481/// ```
1482/// # use rapier3d::prelude::*;
1483/// let body = RigidBodyBuilder::dynamic()
1484/// .translation(Vector::new(0.0, 5.0, 0.0)) // Start 5 units above ground
1485/// .linvel(Vector::new(1.0, 0.0, 0.0)) // Initial velocity to the right
1486/// .can_sleep(false) // Keep always active
1487/// .build();
1488/// ```
1489#[derive(Clone, Debug, PartialEq)]
1490#[must_use = "Builder functions return the updated builder"]
1491pub struct RigidBodyBuilder {
1492 /// The initial position of the rigid-body to be built.
1493 pub position: Pose,
1494 /// The linear velocity of the rigid-body to be built.
1495 pub linvel: Vector,
1496 /// The angular velocity of the rigid-body to be built.
1497 pub angvel: AngVector,
1498 /// The scale factor applied to the gravity affecting the rigid-body to be built, `1.0` by default.
1499 pub gravity_scale: Real,
1500 /// Damping factor for gradually slowing down the translational motion of the rigid-body, `0.0` by default.
1501 pub linear_damping: Real,
1502 /// Damping factor for gradually slowing down the angular motion of the rigid-body, `0.0` by default.
1503 pub angular_damping: Real,
1504 /// The type of rigid-body being constructed.
1505 pub body_type: RigidBodyType,
1506 mprops_flags: LockedAxes,
1507 /// The additional mass-properties of the rigid-body being built. See [`RigidBodyBuilder::additional_mass_properties`] for more information.
1508 additional_mass_properties: RigidBodyAdditionalMassProps,
1509 /// Whether the rigid-body to be created can sleep if it reaches a dynamic equilibrium.
1510 pub can_sleep: bool,
1511 /// Whether the rigid-body is to be created asleep.
1512 pub sleeping: bool,
1513 /// Whether full ("bullet") Continuous Collision-Detection is enabled for the rigid-body to be
1514 /// built. Fast dynamic bodies always sweep fixed colliders; this also sweeps kinematic and
1515 /// dynamic bodies. CCD prevents tunneling but may allow limited interpenetration of colliders.
1516 pub ccd_enabled: bool,
1517 /// The maximum prediction distance Soft Continuous Collision-Detection.
1518 ///
1519 /// When set to 0, soft CCD is disabled. Soft-CCD helps prevent tunneling especially of
1520 /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how
1521 /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact
1522 /// performance badly by increasing the work needed from the broad-phase.
1523 ///
1524 /// It is a generally cheaper variant of regular CCD (that can be enabled with
1525 /// [`RigidBodyBuilder::ccd_enabled`] since it relies on predictive constraints instead of
1526 /// shape-cast and substeps.
1527 pub soft_ccd_prediction: Real,
1528 /// Allow the rigid-body being built to exceed the angular speed cap.
1529 /// See [`RigidBody::set_allow_fast_rotation`].
1530 pub allow_fast_rotation: bool,
1531 /// The dominance group of the rigid-body to be built.
1532 pub dominance_group: i8,
1533 /// Will the rigid-body being built be enabled?
1534 pub enabled: bool,
1535 /// An arbitrary user-defined 128-bit integer associated to the rigid-bodies built by this builder.
1536 pub user_data: u128,
1537 /// The additional number of solver iterations run for the constraints directly
1538 /// involving this rigid-body.
1539 ///
1540 /// See [`RigidBody::set_additional_solver_iterations`] for additional information.
1541 pub additional_solver_iterations: usize,
1542 /// Are gyroscopic forces enabled for this rigid-body?
1543 pub gyroscopic_forces_enabled: bool,
1544}
1545
1546impl Default for RigidBodyBuilder {
1547 fn default() -> Self {
1548 Self::dynamic()
1549 }
1550}
1551
1552impl RigidBodyBuilder {
1553 /// Initialize a new builder for a rigid body which is either fixed, dynamic, or kinematic.
1554 pub fn new(body_type: RigidBodyType) -> Self {
1555 #[cfg(feature = "dim2")]
1556 let angvel = 0.0;
1557 #[cfg(feature = "dim3")]
1558 let angvel = AngVector::ZERO;
1559
1560 Self {
1561 position: Pose::IDENTITY,
1562 linvel: Vector::ZERO,
1563 angvel,
1564 gravity_scale: 1.0,
1565 linear_damping: 0.0,
1566 angular_damping: 0.0,
1567 body_type,
1568 mprops_flags: LockedAxes::empty(),
1569 additional_mass_properties: RigidBodyAdditionalMassProps::default(),
1570 can_sleep: true,
1571 sleeping: false,
1572 ccd_enabled: false,
1573 soft_ccd_prediction: 0.0,
1574 allow_fast_rotation: false,
1575 dominance_group: 0,
1576 enabled: true,
1577 user_data: 0,
1578 additional_solver_iterations: 0,
1579 gyroscopic_forces_enabled: true,
1580 }
1581 }
1582
1583 /// Initializes the builder of a new fixed rigid body.
1584 #[deprecated(note = "use `RigidBodyBuilder::fixed()` instead")]
1585 pub fn new_static() -> Self {
1586 Self::fixed()
1587 }
1588 /// Initializes the builder of a new velocity-based kinematic rigid body.
1589 #[deprecated(note = "use `RigidBodyBuilder::kinematic_velocity_based()` instead")]
1590 pub fn new_kinematic_velocity_based() -> Self {
1591 Self::kinematic_velocity_based()
1592 }
1593 /// Initializes the builder of a new position-based kinematic rigid body.
1594 #[deprecated(note = "use `RigidBodyBuilder::kinematic_position_based()` instead")]
1595 pub fn new_kinematic_position_based() -> Self {
1596 Self::kinematic_position_based()
1597 }
1598
1599 /// Creates a builder for a **fixed** (static) rigid body.
1600 ///
1601 /// Fixed bodies never move and are not affected by any forces. Use them for:
1602 /// - Walls, floors, and ceilings
1603 /// - Static terrain and level geometry
1604 /// - Any object that should never move in your simulation
1605 ///
1606 /// Fixed bodies have infinite mass and never sleep.
1607 pub fn fixed() -> Self {
1608 Self::new(RigidBodyType::Fixed)
1609 }
1610
1611 /// Creates a builder for a **velocity-based kinematic** rigid body.
1612 ///
1613 /// Kinematic bodies are moved by directly setting their velocity (not by applying forces).
1614 /// They can push dynamic bodies but are not affected by them. Use for:
1615 /// - Moving platforms and elevators
1616 /// - Doors and sliding panels
1617 /// - Any object you want to control directly while still affecting other physics objects
1618 ///
1619 /// Set velocity with [`RigidBody::set_linvel`] and [`RigidBody::set_angvel`].
1620 pub fn kinematic_velocity_based() -> Self {
1621 Self::new(RigidBodyType::KinematicVelocityBased)
1622 }
1623
1624 /// Creates a builder for a **position-based kinematic** rigid body.
1625 ///
1626 /// Similar to velocity-based kinematic, but you control it by setting its next position
1627 /// directly rather than setting velocity. Rapier will automatically compute the velocity
1628 /// needed to reach that position. Use for objects animated by external systems.
1629 pub fn kinematic_position_based() -> Self {
1630 Self::new(RigidBodyType::KinematicPositionBased)
1631 }
1632
1633 /// Creates a builder for a **dynamic** rigid body.
1634 ///
1635 /// Dynamic bodies are fully simulated - they respond to gravity, forces, collisions, and
1636 /// constraints. This is the most common type for interactive objects. Use for:
1637 /// - Physics objects that should fall and bounce (boxes, spheres, ragdolls)
1638 /// - Projectiles and debris
1639 /// - Vehicles and moving characters (when not using kinematic control)
1640 /// - Any object that should behave realistically under physics
1641 ///
1642 /// Dynamic bodies can sleep (become inactive) when at rest to save performance.
1643 pub fn dynamic() -> Self {
1644 Self::new(RigidBodyType::Dynamic)
1645 }
1646
1647 /// Sets the additional number of solver iterations run for the constraints directly
1648 /// involving this rigid-body.
1649 ///
1650 /// See [`RigidBody::set_additional_solver_iterations`] for additional information.
1651 pub fn additional_solver_iterations(mut self, additional_iterations: usize) -> Self {
1652 self.additional_solver_iterations = additional_iterations;
1653 self
1654 }
1655
1656 /// Sets the scale applied to the gravity force affecting the rigid-body to be created.
1657 pub fn gravity_scale(mut self, scale_factor: Real) -> Self {
1658 self.gravity_scale = scale_factor;
1659 self
1660 }
1661
1662 /// Sets the dominance group (advanced collision priority system).
1663 ///
1664 /// Higher dominance groups can push lower ones but not vice versa.
1665 /// Rarely needed - most games don't use this. Default is 0 (all equal priority).
1666 ///
1667 /// Use case: Heavy objects that should always push lighter ones in contacts.
1668 pub fn dominance_group(mut self, group: i8) -> Self {
1669 self.dominance_group = group;
1670 self
1671 }
1672
1673 /// Sets the initial position (XYZ coordinates) where this body will be created.
1674 ///
1675 /// # Example
1676 /// ```
1677 /// # use rapier3d::prelude::*;
1678 /// let body = RigidBodyBuilder::dynamic()
1679 /// .translation(Vector::new(10.0, 5.0, -3.0))
1680 /// .build();
1681 /// ```
1682 pub fn translation(mut self, translation: Vector) -> Self {
1683 self.position.translation = translation;
1684 self
1685 }
1686
1687 /// Sets the initial rotation/orientation of the body to be created.
1688 ///
1689 /// # Example
1690 /// ```
1691 /// # use rapier3d::prelude::*;
1692 /// // Rotate 45 degrees around Y axis (in 3D)
1693 /// let body = RigidBodyBuilder::dynamic()
1694 /// .rotation(Vector::new(0.0, std::f32::consts::PI / 4.0, 0.0))
1695 /// .build();
1696 /// ```
1697 pub fn rotation(mut self, angle: AngVector) -> Self {
1698 self.position.rotation = rotation_from_angle(angle);
1699 self
1700 }
1701
1702 /// Sets the initial position (translation and orientation) of the rigid-body to be created.
1703 #[deprecated = "renamed to `RigidBodyBuilder::pose`"]
1704 pub fn position(mut self, pos: Pose) -> Self {
1705 self.position = pos;
1706 self
1707 }
1708
1709 /// Sets the initial pose (translation and orientation) of the rigid-body to be created.
1710 pub fn pose(mut self, pos: Pose) -> Self {
1711 self.position = pos;
1712 self
1713 }
1714
1715 /// An arbitrary user-defined 128-bit integer associated to the rigid-bodies built by this builder.
1716 pub fn user_data(mut self, data: u128) -> Self {
1717 self.user_data = data;
1718 self
1719 }
1720
1721 /// Sets the additional mass-properties of the rigid-body being built.
1722 ///
1723 /// This will be overridden by a call to [`Self::additional_mass`] so it only makes sense to call
1724 /// either [`Self::additional_mass`] or [`Self::additional_mass_properties`].
1725 ///
1726 /// Note that "additional" means that the final mass-properties of the rigid-bodies depends
1727 /// on the initial mass-properties of the rigid-body (set by this method)
1728 /// to which is added the contributions of all the colliders with non-zero density
1729 /// attached to this rigid-body.
1730 ///
1731 /// Therefore, if you want your provided mass-properties to be the final
1732 /// mass-properties of your rigid-body, don't attach colliders to it, or
1733 /// only attach colliders with densities equal to zero.
1734 pub fn additional_mass_properties(mut self, mprops: MassProperties) -> Self {
1735 self.additional_mass_properties = RigidBodyAdditionalMassProps::MassProps(mprops);
1736 self
1737 }
1738
1739 /// Sets the additional mass of the rigid-body being built.
1740 ///
1741 /// This will be overridden by a call to [`Self::additional_mass_properties`] so it only makes
1742 /// sense to call either [`Self::additional_mass`] or [`Self::additional_mass_properties`].
1743 ///
1744 /// This is only the "additional" mass because the total mass of the rigid-body is
1745 /// equal to the sum of this additional mass and the mass computed from the colliders
1746 /// (with non-zero densities) attached to this rigid-body.
1747 ///
1748 /// The total angular inertia of the rigid-body will be scaled automatically based on this
1749 /// additional mass. If this scaling effect isn’t desired, use [`Self::additional_mass_properties`]
1750 /// instead of this method.
1751 ///
1752 /// # Parameters
1753 /// * `mass`- The mass that will be added to the created rigid-body.
1754 pub fn additional_mass(mut self, mass: Real) -> Self {
1755 self.additional_mass_properties = RigidBodyAdditionalMassProps::Mass(mass);
1756 self
1757 }
1758
1759 /// Sets which movement axes are locked (cannot move/rotate).
1760 ///
1761 /// See [`LockedAxes`] for examples of constraining movement to specific directions.
1762 pub fn locked_axes(mut self, locked_axes: LockedAxes) -> Self {
1763 self.mprops_flags = locked_axes;
1764 self
1765 }
1766
1767 /// Prevents all translational movement (body can still rotate).
1768 ///
1769 /// Use for turrets, spinning objects fixed in place, etc.
1770 pub fn lock_translations(mut self) -> Self {
1771 self.mprops_flags.set(LockedAxes::TRANSLATION_LOCKED, true);
1772 self
1773 }
1774
1775 /// Locks translation along specific axes.
1776 ///
1777 /// # Example
1778 /// ```
1779 /// # use rapier3d::prelude::*;
1780 /// // 2D game in 3D: lock Z movement
1781 /// let body = RigidBodyBuilder::dynamic()
1782 /// .enabled_translations(true, true, false) // X, Y free; Z locked
1783 /// .build();
1784 /// ```
1785 pub fn enabled_translations(
1786 mut self,
1787 allow_translations_x: bool,
1788 allow_translations_y: bool,
1789 #[cfg(feature = "dim3")] allow_translations_z: bool,
1790 ) -> Self {
1791 self.mprops_flags
1792 .set(LockedAxes::TRANSLATION_LOCKED_X, !allow_translations_x);
1793 self.mprops_flags
1794 .set(LockedAxes::TRANSLATION_LOCKED_Y, !allow_translations_y);
1795 #[cfg(feature = "dim3")]
1796 self.mprops_flags
1797 .set(LockedAxes::TRANSLATION_LOCKED_Z, !allow_translations_z);
1798 self
1799 }
1800
1801 #[deprecated(note = "Use `enabled_translations` instead")]
1802 /// Only allow translations of this rigid-body around specific coordinate axes.
1803 pub fn restrict_translations(
1804 self,
1805 allow_translations_x: bool,
1806 allow_translations_y: bool,
1807 #[cfg(feature = "dim3")] allow_translations_z: bool,
1808 ) -> Self {
1809 self.enabled_translations(
1810 allow_translations_x,
1811 allow_translations_y,
1812 #[cfg(feature = "dim3")]
1813 allow_translations_z,
1814 )
1815 }
1816
1817 /// Prevents all rotational movement (body can still translate).
1818 ///
1819 /// Use for characters that shouldn't tip over, objects that should only slide, etc.
1820 pub fn lock_rotations(mut self) -> Self {
1821 self.mprops_flags.set(LockedAxes::ROTATION_LOCKED_X, true);
1822 self.mprops_flags.set(LockedAxes::ROTATION_LOCKED_Y, true);
1823 self.mprops_flags.set(LockedAxes::ROTATION_LOCKED_Z, true);
1824 self
1825 }
1826
1827 /// Only allow rotations of this rigid-body around specific coordinate axes.
1828 #[cfg(feature = "dim3")]
1829 pub fn enabled_rotations(
1830 mut self,
1831 allow_rotations_x: bool,
1832 allow_rotations_y: bool,
1833 allow_rotations_z: bool,
1834 ) -> Self {
1835 self.mprops_flags
1836 .set(LockedAxes::ROTATION_LOCKED_X, !allow_rotations_x);
1837 self.mprops_flags
1838 .set(LockedAxes::ROTATION_LOCKED_Y, !allow_rotations_y);
1839 self.mprops_flags
1840 .set(LockedAxes::ROTATION_LOCKED_Z, !allow_rotations_z);
1841 self
1842 }
1843
1844 /// Locks or unlocks rotations of this rigid-body along each cartesian axes.
1845 #[deprecated(note = "Use `enabled_rotations` instead")]
1846 #[cfg(feature = "dim3")]
1847 pub fn restrict_rotations(
1848 self,
1849 allow_rotations_x: bool,
1850 allow_rotations_y: bool,
1851 allow_rotations_z: bool,
1852 ) -> Self {
1853 self.enabled_rotations(allow_rotations_x, allow_rotations_y, allow_rotations_z)
1854 }
1855
1856 /// Sets linear damping (how quickly linear velocity decreases over time).
1857 ///
1858 /// Models air resistance, drag, etc. Higher values = faster slowdown.
1859 /// - `0.0` = no drag (space)
1860 /// - `0.1` = light drag (air)
1861 /// - `1.0+` = heavy drag (underwater)
1862 pub fn linear_damping(mut self, factor: Real) -> Self {
1863 self.linear_damping = factor;
1864 self
1865 }
1866
1867 /// Sets angular damping (how quickly rotation speed decreases over time).
1868 ///
1869 /// Models rotational drag. Higher values = spinning stops faster.
1870 pub fn angular_damping(mut self, factor: Real) -> Self {
1871 self.angular_damping = factor;
1872 self
1873 }
1874
1875 /// Sets the initial linear velocity (movement speed and direction).
1876 ///
1877 /// The body will start moving at this velocity when created.
1878 pub fn linvel(mut self, linvel: Vector) -> Self {
1879 self.linvel = linvel;
1880 self
1881 }
1882
1883 /// Sets the initial angular velocity (rotation speed).
1884 ///
1885 /// The body will start rotating at this speed when created.
1886 pub fn angvel(mut self, angvel: AngVector) -> Self {
1887 self.angvel = angvel;
1888 self
1889 }
1890
1891 /// Sets whether this body can go to sleep when at rest (default: `true`).
1892 ///
1893 /// Sleeping bodies are excluded from simulation until disturbed, saving CPU.
1894 /// Set to `false` if you need the body always active (e.g., for continuous queries).
1895 pub fn can_sleep(mut self, can_sleep: bool) -> Self {
1896 self.can_sleep = can_sleep;
1897 self
1898 }
1899
1900 /// Enables full ("bullet") Continuous Collision Detection: fast dynamic bodies already sweep
1901 /// **fixed** colliders automatically; this upgrades the body to also sweep **kinematic and
1902 /// dynamic** bodies at extra cost — for projectiles and fast small
1903 /// objects that must not tunnel through other moving bodies. Setting
1904 /// [`IntegrationParameters::max_ccd_substeps`] to `0` disables CCD world-wide.
1905 ///
1906 /// # Example
1907 /// ```
1908 /// # use rapier3d::prelude::*;
1909 /// // Bullet that should never tunnel through walls or other moving bodies
1910 /// let bullet = RigidBodyBuilder::dynamic()
1911 /// .ccd_enabled(true)
1912 /// .build();
1913 /// ```
1914 pub fn ccd_enabled(mut self, enabled: bool) -> Self {
1915 self.ccd_enabled = enabled;
1916 self
1917 }
1918
1919 /// Sets the maximum prediction distance Soft Continuous Collision-Detection.
1920 ///
1921 /// When set to 0, soft-CCD is disabled. Soft-CCD helps prevent tunneling especially of
1922 /// slow-but-thin to moderately fast objects. The soft CCD prediction distance indicates how
1923 /// far in the object’s path the CCD algorithm is allowed to inspect. Large values can impact
1924 /// performance badly by increasing the work needed from the broad-phase.
1925 ///
1926 /// It is a generally cheaper variant of regular CCD (that can be enabled with
1927 /// [`RigidBodyBuilder::ccd_enabled`] since it relies on predictive constraints instead of
1928 /// shape-cast and substeps.
1929 pub fn soft_ccd_prediction(mut self, prediction_distance: Real) -> Self {
1930 self.soft_ccd_prediction = prediction_distance;
1931 self
1932 }
1933
1934 /// Allow the rigid-body being built to exceed the angular speed cap.
1935 ///
1936 /// By default angular velocity is clamped each substep to ~45°/step to keep CCD reliable;
1937 /// pass `true` for bodies that must spin fast, e.g. wheels.
1938 pub fn allow_fast_rotation(mut self, allow: bool) -> Self {
1939 self.allow_fast_rotation = allow;
1940 self
1941 }
1942
1943 /// Sets whether the rigid-body is to be created asleep.
1944 pub fn sleeping(mut self, sleeping: bool) -> Self {
1945 self.sleeping = sleeping;
1946 self
1947 }
1948
1949 /// Are gyroscopic forces enabled for this rigid-body?
1950 ///
1951 /// Enabling gyroscopic forces allows more realistic behaviors like gyroscopic precession,
1952 /// but result in a slight performance overhead.
1953 ///
1954 /// Disabled by default.
1955 #[cfg(feature = "dim3")]
1956 pub fn gyroscopic_forces_enabled(mut self, enabled: bool) -> Self {
1957 self.gyroscopic_forces_enabled = enabled;
1958 self
1959 }
1960
1961 /// Enable or disable the rigid-body after its creation.
1962 pub fn enabled(mut self, enabled: bool) -> Self {
1963 self.enabled = enabled;
1964 self
1965 }
1966
1967 /// Build a new rigid-body with the parameters configured with this builder.
1968 pub fn build(&self) -> RigidBody {
1969 let mut rb = RigidBody::new();
1970 rb.pos.next_position = self.position;
1971 rb.pos.position = self.position;
1972 rb.vels.linvel = self.linvel;
1973 rb.vels.angvel = self.angvel;
1974 rb.body_type = self.body_type;
1975 rb.user_data = self.user_data;
1976 rb.additional_solver_iterations = self.additional_solver_iterations;
1977
1978 if self.additional_mass_properties
1979 != RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
1980 && self.additional_mass_properties != RigidBodyAdditionalMassProps::Mass(0.0)
1981 {
1982 rb.mprops.additional_local_mprops = Some(Box::new(self.additional_mass_properties));
1983 }
1984
1985 rb.mprops.flags = self.mprops_flags;
1986 rb.damping.linear_damping = self.linear_damping;
1987 rb.damping.angular_damping = self.angular_damping;
1988 rb.forces.gravity_scale = self.gravity_scale;
1989 #[cfg(feature = "dim3")]
1990 {
1991 rb.forces.gyroscopic_forces_enabled = self.gyroscopic_forces_enabled;
1992 }
1993 rb.dominance = RigidBodyDominance(self.dominance_group);
1994 rb.enabled = self.enabled;
1995 rb.enable_ccd(self.ccd_enabled);
1996 rb.set_soft_ccd_prediction(self.soft_ccd_prediction);
1997 rb.set_allow_fast_rotation(self.allow_fast_rotation);
1998
1999 if self.can_sleep && self.sleeping {
2000 rb.sleep();
2001 }
2002
2003 if !self.can_sleep {
2004 rb.activation.normalized_linear_threshold = -1.0;
2005 rb.activation.angular_threshold = -1.0;
2006 }
2007
2008 rb
2009 }
2010}
2011
2012impl From<RigidBodyBuilder> for RigidBody {
2013 fn from(val: RigidBodyBuilder) -> RigidBody {
2014 val.build()
2015 }
2016}
2017
2018/// One explicit, angular-momentum-preserving gyroscopic correction of a world-space angular velocity,
2019/// computed in the world principal-inertia frame (`principal_axes`) so `w × I·w` is exact for tilted
2020/// axes. Shared by [`RigidBody::angvel_with_gyroscopic_forces`] and the solver's per-substep pass.
2021#[cfg(feature = "dim3")]
2022#[inline]
2023pub(crate) fn gyroscopic_corrected_angvel(
2024 angvel: AngVector,
2025 principal_axes: Rotation,
2026 principal_inertia: AngVector,
2027 inv_principal_inertia: AngVector,
2028 dt: Real,
2029) -> AngVector {
2030 // NOTE: integrating the gyroscopic forces implicitly are both slower and
2031 // very dissipative. Instead, we only keep the explicit term and
2032 // ensure angular momentum is preserved (similar to Jolt).
2033 let w = principal_axes.inverse() * angvel;
2034 let curr_momentum = principal_inertia * w;
2035 let explicit_gyro_momentum = -w.cross(curr_momentum) * dt;
2036 let total_momentum = curr_momentum + explicit_gyro_momentum;
2037 let total_momentum_sqnorm = total_momentum.length_squared();
2038
2039 if total_momentum_sqnorm != 0.0 {
2040 let capped_momentum =
2041 total_momentum * (curr_momentum.length_squared() / total_momentum_sqnorm).sqrt();
2042 principal_axes * (inv_principal_inertia * capped_momentum)
2043 } else {
2044 angvel
2045 }
2046}