1#[cfg(doc)]
2use super::IntegrationParameters;
3use crate::control::PdErrors;
4#[cfg(doc)]
5use crate::control::PidController;
6use crate::dynamics::MassProperties;
7use crate::geometry::{
8 ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
9 ColliderSet, ColliderShape, ModifiedColliders,
10};
11use crate::math::{AngVector, AngularInertia, Pose, Real, Rotation, Vector};
12use crate::utils::{
13 AngularInertiaOps, CrossProduct, DotProduct, PoseOps, ScalarType, SimdRealCopy,
14};
15use num::Zero;
16#[cfg(feature = "dim2")]
17use parry::math::Rot2;
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
21#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
22#[repr(transparent)]
23pub struct RigidBodyHandle(pub crate::data::arena::Index);
24
25impl RigidBodyHandle {
26 pub fn into_raw_parts(self) -> (u32, u32) {
28 self.0.into_raw_parts()
29 }
30
31 pub fn from_raw_parts(id: u32, generation: u32) -> Self {
33 Self(crate::data::arena::Index::from_raw_parts(id, generation))
34 }
35
36 pub fn invalid() -> Self {
38 Self(crate::data::arena::Index::from_raw_parts(
39 crate::INVALID_U32,
40 crate::INVALID_U32,
41 ))
42 }
43}
44
45#[deprecated(note = "renamed as RigidBodyType")]
47pub type BodyStatus = RigidBodyType;
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
51pub enum RigidBodyType {
53 Dynamic = 0,
58
59 Fixed = 1,
63
64 KinematicPositionBased = 2,
72
73 KinematicVelocityBased = 3,
81 }
84
85impl RigidBodyType {
86 pub fn is_fixed(self) -> bool {
88 self == RigidBodyType::Fixed
89 }
90
91 pub fn is_dynamic(self) -> bool {
93 self == RigidBodyType::Dynamic
94 }
95
96 pub fn is_kinematic(self) -> bool {
98 self == RigidBodyType::KinematicPositionBased
99 || self == RigidBodyType::KinematicVelocityBased
100 }
101
102 pub fn is_dynamic_or_kinematic(self) -> bool {
107 self != RigidBodyType::Fixed
108 }
109}
110
111bitflags::bitflags! {
112 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
113 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
114 pub struct RigidBodyChanges: u32 {
116 const IN_MODIFIED_SET = 1 << 0;
118 const POSITION = 1 << 1;
120 const SLEEP = 1 << 2;
122 const COLLIDERS = 1 << 3;
124 const TYPE = 1 << 4;
126 const DOMINANCE = 1 << 5;
128 const LOCAL_MASS_PROPERTIES = 1 << 6;
130 const ENABLED_OR_DISABLED = 1 << 7;
132 }
133}
134
135impl Default for RigidBodyChanges {
136 fn default() -> Self {
137 RigidBodyChanges::empty()
138 }
139}
140
141#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
142#[derive(Clone, Debug, Copy, PartialEq)]
143pub struct RigidBodyPosition {
145 pub position: Pose,
147 pub next_position: Pose,
157}
158
159impl Default for RigidBodyPosition {
160 fn default() -> Self {
161 Self {
162 position: Pose::IDENTITY,
163 next_position: Pose::IDENTITY,
164 }
165 }
166}
167
168impl RigidBodyPosition {
169 #[must_use]
172 pub fn interpolate_velocity(&self, inv_dt: Real, local_com: Vector) -> RigidBodyVelocity<Real> {
173 let pose_err = self.pose_errors(local_com);
174 RigidBodyVelocity {
175 linvel: pose_err.linear * inv_dt,
176 angvel: pose_err.angular * inv_dt,
177 }
178 }
179
180 #[must_use]
184 pub fn integrate_forces_and_velocities(
185 &self,
186 dt: Real,
187 forces: &RigidBodyForces,
188 vels: &RigidBodyVelocity<Real>,
189 mprops: &RigidBodyMassProps,
190 ) -> Pose {
191 let new_vels = forces.integrate(dt, vels, mprops);
192 let local_com = mprops.local_mprops.local_com;
193 new_vels.integrate(dt, &self.position, &local_com)
194 }
195
196 pub fn pose_errors(&self, local_com: Vector) -> PdErrors {
204 let com = self.position * local_com;
205 let shift = Pose::from_translation(com);
206 let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
207
208 let angular;
209 #[cfg(feature = "dim2")]
210 {
211 angular = dpos.rotation.angle();
212 }
213 #[cfg(feature = "dim3")]
214 {
215 angular = dpos.rotation.to_scaled_axis();
216 }
217 let linear = dpos.translation;
218
219 PdErrors { linear, angular }
220 }
221}
222
223impl<T> From<T> for RigidBodyPosition
224where
225 Pose: From<T>,
226{
227 fn from(position: T) -> Self {
228 let position = position.into();
229 Self {
230 position,
231 next_position: position,
232 }
233 }
234}
235
236bitflags::bitflags! {
237 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
238 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
239 pub struct AxesMask: u8 {
241 const LIN_X = 1 << 0;
243 const LIN_Y = 1 << 1;
245 #[cfg(feature = "dim3")]
247 const LIN_Z = 1 << 2;
248 #[cfg(feature = "dim3")]
250 const ANG_X = 1 << 3;
251 #[cfg(feature = "dim3")]
253 const ANG_Y = 1 << 4;
254 const ANG_Z = 1 << 5;
256 }
257}
258
259impl Default for AxesMask {
260 fn default() -> Self {
261 AxesMask::empty()
262 }
263}
264
265bitflags::bitflags! {
266 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
267 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
268 pub struct LockedAxes: u8 {
297 const TRANSLATION_LOCKED_X = 1 << 0;
299 const TRANSLATION_LOCKED_Y = 1 << 1;
301 const TRANSLATION_LOCKED_Z = 1 << 2;
303 const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
305 const ROTATION_LOCKED_X = 1 << 3;
307 const ROTATION_LOCKED_Y = 1 << 4;
309 const ROTATION_LOCKED_Z = 1 << 5;
311 const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
313 }
314}
315
316#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
318#[derive(Copy, Clone, Debug, PartialEq)]
319pub enum RigidBodyAdditionalMassProps {
320 MassProps(MassProperties),
322 Mass(Real),
325}
326
327impl Default for RigidBodyAdditionalMassProps {
328 fn default() -> Self {
329 RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
330 }
331}
332
333#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
334#[derive(Clone, Debug, PartialEq)]
335pub struct RigidBodyMassProps {
338 pub world_com: Vector,
340 pub effective_inv_mass: Vector,
342 pub effective_world_inv_inertia: AngularInertia,
345 pub local_mprops: MassProperties,
347 pub flags: LockedAxes,
349 pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
351}
352
353impl Default for RigidBodyMassProps {
354 fn default() -> Self {
355 Self {
356 flags: LockedAxes::empty(),
357 local_mprops: MassProperties::zero(),
358 additional_local_mprops: None,
359 world_com: Vector::ZERO,
360 effective_inv_mass: Vector::ZERO,
361 effective_world_inv_inertia: AngularInertia::zero(),
362 }
363 }
364}
365
366impl From<LockedAxes> for RigidBodyMassProps {
367 fn from(flags: LockedAxes) -> Self {
368 Self {
369 flags,
370 ..Self::default()
371 }
372 }
373}
374
375impl From<MassProperties> for RigidBodyMassProps {
376 fn from(local_mprops: MassProperties) -> Self {
377 Self {
378 local_mprops,
379 ..Default::default()
380 }
381 }
382}
383
384impl RigidBodyMassProps {
385 #[must_use]
387 pub fn mass(&self) -> Real {
388 crate::utils::inv(self.local_mprops.inv_mass)
389 }
390
391 #[must_use]
394 pub fn effective_mass(&self) -> Vector {
395 self.effective_inv_mass.map(crate::utils::inv)
396 }
397
398 #[must_use]
401 pub fn effective_angular_inertia(&self) -> AngularInertia {
402 #[allow(unused_mut)] let mut ang_inertia = self.effective_world_inv_inertia;
404
405 #[cfg(feature = "dim3")]
407 {
408 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
409 ang_inertia.m11 = 1.0;
410 }
411 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
412 ang_inertia.m22 = 1.0;
413 }
414 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
415 ang_inertia.m33 = 1.0;
416 }
417 }
418
419 #[allow(unused_mut)] let mut result = ang_inertia.inverse();
421
422 #[cfg(feature = "dim3")]
424 {
425 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
426 result.m11 = 0.0;
427 }
428 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
429 result.m22 = 0.0;
430 }
431 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
432 result.m33 = 0.0;
433 }
434 }
435
436 result
437 }
438
439 pub fn recompute_mass_properties_from_colliders(
441 &mut self,
442 colliders: &ColliderSet,
443 attached_colliders: &RigidBodyColliders,
444 body_type: RigidBodyType,
445 position: &Pose,
446 ) {
447 let added_mprops = self
448 .additional_local_mprops
449 .as_ref()
450 .map(|mprops| **mprops)
451 .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
452
453 self.local_mprops = MassProperties::default();
454
455 for handle in &attached_colliders.0 {
456 if let Some(co) = colliders.get(*handle) {
457 if co.is_enabled() {
458 if let Some(co_parent) = co.parent {
459 let to_add = co
460 .mprops
461 .mass_properties(&*co.shape)
462 .transform_by(&co_parent.pos_wrt_parent);
463 self.local_mprops += to_add;
464 }
465 }
466 }
467 }
468
469 match added_mprops {
470 RigidBodyAdditionalMassProps::MassProps(mprops) => {
471 self.local_mprops += mprops;
472 }
473 RigidBodyAdditionalMassProps::Mass(mass) => {
474 let new_mass = self.local_mprops.mass() + mass;
475 self.local_mprops.set_mass(new_mass, true);
476 }
477 }
478
479 self.update_world_mass_properties(body_type, position);
480 }
481
482 pub fn update_world_mass_properties(&mut self, body_type: RigidBodyType, position: &Pose) {
484 self.world_com = self.local_mprops.world_com(position);
485 self.effective_inv_mass = Vector::splat(self.local_mprops.inv_mass);
486 self.effective_world_inv_inertia = self.local_mprops.world_inv_inertia(&position.rotation);
487
488 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
490 self.effective_inv_mass.x = 0.0;
491 }
492
493 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
494 self.effective_inv_mass.y = 0.0;
495 }
496
497 #[cfg(feature = "dim3")]
498 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
499 self.effective_inv_mass.z = 0.0;
500 }
501
502 #[cfg(feature = "dim2")]
503 {
504 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
505 self.effective_world_inv_inertia = 0.0;
506 }
507 }
508 #[cfg(feature = "dim3")]
509 {
510 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
511 self.effective_world_inv_inertia.m11 = 0.0;
512 self.effective_world_inv_inertia.m12 = 0.0;
513 self.effective_world_inv_inertia.m13 = 0.0;
514 }
515
516 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
517 self.effective_world_inv_inertia.m22 = 0.0;
518 self.effective_world_inv_inertia.m12 = 0.0;
519 self.effective_world_inv_inertia.m23 = 0.0;
520 }
521 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
522 self.effective_world_inv_inertia.m33 = 0.0;
523 self.effective_world_inv_inertia.m13 = 0.0;
524 self.effective_world_inv_inertia.m23 = 0.0;
525 }
526 }
527 }
528}
529
530#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
531#[derive(Clone, Debug, Copy, PartialEq)]
532pub struct RigidBodyVelocity<T: ScalarType> {
534 pub linvel: T::Vector,
536 pub angvel: T::AngVector,
538}
539
540impl Default for RigidBodyVelocity<Real> {
541 fn default() -> Self {
542 Self::zero()
543 }
544}
545
546impl RigidBodyVelocity<Real> {
547 #[must_use]
549 #[cfg(feature = "dim2")]
550 pub fn new(linvel: Vector, angvel: AngVector) -> Self {
551 Self { linvel, angvel }
552 }
553
554 #[must_use]
556 #[cfg(feature = "dim3")]
557 pub fn new(linvel: Vector, angvel: AngVector) -> Self {
558 Self {
559 linvel: Vector::new(linvel.x, linvel.y, linvel.z),
560 angvel: AngVector::new(angvel.x, angvel.y, angvel.z),
561 }
562 }
563
564 #[must_use]
569 #[cfg(feature = "dim2")]
570 pub fn from_slice(slice: &[Real]) -> Self {
571 Self {
572 linvel: Vector::new(slice[0], slice[1]),
573 angvel: slice[2],
574 }
575 }
576
577 #[must_use]
582 #[cfg(feature = "dim3")]
583 pub fn from_slice(slice: &[Real]) -> Self {
584 Self {
585 linvel: Vector::new(slice[0], slice[1], slice[2]),
586 angvel: AngVector::new(slice[3], slice[4], slice[5]),
587 }
588 }
589
590 #[must_use]
592 pub fn zero() -> Self {
593 Self {
594 linvel: Default::default(),
595 angvel: Default::default(),
596 }
597 }
598
599 #[inline]
603 pub fn as_slice(&self) -> &[Real] {
604 self.as_vector().as_slice()
605 }
606
607 #[inline]
611 pub fn as_mut_slice(&mut self) -> &mut [Real] {
612 self.as_vector_mut().as_mut_slice()
613 }
614
615 #[inline]
619 #[cfg(feature = "dim2")]
620 pub fn as_vector(&self) -> &na::Vector3<Real> {
621 unsafe { std::mem::transmute(self) }
622 }
623
624 #[inline]
628 #[cfg(feature = "dim2")]
629 pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
630 unsafe { std::mem::transmute(self) }
631 }
632
633 #[inline]
637 #[cfg(feature = "dim3")]
638 pub fn as_vector(&self) -> &na::Vector6<Real> {
639 unsafe { std::mem::transmute(self) }
640 }
641
642 #[inline]
646 #[cfg(feature = "dim3")]
647 pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
648 unsafe { std::mem::transmute(self) }
649 }
650
651 #[must_use]
653 #[cfg(feature = "dim2")]
654 pub fn transformed(self, rotation: &Rotation) -> Self {
655 Self {
656 linvel: *rotation * self.linvel,
657 angvel: self.angvel,
658 }
659 }
660
661 #[must_use]
663 #[cfg(feature = "dim3")]
664 pub fn transformed(self, rotation: &Rotation) -> Self {
665 Self {
666 linvel: *rotation * self.linvel,
667 angvel: *rotation * self.angvel,
668 }
669 }
670
671 #[must_use]
677 pub fn pseudo_kinetic_energy(&self) -> Real {
678 0.5 * (self.linvel.length_squared() + self.angvel.gdot(self.angvel))
679 }
680
681 #[must_use]
683 #[cfg(feature = "dim2")]
684 pub fn velocity_at_point(&self, point: Vector, world_com: Vector) -> Vector {
685 let dpt = point - world_com;
686 self.linvel + self.angvel.gcross(dpt)
687 }
688
689 #[must_use]
691 #[cfg(feature = "dim3")]
692 pub fn velocity_at_point(&self, point: Vector, world_com: Vector) -> Vector {
693 let dpt = point - world_com;
694 self.linvel + self.angvel.gcross(dpt)
695 }
696
697 #[must_use]
699 pub fn is_zero(&self) -> bool {
700 self.linvel == Vector::ZERO && self.angvel == AngVector::default()
701 }
702
703 #[must_use]
705 #[profiling::function]
706 pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
707 let mut energy = (rb_mprops.mass() * self.linvel.length_squared()) / 2.0;
708
709 #[cfg(feature = "dim2")]
710 if !num::Zero::is_zero(&rb_mprops.effective_world_inv_inertia) {
711 let inertia = 1.0 / rb_mprops.effective_world_inv_inertia;
712 energy += inertia * self.angvel * self.angvel / 2.0;
713 }
714
715 #[cfg(feature = "dim3")]
716 if !rb_mprops.effective_world_inv_inertia.is_zero() {
717 let inertia = rb_mprops.effective_world_inv_inertia.inverse_unchecked();
718 energy += self.angvel.gdot(inertia * self.angvel) / 2.0;
719 }
720
721 energy
722 }
723
724 pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector) {
728 self.linvel += impulse * rb_mprops.effective_inv_mass;
729 }
730
731 #[cfg(feature = "dim2")]
735 pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
736 self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
737 }
738
739 #[cfg(feature = "dim3")]
743 pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Vector) {
744 self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
745 }
746
747 #[cfg(feature = "dim2")]
751 pub fn apply_impulse_at_point(
752 &mut self,
753 rb_mprops: &RigidBodyMassProps,
754 impulse: Vector,
755 point: Vector,
756 ) {
757 let torque_impulse = (point - rb_mprops.world_com).perp_dot(impulse);
758 self.apply_impulse(rb_mprops, impulse);
759 self.apply_torque_impulse(rb_mprops, torque_impulse);
760 }
761
762 #[cfg(feature = "dim3")]
766 pub fn apply_impulse_at_point(
767 &mut self,
768 rb_mprops: &RigidBodyMassProps,
769 impulse: Vector,
770 point: Vector,
771 ) {
772 let torque_impulse = (point - rb_mprops.world_com).cross(impulse);
773 self.apply_impulse(rb_mprops, impulse);
774 self.apply_torque_impulse(rb_mprops, torque_impulse);
775 }
776}
777
778impl<T: ScalarType> RigidBodyVelocity<T> {
779 #[must_use]
781 pub fn apply_damping(&self, dt: T, damping: &RigidBodyDamping<T>) -> Self {
782 let one = T::one();
783 RigidBodyVelocity {
784 linvel: self.linvel * (one / (one + dt * damping.linear_damping)),
785 angvel: self.angvel * (one / (one + dt * damping.angular_damping)),
786 }
787 }
788
789 #[must_use]
792 #[inline]
793 #[allow(clippy::let_and_return)] pub fn integrate(&self, dt: T, init_pos: &T::Pose, local_com: &T::Vector) -> T::Pose {
795 let com = *init_pos * *local_com;
796 let result = init_pos
797 .append_translation(-com)
798 .append_rotation(self.angvel * dt)
799 .append_translation(com + self.linvel * dt);
800 result
803 }
804}
805
806impl RigidBodyVelocity<Real> {
807 #[inline]
810 #[cfg(feature = "dim2")]
811 pub(crate) fn integrate_linearized(
812 &self,
813 dt: Real,
814 translation: &mut Vector,
815 rotation: &mut Rotation,
816 ) {
817 let dang = self.angvel * dt;
818 let new_cos = rotation.re - dang * rotation.im;
819 let new_sin = rotation.im + dang * rotation.re;
820 *rotation = Rot2::from_cos_sin_unchecked(new_cos, new_sin);
821 rotation.normalize_mut();
823 *translation += self.linvel * dt;
824 }
825
826 #[inline]
829 #[cfg(feature = "dim3")]
830 pub(crate) fn integrate_linearized(
831 &self,
832 dt: Real,
833 translation: &mut Vector,
834 rotation: &mut Rotation,
835 ) {
836 let hang = self.angvel * (dt * 0.5);
839 let id_plus_hang = Rotation::from_xyzw(hang.x, hang.y, hang.z, 1.0);
841 *rotation = id_plus_hang * *rotation;
842 *rotation = rotation.normalize();
843 *translation += self.linvel * dt;
844 }
845}
846
847impl std::ops::Mul<Real> for RigidBodyVelocity<Real> {
848 type Output = Self;
849
850 fn mul(self, rhs: Real) -> Self {
851 RigidBodyVelocity {
852 linvel: self.linvel * rhs,
853 angvel: self.angvel * rhs,
854 }
855 }
856}
857
858impl std::ops::Add<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
859 type Output = Self;
860
861 fn add(self, rhs: Self) -> Self {
862 RigidBodyVelocity {
863 linvel: self.linvel + rhs.linvel,
864 angvel: self.angvel + rhs.angvel,
865 }
866 }
867}
868
869impl std::ops::AddAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
870 fn add_assign(&mut self, rhs: Self) {
871 self.linvel += rhs.linvel;
872 self.angvel += rhs.angvel;
873 }
874}
875
876impl std::ops::Sub<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
877 type Output = Self;
878
879 fn sub(self, rhs: Self) -> Self {
880 RigidBodyVelocity {
881 linvel: self.linvel - rhs.linvel,
882 angvel: self.angvel - rhs.angvel,
883 }
884 }
885}
886
887impl std::ops::SubAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
888 fn sub_assign(&mut self, rhs: Self) {
889 self.linvel -= rhs.linvel;
890 self.angvel -= rhs.angvel;
891 }
892}
893
894#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
895#[derive(Clone, Debug, Copy, PartialEq)]
896pub struct RigidBodyDamping<T> {
898 pub linear_damping: T,
900 pub angular_damping: T,
902}
903
904impl<T: SimdRealCopy> Default for RigidBodyDamping<T> {
905 fn default() -> Self {
906 Self {
907 linear_damping: T::zero(),
908 angular_damping: T::zero(),
909 }
910 }
911}
912
913#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
914#[derive(Clone, Debug, Copy, PartialEq)]
915pub struct RigidBodyForces {
917 pub force: Vector,
919 pub torque: AngVector,
921 pub gravity_scale: Real,
924 pub user_force: Vector,
926 pub user_torque: AngVector,
928 #[cfg(feature = "dim3")]
930 pub gyroscopic_forces_enabled: bool,
931}
932
933impl Default for RigidBodyForces {
934 fn default() -> Self {
935 #[cfg(feature = "dim2")]
936 return Self {
937 force: Vector::ZERO,
938 torque: 0.0,
939 gravity_scale: 1.0,
940 user_force: Vector::ZERO,
941 user_torque: 0.0,
942 };
943
944 #[cfg(feature = "dim3")]
945 return Self {
946 force: Vector::ZERO,
947 torque: AngVector::ZERO,
948 gravity_scale: 1.0,
949 user_force: Vector::ZERO,
950 user_torque: AngVector::ZERO,
951 gyroscopic_forces_enabled: false,
952 };
953 }
954}
955
956impl RigidBodyForces {
957 #[must_use]
959 pub fn integrate(
960 &self,
961 dt: Real,
962 init_vels: &RigidBodyVelocity<Real>,
963 mprops: &RigidBodyMassProps,
964 ) -> RigidBodyVelocity<Real> {
965 let linear_acc = self.force * mprops.effective_inv_mass;
966 let angular_acc = mprops.effective_world_inv_inertia * self.torque;
967
968 RigidBodyVelocity {
969 linvel: init_vels.linvel + linear_acc * dt,
970 angvel: init_vels.angvel + angular_acc * dt,
971 }
972 }
973
974 pub fn compute_effective_force_and_torque(&mut self, gravity: Vector, mass: Vector) {
977 self.force = self.user_force + gravity * mass * self.gravity_scale;
978 self.torque = self.user_torque;
979 }
980
981 pub fn apply_force_at_point(
983 &mut self,
984 rb_mprops: &RigidBodyMassProps,
985 force: Vector,
986 point: Vector,
987 ) {
988 self.user_force += force;
989 self.user_torque += (point - rb_mprops.world_com).gcross(force);
990 }
991}
992
993#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
994#[derive(Clone, Debug, Copy, PartialEq)]
995pub struct RigidBodyCcd {
997 pub ccd_thickness: Real,
1000 pub ccd_max_dist: Real,
1003 pub ccd_active: bool,
1009 pub ccd_enabled: bool,
1011 pub soft_ccd_prediction: Real,
1013}
1014
1015impl Default for RigidBodyCcd {
1016 fn default() -> Self {
1017 Self {
1018 ccd_thickness: Real::MAX,
1019 ccd_max_dist: 0.0,
1020 ccd_active: false,
1021 ccd_enabled: false,
1022 soft_ccd_prediction: 0.0,
1023 }
1024 }
1025}
1026
1027impl RigidBodyCcd {
1028 pub fn max_point_velocity(&self, vels: &RigidBodyVelocity<Real>) -> Real {
1031 #[cfg(feature = "dim2")]
1032 return vels.linvel.length() + vels.angvel.abs() * self.ccd_max_dist;
1033 #[cfg(feature = "dim3")]
1034 return vels.linvel.length() + vels.angvel.length() * self.ccd_max_dist;
1035 }
1036
1037 pub fn is_moving_fast(
1039 &self,
1040 dt: Real,
1041 vels: &RigidBodyVelocity<Real>,
1042 forces: Option<&RigidBodyForces>,
1043 ) -> bool {
1044 let threshold = self.ccd_thickness / 10.0;
1053
1054 if let Some(forces) = forces {
1055 let linear_part = (vels.linvel + forces.force * dt).length();
1056 #[cfg(feature = "dim2")]
1057 let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
1058 #[cfg(feature = "dim3")]
1059 let angular_part = (vels.angvel + forces.torque * dt).length() * self.ccd_max_dist;
1060 let vel_with_forces = linear_part + angular_part;
1061 vel_with_forces > threshold
1062 } else {
1063 self.max_point_velocity(vels) * dt > threshold
1064 }
1065 }
1066}
1067
1068#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1069#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
1070pub struct RigidBodyIds {
1072 pub(crate) active_island_id: usize,
1073 pub(crate) active_set_id: usize,
1074 pub(crate) active_set_timestamp: u32,
1075}
1076
1077impl Default for RigidBodyIds {
1078 fn default() -> Self {
1079 Self {
1080 active_island_id: usize::MAX,
1081 active_set_id: usize::MAX,
1082 active_set_timestamp: 0,
1083 }
1084 }
1085}
1086
1087#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1088#[derive(Default, Clone, Debug, PartialEq, Eq)]
1089pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
1095
1096impl RigidBodyColliders {
1097 pub fn detach_collider(
1099 &mut self,
1100 rb_changes: &mut RigidBodyChanges,
1101 co_handle: ColliderHandle,
1102 ) {
1103 if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
1104 rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1105 self.0.swap_remove(i);
1106 }
1107 }
1108
1109 pub fn attach_collider(
1111 &mut self,
1112 rb_type: RigidBodyType,
1113 rb_changes: &mut RigidBodyChanges,
1114 rb_ccd: &mut RigidBodyCcd,
1115 rb_mprops: &mut RigidBodyMassProps,
1116 rb_pos: &RigidBodyPosition,
1117 co_handle: ColliderHandle,
1118 co_pos: &mut ColliderPosition,
1119 co_parent: &ColliderParent,
1120 co_shape: &ColliderShape,
1121 co_mprops: &ColliderMassProps,
1122 ) {
1123 rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1124
1125 co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
1126 rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
1127
1128 let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
1129 rb_ccd.ccd_max_dist = rb_ccd
1130 .ccd_max_dist
1131 .max(shape_bsphere.center.length() + shape_bsphere.radius);
1132
1133 let mass_properties = co_mprops
1134 .mass_properties(&**co_shape)
1135 .transform_by(&co_parent.pos_wrt_parent);
1136 self.0.push(co_handle);
1137 rb_mprops.local_mprops += mass_properties;
1138 rb_mprops.update_world_mass_properties(rb_type, &rb_pos.position);
1139 }
1140
1141 pub(crate) fn update_positions(
1143 &self,
1144 colliders: &mut ColliderSet,
1145 modified_colliders: &mut ModifiedColliders,
1146 parent_pos: &Pose,
1147 ) {
1148 for handle in &self.0 {
1149 let co = colliders.index_mut_internal(*handle);
1153 let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1154
1155 modified_colliders.push_once(*handle, co);
1158
1159 co.changes |= ColliderChanges::POSITION;
1160 co.pos = ColliderPosition(new_pos);
1161 }
1162 }
1163}
1164
1165#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1166#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1167pub struct RigidBodyDominance(pub i8);
1169
1170impl RigidBodyDominance {
1171 pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1173 if status.is_dynamic_or_kinematic() {
1174 self.0 as i16
1175 } else {
1176 i8::MAX as i16 + 1
1177 }
1178 }
1179}
1180
1181#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1182#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1183pub(crate) enum SleepRootState {
1184 Traversed,
1187 TraversalPending,
1189 #[default]
1191 Unknown,
1192}
1193
1194#[derive(Copy, Clone, Debug, PartialEq)]
1217#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1218pub struct RigidBodyActivation {
1219 pub normalized_linear_threshold: Real,
1223
1224 pub angular_threshold: Real,
1228
1229 pub time_until_sleep: Real,
1233
1234 pub time_since_can_sleep: Real,
1236
1237 pub sleeping: bool,
1239
1240 pub(crate) sleep_root_state: SleepRootState,
1241}
1242
1243impl Default for RigidBodyActivation {
1244 fn default() -> Self {
1245 Self::active()
1246 }
1247}
1248
1249impl RigidBodyActivation {
1250 pub fn default_normalized_linear_threshold() -> Real {
1252 0.4
1253 }
1254
1255 pub fn default_angular_threshold() -> Real {
1257 0.5
1258 }
1259
1260 pub fn default_time_until_sleep() -> Real {
1263 2.0
1264 }
1265
1266 pub fn active() -> Self {
1268 RigidBodyActivation {
1269 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1270 angular_threshold: Self::default_angular_threshold(),
1271 time_until_sleep: Self::default_time_until_sleep(),
1272 time_since_can_sleep: 0.0,
1273 sleeping: false,
1274 sleep_root_state: SleepRootState::Unknown,
1275 }
1276 }
1277
1278 pub fn inactive() -> Self {
1280 RigidBodyActivation {
1281 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1282 angular_threshold: Self::default_angular_threshold(),
1283 time_until_sleep: Self::default_time_until_sleep(),
1284 time_since_can_sleep: Self::default_time_until_sleep(),
1285 sleeping: true,
1286 sleep_root_state: SleepRootState::Unknown,
1287 }
1288 }
1289
1290 pub fn cannot_sleep() -> Self {
1292 RigidBodyActivation {
1293 normalized_linear_threshold: -1.0,
1294 angular_threshold: -1.0,
1295 ..Self::active()
1296 }
1297 }
1298
1299 #[inline]
1301 pub fn is_active(&self) -> bool {
1302 !self.sleeping
1303 }
1304
1305 #[inline]
1307 pub fn wake_up(&mut self, strong: bool) {
1308 self.sleeping = false;
1309
1310 if self.sleep_root_state != SleepRootState::TraversalPending {
1312 self.sleep_root_state = SleepRootState::Unknown;
1313 }
1314
1315 if strong {
1316 self.time_since_can_sleep = 0.0;
1317 }
1318 }
1319
1320 #[inline]
1322 pub fn sleep(&mut self) {
1323 self.sleeping = true;
1324 self.time_since_can_sleep = self.time_until_sleep;
1325 }
1326
1327 pub fn is_eligible_for_sleep(&self) -> bool {
1330 self.time_since_can_sleep >= self.time_until_sleep
1331 }
1332
1333 pub(crate) fn update_energy(
1334 &mut self,
1335 body_type: RigidBodyType,
1336 length_unit: Real,
1337 sq_linvel: Real,
1338 sq_angvel: Real,
1339 dt: Real,
1340 ) {
1341 let can_sleep = match body_type {
1342 RigidBodyType::Dynamic => {
1343 let linear_threshold = self.normalized_linear_threshold * length_unit;
1344 sq_linvel < linear_threshold * linear_threshold.abs()
1345 && sq_angvel < self.angular_threshold * self.angular_threshold.abs()
1346 }
1347 RigidBodyType::KinematicPositionBased | RigidBodyType::KinematicVelocityBased => {
1348 sq_linvel == 0.0 && sq_angvel == 0.0
1351 }
1352 RigidBodyType::Fixed => true,
1353 };
1354
1355 if can_sleep {
1356 self.time_since_can_sleep += dt;
1357 } else {
1358 self.time_since_can_sleep = 0.0;
1359 }
1360 }
1361}
1362
1363#[cfg(test)]
1364mod tests {
1365 use super::*;
1366 use crate::math::Real;
1367
1368 #[test]
1369 fn test_interpolate_velocity() {
1370 #[cfg(feature = "f32")]
1373 let mut rng = oorandom::Rand32::new(0);
1374 #[cfg(feature = "f64")]
1375 let mut rng = oorandom::Rand64::new(0);
1376
1377 for i in -10..=10 {
1378 let mult = i as Real;
1379 let (local_com, curr_pos, next_pos);
1380 #[cfg(feature = "dim2")]
1381 {
1382 local_com = Vector::new(rng.rand_float(), rng.rand_float());
1383 curr_pos = Pose::new(
1384 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1385 rng.rand_float(),
1386 );
1387 next_pos = Pose::new(
1388 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1389 rng.rand_float(),
1390 );
1391 }
1392 #[cfg(feature = "dim3")]
1393 {
1394 local_com = Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1395 curr_pos = Pose::new(
1396 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1397 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1398 );
1399 next_pos = Pose::new(
1400 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1401 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1402 );
1403 }
1404
1405 let dt = 0.016;
1406 let rb_pos = RigidBodyPosition {
1407 position: curr_pos,
1408 next_position: next_pos,
1409 };
1410 let vel = rb_pos.interpolate_velocity(1.0 / dt, local_com);
1411 let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1412 approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1413 }
1414 }
1415}