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)] 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)] 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 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#[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>, pub angular: AngVector<T>, #[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 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 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 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 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#[repr(C)]
333#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
334#[derive(Copy, Clone)]
335pub struct SolverPose<T> {
336 pub pose: Isometry<T>, pub ii: AngularInertia<T>, pub im: Vector<T>, #[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 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 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 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 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 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 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 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}