Skip to main content

rapier2d/dynamics/solver/
solver_body.rs

1use crate::dynamics::RigidBody;
2use crate::math::{Real, SPATIAL_DIM, Vector};
3use crate::utils::{RotationOps, ScalarType};
4use na::{DVectorView, DVectorViewMut};
5use parry::math::{Pose, Rotation, SIMD_WIDTH, SimdReal};
6use std::ops::{AddAssign, Sub, SubAssign};
7
8#[cfg(feature = "simd-is-enabled")]
9use crate::utils::transmute_to_wide;
10
11#[cfg(feature = "simd-is-enabled")]
12macro_rules! aos(
13    ($data_repr: ident [ $idx: ident ] . $data_n: ident, $fallback: ident) => {
14        [
15            if ($idx[0] as usize) < $data_repr.len() {
16                $data_repr[$idx[0] as usize].$data_n.0
17            } else {
18                $fallback.$data_n.0
19            },
20            if ($idx[1] as usize) < $data_repr.len() {
21                $data_repr[$idx[1] as usize].$data_n.0
22            } else {
23                $fallback.$data_n.0
24            },
25            if ($idx[2] as usize) < $data_repr.len() {
26                $data_repr[$idx[2] as usize].$data_n.0
27            } else {
28                $fallback.$data_n.0
29            },
30            if ($idx[3] as usize) < $data_repr.len() {
31                $data_repr[$idx[3] as usize].$data_n.0
32            } else {
33                $fallback.$data_n.0
34            },
35        ]
36    }
37);
38
39#[cfg(feature = "simd-is-enabled")]
40macro_rules! aos_unchecked(
41    ($data_repr: ident [ $idx: ident ] . $data_n: ident) => {
42        [
43            unsafe { $data_repr.get_unchecked($idx[0] as usize).$data_n.0 },
44            unsafe { $data_repr.get_unchecked($idx[1] as usize).$data_n.0 },
45            unsafe { $data_repr.get_unchecked($idx[2] as usize).$data_n.0 },
46            unsafe { $data_repr.get_unchecked($idx[3] as usize).$data_n.0 },
47        ]
48    }
49);
50
51#[cfg(feature = "simd-is-enabled")]
52macro_rules! scatter(
53    ($data: ident [ $idx: ident [ $i: expr ] ] = [$($aos: ident),*]) => {
54       unsafe {
55            #[allow(clippy::missing_transmute_annotations)] // Different macro calls transmute to different types
56            if ($idx[$i] as usize) < $data.len() {
57                $data[$idx[$i] as usize] = std::mem::transmute([$($aos[$i]),*]);
58            }
59        }
60    }
61);
62
63#[cfg(feature = "simd-is-enabled")]
64macro_rules! scatter_unchecked(
65    ($data: ident [ $idx: ident [ $i: expr ] ] = [$($aos: ident),*]) => {
66       #[allow(clippy::missing_transmute_annotations)] // Different macro calls transmute to different types
67       unsafe {
68           *$data.get_unchecked_mut($idx[$i] as usize) = std::mem::transmute([$($aos[$i]),*]);
69       }
70    }
71);
72
73#[derive(Default)]
74pub struct SolverBodies {
75    pub vels: Vec<SolverVel<Real>>,
76    pub poses: Vec<SolverPose<Real>>,
77}
78
79impl SolverBodies {
80    pub fn clear(&mut self) {
81        self.vels.clear();
82        self.poses.clear();
83    }
84
85    pub fn resize(&mut self, sz: usize) {
86        self.vels.resize(sz, Default::default());
87        self.poses.resize(sz, Default::default());
88    }
89
90    pub fn len(&self) -> usize {
91        self.vels.len()
92    }
93
94    // TODO: add a SIMD version?
95    pub fn copy_from(&mut self, _dt: Real, i: usize, rb: &RigidBody) {
96        let poses = &mut self.poses[i];
97        let vels = &mut self.vels[i];
98
99        #[cfg(feature = "dim2")]
100        {
101            vels.angular = rb.vels.angvel;
102        }
103
104        #[cfg(feature = "dim3")]
105        {
106            if rb.forces.gyroscopic_forces_enabled {
107                vels.angular = rb.angvel_with_gyroscopic_forces(_dt);
108            } else {
109                vels.angular = rb.angvel();
110            }
111        }
112        vels.linear = rb.vels.linvel;
113
114        let pose = rb
115            .pos
116            .position
117            .prepend_translation(rb.mprops.local_mprops.local_com);
118        poses.rotation = pose.rotation;
119        poses.translation = pose.translation;
120
121        if rb.is_dynamic_or_kinematic() {
122            poses.ii = rb.mprops.effective_world_inv_inertia;
123            poses.im = rb.mprops.effective_inv_mass;
124        } else {
125            poses.ii = Default::default();
126            poses.im = Default::default();
127        }
128    }
129
130    #[inline]
131    pub unsafe fn gather_vels_unchecked(&self, idx: [u32; SIMD_WIDTH]) -> SolverVel<SimdReal> {
132        #[cfg(not(feature = "simd-is-enabled"))]
133        unsafe {
134            *self.vels.get_unchecked(idx[0] as usize)
135        }
136        #[cfg(feature = "simd-is-enabled")]
137        unsafe {
138            SolverVel::gather_unchecked(&self.vels, idx)
139        }
140    }
141
142    #[inline]
143    pub fn gather_vels(&self, idx: [u32; SIMD_WIDTH]) -> SolverVel<SimdReal> {
144        #[cfg(not(feature = "simd-is-enabled"))]
145        return self.vels.get(idx[0] as usize).copied().unwrap_or_default();
146        #[cfg(feature = "simd-is-enabled")]
147        return SolverVel::gather(&self.vels, idx);
148    }
149
150    #[inline]
151    pub fn get_vel(&self, i: u32) -> SolverVel<Real> {
152        self.vels.get(i as usize).copied().unwrap_or_default()
153    }
154
155    #[inline]
156    pub fn scatter_vels(&mut self, idx: [u32; SIMD_WIDTH], vels: SolverVel<SimdReal>) {
157        #[cfg(not(feature = "simd-is-enabled"))]
158        if (idx[0] as usize) < self.vels.len() {
159            self.vels[idx[0] as usize] = vels
160        }
161
162        #[cfg(feature = "simd-is-enabled")]
163        vels.scatter(&mut self.vels, idx);
164    }
165
166    #[inline]
167    pub fn set_vel(&mut self, i: u32, vel: SolverVel<Real>) {
168        if (i as usize) < self.vels.len() {
169            self.vels[i as usize] = vel;
170        }
171    }
172
173    #[inline]
174    pub fn get_pose(&self, i: u32) -> SolverPose<Real> {
175        self.poses.get(i as usize).copied().unwrap_or_default()
176    }
177
178    #[inline]
179    pub unsafe fn gather_poses_unchecked(&self, idx: [u32; SIMD_WIDTH]) -> SolverPose<SimdReal> {
180        #[cfg(not(feature = "simd-is-enabled"))]
181        unsafe {
182            *self.poses.get_unchecked(idx[0] as usize)
183        }
184
185        #[cfg(feature = "simd-is-enabled")]
186        unsafe {
187            SolverPose::gather_unchecked(&self.poses, idx)
188        }
189    }
190
191    #[inline]
192    pub fn gather_poses(&self, idx: [u32; SIMD_WIDTH]) -> SolverPose<SimdReal> {
193        #[cfg(not(feature = "simd-is-enabled"))]
194        return self.poses.get(idx[0] as usize).copied().unwrap_or_default();
195
196        #[cfg(feature = "simd-is-enabled")]
197        return SolverPose::gather(&self.poses, idx);
198    }
199
200    #[inline]
201    pub fn scatter_poses(&mut self, idx: [u32; SIMD_WIDTH], poses: SolverPose<SimdReal>) {
202        #[cfg(not(feature = "simd-is-enabled"))]
203        if (idx[0] as usize) < self.poses.len() {
204            self.poses[idx[0] as usize] = poses;
205        }
206
207        #[cfg(feature = "simd-is-enabled")]
208        poses.scatter(&mut self.poses, idx);
209    }
210
211    #[inline]
212    pub fn scatter_poses_unchecked(&mut self, idx: [u32; SIMD_WIDTH], poses: SolverPose<SimdReal>) {
213        #[cfg(not(feature = "simd-is-enabled"))]
214        unsafe {
215            *self.poses.get_unchecked_mut(idx[0] as usize) = poses
216        }
217
218        #[cfg(feature = "simd-is-enabled")]
219        poses.scatter_unchecked(&mut self.poses, idx);
220    }
221}
222
223// Total 7/13
224#[repr(C)]
225#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
226#[derive(Copy, Clone, Default)]
227pub struct SolverVel<T: ScalarType> {
228    pub linear: T::Vector,     // 2/3
229    pub angular: T::AngVector, // 1/3
230    // TODO: explicit padding are useful for static assertions.
231    //       But might be wasteful for the SolverVel<SimdReal>
232    //       specialization.
233    #[cfg(feature = "simd-is-enabled")]
234    #[cfg(feature = "dim2")]
235    padding: [T; 1],
236    #[cfg(feature = "simd-is-enabled")]
237    #[cfg(feature = "dim3")]
238    padding: [T; 2],
239}
240
241#[cfg(feature = "simd-is-enabled")]
242#[repr(C)]
243struct SolverVelRepr {
244    data0: SimdReal,
245    #[cfg(feature = "dim3")]
246    data1: SimdReal,
247}
248
249#[cfg(feature = "simd-is-enabled")]
250impl SolverVelRepr {
251    pub fn zero() -> Self {
252        Self {
253            data0: na::zero(),
254            #[cfg(feature = "dim3")]
255            data1: na::zero(),
256        }
257    }
258}
259
260#[cfg(feature = "simd-is-enabled")]
261impl SolverVel<SimdReal> {
262    #[inline]
263    pub unsafe fn gather_unchecked(data: &[SolverVel<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
264        // TODO: double-check that the compiler is using simd loads and
265        //       isn’t generating useless copies.
266
267        let data_repr: &[SolverVelRepr] = unsafe { std::mem::transmute(data) };
268
269        #[cfg(feature = "dim2")]
270        {
271            let aos = aos_unchecked!(data_repr[idx].data0);
272            let soa = wide::f32x4::transpose(transmute_to_wide(aos));
273            unsafe { std::mem::transmute(soa) }
274        }
275
276        #[cfg(feature = "dim3")]
277        {
278            let aos0 = aos_unchecked!(data_repr[idx].data0);
279            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
280            let aos1 = aos_unchecked!(data_repr[idx].data1);
281            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
282            unsafe { std::mem::transmute((soa0, soa1)) }
283        }
284    }
285
286    #[inline]
287    pub fn gather(data: &[SolverVel<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
288        // TODO: double-check that the compiler is using simd loads and
289        //       isn’t generating useless copies.
290
291        let zero = SolverVelRepr::zero();
292        let data_repr: &[SolverVelRepr] = unsafe { std::mem::transmute(data) };
293
294        #[cfg(feature = "dim2")]
295        {
296            let aos = aos!(data_repr[idx].data0, zero);
297            let soa = wide::f32x4::transpose(transmute_to_wide(aos));
298            unsafe { std::mem::transmute(soa) }
299        }
300
301        #[cfg(feature = "dim3")]
302        {
303            let aos0 = aos!(data_repr[idx].data0, zero);
304            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
305            let aos1 = aos!(data_repr[idx].data1, zero);
306            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
307            unsafe { std::mem::transmute((soa0, soa1)) }
308        }
309    }
310
311    #[inline]
312    #[cfg(feature = "dim2")]
313    pub fn scatter(self, data: &mut [SolverVel<Real>], idx: [u32; SIMD_WIDTH]) {
314        // TODO: double-check that the compiler is using simd loads and no useless copies.
315        let soa: [wide::f32x4; 4] = unsafe { std::mem::transmute(self) };
316        let aos = wide::f32x4::transpose(soa);
317        scatter!(data[idx[0]] = [aos]);
318        scatter!(data[idx[1]] = [aos]);
319        scatter!(data[idx[2]] = [aos]);
320        scatter!(data[idx[3]] = [aos]);
321    }
322
323    #[inline]
324    #[cfg(feature = "dim3")]
325    pub fn scatter(self, data: &mut [SolverVel<Real>], idx: [u32; SIMD_WIDTH]) {
326        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
327        // TODO: double-check that the compiler is using simd loads and no useless copies.
328        let aos0 = wide::f32x4::transpose(soa[0]);
329        let aos1 = wide::f32x4::transpose(soa[1]);
330        scatter!(data[idx[0]] = [aos0, aos1]);
331        scatter!(data[idx[1]] = [aos0, aos1]);
332        scatter!(data[idx[2]] = [aos0, aos1]);
333        scatter!(data[idx[3]] = [aos0, aos1]);
334    }
335}
336
337// Total: 7/16
338#[repr(C)]
339#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
340#[derive(Copy, Clone)]
341pub struct SolverPose<N: ScalarType> {
342    /// Positional change of the rigid-body’s center of mass.
343    pub rotation: N::Rotation, // 2/4
344    pub translation: N::Vector, // 2/3
345    pub ii: N::AngInertia,      // 1/6
346    pub im: N::Vector,          // 2/3
347    #[cfg(feature = "dim2")]
348    pub padding: [N; 1],
349}
350
351impl SolverPose<Real> {
352    pub fn pose(&self) -> Pose {
353        Pose::from_parts(self.translation, self.rotation)
354    }
355}
356
357#[cfg(feature = "simd-is-enabled")]
358impl SolverPose<SimdReal> {
359    pub fn pose(&self) -> <SimdReal as ScalarType>::Pose {
360        <SimdReal as ScalarType>::Pose::from_parts(self.translation.into(), self.rotation)
361    }
362}
363
364impl<N: ScalarType> SolverPose<N> {
365    #[inline]
366    pub fn transform_point(&self, pt: N::Vector) -> N::Vector {
367        self.rotation * pt + self.translation
368    }
369
370    #[inline]
371    pub fn inverse_transform_point(&self, pt: N::Vector) -> N::Vector {
372        self.rotation.inverse() * (pt - self.translation)
373    }
374}
375
376impl Default for SolverPose<Real> {
377    #[inline]
378    fn default() -> Self {
379        Self {
380            rotation: Rotation::IDENTITY,
381            translation: Vector::ZERO,
382            ii: Default::default(),
383            im: Default::default(),
384            #[cfg(feature = "dim2")]
385            padding: Default::default(),
386        }
387    }
388}
389
390#[cfg(feature = "simd-is-enabled")]
391#[repr(C)]
392struct SolverPoseRepr {
393    data0: SimdReal,
394    data1: SimdReal,
395    #[cfg(feature = "dim3")]
396    data2: SimdReal,
397    #[cfg(feature = "dim3")]
398    data3: SimdReal,
399}
400
401#[cfg(feature = "simd-is-enabled")]
402impl SolverPoseRepr {
403    pub fn identity() -> Self {
404        // TODO PERF: will the compiler handle this efficiently and generate
405        //            everything at compile-time?
406        unsafe { std::mem::transmute(SolverPose::default()) }
407    }
408}
409
410#[cfg(feature = "simd-is-enabled")]
411impl SolverPose<SimdReal> {
412    #[inline]
413    pub unsafe fn gather_unchecked(data: &[SolverPose<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
414        // TODO: double-check that the compiler is using simd loads and
415        //       isn’t generating useless copies.
416
417        let data_repr: &[SolverPoseRepr] = unsafe { std::mem::transmute(data) };
418
419        #[cfg(feature = "dim2")]
420        {
421            let aos0 = aos_unchecked!(data_repr[idx].data0);
422            let aos1 = aos_unchecked!(data_repr[idx].data1);
423            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
424            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
425            unsafe { std::mem::transmute([soa0, soa1]) }
426        }
427
428        #[cfg(feature = "dim3")]
429        {
430            let aos0 = aos_unchecked!(data_repr[idx].data0);
431            let aos1 = aos_unchecked!(data_repr[idx].data1);
432            let aos2 = aos_unchecked!(data_repr[idx].data2);
433            let aos3 = aos_unchecked!(data_repr[idx].data3);
434            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
435            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
436            let soa2 = wide::f32x4::transpose(transmute_to_wide(aos2));
437            let soa3 = wide::f32x4::transpose(transmute_to_wide(aos3));
438            unsafe { std::mem::transmute([soa0, soa1, soa2, soa3]) }
439        }
440    }
441
442    #[inline]
443    pub fn gather(data: &[SolverPose<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
444        // TODO: double-check that the compiler is using simd loads and
445        //       isn’t generating useless copies.
446
447        let identity = SolverPoseRepr::identity();
448        let data_repr: &[SolverPoseRepr] = unsafe { std::mem::transmute(data) };
449
450        #[cfg(feature = "dim2")]
451        {
452            let aos0 = aos!(data_repr[idx].data0, identity);
453            let aos1 = aos!(data_repr[idx].data1, identity);
454            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
455            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
456            unsafe { std::mem::transmute([soa0, soa1]) }
457        }
458
459        #[cfg(feature = "dim3")]
460        {
461            let aos0 = aos!(data_repr[idx].data0, identity);
462            let aos1 = aos!(data_repr[idx].data1, identity);
463            let aos2 = aos!(data_repr[idx].data2, identity);
464            let aos3 = aos!(data_repr[idx].data3, identity);
465            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
466            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
467            let soa2 = wide::f32x4::transpose(transmute_to_wide(aos2));
468            let soa3 = wide::f32x4::transpose(transmute_to_wide(aos3));
469            unsafe { std::mem::transmute([soa0, soa1, soa2, soa3]) }
470        }
471    }
472
473    #[inline]
474    #[cfg(feature = "dim2")]
475    pub fn scatter_unchecked(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
476        // TODO: double-check that the compiler is using simd loads and no useless copies.
477        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
478        let aos0 = wide::f32x4::transpose(soa[0]);
479        let aos1 = wide::f32x4::transpose(soa[1]);
480        scatter_unchecked!(data[idx[0]] = [aos0, aos1]);
481        scatter_unchecked!(data[idx[1]] = [aos0, aos1]);
482        scatter_unchecked!(data[idx[2]] = [aos0, aos1]);
483        scatter_unchecked!(data[idx[3]] = [aos0, aos1]);
484    }
485
486    #[inline]
487    #[cfg(feature = "dim3")]
488    pub fn scatter_unchecked(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
489        let soa: [[wide::f32x4; 4]; 4] = unsafe { std::mem::transmute(self) };
490        // TODO: double-check that the compiler is using simd loads and no useless copies.
491        let aos0 = wide::f32x4::transpose(soa[0]);
492        let aos1 = wide::f32x4::transpose(soa[1]);
493        let aos2 = wide::f32x4::transpose(soa[2]);
494        let aos3 = wide::f32x4::transpose(soa[3]);
495        scatter_unchecked!(data[idx[0]] = [aos0, aos1, aos2, aos3]);
496        scatter_unchecked!(data[idx[1]] = [aos0, aos1, aos2, aos3]);
497        scatter_unchecked!(data[idx[2]] = [aos0, aos1, aos2, aos3]);
498        scatter_unchecked!(data[idx[3]] = [aos0, aos1, aos2, aos3]);
499    }
500
501    #[inline]
502    #[cfg(feature = "dim2")]
503    pub fn scatter(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
504        // TODO: double-check that the compiler is using simd loads and no useless copies.
505        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
506        let aos0 = wide::f32x4::transpose(soa[0]);
507        let aos1 = wide::f32x4::transpose(soa[1]);
508        scatter!(data[idx[0]] = [aos0, aos1]);
509        scatter!(data[idx[1]] = [aos0, aos1]);
510        scatter!(data[idx[2]] = [aos0, aos1]);
511        scatter!(data[idx[3]] = [aos0, aos1]);
512    }
513
514    #[inline]
515    #[cfg(feature = "dim3")]
516    pub fn scatter(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
517        let soa: [[wide::f32x4; 4]; 4] = unsafe { std::mem::transmute(self) };
518        // TODO: double-check that the compiler is using simd loads and no useless copies.
519        let aos0 = wide::f32x4::transpose(soa[0]);
520        let aos1 = wide::f32x4::transpose(soa[1]);
521        let aos2 = wide::f32x4::transpose(soa[2]);
522        let aos3 = wide::f32x4::transpose(soa[3]);
523        scatter!(data[idx[0]] = [aos0, aos1, aos2, aos3]);
524        scatter!(data[idx[1]] = [aos0, aos1, aos2, aos3]);
525        scatter!(data[idx[2]] = [aos0, aos1, aos2, aos3]);
526        scatter!(data[idx[3]] = [aos0, aos1, aos2, aos3]);
527    }
528}
529
530impl<N: ScalarType> SolverVel<N> {
531    pub fn as_slice(&self) -> &[N; SPATIAL_DIM] {
532        unsafe { std::mem::transmute(self) }
533    }
534
535    pub fn as_mut_slice(&mut self) -> &mut [N; SPATIAL_DIM] {
536        unsafe { std::mem::transmute(self) }
537    }
538
539    pub fn as_vector_slice(&self) -> DVectorView<'_, N> {
540        DVectorView::from_slice(&self.as_slice()[..], SPATIAL_DIM)
541    }
542
543    pub fn as_vector_slice_mut(&mut self) -> DVectorViewMut<'_, N> {
544        DVectorViewMut::from_slice(&mut self.as_mut_slice()[..], SPATIAL_DIM)
545    }
546}
547
548impl<N: ScalarType> SolverVel<N> {
549    pub fn zero() -> Self {
550        Self {
551            linear: Default::default(),
552            angular: Default::default(),
553            #[cfg(feature = "simd-is-enabled")]
554            #[cfg(feature = "dim2")]
555            padding: [na::zero(); 1],
556            #[cfg(feature = "simd-is-enabled")]
557            #[cfg(feature = "dim3")]
558            padding: [na::zero(); 2],
559        }
560    }
561}
562
563impl<N: ScalarType> AddAssign for SolverVel<N> {
564    fn add_assign(&mut self, rhs: Self) {
565        self.linear += rhs.linear;
566        self.angular += rhs.angular;
567    }
568}
569
570impl<N: ScalarType> SubAssign for SolverVel<N> {
571    fn sub_assign(&mut self, rhs: Self) {
572        self.linear -= rhs.linear;
573        self.angular -= rhs.angular;
574    }
575}
576
577impl<N: ScalarType> Sub for SolverVel<N> {
578    type Output = Self;
579
580    fn sub(self, rhs: Self) -> Self {
581        SolverVel {
582            linear: self.linear - rhs.linear,
583            angular: self.angular - rhs.angular,
584            #[cfg(feature = "simd-is-enabled")]
585            padding: self.padding,
586        }
587    }
588}