rapier2d/dynamics/solver/
solver_body.rs

1use crate::dynamics::RigidBody;
2use crate::math::{AngularInertia, Isometry, Real, SPATIAL_DIM, Vector};
3use crate::utils::SimdRealCopy;
4use na::{DVectorView, DVectorViewMut};
5use parry::math::{AngVector, SIMD_WIDTH, SimdReal, Translation};
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        poses.pose = rb.pos.position * Translation::from(rb.mprops.local_mprops.local_com);
114
115        if rb.is_dynamic_or_kinematic() {
116            poses.ii = rb.mprops.effective_world_inv_inertia;
117            poses.im = rb.mprops.effective_inv_mass;
118        } else {
119            poses.ii = Default::default();
120            poses.im = Default::default();
121        }
122    }
123
124    #[inline]
125    pub unsafe fn gather_vels_unchecked(&self, idx: [u32; SIMD_WIDTH]) -> SolverVel<SimdReal> {
126        #[cfg(not(feature = "simd-is-enabled"))]
127        unsafe {
128            *self.vels.get_unchecked(idx[0] as usize)
129        }
130        #[cfg(feature = "simd-is-enabled")]
131        unsafe {
132            SolverVel::gather_unchecked(&self.vels, idx)
133        }
134    }
135
136    #[inline]
137    pub fn gather_vels(&self, idx: [u32; SIMD_WIDTH]) -> SolverVel<SimdReal> {
138        #[cfg(not(feature = "simd-is-enabled"))]
139        return self.vels.get(idx[0] as usize).copied().unwrap_or_default();
140        #[cfg(feature = "simd-is-enabled")]
141        return SolverVel::gather(&self.vels, idx);
142    }
143
144    #[inline]
145    pub fn get_vel(&self, i: u32) -> SolverVel<Real> {
146        self.vels.get(i as usize).copied().unwrap_or_default()
147    }
148
149    #[inline]
150    pub fn scatter_vels(&mut self, idx: [u32; SIMD_WIDTH], vels: SolverVel<SimdReal>) {
151        #[cfg(not(feature = "simd-is-enabled"))]
152        if (idx[0] as usize) < self.vels.len() {
153            self.vels[idx[0] as usize] = vels
154        }
155
156        #[cfg(feature = "simd-is-enabled")]
157        vels.scatter(&mut self.vels, idx);
158    }
159
160    #[inline]
161    pub fn set_vel(&mut self, i: u32, vel: SolverVel<Real>) {
162        if (i as usize) < self.vels.len() {
163            self.vels[i as usize] = vel;
164        }
165    }
166
167    #[inline]
168    pub fn get_pose(&self, i: u32) -> SolverPose<Real> {
169        self.poses.get(i as usize).copied().unwrap_or_default()
170    }
171
172    #[inline]
173    pub unsafe fn gather_poses_unchecked(&self, idx: [u32; SIMD_WIDTH]) -> SolverPose<SimdReal> {
174        #[cfg(not(feature = "simd-is-enabled"))]
175        unsafe {
176            *self.poses.get_unchecked(idx[0] as usize)
177        }
178
179        #[cfg(feature = "simd-is-enabled")]
180        unsafe {
181            SolverPose::gather_unchecked(&self.poses, idx)
182        }
183    }
184
185    #[inline]
186    pub fn gather_poses(&self, idx: [u32; SIMD_WIDTH]) -> SolverPose<SimdReal> {
187        #[cfg(not(feature = "simd-is-enabled"))]
188        return self.poses.get(idx[0] as usize).copied().unwrap_or_default();
189
190        #[cfg(feature = "simd-is-enabled")]
191        return SolverPose::gather(&self.poses, idx);
192    }
193
194    #[inline]
195    pub fn scatter_poses(&mut self, idx: [u32; SIMD_WIDTH], poses: SolverPose<SimdReal>) {
196        #[cfg(not(feature = "simd-is-enabled"))]
197        if (idx[0] as usize) < self.poses.len() {
198            self.poses[idx[0] as usize] = poses;
199        }
200
201        #[cfg(feature = "simd-is-enabled")]
202        poses.scatter(&mut self.poses, idx);
203    }
204
205    #[inline]
206    pub fn scatter_poses_unchecked(&mut self, idx: [u32; SIMD_WIDTH], poses: SolverPose<SimdReal>) {
207        #[cfg(not(feature = "simd-is-enabled"))]
208        unsafe {
209            *self.poses.get_unchecked_mut(idx[0] as usize) = poses
210        }
211
212        #[cfg(feature = "simd-is-enabled")]
213        poses.scatter_unchecked(&mut self.poses, idx);
214    }
215}
216
217// Total 7/13
218#[repr(C)]
219#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
220#[derive(Copy, Clone, Default)]
221pub struct SolverVel<T: SimdRealCopy> {
222    pub linear: Vector<T>,     // 2/3
223    pub angular: AngVector<T>, // 1/3
224    // TODO: explicit padding are useful for static assertions.
225    //       But might be wasteful for the SolverVel<SimdReal>
226    //       specialization.
227    #[cfg(feature = "simd-is-enabled")]
228    #[cfg(feature = "dim2")]
229    padding: [T; 1],
230    #[cfg(feature = "simd-is-enabled")]
231    #[cfg(feature = "dim3")]
232    padding: [T; 2],
233}
234
235#[cfg(feature = "simd-is-enabled")]
236#[repr(C)]
237struct SolverVelRepr {
238    data0: SimdReal,
239    #[cfg(feature = "dim3")]
240    data1: SimdReal,
241}
242
243#[cfg(feature = "simd-is-enabled")]
244impl SolverVelRepr {
245    pub fn zero() -> Self {
246        Self {
247            data0: na::zero(),
248            #[cfg(feature = "dim3")]
249            data1: na::zero(),
250        }
251    }
252}
253
254#[cfg(feature = "simd-is-enabled")]
255impl SolverVel<SimdReal> {
256    #[inline]
257    pub unsafe fn gather_unchecked(data: &[SolverVel<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
258        // TODO: double-check that the compiler is using simd loads and
259        //       isn’t generating useless copies.
260
261        let data_repr: &[SolverVelRepr] = unsafe { std::mem::transmute(data) };
262
263        #[cfg(feature = "dim2")]
264        {
265            let aos = aos_unchecked!(data_repr[idx].data0);
266            let soa = wide::f32x4::transpose(transmute_to_wide(aos));
267            unsafe { std::mem::transmute(soa) }
268        }
269
270        #[cfg(feature = "dim3")]
271        {
272            let aos0 = aos_unchecked!(data_repr[idx].data0);
273            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
274            let aos1 = aos_unchecked!(data_repr[idx].data1);
275            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
276            unsafe { std::mem::transmute((soa0, soa1)) }
277        }
278    }
279
280    #[inline]
281    pub fn gather(data: &[SolverVel<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
282        // TODO: double-check that the compiler is using simd loads and
283        //       isn’t generating useless copies.
284
285        let zero = SolverVelRepr::zero();
286        let data_repr: &[SolverVelRepr] = unsafe { std::mem::transmute(data) };
287
288        #[cfg(feature = "dim2")]
289        {
290            let aos = aos!(data_repr[idx].data0, zero);
291            let soa = wide::f32x4::transpose(transmute_to_wide(aos));
292            unsafe { std::mem::transmute(soa) }
293        }
294
295        #[cfg(feature = "dim3")]
296        {
297            let aos0 = aos!(data_repr[idx].data0, zero);
298            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
299            let aos1 = aos!(data_repr[idx].data1, zero);
300            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
301            unsafe { std::mem::transmute((soa0, soa1)) }
302        }
303    }
304
305    #[inline]
306    #[cfg(feature = "dim2")]
307    pub fn scatter(self, data: &mut [SolverVel<Real>], idx: [u32; SIMD_WIDTH]) {
308        // TODO: double-check that the compiler is using simd loads and no useless copies.
309        let soa: [wide::f32x4; 4] = unsafe { std::mem::transmute(self) };
310        let aos = wide::f32x4::transpose(soa);
311        scatter!(data[idx[0]] = [aos]);
312        scatter!(data[idx[1]] = [aos]);
313        scatter!(data[idx[2]] = [aos]);
314        scatter!(data[idx[3]] = [aos]);
315    }
316
317    #[inline]
318    #[cfg(feature = "dim3")]
319    pub fn scatter(self, data: &mut [SolverVel<Real>], idx: [u32; SIMD_WIDTH]) {
320        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
321        // TODO: double-check that the compiler is using simd loads and no useless copies.
322        let aos0 = wide::f32x4::transpose(soa[0]);
323        let aos1 = wide::f32x4::transpose(soa[1]);
324        scatter!(data[idx[0]] = [aos0, aos1]);
325        scatter!(data[idx[1]] = [aos0, aos1]);
326        scatter!(data[idx[2]] = [aos0, aos1]);
327        scatter!(data[idx[3]] = [aos0, aos1]);
328    }
329}
330
331// Total: 7/16
332#[repr(C)]
333#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
334#[derive(Copy, Clone)]
335pub struct SolverPose<T> {
336    /// Positional change of the rigid-body’s center of mass.
337    pub pose: Isometry<T>, // 4/7
338    pub ii: AngularInertia<T>, // 1/6
339    pub im: Vector<T>,         // 2/3
340    #[cfg(feature = "dim2")]
341    pub padding: [T; 1],
342}
343
344impl Default for SolverPose<Real> {
345    #[inline]
346    fn default() -> Self {
347        Self {
348            pose: Isometry::identity(),
349            ii: Default::default(),
350            im: Default::default(),
351            #[cfg(feature = "dim2")]
352            padding: Default::default(),
353        }
354    }
355}
356
357#[cfg(feature = "simd-is-enabled")]
358#[repr(C)]
359struct SolverPoseRepr {
360    data0: SimdReal,
361    data1: SimdReal,
362    #[cfg(feature = "dim3")]
363    data2: SimdReal,
364    #[cfg(feature = "dim3")]
365    data3: SimdReal,
366}
367
368#[cfg(feature = "simd-is-enabled")]
369impl SolverPoseRepr {
370    pub fn identity() -> Self {
371        // TODO PERF: will the compiler handle this efficiently and generate
372        //            everything at compile-time?
373        unsafe { std::mem::transmute(SolverPose::default()) }
374    }
375}
376
377#[cfg(feature = "simd-is-enabled")]
378impl SolverPose<SimdReal> {
379    #[inline]
380    pub unsafe fn gather_unchecked(data: &[SolverPose<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
381        // TODO: double-check that the compiler is using simd loads and
382        //       isn’t generating useless copies.
383
384        let data_repr: &[SolverPoseRepr] = unsafe { std::mem::transmute(data) };
385
386        #[cfg(feature = "dim2")]
387        {
388            let aos0 = aos_unchecked!(data_repr[idx].data0);
389            let aos1 = aos_unchecked!(data_repr[idx].data1);
390            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
391            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
392            unsafe { std::mem::transmute([soa0, soa1]) }
393        }
394
395        #[cfg(feature = "dim3")]
396        {
397            let aos0 = aos_unchecked!(data_repr[idx].data0);
398            let aos1 = aos_unchecked!(data_repr[idx].data1);
399            let aos2 = aos_unchecked!(data_repr[idx].data2);
400            let aos3 = aos_unchecked!(data_repr[idx].data3);
401            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
402            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
403            let soa2 = wide::f32x4::transpose(transmute_to_wide(aos2));
404            let soa3 = wide::f32x4::transpose(transmute_to_wide(aos3));
405            unsafe { std::mem::transmute([soa0, soa1, soa2, soa3]) }
406        }
407    }
408
409    #[inline]
410    pub fn gather(data: &[SolverPose<Real>], idx: [u32; SIMD_WIDTH]) -> Self {
411        // TODO: double-check that the compiler is using simd loads and
412        //       isn’t generating useless copies.
413
414        let identity = SolverPoseRepr::identity();
415        let data_repr: &[SolverPoseRepr] = unsafe { std::mem::transmute(data) };
416
417        #[cfg(feature = "dim2")]
418        {
419            let aos0 = aos!(data_repr[idx].data0, identity);
420            let aos1 = aos!(data_repr[idx].data1, identity);
421            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
422            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
423            unsafe { std::mem::transmute([soa0, soa1]) }
424        }
425
426        #[cfg(feature = "dim3")]
427        {
428            let aos0 = aos!(data_repr[idx].data0, identity);
429            let aos1 = aos!(data_repr[idx].data1, identity);
430            let aos2 = aos!(data_repr[idx].data2, identity);
431            let aos3 = aos!(data_repr[idx].data3, identity);
432            let soa0 = wide::f32x4::transpose(transmute_to_wide(aos0));
433            let soa1 = wide::f32x4::transpose(transmute_to_wide(aos1));
434            let soa2 = wide::f32x4::transpose(transmute_to_wide(aos2));
435            let soa3 = wide::f32x4::transpose(transmute_to_wide(aos3));
436            unsafe { std::mem::transmute([soa0, soa1, soa2, soa3]) }
437        }
438    }
439
440    #[inline]
441    #[cfg(feature = "dim2")]
442    pub fn scatter_unchecked(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
443        // TODO: double-check that the compiler is using simd loads and no useless copies.
444        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
445        let aos0 = wide::f32x4::transpose(soa[0]);
446        let aos1 = wide::f32x4::transpose(soa[1]);
447        scatter_unchecked!(data[idx[0]] = [aos0, aos1]);
448        scatter_unchecked!(data[idx[1]] = [aos0, aos1]);
449        scatter_unchecked!(data[idx[2]] = [aos0, aos1]);
450        scatter_unchecked!(data[idx[3]] = [aos0, aos1]);
451    }
452
453    #[inline]
454    #[cfg(feature = "dim3")]
455    pub fn scatter_unchecked(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
456        let soa: [[wide::f32x4; 4]; 4] = unsafe { std::mem::transmute(self) };
457        // TODO: double-check that the compiler is using simd loads and no useless copies.
458        let aos0 = wide::f32x4::transpose(soa[0]);
459        let aos1 = wide::f32x4::transpose(soa[1]);
460        let aos2 = wide::f32x4::transpose(soa[2]);
461        let aos3 = wide::f32x4::transpose(soa[3]);
462        scatter_unchecked!(data[idx[0]] = [aos0, aos1, aos2, aos3]);
463        scatter_unchecked!(data[idx[1]] = [aos0, aos1, aos2, aos3]);
464        scatter_unchecked!(data[idx[2]] = [aos0, aos1, aos2, aos3]);
465        scatter_unchecked!(data[idx[3]] = [aos0, aos1, aos2, aos3]);
466    }
467
468    #[inline]
469    #[cfg(feature = "dim2")]
470    pub fn scatter(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
471        // TODO: double-check that the compiler is using simd loads and no useless copies.
472        let soa: [[wide::f32x4; 4]; 2] = unsafe { std::mem::transmute(self) };
473        let aos0 = wide::f32x4::transpose(soa[0]);
474        let aos1 = wide::f32x4::transpose(soa[1]);
475        scatter!(data[idx[0]] = [aos0, aos1]);
476        scatter!(data[idx[1]] = [aos0, aos1]);
477        scatter!(data[idx[2]] = [aos0, aos1]);
478        scatter!(data[idx[3]] = [aos0, aos1]);
479    }
480
481    #[inline]
482    #[cfg(feature = "dim3")]
483    pub fn scatter(self, data: &mut [SolverPose<Real>], idx: [u32; SIMD_WIDTH]) {
484        let soa: [[wide::f32x4; 4]; 4] = unsafe { std::mem::transmute(self) };
485        // TODO: double-check that the compiler is using simd loads and no useless copies.
486        let aos0 = wide::f32x4::transpose(soa[0]);
487        let aos1 = wide::f32x4::transpose(soa[1]);
488        let aos2 = wide::f32x4::transpose(soa[2]);
489        let aos3 = wide::f32x4::transpose(soa[3]);
490        scatter!(data[idx[0]] = [aos0, aos1, aos2, aos3]);
491        scatter!(data[idx[1]] = [aos0, aos1, aos2, aos3]);
492        scatter!(data[idx[2]] = [aos0, aos1, aos2, aos3]);
493        scatter!(data[idx[3]] = [aos0, aos1, aos2, aos3]);
494    }
495}
496
497impl<N: SimdRealCopy> SolverVel<N> {
498    pub fn as_slice(&self) -> &[N; SPATIAL_DIM] {
499        unsafe { std::mem::transmute(self) }
500    }
501
502    pub fn as_mut_slice(&mut self) -> &mut [N; SPATIAL_DIM] {
503        unsafe { std::mem::transmute(self) }
504    }
505
506    pub fn as_vector_slice(&self) -> DVectorView<'_, N> {
507        DVectorView::from_slice(&self.as_slice()[..], SPATIAL_DIM)
508    }
509
510    pub fn as_vector_slice_mut(&mut self) -> DVectorViewMut<'_, N> {
511        DVectorViewMut::from_slice(&mut self.as_mut_slice()[..], SPATIAL_DIM)
512    }
513}
514
515impl<N: SimdRealCopy> SolverVel<N> {
516    pub fn zero() -> Self {
517        Self {
518            linear: na::zero(),
519            angular: na::zero(),
520            #[cfg(feature = "simd-is-enabled")]
521            #[cfg(feature = "dim2")]
522            padding: [na::zero(); 1],
523            #[cfg(feature = "simd-is-enabled")]
524            #[cfg(feature = "dim3")]
525            padding: [na::zero(); 2],
526        }
527    }
528}
529
530impl<N: SimdRealCopy> AddAssign for SolverVel<N> {
531    fn add_assign(&mut self, rhs: Self) {
532        self.linear += rhs.linear;
533        self.angular += rhs.angular;
534    }
535}
536
537impl<N: SimdRealCopy> SubAssign for SolverVel<N> {
538    fn sub_assign(&mut self, rhs: Self) {
539        self.linear -= rhs.linear;
540        self.angular -= rhs.angular;
541    }
542}
543
544impl<N: SimdRealCopy> Sub for SolverVel<N> {
545    type Output = Self;
546
547    fn sub(self, rhs: Self) -> Self {
548        SolverVel {
549            linear: self.linear - rhs.linear,
550            angular: self.angular - rhs.angular,
551            #[cfg(feature = "simd-is-enabled")]
552            padding: self.padding,
553        }
554    }
555}