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