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::{
12 AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector,
13};
14use crate::utils::{SimdAngularInertia, SimdCross, SimdDot, SimdRealCopy};
15use num::Zero;
16
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
19#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
20#[repr(transparent)]
21pub struct RigidBodyHandle(pub crate::data::arena::Index);
22
23impl RigidBodyHandle {
24 pub fn into_raw_parts(self) -> (u32, u32) {
26 self.0.into_raw_parts()
27 }
28
29 pub fn from_raw_parts(id: u32, generation: u32) -> Self {
31 Self(crate::data::arena::Index::from_raw_parts(id, generation))
32 }
33
34 pub fn invalid() -> Self {
36 Self(crate::data::arena::Index::from_raw_parts(
37 crate::INVALID_U32,
38 crate::INVALID_U32,
39 ))
40 }
41}
42
43#[deprecated(note = "renamed as RigidBodyType")]
45pub type BodyStatus = RigidBodyType;
46
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
49pub enum RigidBodyType {
51 Dynamic = 0,
56
57 Fixed = 1,
61
62 KinematicPositionBased = 2,
70
71 KinematicVelocityBased = 3,
79 }
82
83impl RigidBodyType {
84 pub fn is_fixed(self) -> bool {
86 self == RigidBodyType::Fixed
87 }
88
89 pub fn is_dynamic(self) -> bool {
91 self == RigidBodyType::Dynamic
92 }
93
94 pub fn is_kinematic(self) -> bool {
96 self == RigidBodyType::KinematicPositionBased
97 || self == RigidBodyType::KinematicVelocityBased
98 }
99
100 pub fn is_dynamic_or_kinematic(self) -> bool {
105 self != RigidBodyType::Fixed
106 }
107}
108
109bitflags::bitflags! {
110 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
111 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
112 pub struct RigidBodyChanges: u32 {
114 const IN_MODIFIED_SET = 1 << 0;
116 const POSITION = 1 << 1;
118 const SLEEP = 1 << 2;
120 const COLLIDERS = 1 << 3;
122 const TYPE = 1 << 4;
124 const DOMINANCE = 1 << 5;
126 const LOCAL_MASS_PROPERTIES = 1 << 6;
128 const ENABLED_OR_DISABLED = 1 << 7;
130 }
131}
132
133impl Default for RigidBodyChanges {
134 fn default() -> Self {
135 RigidBodyChanges::empty()
136 }
137}
138
139#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
140#[derive(Clone, Debug, Copy, PartialEq)]
141pub struct RigidBodyPosition {
143 pub position: Isometry<Real>,
145 pub next_position: Isometry<Real>,
155}
156
157impl Default for RigidBodyPosition {
158 fn default() -> Self {
159 Self {
160 position: Isometry::identity(),
161 next_position: Isometry::identity(),
162 }
163 }
164}
165
166impl RigidBodyPosition {
167 #[must_use]
170 pub fn interpolate_velocity(
171 &self,
172 inv_dt: Real,
173 local_com: &Point<Real>,
174 ) -> RigidBodyVelocity<Real> {
175 let pose_err = self.pose_errors(local_com);
176 RigidBodyVelocity {
177 linvel: pose_err.linear * inv_dt,
178 angvel: pose_err.angular * inv_dt,
179 }
180 }
181
182 #[must_use]
186 pub fn integrate_forces_and_velocities(
187 &self,
188 dt: Real,
189 forces: &RigidBodyForces,
190 vels: &RigidBodyVelocity<Real>,
191 mprops: &RigidBodyMassProps,
192 ) -> Isometry<Real> {
193 let new_vels = forces.integrate(dt, vels, mprops);
194 new_vels.integrate(dt, &self.position, &mprops.local_mprops.local_com)
195 }
196
197 pub fn pose_errors(&self, local_com: &Point<Real>) -> PdErrors {
205 let com = self.position * local_com;
206 let shift = Translation::from(com.coords);
207 let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
208
209 let angular;
210 #[cfg(feature = "dim2")]
211 {
212 angular = dpos.rotation.angle();
213 }
214 #[cfg(feature = "dim3")]
215 {
216 angular = dpos.rotation.scaled_axis();
217 }
218 let linear = dpos.translation.vector;
219
220 PdErrors { linear, angular }
221 }
222}
223
224impl<T> From<T> for RigidBodyPosition
225where
226 Isometry<Real>: From<T>,
227{
228 fn from(position: T) -> Self {
229 let position = position.into();
230 Self {
231 position,
232 next_position: position,
233 }
234 }
235}
236
237bitflags::bitflags! {
238 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
239 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
240 pub struct AxesMask: u8 {
242 const LIN_X = 1 << 0;
244 const LIN_Y = 1 << 1;
246 #[cfg(feature = "dim3")]
248 const LIN_Z = 1 << 2;
249 #[cfg(feature = "dim3")]
251 const ANG_X = 1 << 3;
252 #[cfg(feature = "dim3")]
254 const ANG_Y = 1 << 4;
255 const ANG_Z = 1 << 5;
257 }
258}
259
260impl Default for AxesMask {
261 fn default() -> Self {
262 AxesMask::empty()
263 }
264}
265
266bitflags::bitflags! {
267 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
268 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
269 pub struct LockedAxes: u8 {
298 const TRANSLATION_LOCKED_X = 1 << 0;
300 const TRANSLATION_LOCKED_Y = 1 << 1;
302 const TRANSLATION_LOCKED_Z = 1 << 2;
304 const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
306 const ROTATION_LOCKED_X = 1 << 3;
308 const ROTATION_LOCKED_Y = 1 << 4;
310 const ROTATION_LOCKED_Z = 1 << 5;
312 const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
314 }
315}
316
317#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
319#[derive(Copy, Clone, Debug, PartialEq)]
320pub enum RigidBodyAdditionalMassProps {
321 MassProps(MassProperties),
323 Mass(Real),
326}
327
328impl Default for RigidBodyAdditionalMassProps {
329 fn default() -> Self {
330 RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
331 }
332}
333
334#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
335#[derive(Clone, Debug, PartialEq)]
336pub struct RigidBodyMassProps {
339 pub world_com: Point<Real>,
341 pub effective_inv_mass: Vector<Real>,
343 pub effective_world_inv_inertia: AngularInertia<Real>,
346 pub local_mprops: MassProperties,
348 pub flags: LockedAxes,
350 pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
352}
353
354impl Default for RigidBodyMassProps {
355 fn default() -> Self {
356 Self {
357 flags: LockedAxes::empty(),
358 local_mprops: MassProperties::zero(),
359 additional_local_mprops: None,
360 world_com: Point::origin(),
361 effective_inv_mass: Vector::zero(),
362 effective_world_inv_inertia: AngularInertia::zero(),
363 }
364 }
365}
366
367impl From<LockedAxes> for RigidBodyMassProps {
368 fn from(flags: LockedAxes) -> Self {
369 Self {
370 flags,
371 ..Self::default()
372 }
373 }
374}
375
376impl From<MassProperties> for RigidBodyMassProps {
377 fn from(local_mprops: MassProperties) -> Self {
378 Self {
379 local_mprops,
380 ..Default::default()
381 }
382 }
383}
384
385impl RigidBodyMassProps {
386 #[must_use]
388 pub fn mass(&self) -> Real {
389 crate::utils::inv(self.local_mprops.inv_mass)
390 }
391
392 #[must_use]
395 pub fn effective_mass(&self) -> Vector<Real> {
396 self.effective_inv_mass.map(crate::utils::inv)
397 }
398
399 #[must_use]
402 pub fn effective_angular_inertia(&self) -> AngularInertia<Real> {
403 #[allow(unused_mut)] let mut ang_inertia = self.effective_world_inv_inertia;
405
406 #[cfg(feature = "dim3")]
408 {
409 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
410 ang_inertia.m11 = 1.0;
411 }
412 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
413 ang_inertia.m22 = 1.0;
414 }
415 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
416 ang_inertia.m33 = 1.0;
417 }
418 }
419
420 #[allow(unused_mut)] let mut result = ang_inertia.inverse();
422
423 #[cfg(feature = "dim3")]
425 {
426 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
427 result.m11 = 0.0;
428 }
429 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
430 result.m22 = 0.0;
431 }
432 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
433 result.m33 = 0.0;
434 }
435 }
436
437 result
438 }
439
440 pub fn recompute_mass_properties_from_colliders(
442 &mut self,
443 colliders: &ColliderSet,
444 attached_colliders: &RigidBodyColliders,
445 body_type: RigidBodyType,
446 position: &Isometry<Real>,
447 ) {
448 let added_mprops = self
449 .additional_local_mprops
450 .as_ref()
451 .map(|mprops| **mprops)
452 .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
453
454 self.local_mprops = MassProperties::default();
455
456 for handle in &attached_colliders.0 {
457 if let Some(co) = colliders.get(*handle) {
458 if co.is_enabled() {
459 if let Some(co_parent) = co.parent {
460 let to_add = co
461 .mprops
462 .mass_properties(&*co.shape)
463 .transform_by(&co_parent.pos_wrt_parent);
464 self.local_mprops += to_add;
465 }
466 }
467 }
468 }
469
470 match added_mprops {
471 RigidBodyAdditionalMassProps::MassProps(mprops) => {
472 self.local_mprops += mprops;
473 }
474 RigidBodyAdditionalMassProps::Mass(mass) => {
475 let new_mass = self.local_mprops.mass() + mass;
476 self.local_mprops.set_mass(new_mass, true);
477 }
478 }
479
480 self.update_world_mass_properties(body_type, position);
481 }
482
483 pub fn update_world_mass_properties(
485 &mut self,
486 body_type: RigidBodyType,
487 position: &Isometry<Real>,
488 ) {
489 self.world_com = self.local_mprops.world_com(position);
490 self.effective_inv_mass = Vector::repeat(self.local_mprops.inv_mass);
491 self.effective_world_inv_inertia = self.local_mprops.world_inv_inertia(&position.rotation);
492
493 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
495 self.effective_inv_mass.x = 0.0;
496 }
497
498 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
499 self.effective_inv_mass.y = 0.0;
500 }
501
502 #[cfg(feature = "dim3")]
503 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
504 self.effective_inv_mass.z = 0.0;
505 }
506
507 #[cfg(feature = "dim2")]
508 {
509 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
510 self.effective_world_inv_inertia = 0.0;
511 }
512 }
513 #[cfg(feature = "dim3")]
514 {
515 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
516 self.effective_world_inv_inertia.m11 = 0.0;
517 self.effective_world_inv_inertia.m12 = 0.0;
518 self.effective_world_inv_inertia.m13 = 0.0;
519 }
520
521 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
522 self.effective_world_inv_inertia.m22 = 0.0;
523 self.effective_world_inv_inertia.m12 = 0.0;
524 self.effective_world_inv_inertia.m23 = 0.0;
525 }
526 if !body_type.is_dynamic() || self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
527 self.effective_world_inv_inertia.m33 = 0.0;
528 self.effective_world_inv_inertia.m13 = 0.0;
529 self.effective_world_inv_inertia.m23 = 0.0;
530 }
531 }
532 }
533}
534
535#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
536#[derive(Clone, Debug, Copy, PartialEq)]
537pub struct RigidBodyVelocity<T: SimdRealCopy> {
539 pub linvel: Vector<T>,
541 pub angvel: AngVector<T>,
543}
544
545impl Default for RigidBodyVelocity<Real> {
546 fn default() -> Self {
547 Self::zero()
548 }
549}
550
551impl RigidBodyVelocity<Real> {
552 #[must_use]
554 pub fn new(linvel: Vector<Real>, angvel: AngVector<Real>) -> Self {
555 Self { linvel, angvel }
556 }
557
558 #[must_use]
563 #[cfg(feature = "dim2")]
564 pub fn from_slice(slice: &[Real]) -> Self {
565 Self {
566 linvel: Vector::new(slice[0], slice[1]),
567 angvel: slice[2],
568 }
569 }
570
571 #[must_use]
576 #[cfg(feature = "dim3")]
577 pub fn from_slice(slice: &[Real]) -> Self {
578 Self {
579 linvel: Vector::new(slice[0], slice[1], slice[2]),
580 angvel: AngVector::new(slice[3], slice[4], slice[5]),
581 }
582 }
583
584 #[must_use]
586 pub fn zero() -> Self {
587 Self {
588 linvel: na::zero(),
589 angvel: na::zero(),
590 }
591 }
592
593 #[inline]
597 pub fn as_slice(&self) -> &[Real] {
598 self.as_vector().as_slice()
599 }
600
601 #[inline]
605 pub fn as_mut_slice(&mut self) -> &mut [Real] {
606 self.as_vector_mut().as_mut_slice()
607 }
608
609 #[inline]
613 #[cfg(feature = "dim2")]
614 pub fn as_vector(&self) -> &na::Vector3<Real> {
615 unsafe { std::mem::transmute(self) }
616 }
617
618 #[inline]
622 #[cfg(feature = "dim2")]
623 pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
624 unsafe { std::mem::transmute(self) }
625 }
626
627 #[inline]
631 #[cfg(feature = "dim3")]
632 pub fn as_vector(&self) -> &na::Vector6<Real> {
633 unsafe { std::mem::transmute(self) }
634 }
635
636 #[inline]
640 #[cfg(feature = "dim3")]
641 pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
642 unsafe { std::mem::transmute(self) }
643 }
644
645 #[must_use]
647 pub fn transformed(self, rotation: &Rotation<Real>) -> Self {
648 Self {
649 linvel: rotation * self.linvel,
650 #[cfg(feature = "dim2")]
651 angvel: self.angvel,
652 #[cfg(feature = "dim3")]
653 angvel: rotation * self.angvel,
654 }
655 }
656
657 #[must_use]
663 pub fn pseudo_kinetic_energy(&self) -> Real {
664 0.5 * (self.linvel.norm_squared() + self.angvel.gdot(self.angvel))
665 }
666
667 #[must_use]
669 pub fn velocity_at_point(&self, point: &Point<Real>, world_com: &Point<Real>) -> Vector<Real> {
670 let dpt = point - world_com;
671 self.linvel + self.angvel.gcross(dpt)
672 }
673
674 #[must_use]
676 pub fn is_zero(&self) -> bool {
677 self.linvel.is_zero() && self.angvel.is_zero()
678 }
679
680 #[must_use]
682 #[profiling::function]
683 pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
684 let mut energy = (rb_mprops.mass() * self.linvel.norm_squared()) / 2.0;
685
686 #[cfg(feature = "dim2")]
687 if !rb_mprops.effective_world_inv_inertia.is_zero() {
688 let inertia = 1.0 / rb_mprops.effective_world_inv_inertia;
689 energy += inertia * self.angvel * self.angvel / 2.0;
690 }
691
692 #[cfg(feature = "dim3")]
693 if !rb_mprops.effective_world_inv_inertia.is_zero() {
694 let inertia = rb_mprops.effective_world_inv_inertia.inverse_unchecked();
695 energy += self.angvel.dot(&(inertia * self.angvel)) / 2.0;
696 }
697
698 energy
699 }
700
701 pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector<Real>) {
705 self.linvel += impulse.component_mul(&rb_mprops.effective_inv_mass);
706 }
707
708 #[cfg(feature = "dim2")]
712 pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
713 self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
714 }
715
716 #[cfg(feature = "dim3")]
720 pub fn apply_torque_impulse(
721 &mut self,
722 rb_mprops: &RigidBodyMassProps,
723 torque_impulse: Vector<Real>,
724 ) {
725 self.angvel += rb_mprops.effective_world_inv_inertia * torque_impulse;
726 }
727
728 pub fn apply_impulse_at_point(
732 &mut self,
733 rb_mprops: &RigidBodyMassProps,
734 impulse: Vector<Real>,
735 point: Point<Real>,
736 ) {
737 let torque_impulse = (point - rb_mprops.world_com).gcross(impulse);
738 self.apply_impulse(rb_mprops, impulse);
739 self.apply_torque_impulse(rb_mprops, torque_impulse);
740 }
741}
742
743impl<T: SimdRealCopy> RigidBodyVelocity<T> {
744 #[must_use]
746 pub fn apply_damping(&self, dt: T, damping: &RigidBodyDamping<T>) -> Self {
747 let one = T::one();
748 RigidBodyVelocity {
749 linvel: self.linvel * (one / (one + dt * damping.linear_damping)),
750 angvel: self.angvel * (one / (one + dt * damping.angular_damping)),
751 }
752 }
753
754 #[must_use]
757 #[inline]
758 pub fn integrate(&self, dt: T, init_pos: &Isometry<T>, local_com: &Point<T>) -> Isometry<T> {
759 let com = init_pos * local_com;
760 let shift = Translation::from(com.coords);
761 let mut result =
762 shift * Isometry::new(self.linvel * dt, self.angvel * dt) * shift.inverse() * init_pos;
763 result.rotation.renormalize_fast();
764 result
765 }
766
767 #[must_use]
769 #[inline]
770 pub fn integrate_centered(&self, dt: T, mut pose: Isometry<T>) -> Isometry<T> {
771 pose.translation.vector += self.linvel * dt;
772 pose.rotation = Rotation::new(self.angvel * dt) * pose.rotation;
773 pose.rotation.renormalize_fast();
774 pose
775 }
776
777 #[inline]
780 #[cfg(feature = "dim2")]
781 pub fn integrate_linearized(&self, dt: T, pose: &mut Isometry<T>) {
782 let dang = self.angvel * dt;
783 let new_cos = pose.rotation.re - dang * pose.rotation.im;
784 let new_sin = pose.rotation.im + dang * pose.rotation.re;
785 pose.rotation = Rotation::from_cos_sin_unchecked(new_cos, new_sin);
786 pose.rotation.renormalize();
789 pose.translation.vector += self.linvel * dt;
790 }
791
792 #[inline]
795 #[cfg(feature = "dim3")]
796 pub fn integrate_linearized(&self, dt: T, pose: &mut Isometry<T>) {
797 let hang = self.angvel * (dt * T::splat(0.5));
800 let id_plus_hang = na::Quaternion::new(T::one(), hang.x, hang.y, hang.z);
802 pose.rotation = Rotation::new_unchecked(id_plus_hang * pose.rotation.into_inner());
803 pose.rotation.renormalize();
806 pose.translation.vector += self.linvel * dt;
807 }
808}
809
810impl std::ops::Mul<Real> for RigidBodyVelocity<Real> {
811 type Output = Self;
812
813 fn mul(self, rhs: Real) -> Self {
814 RigidBodyVelocity {
815 linvel: self.linvel * rhs,
816 angvel: self.angvel * rhs,
817 }
818 }
819}
820
821impl std::ops::Add<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
822 type Output = Self;
823
824 fn add(self, rhs: Self) -> Self {
825 RigidBodyVelocity {
826 linvel: self.linvel + rhs.linvel,
827 angvel: self.angvel + rhs.angvel,
828 }
829 }
830}
831
832impl std::ops::AddAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
833 fn add_assign(&mut self, rhs: Self) {
834 self.linvel += rhs.linvel;
835 self.angvel += rhs.angvel;
836 }
837}
838
839impl std::ops::Sub<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
840 type Output = Self;
841
842 fn sub(self, rhs: Self) -> Self {
843 RigidBodyVelocity {
844 linvel: self.linvel - rhs.linvel,
845 angvel: self.angvel - rhs.angvel,
846 }
847 }
848}
849
850impl std::ops::SubAssign<RigidBodyVelocity<Real>> for RigidBodyVelocity<Real> {
851 fn sub_assign(&mut self, rhs: Self) {
852 self.linvel -= rhs.linvel;
853 self.angvel -= rhs.angvel;
854 }
855}
856
857#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
858#[derive(Clone, Debug, Copy, PartialEq)]
859pub struct RigidBodyDamping<T> {
861 pub linear_damping: T,
863 pub angular_damping: T,
865}
866
867impl<T: SimdRealCopy> Default for RigidBodyDamping<T> {
868 fn default() -> Self {
869 Self {
870 linear_damping: T::zero(),
871 angular_damping: T::zero(),
872 }
873 }
874}
875
876#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
877#[derive(Clone, Debug, Copy, PartialEq)]
878pub struct RigidBodyForces {
880 pub force: Vector<Real>,
882 pub torque: AngVector<Real>,
884 pub gravity_scale: Real,
887 pub user_force: Vector<Real>,
889 pub user_torque: AngVector<Real>,
891 #[cfg(feature = "dim3")]
893 pub gyroscopic_forces_enabled: bool,
894}
895
896impl Default for RigidBodyForces {
897 fn default() -> Self {
898 Self {
899 force: na::zero(),
900 torque: na::zero(),
901 gravity_scale: 1.0,
902 user_force: na::zero(),
903 user_torque: na::zero(),
904 #[cfg(feature = "dim3")]
905 gyroscopic_forces_enabled: false,
906 }
907 }
908}
909
910impl RigidBodyForces {
911 #[must_use]
913 pub fn integrate(
914 &self,
915 dt: Real,
916 init_vels: &RigidBodyVelocity<Real>,
917 mprops: &RigidBodyMassProps,
918 ) -> RigidBodyVelocity<Real> {
919 let linear_acc = self.force.component_mul(&mprops.effective_inv_mass);
920 let angular_acc = mprops.effective_world_inv_inertia * self.torque;
921
922 RigidBodyVelocity {
923 linvel: init_vels.linvel + linear_acc * dt,
924 angvel: init_vels.angvel + angular_acc * dt,
925 }
926 }
927
928 pub fn compute_effective_force_and_torque(
931 &mut self,
932 gravity: &Vector<Real>,
933 mass: &Vector<Real>,
934 ) {
935 self.force = self.user_force + gravity.component_mul(mass) * self.gravity_scale;
936 self.torque = self.user_torque;
937 }
938
939 pub fn apply_force_at_point(
941 &mut self,
942 rb_mprops: &RigidBodyMassProps,
943 force: Vector<Real>,
944 point: Point<Real>,
945 ) {
946 self.user_force += force;
947 self.user_torque += (point - rb_mprops.world_com).gcross(force);
948 }
949}
950
951#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
952#[derive(Clone, Debug, Copy, PartialEq)]
953pub struct RigidBodyCcd {
955 pub ccd_thickness: Real,
958 pub ccd_max_dist: Real,
961 pub ccd_active: bool,
967 pub ccd_enabled: bool,
969 pub soft_ccd_prediction: Real,
971}
972
973impl Default for RigidBodyCcd {
974 fn default() -> Self {
975 Self {
976 ccd_thickness: Real::MAX,
977 ccd_max_dist: 0.0,
978 ccd_active: false,
979 ccd_enabled: false,
980 soft_ccd_prediction: 0.0,
981 }
982 }
983}
984
985impl RigidBodyCcd {
986 pub fn max_point_velocity(&self, vels: &RigidBodyVelocity<Real>) -> Real {
989 #[cfg(feature = "dim2")]
990 return vels.linvel.norm() + vels.angvel.abs() * self.ccd_max_dist;
991 #[cfg(feature = "dim3")]
992 return vels.linvel.norm() + vels.angvel.norm() * self.ccd_max_dist;
993 }
994
995 pub fn is_moving_fast(
997 &self,
998 dt: Real,
999 vels: &RigidBodyVelocity<Real>,
1000 forces: Option<&RigidBodyForces>,
1001 ) -> bool {
1002 let threshold = self.ccd_thickness / 10.0;
1011
1012 if let Some(forces) = forces {
1013 let linear_part = (vels.linvel + forces.force * dt).norm();
1014 #[cfg(feature = "dim2")]
1015 let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
1016 #[cfg(feature = "dim3")]
1017 let angular_part = (vels.angvel + forces.torque * dt).norm() * self.ccd_max_dist;
1018 let vel_with_forces = linear_part + angular_part;
1019 vel_with_forces > threshold
1020 } else {
1021 self.max_point_velocity(vels) * dt > threshold
1022 }
1023 }
1024}
1025
1026#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1027#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
1028pub struct RigidBodyIds {
1030 pub(crate) active_island_id: usize,
1031 pub(crate) active_set_id: usize,
1032 pub(crate) active_set_offset: u32,
1033 pub(crate) active_set_timestamp: u32,
1034}
1035
1036impl Default for RigidBodyIds {
1037 fn default() -> Self {
1038 Self {
1039 active_island_id: usize::MAX,
1040 active_set_id: usize::MAX,
1041 active_set_offset: u32::MAX,
1042 active_set_timestamp: 0,
1043 }
1044 }
1045}
1046
1047#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1048#[derive(Default, Clone, Debug, PartialEq, Eq)]
1049pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
1055
1056impl RigidBodyColliders {
1057 pub fn detach_collider(
1059 &mut self,
1060 rb_changes: &mut RigidBodyChanges,
1061 co_handle: ColliderHandle,
1062 ) {
1063 if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
1064 rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1065 self.0.swap_remove(i);
1066 }
1067 }
1068
1069 pub fn attach_collider(
1071 &mut self,
1072 rb_type: RigidBodyType,
1073 rb_changes: &mut RigidBodyChanges,
1074 rb_ccd: &mut RigidBodyCcd,
1075 rb_mprops: &mut RigidBodyMassProps,
1076 rb_pos: &RigidBodyPosition,
1077 co_handle: ColliderHandle,
1078 co_pos: &mut ColliderPosition,
1079 co_parent: &ColliderParent,
1080 co_shape: &ColliderShape,
1081 co_mprops: &ColliderMassProps,
1082 ) {
1083 rb_changes.set(RigidBodyChanges::COLLIDERS, true);
1084
1085 co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
1086 rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
1087
1088 let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
1089 rb_ccd.ccd_max_dist = rb_ccd
1090 .ccd_max_dist
1091 .max(shape_bsphere.center.coords.norm() + shape_bsphere.radius);
1092
1093 let mass_properties = co_mprops
1094 .mass_properties(&**co_shape)
1095 .transform_by(&co_parent.pos_wrt_parent);
1096 self.0.push(co_handle);
1097 rb_mprops.local_mprops += mass_properties;
1098 rb_mprops.update_world_mass_properties(rb_type, &rb_pos.position);
1099 }
1100
1101 pub(crate) fn update_positions(
1103 &self,
1104 colliders: &mut ColliderSet,
1105 modified_colliders: &mut ModifiedColliders,
1106 parent_pos: &Isometry<Real>,
1107 ) {
1108 for handle in &self.0 {
1109 let co = colliders.index_mut_internal(*handle);
1113 let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1114
1115 modified_colliders.push_once(*handle, co);
1118
1119 co.changes |= ColliderChanges::POSITION;
1120 co.pos = ColliderPosition(new_pos);
1121 }
1122 }
1123}
1124
1125#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1126#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1127pub struct RigidBodyDominance(pub i8);
1129
1130impl RigidBodyDominance {
1131 pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1133 if status.is_dynamic_or_kinematic() {
1134 self.0 as i16
1135 } else {
1136 i8::MAX as i16 + 1
1137 }
1138 }
1139}
1140
1141#[derive(Copy, Clone, Debug, PartialEq)]
1164#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1165pub struct RigidBodyActivation {
1166 pub normalized_linear_threshold: Real,
1170
1171 pub angular_threshold: Real,
1175
1176 pub time_until_sleep: Real,
1180
1181 pub time_since_can_sleep: Real,
1183
1184 pub sleeping: bool,
1186}
1187
1188impl Default for RigidBodyActivation {
1189 fn default() -> Self {
1190 Self::active()
1191 }
1192}
1193
1194impl RigidBodyActivation {
1195 pub fn default_normalized_linear_threshold() -> Real {
1197 0.4
1198 }
1199
1200 pub fn default_angular_threshold() -> Real {
1202 0.5
1203 }
1204
1205 pub fn default_time_until_sleep() -> Real {
1208 2.0
1209 }
1210
1211 pub fn active() -> Self {
1213 RigidBodyActivation {
1214 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1215 angular_threshold: Self::default_angular_threshold(),
1216 time_until_sleep: Self::default_time_until_sleep(),
1217 time_since_can_sleep: 0.0,
1218 sleeping: false,
1219 }
1220 }
1221
1222 pub fn inactive() -> Self {
1224 RigidBodyActivation {
1225 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1226 angular_threshold: Self::default_angular_threshold(),
1227 time_until_sleep: Self::default_time_until_sleep(),
1228 time_since_can_sleep: Self::default_time_until_sleep(),
1229 sleeping: true,
1230 }
1231 }
1232
1233 pub fn cannot_sleep() -> Self {
1235 RigidBodyActivation {
1236 normalized_linear_threshold: -1.0,
1237 angular_threshold: -1.0,
1238 ..Self::active()
1239 }
1240 }
1241
1242 #[inline]
1244 pub fn is_active(&self) -> bool {
1245 !self.sleeping
1246 }
1247
1248 #[inline]
1250 pub fn wake_up(&mut self, strong: bool) {
1251 self.sleeping = false;
1252 if strong {
1253 self.time_since_can_sleep = 0.0;
1254 }
1255 }
1256
1257 #[inline]
1259 pub fn sleep(&mut self) {
1260 self.sleeping = true;
1261 self.time_since_can_sleep = self.time_until_sleep;
1262 }
1263}
1264
1265#[cfg(test)]
1266mod tests {
1267 use super::*;
1268 use crate::math::Real;
1269
1270 #[test]
1271 fn test_interpolate_velocity() {
1272 #[cfg(feature = "f32")]
1275 let mut rng = oorandom::Rand32::new(0);
1276 #[cfg(feature = "f64")]
1277 let mut rng = oorandom::Rand64::new(0);
1278
1279 for i in -10..=10 {
1280 let mult = i as Real;
1281 let (local_com, curr_pos, next_pos);
1282 #[cfg(feature = "dim2")]
1283 {
1284 local_com = Point::new(rng.rand_float(), rng.rand_float());
1285 curr_pos = Isometry::new(
1286 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1287 rng.rand_float(),
1288 );
1289 next_pos = Isometry::new(
1290 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1291 rng.rand_float(),
1292 );
1293 }
1294 #[cfg(feature = "dim3")]
1295 {
1296 local_com = Point::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1297 curr_pos = Isometry::new(
1298 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1299 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1300 );
1301 next_pos = Isometry::new(
1302 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1303 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1304 );
1305 }
1306
1307 let dt = 0.016;
1308 let rb_pos = RigidBodyPosition {
1309 position: curr_pos,
1310 next_position: next_pos,
1311 };
1312 let vel = rb_pos.interpolate_velocity(1.0 / dt, &local_com);
1313 let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1314 approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1315 }
1316 }
1317}