1#[cfg(doc)]
2use super::IntegrationParameters;
3use crate::control::PdErrors;
4use crate::dynamics::MassProperties;
5use crate::geometry::{
6 ColliderChanges, ColliderHandle, ColliderMassProps, ColliderParent, ColliderPosition,
7 ColliderSet, ColliderShape, ModifiedColliders,
8};
9use crate::math::{
10 AngVector, AngularInertia, Isometry, Point, Real, Rotation, Translation, Vector,
11};
12use crate::utils::{SimdAngularInertia, SimdCross, SimdDot};
13use num::Zero;
14
15#[cfg(doc)]
16use crate::control::PidController;
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
20#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
21#[repr(transparent)]
22pub struct RigidBodyHandle(pub crate::data::arena::Index);
23
24impl RigidBodyHandle {
25 pub fn into_raw_parts(self) -> (u32, u32) {
27 self.0.into_raw_parts()
28 }
29
30 pub fn from_raw_parts(id: u32, generation: u32) -> Self {
32 Self(crate::data::arena::Index::from_raw_parts(id, generation))
33 }
34
35 pub fn invalid() -> Self {
37 Self(crate::data::arena::Index::from_raw_parts(
38 crate::INVALID_U32,
39 crate::INVALID_U32,
40 ))
41 }
42}
43
44#[deprecated(note = "renamed as RigidBodyType")]
46pub type BodyStatus = RigidBodyType;
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq)]
49#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
50pub enum RigidBodyType {
52 Dynamic = 0,
54 Fixed = 1,
56 KinematicPositionBased = 2,
63 KinematicVelocityBased = 3,
70 }
73
74impl RigidBodyType {
75 pub fn is_fixed(self) -> bool {
77 self == RigidBodyType::Fixed
78 }
79
80 pub fn is_dynamic(self) -> bool {
82 self == RigidBodyType::Dynamic
83 }
84
85 pub fn is_kinematic(self) -> bool {
87 self == RigidBodyType::KinematicPositionBased
88 || self == RigidBodyType::KinematicVelocityBased
89 }
90}
91
92bitflags::bitflags! {
93 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
94 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
95 pub struct RigidBodyChanges: u32 {
97 const MODIFIED = 1 << 0;
99 const POSITION = 1 << 1;
101 const SLEEP = 1 << 2;
103 const COLLIDERS = 1 << 3;
105 const TYPE = 1 << 4;
107 const DOMINANCE = 1 << 5;
109 const LOCAL_MASS_PROPERTIES = 1 << 6;
111 const ENABLED_OR_DISABLED = 1 << 7;
113 }
114}
115
116impl Default for RigidBodyChanges {
117 fn default() -> Self {
118 RigidBodyChanges::empty()
119 }
120}
121
122#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
123#[derive(Clone, Debug, Copy, PartialEq)]
124pub struct RigidBodyPosition {
126 pub position: Isometry<Real>,
128 pub next_position: Isometry<Real>,
138}
139
140impl Default for RigidBodyPosition {
141 fn default() -> Self {
142 Self {
143 position: Isometry::identity(),
144 next_position: Isometry::identity(),
145 }
146 }
147}
148
149impl RigidBodyPosition {
150 #[must_use]
153 pub fn interpolate_velocity(&self, inv_dt: Real, local_com: &Point<Real>) -> RigidBodyVelocity {
154 let pose_err = self.pose_errors(local_com);
155 RigidBodyVelocity {
156 linvel: pose_err.linear * inv_dt,
157 angvel: pose_err.angular * inv_dt,
158 }
159 }
160
161 #[must_use]
165 pub fn integrate_forces_and_velocities(
166 &self,
167 dt: Real,
168 forces: &RigidBodyForces,
169 vels: &RigidBodyVelocity,
170 mprops: &RigidBodyMassProps,
171 ) -> Isometry<Real> {
172 let new_vels = forces.integrate(dt, vels, mprops);
173 new_vels.integrate(dt, &self.position, &mprops.local_mprops.local_com)
174 }
175
176 pub fn pose_errors(&self, local_com: &Point<Real>) -> PdErrors {
184 let com = self.position * local_com;
185 let shift = Translation::from(com.coords);
186 let dpos = shift.inverse() * self.next_position * self.position.inverse() * shift;
187
188 let angular;
189 #[cfg(feature = "dim2")]
190 {
191 angular = dpos.rotation.angle();
192 }
193 #[cfg(feature = "dim3")]
194 {
195 angular = dpos.rotation.scaled_axis();
196 }
197 let linear = dpos.translation.vector;
198
199 PdErrors { linear, angular }
200 }
201}
202
203impl<T> From<T> for RigidBodyPosition
204where
205 Isometry<Real>: From<T>,
206{
207 fn from(position: T) -> Self {
208 let position = position.into();
209 Self {
210 position,
211 next_position: position,
212 }
213 }
214}
215
216bitflags::bitflags! {
217 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
218 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
219 pub struct AxesMask: u8 {
221 const LIN_X = 1 << 0;
223 const LIN_Y = 1 << 1;
225 #[cfg(feature = "dim3")]
227 const LIN_Z = 1 << 2;
228 #[cfg(feature = "dim3")]
230 const ANG_X = 1 << 3;
231 #[cfg(feature = "dim3")]
233 const ANG_Y = 1 << 4;
234 const ANG_Z = 1 << 5;
236 }
237}
238
239impl Default for AxesMask {
240 fn default() -> Self {
241 AxesMask::empty()
242 }
243}
244
245bitflags::bitflags! {
246 #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
247 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
248 pub struct LockedAxes: u8 {
250 const TRANSLATION_LOCKED_X = 1 << 0;
252 const TRANSLATION_LOCKED_Y = 1 << 1;
254 const TRANSLATION_LOCKED_Z = 1 << 2;
256 const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits();
258 const ROTATION_LOCKED_X = 1 << 3;
260 const ROTATION_LOCKED_Y = 1 << 4;
262 const ROTATION_LOCKED_Z = 1 << 5;
264 const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits();
266 }
267}
268
269#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
271#[derive(Copy, Clone, Debug, PartialEq)]
272pub enum RigidBodyAdditionalMassProps {
273 MassProps(MassProperties),
275 Mass(Real),
278}
279
280impl Default for RigidBodyAdditionalMassProps {
281 fn default() -> Self {
282 RigidBodyAdditionalMassProps::MassProps(MassProperties::default())
283 }
284}
285
286#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
287#[derive(Clone, Debug, PartialEq)]
288pub struct RigidBodyMassProps {
290 pub flags: LockedAxes,
292 pub local_mprops: MassProperties,
294 pub additional_local_mprops: Option<Box<RigidBodyAdditionalMassProps>>,
296 pub world_com: Point<Real>,
298 pub effective_inv_mass: Vector<Real>,
300 pub effective_world_inv_inertia_sqrt: AngularInertia<Real>,
303}
304
305impl Default for RigidBodyMassProps {
306 fn default() -> Self {
307 Self {
308 flags: LockedAxes::empty(),
309 local_mprops: MassProperties::zero(),
310 additional_local_mprops: None,
311 world_com: Point::origin(),
312 effective_inv_mass: Vector::zero(),
313 effective_world_inv_inertia_sqrt: AngularInertia::zero(),
314 }
315 }
316}
317
318impl From<LockedAxes> for RigidBodyMassProps {
319 fn from(flags: LockedAxes) -> Self {
320 Self {
321 flags,
322 ..Self::default()
323 }
324 }
325}
326
327impl From<MassProperties> for RigidBodyMassProps {
328 fn from(local_mprops: MassProperties) -> Self {
329 Self {
330 local_mprops,
331 ..Default::default()
332 }
333 }
334}
335
336impl RigidBodyMassProps {
337 #[must_use]
339 pub fn mass(&self) -> Real {
340 crate::utils::inv(self.local_mprops.inv_mass)
341 }
342
343 #[must_use]
346 pub fn effective_mass(&self) -> Vector<Real> {
347 self.effective_inv_mass.map(crate::utils::inv)
348 }
349
350 #[must_use]
353 pub fn effective_angular_inertia_sqrt(&self) -> AngularInertia<Real> {
354 #[allow(unused_mut)] let mut ang_inertia = self.effective_world_inv_inertia_sqrt;
356
357 #[cfg(feature = "dim3")]
359 {
360 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
361 ang_inertia.m11 = 1.0;
362 }
363 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
364 ang_inertia.m22 = 1.0;
365 }
366 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
367 ang_inertia.m33 = 1.0;
368 }
369 }
370
371 #[allow(unused_mut)] let mut result = ang_inertia.inverse();
373
374 #[cfg(feature = "dim3")]
376 {
377 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
378 result.m11 = 0.0;
379 }
380 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
381 result.m22 = 0.0;
382 }
383 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
384 result.m33 = 0.0;
385 }
386 }
387
388 result
389 }
390
391 #[must_use]
394 pub fn effective_angular_inertia(&self) -> AngularInertia<Real> {
395 self.effective_angular_inertia_sqrt().squared()
396 }
397
398 pub fn recompute_mass_properties_from_colliders(
400 &mut self,
401 colliders: &ColliderSet,
402 attached_colliders: &RigidBodyColliders,
403 position: &Isometry<Real>,
404 ) {
405 let added_mprops = self
406 .additional_local_mprops
407 .as_ref()
408 .map(|mprops| **mprops)
409 .unwrap_or_else(|| RigidBodyAdditionalMassProps::MassProps(MassProperties::default()));
410
411 self.local_mprops = MassProperties::default();
412
413 for handle in &attached_colliders.0 {
414 if let Some(co) = colliders.get(*handle) {
415 if co.is_enabled() {
416 if let Some(co_parent) = co.parent {
417 let to_add = co
418 .mprops
419 .mass_properties(&*co.shape)
420 .transform_by(&co_parent.pos_wrt_parent);
421 self.local_mprops += to_add;
422 }
423 }
424 }
425 }
426
427 match added_mprops {
428 RigidBodyAdditionalMassProps::MassProps(mprops) => {
429 self.local_mprops += mprops;
430 }
431 RigidBodyAdditionalMassProps::Mass(mass) => {
432 let new_mass = self.local_mprops.mass() + mass;
433 self.local_mprops.set_mass(new_mass, true);
434 }
435 }
436
437 self.update_world_mass_properties(position);
438 }
439
440 pub fn update_world_mass_properties(&mut self, position: &Isometry<Real>) {
442 self.world_com = self.local_mprops.world_com(position);
443 self.effective_inv_mass = Vector::repeat(self.local_mprops.inv_mass);
444 self.effective_world_inv_inertia_sqrt =
445 self.local_mprops.world_inv_inertia_sqrt(&position.rotation);
446
447 if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_X) {
449 self.effective_inv_mass.x = 0.0;
450 }
451
452 if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Y) {
453 self.effective_inv_mass.y = 0.0;
454 }
455
456 #[cfg(feature = "dim3")]
457 if self.flags.contains(LockedAxes::TRANSLATION_LOCKED_Z) {
458 self.effective_inv_mass.z = 0.0;
459 }
460
461 #[cfg(feature = "dim2")]
462 {
463 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
464 self.effective_world_inv_inertia_sqrt = 0.0;
465 }
466 }
467 #[cfg(feature = "dim3")]
468 {
469 if self.flags.contains(LockedAxes::ROTATION_LOCKED_X) {
470 self.effective_world_inv_inertia_sqrt.m11 = 0.0;
471 self.effective_world_inv_inertia_sqrt.m12 = 0.0;
472 self.effective_world_inv_inertia_sqrt.m13 = 0.0;
473 }
474
475 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Y) {
476 self.effective_world_inv_inertia_sqrt.m22 = 0.0;
477 self.effective_world_inv_inertia_sqrt.m12 = 0.0;
478 self.effective_world_inv_inertia_sqrt.m23 = 0.0;
479 }
480 if self.flags.contains(LockedAxes::ROTATION_LOCKED_Z) {
481 self.effective_world_inv_inertia_sqrt.m33 = 0.0;
482 self.effective_world_inv_inertia_sqrt.m13 = 0.0;
483 self.effective_world_inv_inertia_sqrt.m23 = 0.0;
484 }
485 }
486 }
487}
488
489#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
490#[derive(Clone, Debug, Copy, PartialEq)]
491pub struct RigidBodyVelocity {
493 pub linvel: Vector<Real>,
495 pub angvel: AngVector<Real>,
497}
498
499impl Default for RigidBodyVelocity {
500 fn default() -> Self {
501 Self::zero()
502 }
503}
504
505impl RigidBodyVelocity {
506 #[must_use]
508 pub fn new(linvel: Vector<Real>, angvel: AngVector<Real>) -> Self {
509 Self { linvel, angvel }
510 }
511
512 #[must_use]
517 #[cfg(feature = "dim2")]
518 pub fn from_slice(slice: &[Real]) -> Self {
519 Self {
520 linvel: Vector::new(slice[0], slice[1]),
521 angvel: slice[2],
522 }
523 }
524
525 #[must_use]
530 #[cfg(feature = "dim3")]
531 pub fn from_slice(slice: &[Real]) -> Self {
532 Self {
533 linvel: Vector::new(slice[0], slice[1], slice[2]),
534 angvel: AngVector::new(slice[3], slice[4], slice[5]),
535 }
536 }
537
538 #[must_use]
540 pub fn zero() -> Self {
541 Self {
542 linvel: na::zero(),
543 angvel: na::zero(),
544 }
545 }
546
547 #[inline]
551 pub fn as_slice(&self) -> &[Real] {
552 self.as_vector().as_slice()
553 }
554
555 #[inline]
559 pub fn as_mut_slice(&mut self) -> &mut [Real] {
560 self.as_vector_mut().as_mut_slice()
561 }
562
563 #[inline]
567 #[cfg(feature = "dim2")]
568 pub fn as_vector(&self) -> &na::Vector3<Real> {
569 unsafe { std::mem::transmute(self) }
570 }
571
572 #[inline]
576 #[cfg(feature = "dim2")]
577 pub fn as_vector_mut(&mut self) -> &mut na::Vector3<Real> {
578 unsafe { std::mem::transmute(self) }
579 }
580
581 #[inline]
585 #[cfg(feature = "dim3")]
586 pub fn as_vector(&self) -> &na::Vector6<Real> {
587 unsafe { std::mem::transmute(self) }
588 }
589
590 #[inline]
594 #[cfg(feature = "dim3")]
595 pub fn as_vector_mut(&mut self) -> &mut na::Vector6<Real> {
596 unsafe { std::mem::transmute(self) }
597 }
598
599 #[must_use]
601 pub fn transformed(self, rotation: &Rotation<Real>) -> Self {
602 Self {
603 linvel: rotation * self.linvel,
604 #[cfg(feature = "dim2")]
605 angvel: self.angvel,
606 #[cfg(feature = "dim3")]
607 angvel: rotation * self.angvel,
608 }
609 }
610
611 #[must_use]
617 pub fn pseudo_kinetic_energy(&self) -> Real {
618 0.5 * (self.linvel.norm_squared() + self.angvel.gdot(self.angvel))
619 }
620
621 #[must_use]
623 pub fn apply_damping(&self, dt: Real, damping: &RigidBodyDamping) -> Self {
624 RigidBodyVelocity {
625 linvel: self.linvel * (1.0 / (1.0 + dt * damping.linear_damping)),
626 angvel: self.angvel * (1.0 / (1.0 + dt * damping.angular_damping)),
627 }
628 }
629
630 #[must_use]
632 pub fn velocity_at_point(&self, point: &Point<Real>, world_com: &Point<Real>) -> Vector<Real> {
633 let dpt = point - world_com;
634 self.linvel + self.angvel.gcross(dpt)
635 }
636
637 #[must_use]
640 pub fn integrate(
641 &self,
642 dt: Real,
643 init_pos: &Isometry<Real>,
644 local_com: &Point<Real>,
645 ) -> Isometry<Real> {
646 let com = init_pos * local_com;
647 let shift = Translation::from(com.coords);
648 let mut result =
649 shift * Isometry::new(self.linvel * dt, self.angvel * dt) * shift.inverse() * init_pos;
650 result.rotation.renormalize_fast();
651 result
652 }
653
654 #[must_use]
656 pub fn is_zero(&self) -> bool {
657 self.linvel.is_zero() && self.angvel.is_zero()
658 }
659
660 #[must_use]
662 #[profiling::function]
663 pub fn kinetic_energy(&self, rb_mprops: &RigidBodyMassProps) -> Real {
664 let mut energy = (rb_mprops.mass() * self.linvel.norm_squared()) / 2.0;
665
666 #[cfg(feature = "dim2")]
667 if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() {
668 let inertia_sqrt = 1.0 / rb_mprops.effective_world_inv_inertia_sqrt;
669 energy += (inertia_sqrt * self.angvel).powi(2) / 2.0;
670 }
671
672 #[cfg(feature = "dim3")]
673 if !rb_mprops.effective_world_inv_inertia_sqrt.is_zero() {
674 let inertia_sqrt = rb_mprops
675 .effective_world_inv_inertia_sqrt
676 .inverse_unchecked();
677 energy += (inertia_sqrt * self.angvel).norm_squared() / 2.0;
678 }
679
680 energy
681 }
682
683 pub fn apply_impulse(&mut self, rb_mprops: &RigidBodyMassProps, impulse: Vector<Real>) {
687 self.linvel += impulse.component_mul(&rb_mprops.effective_inv_mass);
688 }
689
690 #[cfg(feature = "dim2")]
694 pub fn apply_torque_impulse(&mut self, rb_mprops: &RigidBodyMassProps, torque_impulse: Real) {
695 self.angvel += rb_mprops.effective_world_inv_inertia_sqrt
696 * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse);
697 }
698
699 #[cfg(feature = "dim3")]
703 pub fn apply_torque_impulse(
704 &mut self,
705 rb_mprops: &RigidBodyMassProps,
706 torque_impulse: Vector<Real>,
707 ) {
708 self.angvel += rb_mprops.effective_world_inv_inertia_sqrt
709 * (rb_mprops.effective_world_inv_inertia_sqrt * torque_impulse);
710 }
711
712 pub fn apply_impulse_at_point(
716 &mut self,
717 rb_mprops: &RigidBodyMassProps,
718 impulse: Vector<Real>,
719 point: Point<Real>,
720 ) {
721 let torque_impulse = (point - rb_mprops.world_com).gcross(impulse);
722 self.apply_impulse(rb_mprops, impulse);
723 self.apply_torque_impulse(rb_mprops, torque_impulse);
724 }
725}
726
727impl std::ops::Mul<Real> for RigidBodyVelocity {
728 type Output = Self;
729
730 fn mul(self, rhs: Real) -> Self {
731 RigidBodyVelocity {
732 linvel: self.linvel * rhs,
733 angvel: self.angvel * rhs,
734 }
735 }
736}
737
738impl std::ops::Add<RigidBodyVelocity> for RigidBodyVelocity {
739 type Output = Self;
740
741 fn add(self, rhs: Self) -> Self {
742 RigidBodyVelocity {
743 linvel: self.linvel + rhs.linvel,
744 angvel: self.angvel + rhs.angvel,
745 }
746 }
747}
748
749impl std::ops::AddAssign<RigidBodyVelocity> for RigidBodyVelocity {
750 fn add_assign(&mut self, rhs: Self) {
751 self.linvel += rhs.linvel;
752 self.angvel += rhs.angvel;
753 }
754}
755
756impl std::ops::Sub<RigidBodyVelocity> for RigidBodyVelocity {
757 type Output = Self;
758
759 fn sub(self, rhs: Self) -> Self {
760 RigidBodyVelocity {
761 linvel: self.linvel - rhs.linvel,
762 angvel: self.angvel - rhs.angvel,
763 }
764 }
765}
766
767impl std::ops::SubAssign<RigidBodyVelocity> for RigidBodyVelocity {
768 fn sub_assign(&mut self, rhs: Self) {
769 self.linvel -= rhs.linvel;
770 self.angvel -= rhs.angvel;
771 }
772}
773
774#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
775#[derive(Clone, Debug, Copy, PartialEq)]
776pub struct RigidBodyDamping {
778 pub linear_damping: Real,
780 pub angular_damping: Real,
782}
783
784impl Default for RigidBodyDamping {
785 fn default() -> Self {
786 Self {
787 linear_damping: 0.0,
788 angular_damping: 0.0,
789 }
790 }
791}
792
793#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
794#[derive(Clone, Debug, Copy, PartialEq)]
795pub struct RigidBodyForces {
797 pub force: Vector<Real>,
799 pub torque: AngVector<Real>,
801 pub gravity_scale: Real,
804 pub user_force: Vector<Real>,
806 pub user_torque: AngVector<Real>,
808}
809
810impl Default for RigidBodyForces {
811 fn default() -> Self {
812 Self {
813 force: na::zero(),
814 torque: na::zero(),
815 gravity_scale: 1.0,
816 user_force: na::zero(),
817 user_torque: na::zero(),
818 }
819 }
820}
821
822impl RigidBodyForces {
823 #[must_use]
825 pub fn integrate(
826 &self,
827 dt: Real,
828 init_vels: &RigidBodyVelocity,
829 mprops: &RigidBodyMassProps,
830 ) -> RigidBodyVelocity {
831 let linear_acc = self.force.component_mul(&mprops.effective_inv_mass);
832 let angular_acc = mprops.effective_world_inv_inertia_sqrt
833 * (mprops.effective_world_inv_inertia_sqrt * self.torque);
834
835 RigidBodyVelocity {
836 linvel: init_vels.linvel + linear_acc * dt,
837 angvel: init_vels.angvel + angular_acc * dt,
838 }
839 }
840
841 pub fn compute_effective_force_and_torque(
844 &mut self,
845 gravity: &Vector<Real>,
846 mass: &Vector<Real>,
847 ) {
848 self.force = self.user_force + gravity.component_mul(mass) * self.gravity_scale;
849 self.torque = self.user_torque;
850 }
851
852 pub fn apply_force_at_point(
854 &mut self,
855 rb_mprops: &RigidBodyMassProps,
856 force: Vector<Real>,
857 point: Point<Real>,
858 ) {
859 self.user_force += force;
860 self.user_torque += (point - rb_mprops.world_com).gcross(force);
861 }
862}
863
864#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
865#[derive(Clone, Debug, Copy, PartialEq)]
866pub struct RigidBodyCcd {
868 pub ccd_thickness: Real,
871 pub ccd_max_dist: Real,
874 pub ccd_active: bool,
880 pub ccd_enabled: bool,
882 pub soft_ccd_prediction: Real,
884}
885
886impl Default for RigidBodyCcd {
887 fn default() -> Self {
888 Self {
889 ccd_thickness: Real::MAX,
890 ccd_max_dist: 0.0,
891 ccd_active: false,
892 ccd_enabled: false,
893 soft_ccd_prediction: 0.0,
894 }
895 }
896}
897
898impl RigidBodyCcd {
899 pub fn max_point_velocity(&self, vels: &RigidBodyVelocity) -> Real {
902 #[cfg(feature = "dim2")]
903 return vels.linvel.norm() + vels.angvel.abs() * self.ccd_max_dist;
904 #[cfg(feature = "dim3")]
905 return vels.linvel.norm() + vels.angvel.norm() * self.ccd_max_dist;
906 }
907
908 pub fn is_moving_fast(
910 &self,
911 dt: Real,
912 vels: &RigidBodyVelocity,
913 forces: Option<&RigidBodyForces>,
914 ) -> bool {
915 let threshold = self.ccd_thickness / 10.0;
924
925 if let Some(forces) = forces {
926 let linear_part = (vels.linvel + forces.force * dt).norm();
927 #[cfg(feature = "dim2")]
928 let angular_part = (vels.angvel + forces.torque * dt).abs() * self.ccd_max_dist;
929 #[cfg(feature = "dim3")]
930 let angular_part = (vels.angvel + forces.torque * dt).norm() * self.ccd_max_dist;
931 let vel_with_forces = linear_part + angular_part;
932 vel_with_forces > threshold
933 } else {
934 self.max_point_velocity(vels) * dt > threshold
935 }
936 }
937}
938
939#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
940#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, Hash)]
941pub struct RigidBodyIds {
943 pub(crate) active_island_id: usize,
944 pub(crate) active_set_id: usize,
945 pub(crate) active_set_offset: usize,
946 pub(crate) active_set_timestamp: u32,
947}
948
949#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
950#[derive(Default, Clone, Debug, PartialEq, Eq)]
951pub struct RigidBodyColliders(pub Vec<ColliderHandle>);
957
958impl RigidBodyColliders {
959 pub fn detach_collider(
961 &mut self,
962 rb_changes: &mut RigidBodyChanges,
963 co_handle: ColliderHandle,
964 ) {
965 if let Some(i) = self.0.iter().position(|e| *e == co_handle) {
966 rb_changes.set(
967 RigidBodyChanges::MODIFIED | RigidBodyChanges::COLLIDERS,
968 true,
969 );
970 self.0.swap_remove(i);
971 }
972 }
973
974 pub fn attach_collider(
976 &mut self,
977 rb_changes: &mut RigidBodyChanges,
978 rb_ccd: &mut RigidBodyCcd,
979 rb_mprops: &mut RigidBodyMassProps,
980 rb_pos: &RigidBodyPosition,
981 co_handle: ColliderHandle,
982 co_pos: &mut ColliderPosition,
983 co_parent: &ColliderParent,
984 co_shape: &ColliderShape,
985 co_mprops: &ColliderMassProps,
986 ) {
987 rb_changes.set(
988 RigidBodyChanges::MODIFIED | RigidBodyChanges::COLLIDERS,
989 true,
990 );
991
992 co_pos.0 = rb_pos.position * co_parent.pos_wrt_parent;
993 rb_ccd.ccd_thickness = rb_ccd.ccd_thickness.min(co_shape.ccd_thickness());
994
995 let shape_bsphere = co_shape.compute_bounding_sphere(&co_parent.pos_wrt_parent);
996 rb_ccd.ccd_max_dist = rb_ccd
997 .ccd_max_dist
998 .max(shape_bsphere.center.coords.norm() + shape_bsphere.radius);
999
1000 let mass_properties = co_mprops
1001 .mass_properties(&**co_shape)
1002 .transform_by(&co_parent.pos_wrt_parent);
1003 self.0.push(co_handle);
1004 rb_mprops.local_mprops += mass_properties;
1005 rb_mprops.update_world_mass_properties(&rb_pos.position);
1006 }
1007
1008 pub(crate) fn update_positions(
1010 &self,
1011 colliders: &mut ColliderSet,
1012 modified_colliders: &mut ModifiedColliders,
1013 parent_pos: &Isometry<Real>,
1014 ) {
1015 for handle in &self.0 {
1016 let co = colliders.index_mut_internal(*handle);
1018 let new_pos = parent_pos * co.parent.as_ref().unwrap().pos_wrt_parent;
1019
1020 modified_colliders.push_once(*handle, co);
1023
1024 co.changes |= ColliderChanges::POSITION;
1025 co.pos = ColliderPosition(new_pos);
1026 }
1027 }
1028}
1029
1030#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1031#[derive(Default, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1032pub struct RigidBodyDominance(pub i8);
1034
1035impl RigidBodyDominance {
1036 pub fn effective_group(&self, status: &RigidBodyType) -> i16 {
1038 if status.is_dynamic() {
1039 self.0 as i16
1040 } else {
1041 i8::MAX as i16 + 1
1042 }
1043 }
1044}
1045
1046#[derive(Copy, Clone, Debug, PartialEq)]
1051#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1052pub struct RigidBodyActivation {
1053 pub normalized_linear_threshold: Real,
1058 pub angular_threshold: Real,
1060 pub time_until_sleep: Real,
1062 pub time_since_can_sleep: Real,
1064 pub sleeping: bool,
1066}
1067
1068impl Default for RigidBodyActivation {
1069 fn default() -> Self {
1070 Self::active()
1071 }
1072}
1073
1074impl RigidBodyActivation {
1075 pub fn default_normalized_linear_threshold() -> Real {
1077 0.4
1078 }
1079
1080 pub fn default_angular_threshold() -> Real {
1082 0.5
1083 }
1084
1085 pub fn default_time_until_sleep() -> Real {
1088 2.0
1089 }
1090
1091 pub fn active() -> Self {
1093 RigidBodyActivation {
1094 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1095 angular_threshold: Self::default_angular_threshold(),
1096 time_until_sleep: Self::default_time_until_sleep(),
1097 time_since_can_sleep: 0.0,
1098 sleeping: false,
1099 }
1100 }
1101
1102 pub fn inactive() -> Self {
1104 RigidBodyActivation {
1105 normalized_linear_threshold: Self::default_normalized_linear_threshold(),
1106 angular_threshold: Self::default_angular_threshold(),
1107 time_until_sleep: Self::default_time_until_sleep(),
1108 time_since_can_sleep: Self::default_time_until_sleep(),
1109 sleeping: true,
1110 }
1111 }
1112
1113 pub fn cannot_sleep() -> Self {
1115 RigidBodyActivation {
1116 normalized_linear_threshold: -1.0,
1117 angular_threshold: -1.0,
1118 ..Self::active()
1119 }
1120 }
1121
1122 #[inline]
1124 pub fn is_active(&self) -> bool {
1125 !self.sleeping
1126 }
1127
1128 #[inline]
1130 pub fn wake_up(&mut self, strong: bool) {
1131 self.sleeping = false;
1132 if strong {
1133 self.time_since_can_sleep = 0.0;
1134 }
1135 }
1136
1137 #[inline]
1139 pub fn sleep(&mut self) {
1140 self.sleeping = true;
1141 self.time_since_can_sleep = self.time_until_sleep;
1142 }
1143}
1144
1145#[cfg(test)]
1146mod tests {
1147 use super::*;
1148 use crate::math::Real;
1149
1150 #[test]
1151 fn test_interpolate_velocity() {
1152 #[cfg(feature = "f32")]
1155 let mut rng = oorandom::Rand32::new(0);
1156 #[cfg(feature = "f64")]
1157 let mut rng = oorandom::Rand64::new(0);
1158
1159 for i in -10..=10 {
1160 let mult = i as Real;
1161 let (local_com, curr_pos, next_pos);
1162 #[cfg(feature = "dim2")]
1163 {
1164 local_com = Point::new(rng.rand_float(), rng.rand_float());
1165 curr_pos = Isometry::new(
1166 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1167 rng.rand_float(),
1168 );
1169 next_pos = Isometry::new(
1170 Vector::new(rng.rand_float(), rng.rand_float()) * mult,
1171 rng.rand_float(),
1172 );
1173 }
1174 #[cfg(feature = "dim3")]
1175 {
1176 local_com = Point::new(rng.rand_float(), rng.rand_float(), rng.rand_float());
1177 curr_pos = Isometry::new(
1178 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1179 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1180 );
1181 next_pos = Isometry::new(
1182 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()) * mult,
1183 Vector::new(rng.rand_float(), rng.rand_float(), rng.rand_float()),
1184 );
1185 }
1186
1187 let dt = 0.016;
1188 let rb_pos = RigidBodyPosition {
1189 position: curr_pos,
1190 next_position: next_pos,
1191 };
1192 let vel = rb_pos.interpolate_velocity(1.0 / dt, &local_com);
1193 let interp_pos = vel.integrate(dt, &curr_pos, &local_com);
1194 approx::assert_relative_eq!(interp_pos, next_pos, epsilon = 1.0e-5);
1195 }
1196 }
1197}