Skip to main content

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}