avian3d/dynamics/solver/solver_body/mod.rs
1//! Efficient rigid body definitions used by the performance-critical solver.
2//!
3//! This helps improve memory locality and makes random access faster for the constraint solver.
4//!
5//! This includes the following types:
6//!
7//! - [`SolverBody`]: The body state used by the solver.
8//! - [`SolverBodyInertia`]: The inertial properties of a body used by the solver.
9//! - [`SolverBodyIndex`]: A component storing the index of a body in the [`SolverBodies`] resource.
10//! - [`SolverBodies`]: A resource storing [`SolverBody`]s contiguously for awake, active bodies.
11
12mod plugin;
13
14pub use plugin::SolverBodyPlugin;
15
16use core::marker::PhantomData;
17
18use bevy::prelude::*;
19
20use super::Rot;
21#[cfg(feature = "3d")]
22use crate::prelude::ComputedAngularInertia;
23use crate::{SymmetricTensor, math::Vector, prelude::LockedAxes};
24
25// The `SolverBody` layout is inspired by `b2BodyState` in Box2D v3.
26
27/// Optimized rigid body state that the solver operates on,
28/// designed to improve memory locality and performance.
29///
30/// Only awake dynamic bodies and kinematic bodies have an associated solver body,
31/// stored contiguously in the [`SolverBodies`] resource and indexed by a [`SolverBodyIndex`]
32/// component on the body entity. Static bodies and sleeping dynamic bodies do not move,
33/// so they instead use a "dummy state" with [`SolverBody::default()`].
34///
35/// # Representation
36///
37/// The solver doesn't have access to the position or rotation of static or sleeping bodies,
38/// which is a problem when computing constraint anchors. To work around this, we have two options:
39///
40/// - **Option 1**: Use delta positions and rotations. This requires preparing
41/// base anchors and other necessary positional data in world space,
42/// and computing the updated anchors during substeps.
43/// - **Option 2**: Use full positions and rotations. This requires storing
44/// anchors in world space for static bodies and sleeping bodies,
45/// and in local space for dynamic bodies.
46///
47/// Avian uses **Option 1**, because:
48///
49/// - Using delta positions reduces round-off error when bodies are far from the origin.
50/// - Mixing world space and local space values depending on the body type would be
51/// quite confusing and error-prone, and would possibly require more branching.
52///
53/// In addition to the delta position and rotation, we also store the linear and angular velocities
54/// and some bitflags. This all fits in 32 bytes in 2D or 56 bytes in 3D.
55///
56/// The 2D data layout has been designed to support fast conversion to and from
57/// wide SIMD types via scatter/gather operations in the future when SIMD optimizations
58/// are implemented.
59// TODO: Is there a better layout for 3D?
60#[derive(Clone, Debug, Default, Reflect)]
61#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
62#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
63#[reflect(Debug)]
64pub struct SolverBody {
65 /// The linear velocity of the body.
66 ///
67 /// 8 bytes in 2D and 12 bytes in 3D.
68 pub linear_velocity: Vector,
69 /// The angular velocity of the body.
70 ///
71 /// 4 bytes in 2D and 12 bytes in 3D.
72 #[cfg(feature = "2d")]
73 pub angular_velocity: f32,
74 /// The angular velocity of the body.
75 ///
76 /// 8 bytes in 2D and 12 bytes in 3D.
77 #[cfg(feature = "3d")]
78 pub angular_velocity: Vector,
79 /// The change in position of the body.
80 ///
81 /// Stored as a delta to avoid round-off error when far from the origin.
82 ///
83 /// 8 bytes in 2D and 12 bytes in 3D.
84 pub delta_position: Vector,
85 /// The change in rotation of the body.
86 ///
87 /// Stored as a delta because the rotation of static bodies cannot be accessed
88 /// in the solver, but they have a known delta rotation of zero.
89 ///
90 /// 8 bytes in 2D and 16 bytes in 3D.
91 pub delta_rotation: Rot,
92 /// Flags for the body.
93 ///
94 /// 4 bytes.
95 pub flags: SolverBodyFlags,
96}
97
98impl SolverBody {
99 /// A dummy [`SolverBody`] for static bodies.
100 pub const DUMMY: Self = Self {
101 linear_velocity: Vector::ZERO,
102 #[cfg(feature = "2d")]
103 angular_velocity: 0.0,
104 #[cfg(feature = "3d")]
105 angular_velocity: Vector::ZERO,
106 delta_position: Vector::ZERO,
107 delta_rotation: Rot::IDENTITY,
108 flags: SolverBodyFlags::empty(),
109 };
110
111 /// Computes the velocity at the given `point` relative to the center of the body.
112 pub fn velocity_at_point(&self, point: Vector) -> Vector {
113 #[cfg(feature = "2d")]
114 {
115 self.linear_velocity + self.angular_velocity * point.perp()
116 }
117 #[cfg(feature = "3d")]
118 {
119 self.linear_velocity + self.angular_velocity.cross(point)
120 }
121 }
122
123 /// Returns `true` if gyroscopic motion is enabled for this body.
124 pub fn is_gyroscopic(&self) -> bool {
125 self.flags.contains(SolverBodyFlags::GYROSCOPIC_MOTION)
126 }
127}
128
129/// Flags for [`SolverBody`].
130#[repr(transparent)]
131#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
132#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
133#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
134#[reflect(Debug, PartialEq)]
135pub struct SolverBodyFlags(u32);
136
137bitflags::bitflags! {
138 impl SolverBodyFlags: u32 {
139 /// Set if translation along the `X` axis is locked.
140 const TRANSLATION_X_LOCKED = 0b100_000;
141 /// Set if translation along the `Y` axis is locked.
142 const TRANSLATION_Y_LOCKED = 0b010_000;
143 /// Set if translation along the `Z` axis is locked.
144 const TRANSLATION_Z_LOCKED = 0b001_000;
145 /// Set if rotation around the `X` axis is locked.
146 const ROTATION_X_LOCKED = 0b000_100;
147 /// Set if rotation around the `Y` axis is locked.
148 const ROTATION_Y_LOCKED = 0b000_010;
149 /// Set if rotation around the `Z` axis is locked.
150 const ROTATION_Z_LOCKED = 0b000_001;
151 /// Set if all translational axes are locked.
152 const TRANSLATION_LOCKED = Self::TRANSLATION_X_LOCKED.bits() | Self::TRANSLATION_Y_LOCKED.bits() | Self::TRANSLATION_Z_LOCKED.bits();
153 /// Set if all rotational axes are locked.
154 const ROTATION_LOCKED = Self::ROTATION_X_LOCKED.bits() | Self::ROTATION_Y_LOCKED.bits() | Self::ROTATION_Z_LOCKED.bits();
155 /// Set if all translational and rotational axes are locked.
156 const ALL_LOCKED = Self::TRANSLATION_LOCKED.bits() | Self::ROTATION_LOCKED.bits();
157 /// Set if the body is kinematic. Otherwise, it is dynamic.
158 const IS_KINEMATIC = 1 << 6;
159 /// Set if gyroscopic motion is enabled.
160 const GYROSCOPIC_MOTION = 1 << 7;
161 /// Set if the body has a custom position integration implementation.
162 const CUSTOM_POSITION_INTEGRATION = 1 << 8;
163 /// Set during the continuous collision stage if the body moved fast enough
164 /// to be treated as a "fast body" and have its motion swept. Transient.
165 const IS_FAST = 1 << 9;
166 /// Set during the continuous collision stage if the body's motion was stopped
167 /// at a time of impact. Transient.
168 const HAD_TIME_OF_IMPACT = 1 << 10;
169 }
170}
171
172impl SolverBodyFlags {
173 /// Returns the [`LockedAxes`] of the body.
174 pub fn locked_axes(&self) -> LockedAxes {
175 LockedAxes::from_bits(self.0 as u8)
176 }
177
178 /// Returns `true` if the body is dynamic.
179 pub fn is_dynamic(&self) -> bool {
180 !self.contains(SolverBodyFlags::IS_KINEMATIC)
181 }
182
183 /// Returns `true` if the body is kinematic.
184 pub fn is_kinematic(&self) -> bool {
185 self.contains(SolverBodyFlags::IS_KINEMATIC)
186 }
187}
188
189/*
190Box2D v3 stores mass and angular inertia in constraint data.
191For 2D, this is just 2 floats or 8 bytes for each body in each constraint.
192
193However, we also support 3D and locking translational axes, so our worst case
194would be *9* floats for each body, 3 for the effective mass vector
195and 6 for the symmetric 3x3 inertia tensor. Storing 36 bytes
196for each body in each constraint would be quite wasteful.
197
198Instead, we store a separate `SolverBodyInertia` struct for each `SolverBody`.
199The struct is optimized for memory locality and size.
200
201In 2D, we store the effective inertial properties directly:
202
203- Effective inverse mass (8 bytes)
204- Effective inverse angular inertia (4 bytes)
205- Flags (4 bytes)
206
207for a total of 16 bytes.
208
209In 3D, we instead compute the effective versions on the fly:
210
211- Inverse mass (4 bytes)
212- Inverse angular inertia (36 bytes, matrix with 9 floats)
213- Flags (4 bytes)
214
215for a total of 44 bytes. This will be 32 bytes in the future
216if/when we switch to a symmetric 3x3 matrix representation.
217
218The API abstracts over this difference in representation to reduce complexity.
219*/
220
221/// The inertial properties of a [`SolverBody`].
222///
223/// This includes the effective inverse mass and angular inertia,
224/// and flags indicating whether the body is static or has locked axes.
225///
226/// 16 bytes in 2D and 32 bytes in 3D.
227#[derive(Clone, Debug, Reflect)]
228#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
230#[reflect(Debug)]
231pub struct SolverBodyInertia {
232 /// The effective inverse mass of the body,
233 /// taking into account any locked axes.
234 ///
235 /// 8 bytes.
236 #[cfg(feature = "2d")]
237 effective_inv_mass: Vector,
238
239 /// The inverse mass of the body.
240 ///
241 /// 4 bytes.
242 #[cfg(feature = "3d")]
243 inv_mass: f32,
244
245 /// The effective inverse angular inertia of the body,
246 /// taking into account any locked axes.
247 ///
248 /// 4 bytes.
249 #[cfg(feature = "2d")]
250 effective_inv_angular_inertia: SymmetricTensor,
251
252 /// The world-space inverse angular inertia of the body.
253 ///
254 /// 32 bytes.
255 #[cfg(feature = "3d")]
256 effective_inv_angular_inertia: SymmetricTensor,
257
258 /// The [dominance] of the body.
259 ///
260 /// If the [`Dominance`] component is not specified, the default of `0` is returned for dynamic bodies.
261 /// For static and kinematic bodies, `i8::MAX + 1` (`128`) is always returned instead.
262 ///
263 /// 2 bytes.
264 ///
265 /// [dominance]: crate::dynamics::rigid_body::Dominance
266 /// [`Dominance`]: crate::dynamics::rigid_body::Dominance
267 dominance: i16,
268
269 /// Flags indicating the inertial properties of the body,
270 /// like locked axes and whether the body is static.
271 ///
272 /// 2 bytes.
273 flags: InertiaFlags,
274}
275
276impl SolverBodyInertia {
277 /// A dummy [`SolverBodyInertia`] for static bodies.
278 pub const DUMMY: Self = Self {
279 #[cfg(feature = "2d")]
280 effective_inv_mass: Vector::ZERO,
281 #[cfg(feature = "3d")]
282 inv_mass: 0.0,
283 #[cfg(feature = "2d")]
284 effective_inv_angular_inertia: 0.0,
285 #[cfg(feature = "3d")]
286 effective_inv_angular_inertia: SymmetricTensor::ZERO,
287 dominance: i8::MAX as i16 + 1,
288 flags: InertiaFlags::STATIC,
289 };
290}
291
292impl Default for SolverBodyInertia {
293 fn default() -> Self {
294 Self::DUMMY
295 }
296}
297
298/// Flags indicating the inertial properties of a body.
299#[repr(transparent)]
300#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Reflect)]
301#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
302#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
303#[reflect(Debug, PartialEq)]
304pub struct InertiaFlags(u16);
305
306bitflags::bitflags! {
307 impl InertiaFlags: u16 {
308 /// Set if translation along the `X` axis is locked.
309 const TRANSLATION_X_LOCKED = 0b100_000;
310 /// Set if translation along the `Y` axis is locked.
311 const TRANSLATION_Y_LOCKED = 0b010_000;
312 /// Set if translation along the `Z` axis is locked.
313 const TRANSLATION_Z_LOCKED = 0b001_000;
314 /// Set if rotation around the `X` axis is locked.
315 const ROTATION_X_LOCKED = 0b000_100;
316 /// Set if rotation around the `Y` axis is locked.
317 const ROTATION_Y_LOCKED = 0b000_010;
318 /// Set if rotation around the `Z` axis is locked.
319 const ROTATION_Z_LOCKED = 0b000_001;
320 /// Set if all translational axes are locked.
321 const TRANSLATION_LOCKED = Self::TRANSLATION_X_LOCKED.bits() | Self::TRANSLATION_Y_LOCKED.bits() | Self::TRANSLATION_Z_LOCKED.bits();
322 /// Set if all rotational axes are locked.
323 const ROTATION_LOCKED = Self::ROTATION_X_LOCKED.bits() | Self::ROTATION_Y_LOCKED.bits() | Self::ROTATION_Z_LOCKED.bits();
324 /// Set if all translational and rotational axes are locked.
325 const ALL_LOCKED = Self::TRANSLATION_LOCKED.bits() | Self::ROTATION_LOCKED.bits();
326 /// Set if the body has infinite mass.
327 const INFINITE_MASS = 1 << 6;
328 /// Set if the body has infinite inertia.
329 const INFINITE_ANGULAR_INERTIA = 1 << 7;
330 /// Set if the body is static.
331 const STATIC = Self::INFINITE_MASS.bits() | Self::INFINITE_ANGULAR_INERTIA.bits();
332 }
333}
334
335impl InertiaFlags {
336 /// Returns the [`LockedAxes`] of the body.
337 pub fn locked_axes(&self) -> LockedAxes {
338 LockedAxes::from_bits(self.0 as u8)
339 }
340}
341
342impl SolverBodyInertia {
343 /// Creates a new [`SolverBodyInertia`] with the given mass, angular inertia,
344 /// and locked axes.
345 #[inline]
346 #[cfg(feature = "2d")]
347 pub fn new(
348 inv_mass: f32,
349 inv_inertia: SymmetricTensor,
350 locked_axes: LockedAxes,
351 dominance: i8,
352 is_dynamic: bool,
353 ) -> Self {
354 let mut effective_inv_mass = Vector::splat(inv_mass);
355 let mut effective_inv_angular_inertia = inv_inertia;
356 let mut flags = InertiaFlags(locked_axes.to_bits() as u16);
357
358 if inv_mass == 0.0 {
359 flags |= InertiaFlags::INFINITE_MASS;
360 }
361 if inv_inertia == 0.0 {
362 flags |= InertiaFlags::INFINITE_ANGULAR_INERTIA;
363 }
364
365 if locked_axes.is_translation_x_locked() {
366 effective_inv_mass.x = 0.0;
367 }
368 if locked_axes.is_translation_y_locked() {
369 effective_inv_mass.y = 0.0;
370 }
371 if locked_axes.is_rotation_locked() {
372 effective_inv_angular_inertia = 0.0;
373 }
374
375 Self {
376 effective_inv_mass,
377 effective_inv_angular_inertia,
378 dominance: if is_dynamic {
379 dominance as i16
380 } else {
381 i8::MAX as i16 + 1
382 },
383 flags: InertiaFlags(flags.0),
384 }
385 }
386
387 /// Creates a new [`SolverBodyInertia`] with the given mass, angular inertia,
388 /// and locked axes.
389 #[inline]
390 #[cfg(feature = "3d")]
391 pub fn new(
392 inv_mass: f32,
393 inv_inertia: SymmetricTensor,
394 locked_axes: LockedAxes,
395 dominance: i8,
396 is_dynamic: bool,
397 ) -> Self {
398 let mut effective_inv_angular_inertia = inv_inertia;
399 let mut flags = InertiaFlags(locked_axes.to_bits() as u16);
400
401 if inv_mass == 0.0 {
402 flags |= InertiaFlags::INFINITE_MASS;
403 }
404 if inv_inertia == SymmetricTensor::ZERO {
405 flags |= InertiaFlags::INFINITE_ANGULAR_INERTIA;
406 }
407
408 if locked_axes.is_rotation_x_locked() {
409 effective_inv_angular_inertia.m00 = 0.0;
410 effective_inv_angular_inertia.m01 = 0.0;
411 effective_inv_angular_inertia.m02 = 0.0;
412 }
413
414 if locked_axes.is_rotation_y_locked() {
415 effective_inv_angular_inertia.m01 = 0.0;
416 effective_inv_angular_inertia.m11 = 0.0;
417 effective_inv_angular_inertia.m12 = 0.0;
418 }
419
420 if locked_axes.is_rotation_z_locked() {
421 effective_inv_angular_inertia.m02 = 0.0;
422 effective_inv_angular_inertia.m12 = 0.0;
423 effective_inv_angular_inertia.m22 = 0.0;
424 }
425
426 Self {
427 inv_mass,
428 effective_inv_angular_inertia,
429 dominance: if is_dynamic {
430 dominance as i16
431 } else {
432 i8::MAX as i16 + 1
433 },
434 flags: InertiaFlags(flags.0),
435 }
436 }
437
438 /// Returns the effective inverse mass of the body,
439 /// taking into account any locked axes.
440 #[inline]
441 #[cfg(feature = "2d")]
442 pub fn effective_inv_mass(&self) -> Vector {
443 self.effective_inv_mass
444 }
445
446 /// Returns the effective inverse mass of the body,
447 /// taking into account any locked axes.
448 #[inline]
449 #[cfg(feature = "3d")]
450 pub fn effective_inv_mass(&self) -> Vector {
451 let mut inv_mass = Vector::splat(self.inv_mass);
452
453 if self.flags.contains(InertiaFlags::TRANSLATION_X_LOCKED) {
454 inv_mass.x = 0.0;
455 }
456 if self.flags.contains(InertiaFlags::TRANSLATION_Y_LOCKED) {
457 inv_mass.y = 0.0;
458 }
459 if self.flags.contains(InertiaFlags::TRANSLATION_Z_LOCKED) {
460 inv_mass.z = 0.0;
461 }
462
463 inv_mass
464 }
465
466 /// Returns the effective inverse angular inertia of the body,
467 /// taking into account any locked axes.
468 #[inline]
469 #[cfg(feature = "2d")]
470 pub fn effective_inv_angular_inertia(&self) -> SymmetricTensor {
471 self.effective_inv_angular_inertia
472 }
473
474 /// Returns the effective inverse angular inertia of the body in world space,
475 /// taking into account any locked axes.
476 #[inline]
477 #[cfg(feature = "3d")]
478 pub fn effective_inv_angular_inertia(&self) -> SymmetricTensor {
479 self.effective_inv_angular_inertia
480 }
481
482 /// Updates the effective inverse angular inertia of the body in world space,
483 /// taking into account any locked axes.
484 #[inline]
485 #[cfg(feature = "3d")]
486 pub fn update_effective_inv_angular_inertia(
487 &mut self,
488 computed_angular_inertia: &ComputedAngularInertia,
489 rotation: Quat,
490 ) {
491 let locked_axes = self.flags.locked_axes();
492 let mut effective_inv_angular_inertia =
493 computed_angular_inertia.rotated(rotation).inverse();
494
495 if locked_axes.is_rotation_x_locked() {
496 effective_inv_angular_inertia.m00 = 0.0;
497 effective_inv_angular_inertia.m01 = 0.0;
498 effective_inv_angular_inertia.m02 = 0.0;
499 }
500
501 if locked_axes.is_rotation_y_locked() {
502 effective_inv_angular_inertia.m11 = 0.0;
503 effective_inv_angular_inertia.m01 = 0.0;
504 effective_inv_angular_inertia.m12 = 0.0;
505 }
506
507 if locked_axes.is_rotation_z_locked() {
508 effective_inv_angular_inertia.m22 = 0.0;
509 effective_inv_angular_inertia.m02 = 0.0;
510 effective_inv_angular_inertia.m12 = 0.0;
511 }
512
513 self.effective_inv_angular_inertia = effective_inv_angular_inertia;
514 }
515
516 /// Returns the [dominance] of the body.
517 ///
518 /// If the [`Dominance`] component is not specified, the default of `0` is returned for dynamic bodies.
519 /// For static and kinematic bodies, `i8::MAX + 1` (`128`) is always returned instead.
520 ///
521 /// [dominance]: crate::dynamics::rigid_body::Dominance
522 /// [`Dominance`]: crate::dynamics::rigid_body::Dominance
523 #[inline]
524 pub fn dominance(&self) -> i16 {
525 self.dominance
526 }
527
528 /// Returns the [`InertiaFlags`] of the body.
529 #[inline]
530 pub fn flags(&self) -> InertiaFlags {
531 self.flags
532 }
533}
534
535/// A component storing the index of an entity's [`SolverBody`] in the [`SolverBodies`] resource.
536///
537/// Only awake dynamic and kinematic bodies have an associated solver body.
538/// Static bodies and sleeping dynamic bodies do not have a solver body, and instead
539/// use a "dummy state" with [`SolverBody::default()`] and [`SolverBodyInertia::default()`].
540///
541/// This component is added, removed, and updated automatically by the solver plugin,
542/// and should not be modified by users.
543#[derive(Component, Clone, Copy, Debug, Deref, PartialEq, Eq, PartialOrd, Ord, Hash, Reflect)]
544#[reflect(Component, Debug, PartialEq, Hash)]
545#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
546#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
547pub struct SolverBodyIndex(pub u32);
548
549impl SolverBodyIndex {
550 /// An invalid index used to indicate that a body has no associated [`SolverBody`],
551 /// such as a static body. The solver substitutes a dummy state in this case.
552 pub const INVALID: Self = Self(u32::MAX);
553
554 /// Returns `true` if the index refers to a valid [`SolverBody`] in the [`SolverBodies`] resource.
555 #[inline]
556 pub fn is_valid(&self) -> bool {
557 self.0 != u32::MAX
558 }
559}
560
561impl Default for SolverBodyIndex {
562 fn default() -> Self {
563 Self::INVALID
564 }
565}
566
567/// A resource storing [solver bodies](SolverBody) and their [inertial properties](SolverBodyInertia)
568/// contiguously for awake, active rigid bodies.
569///
570/// Each entity with a solver body stores an index into this resource in a [`SolverBodyIndex`] component.
571#[derive(Resource, Default)]
572pub struct SolverBodies {
573 // TODO: Use `UniqueEntityVec`.
574 entities: Vec<Entity>,
575 bodies: Vec<SolverBody>,
576 inertias: Vec<SolverBodyInertia>,
577}
578
579impl SolverBodies {
580 /// Creates a new empty collection of solver bodies.
581 #[inline]
582 pub const fn new() -> Self {
583 Self {
584 entities: Vec::new(),
585 bodies: Vec::new(),
586 inertias: Vec::new(),
587 }
588 }
589
590 /// Returns the number of solver bodies.
591 #[inline]
592 pub fn len(&self) -> usize {
593 self.bodies.len()
594 }
595
596 /// Returns `true` if there are no solver bodies.
597 #[inline]
598 pub fn is_empty(&self) -> bool {
599 self.bodies.is_empty()
600 }
601
602 /// Returns a slice of the entities that have solver bodies.
603 #[inline]
604 pub fn entities(&self) -> &[Entity] {
605 &self.entities
606 }
607
608 /// Returns a slice of the solver bodies.
609 #[inline]
610 pub fn bodies(&self) -> &[SolverBody] {
611 &self.bodies
612 }
613
614 /// Returns a mutable slice of the solver bodies.
615 #[inline]
616 pub fn bodies_mut(&mut self) -> &mut [SolverBody] {
617 &mut self.bodies
618 }
619
620 /// Returns a slice of the solver body inertias.
621 #[inline]
622 pub fn inertias(&self) -> &[SolverBodyInertia] {
623 &self.inertias
624 }
625
626 /// Returns a mutable slice of the solver body inertias.
627 #[inline]
628 pub fn inertias_mut(&mut self) -> &mut [SolverBodyInertia] {
629 &mut self.inertias
630 }
631
632 /// Returns `true` if the given [`SolverBodyIndex`] refers to a body in this collection.
633 #[inline]
634 pub fn contains_index(&self, index: SolverBodyIndex) -> bool {
635 (index.0 as usize) < self.bodies.len()
636 }
637
638 /// Returns `true` if the given entity has a solver body in this collection.
639 #[inline]
640 pub fn contains_entity(&self, entity: Entity) -> bool {
641 self.entities.contains(&entity)
642 }
643
644 /// Returns the [`Entity`] associated with the given solver body index, if any.
645 #[inline]
646 pub fn get_entity(&self, index: SolverBodyIndex) -> Option<Entity> {
647 self.entities.get(index.0 as usize).copied()
648 }
649
650 /// Returns a reference to the [`SolverBody`] with the given index, if it exists.
651 #[inline]
652 pub fn get(&self, index: SolverBodyIndex) -> Option<&SolverBody> {
653 self.bodies.get(index.0 as usize)
654 }
655
656 /// Returns a mutable reference to the [`SolverBody`] with the given index, if it exists.
657 #[inline]
658 pub fn get_mut(&mut self, index: SolverBodyIndex) -> Option<&mut SolverBody> {
659 self.bodies.get_mut(index.0 as usize)
660 }
661
662 /// Returns a reference to the [`SolverBodyInertia`] with the given index, if it exists.
663 #[inline]
664 pub fn get_inertia(&self, index: SolverBodyIndex) -> Option<&SolverBodyInertia> {
665 self.inertias.get(index.0 as usize)
666 }
667
668 /// Returns a mutable reference to the [`SolverBodyInertia`] with the given index, if it exists.
669 #[inline]
670 pub fn get_inertia_mut(&mut self, index: SolverBodyIndex) -> Option<&mut SolverBodyInertia> {
671 self.inertias.get_mut(index.0 as usize)
672 }
673
674 /// Returns an iterator over mutable references to the solver bodies.
675 #[inline]
676 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SolverBody> {
677 self.bodies.iter_mut()
678 }
679
680 /// Adds a new solver body for the given entity, returning its [`SolverBodyIndex`].
681 #[inline]
682 pub fn push(
683 &mut self,
684 entity: Entity,
685 body: SolverBody,
686 inertia: SolverBodyInertia,
687 ) -> SolverBodyIndex {
688 let index = SolverBodyIndex(self.bodies.len() as u32);
689 self.entities.push(entity);
690 self.bodies.push(body);
691 self.inertias.push(inertia);
692 index
693 }
694
695 /// Removes the solver body with the given index, swapping in the last body to fill the gap.
696 ///
697 /// Returns the entity whose body was moved into `index`, so that its [`SolverBodyIndex`]
698 /// component can be updated. Returns `None` if the removed body was the last one.
699 ///
700 /// # Panics
701 ///
702 /// Panics if the index is out of bounds.
703 #[inline]
704 pub fn swap_remove(&mut self, index: SolverBodyIndex) -> Option<Entity> {
705 self.entities.swap_remove(index.0 as usize);
706 self.bodies.swap_remove(index.0 as usize);
707 self.inertias.swap_remove(index.0 as usize);
708 // TODO: Do this differently
709 // If the removed body was not the last one, a body was swapped into its slot.
710 self.entities.get(index.0 as usize).copied()
711 }
712
713 /// Clears all solver bodies.
714 #[inline]
715 pub fn clear(&mut self) {
716 self.entities.clear();
717 self.bodies.clear();
718 self.inertias.clear();
719 }
720
721 /// Returns a [`SolverBodiesAccess`] that allows obtaining mutable references to solver bodies
722 /// from parallel closures via raw pointers.
723 ///
724 /// This allows the solver to hand out mutable references to disjoint bodies concurrently,
725 /// which is sound as long as the caller ensures that no two closures access the same body
726 /// at the same time. This is guaranteed by constraint graph coloring.
727 #[inline]
728 pub fn access(&mut self) -> SolverBodiesAccess<'_> {
729 SolverBodiesAccess {
730 bodies: self.bodies.as_mut_ptr(),
731 inertias: self.inertias.as_mut_ptr(),
732 len: self.bodies.len(),
733 _marker: PhantomData,
734 }
735 }
736}
737
738/// Mutable disjoint access to the bodies and inertias of a [`SolverBodies`] resource
739/// for use inside the parallel solver.
740///
741/// The caller is responsible for ensuring that no two closures access the same body
742/// at the same time. This is guaranteed by constraint graph coloring and each
743/// [`SolverBodyIndex`] being unique to a single body.
744pub struct SolverBodiesAccess<'a> {
745 bodies: *mut SolverBody,
746 inertias: *mut SolverBodyInertia,
747 len: usize,
748 _marker: PhantomData<&'a mut SolverBodies>,
749}
750
751// SAFETY: The caller is responsible for only accessing disjoint indices concurrently.
752unsafe impl Send for SolverBodiesAccess<'_> {}
753// SAFETY: The caller is responsible for only accessing disjoint indices concurrently.
754unsafe impl Sync for SolverBodiesAccess<'_> {}
755
756impl SolverBodiesAccess<'_> {
757 /// Returns the number of solver bodies.
758 #[inline]
759 pub fn len(&self) -> usize {
760 self.len
761 }
762
763 /// Returns `true` if there are no solver bodies.
764 #[inline]
765 pub fn is_empty(&self) -> bool {
766 self.len == 0
767 }
768
769 /// Returns a mutable reference to the [`SolverBody`] with the given index.
770 ///
771 /// # Safety
772 ///
773 /// The index must be in bounds, and no other reference to the same body may be held concurrently.
774 #[inline]
775 #[expect(clippy::mut_from_ref)]
776 pub unsafe fn body_unchecked_mut(&self, index: SolverBodyIndex) -> &mut SolverBody {
777 debug_assert!((index.0 as usize) < self.len);
778 unsafe { &mut *self.bodies.add(index.0 as usize) }
779 }
780
781 /// Returns a mutable reference to the [`SolverBodyInertia`] with the given index.
782 ///
783 /// # Safety
784 ///
785 /// The index must be in bounds, and no other reference to the same inertia may be held concurrently.
786 #[inline]
787 #[expect(clippy::mut_from_ref)]
788 pub unsafe fn inertia_unchecked_mut(&self, index: SolverBodyIndex) -> &mut SolverBodyInertia {
789 debug_assert!((index.0 as usize) < self.len);
790 unsafe { &mut *self.inertias.add(index.0 as usize) }
791 }
792
793 /// Returns mutable body references and inertia references for the two given indices.
794 ///
795 /// An [`SolverBodyIndex::INVALID`] index yields `None` for that body, in which case the caller
796 /// should substitute [`SolverBody::DUMMY`] and [`SolverBodyInertia::DUMMY`].
797 ///
798 /// # Safety
799 ///
800 /// If both indices are valid, they must be different. Valid indices must be in bounds,
801 /// and no other references to the same bodies may be held concurrently.
802 #[inline]
803 #[expect(clippy::mut_from_ref)]
804 pub unsafe fn get_pair_unchecked_mut(
805 &self,
806 a: SolverBodyIndex,
807 b: SolverBodyIndex,
808 ) -> (
809 Option<(&mut SolverBody, &SolverBodyInertia)>,
810 Option<(&mut SolverBody, &SolverBodyInertia)>,
811 ) {
812 debug_assert!(!a.is_valid() || !b.is_valid() || a != b);
813 unsafe {
814 let first = a.is_valid().then(|| {
815 (
816 &mut *self.bodies.add(a.0 as usize),
817 &*self.inertias.add(a.0 as usize),
818 )
819 });
820 let second = b.is_valid().then(|| {
821 (
822 &mut *self.bodies.add(b.0 as usize),
823 &*self.inertias.add(b.0 as usize),
824 )
825 });
826 (first, second)
827 }
828 }
829}