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)] 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
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#[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, pub angular: T::AngVector, #[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 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 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 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 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#[repr(C)]
339#[cfg_attr(feature = "simd-is-enabled", repr(align(16)))]
340#[derive(Copy, Clone)]
341pub struct SolverPose<N: ScalarType> {
342 pub rotation: N::Rotation, pub translation: N::Vector, pub ii: N::AngInertia, pub im: N::Vector, #[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 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 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 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 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 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 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 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}