1use crate::dynamics::solver::SolverVel;
2use crate::dynamics::solver::joint_constraint::JointConstraintHelper;
3use crate::dynamics::{
4 GenericJoint, IntegrationParameters, JointAxesMask, JointGraphEdge, JointIndex,
5};
6use crate::math::{AngVector, AngularInertia, DIM, Isometry, Point, Real, SPATIAL_DIM, Vector};
7use crate::utils::{SimdDot, SimdRealCopy};
8
9use crate::dynamics::solver::solver_body::SolverBodies;
10#[cfg(feature = "simd-is-enabled")]
11use crate::math::{SIMD_WIDTH, SimdReal};
12#[cfg(feature = "dim2")]
13use crate::num::Zero;
14#[cfg(feature = "simd-is-enabled")]
15use na::SimdValue;
16
17#[derive(Copy, Clone, PartialEq, Debug)]
18pub struct MotorParameters<N: SimdRealCopy> {
19 pub erp_inv_dt: N,
20 pub cfm_coeff: N,
21 pub cfm_gain: N,
22 pub target_pos: N,
23 pub target_vel: N,
24 pub max_impulse: N,
25}
26
27impl<N: SimdRealCopy> Default for MotorParameters<N> {
28 fn default() -> Self {
29 Self {
30 erp_inv_dt: N::zero(),
31 cfm_coeff: N::zero(),
32 cfm_gain: N::zero(),
33 target_pos: N::zero(),
34 target_vel: N::zero(),
35 max_impulse: N::zero(),
36 }
37 }
38}
39
40#[derive(Copy, Clone, PartialEq, Eq, Debug)]
41pub enum WritebackId {
42 Dof(usize),
43 Limit(usize),
44 Motor(usize),
45}
46
47#[derive(Copy, Clone)]
52pub struct JointSolverBody<N: SimdRealCopy, const LANES: usize> {
53 pub im: Vector<N>,
54 pub ii: AngularInertia<N>,
55 pub world_com: Point<N>, pub solver_vel: [u32; LANES],
57}
58
59impl<N: SimdRealCopy, const LANES: usize> JointSolverBody<N, LANES> {
60 pub fn invalid() -> Self {
61 Self {
62 im: Vector::zeros(),
63 ii: AngularInertia::zero(),
64 world_com: Point::origin(),
65 solver_vel: [u32::MAX; LANES],
66 }
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
71pub struct JointConstraint<N: SimdRealCopy, const LANES: usize> {
72 pub solver_vel1: [u32; LANES],
73 pub solver_vel2: [u32; LANES],
74
75 pub joint_id: [JointIndex; LANES],
76
77 pub impulse: N,
78 pub impulse_bounds: [N; 2],
79 pub lin_jac: Vector<N>,
80 pub ang_jac1: AngVector<N>,
81 pub ang_jac2: AngVector<N>,
82
83 pub ii_ang_jac1: AngVector<N>,
84 pub ii_ang_jac2: AngVector<N>,
85
86 pub inv_lhs: N,
87 pub rhs: N,
88 pub rhs_wo_bias: N,
89 pub cfm_gain: N,
90 pub cfm_coeff: N,
91
92 pub im1: Vector<N>,
93 pub im2: Vector<N>,
94
95 pub writeback_id: WritebackId,
96}
97
98impl<N: SimdRealCopy, const LANES: usize> JointConstraint<N, LANES> {
99 #[profiling::function]
100 pub fn solve_generic(
101 &mut self,
102 solver_vel1: &mut SolverVel<N>,
103 solver_vel2: &mut SolverVel<N>,
104 ) {
105 let dlinvel = self.lin_jac.dot(&(solver_vel2.linear - solver_vel1.linear));
106 let dangvel =
107 self.ang_jac2.gdot(solver_vel2.angular) - self.ang_jac1.gdot(solver_vel1.angular);
108
109 let rhs = dlinvel + dangvel + self.rhs;
110 let total_impulse = (self.impulse + self.inv_lhs * (rhs - self.cfm_gain * self.impulse))
111 .simd_clamp(self.impulse_bounds[0], self.impulse_bounds[1]);
112 let delta_impulse = total_impulse - self.impulse;
113 self.impulse = total_impulse;
114
115 let lin_impulse = self.lin_jac * delta_impulse;
116 let ii_ang_impulse1 = self.ii_ang_jac1 * delta_impulse;
117 let ii_ang_impulse2 = self.ii_ang_jac2 * delta_impulse;
118
119 solver_vel1.linear += lin_impulse.component_mul(&self.im1);
120 solver_vel1.angular += ii_ang_impulse1;
121 solver_vel2.linear -= lin_impulse.component_mul(&self.im2);
122 solver_vel2.angular -= ii_ang_impulse2;
123 }
124
125 pub fn remove_bias_from_rhs(&mut self) {
126 self.rhs = self.rhs_wo_bias;
127 }
128}
129
130impl JointConstraint<Real, 1> {
131 pub fn update(
132 params: &IntegrationParameters,
133 joint_id: JointIndex,
134 body1: &JointSolverBody<Real, 1>,
135 body2: &JointSolverBody<Real, 1>,
136 frame1: &Isometry<Real>,
137 frame2: &Isometry<Real>,
138 joint: &GenericJoint,
139 out: &mut [Self],
140 ) -> usize {
141 let mut len = 0;
142 let locked_axes = joint.locked_axes.bits();
143 let motor_axes = joint.motor_axes.bits() & !locked_axes;
144 let limit_axes = joint.limit_axes.bits() & !locked_axes;
145 let coupled_axes = joint.coupled_axes.bits();
146
147 let erp_inv_dt = joint.softness.erp_inv_dt(params.dt);
149 let cfm_coeff = joint.softness.cfm_coeff(params.dt);
150
151 let has_lin_coupling = (coupled_axes & JointAxesMask::LIN_AXES.bits()) != 0;
153 let first_coupled_lin_axis_id =
154 (coupled_axes & JointAxesMask::LIN_AXES.bits()).trailing_zeros() as usize;
155
156 #[cfg(feature = "dim3")]
157 let has_ang_coupling = (coupled_axes & JointAxesMask::ANG_AXES.bits()) != 0;
158 #[cfg(feature = "dim3")]
159 let first_coupled_ang_axis_id =
160 (coupled_axes & JointAxesMask::ANG_AXES.bits()).trailing_zeros() as usize;
161
162 let builder = JointConstraintHelper::new(
163 frame1,
164 frame2,
165 &body1.world_com,
166 &body2.world_com,
167 locked_axes,
168 );
169
170 let start = len;
171 for i in DIM..SPATIAL_DIM {
172 if (motor_axes & !coupled_axes) & (1 << i) != 0 {
173 out[len] = builder.motor_angular(
174 [joint_id],
175 body1,
176 body2,
177 i - DIM,
178 &joint.motors[i].motor_params(params.dt),
179 WritebackId::Motor(i),
180 );
181 len += 1;
182 }
183 }
184 for i in 0..DIM {
185 if (motor_axes & !coupled_axes) & (1 << i) != 0 {
186 let limits = if limit_axes & (1 << i) != 0 {
187 Some([joint.limits[i].min, joint.limits[i].max])
188 } else {
189 None
190 };
191
192 out[len] = builder.motor_linear(
193 params,
194 [joint_id],
195 body1,
196 body2,
197 i,
198 &joint.motors[i].motor_params(params.dt),
199 limits,
200 WritebackId::Motor(i),
201 );
202 len += 1;
203 }
204 }
205
206 if (motor_axes & coupled_axes) & JointAxesMask::ANG_AXES.bits() != 0 {
207 }
209
210 if (motor_axes & coupled_axes) & JointAxesMask::LIN_AXES.bits() != 0 {
211 let limits = if (limit_axes & (1 << first_coupled_lin_axis_id)) != 0 {
212 Some([
213 joint.limits[first_coupled_lin_axis_id].min,
214 joint.limits[first_coupled_lin_axis_id].max,
215 ])
216 } else {
217 None
218 };
219
220 out[len] = builder.motor_linear_coupled(
221 params,
222 [joint_id],
223 body1,
224 body2,
225 coupled_axes,
226 &joint.motors[first_coupled_lin_axis_id].motor_params(params.dt),
227 limits,
228 WritebackId::Motor(first_coupled_lin_axis_id),
229 );
230 len += 1;
231 }
232
233 JointConstraintHelper::finalize_constraints(&mut out[start..len]);
234
235 let start = len;
236 for i in DIM..SPATIAL_DIM {
237 if locked_axes & (1 << i) != 0 {
238 out[len] = builder.lock_angular(
239 params,
240 [joint_id],
241 body1,
242 body2,
243 i - DIM,
244 WritebackId::Dof(i),
245 erp_inv_dt,
246 cfm_coeff,
247 );
248 len += 1;
249 }
250 }
251 for i in 0..DIM {
252 if locked_axes & (1 << i) != 0 {
253 out[len] = builder.lock_linear(
254 params,
255 [joint_id],
256 body1,
257 body2,
258 i,
259 WritebackId::Dof(i),
260 erp_inv_dt,
261 cfm_coeff,
262 );
263 len += 1;
264 }
265 }
266
267 for i in DIM..SPATIAL_DIM {
268 if (limit_axes & !coupled_axes) & (1 << i) != 0 {
269 out[len] = builder.limit_angular(
270 params,
271 [joint_id],
272 body1,
273 body2,
274 i - DIM,
275 [joint.limits[i].min, joint.limits[i].max],
276 WritebackId::Limit(i),
277 erp_inv_dt,
278 cfm_coeff,
279 );
280 len += 1;
281 }
282 }
283 for i in 0..DIM {
284 if (limit_axes & !coupled_axes) & (1 << i) != 0 {
285 out[len] = builder.limit_linear(
286 params,
287 [joint_id],
288 body1,
289 body2,
290 i,
291 [joint.limits[i].min, joint.limits[i].max],
292 WritebackId::Limit(i),
293 erp_inv_dt,
294 cfm_coeff,
295 );
296 len += 1;
297 }
298 }
299
300 #[cfg(feature = "dim3")]
301 if has_ang_coupling && (limit_axes & (1 << first_coupled_ang_axis_id)) != 0 {
302 out[len] = builder.limit_angular_coupled(
303 params,
304 [joint_id],
305 body1,
306 body2,
307 coupled_axes,
308 [
309 joint.limits[first_coupled_ang_axis_id].min,
310 joint.limits[first_coupled_ang_axis_id].max,
311 ],
312 WritebackId::Limit(first_coupled_ang_axis_id),
313 erp_inv_dt,
314 cfm_coeff,
315 );
316 len += 1;
317 }
318
319 if has_lin_coupling && (limit_axes & (1 << first_coupled_lin_axis_id)) != 0 {
320 out[len] = builder.limit_linear_coupled(
321 params,
322 [joint_id],
323 body1,
324 body2,
325 coupled_axes,
326 [
327 joint.limits[first_coupled_lin_axis_id].min,
328 joint.limits[first_coupled_lin_axis_id].max,
329 ],
330 WritebackId::Limit(first_coupled_lin_axis_id),
331 erp_inv_dt,
332 cfm_coeff,
333 );
334 len += 1;
335 }
336 JointConstraintHelper::finalize_constraints(&mut out[start..len]);
337
338 len
339 }
340
341 pub fn solve(&mut self, solver_vels: &mut SolverBodies) {
342 let mut solver_vel1 = solver_vels.get_vel(self.solver_vel1[0]);
343 let mut solver_vel2 = solver_vels.get_vel(self.solver_vel2[0]);
344
345 self.solve_generic(&mut solver_vel1, &mut solver_vel2);
346
347 solver_vels.set_vel(self.solver_vel1[0], solver_vel1);
348 solver_vels.set_vel(self.solver_vel2[0], solver_vel2);
349 }
350
351 pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
352 let joint = &mut joints_all[self.joint_id[0]].weight;
353 match self.writeback_id {
354 WritebackId::Dof(i) => joint.impulses[i] = self.impulse,
355 WritebackId::Limit(i) => joint.data.limits[i].impulse = self.impulse,
356 WritebackId::Motor(i) => joint.data.motors[i].impulse = self.impulse,
357 }
358 }
359}
360
361#[cfg(feature = "simd-is-enabled")]
362impl JointConstraint<SimdReal, SIMD_WIDTH> {
363 pub fn update(
364 params: &IntegrationParameters,
365 joint_id: [JointIndex; SIMD_WIDTH],
366 body1: &JointSolverBody<SimdReal, SIMD_WIDTH>,
367 body2: &JointSolverBody<SimdReal, SIMD_WIDTH>,
368 frame1: &Isometry<SimdReal>,
369 frame2: &Isometry<SimdReal>,
370 locked_axes: u8,
371 softness: crate::dynamics::SpringCoefficients<SimdReal>,
372 out: &mut [Self],
373 ) -> usize {
374 let dt = SimdReal::splat(params.dt);
375 let erp_inv_dt = softness.erp_inv_dt(dt);
376 let cfm_coeff = softness.cfm_coeff(dt);
377
378 let builder = JointConstraintHelper::new(
379 frame1,
380 frame2,
381 &body1.world_com,
382 &body2.world_com,
383 locked_axes,
384 );
385
386 let mut len = 0;
387 for i in 0..DIM {
388 if locked_axes & (1 << i) != 0 {
389 out[len] = builder.lock_linear(
390 params,
391 joint_id,
392 body1,
393 body2,
394 i,
395 WritebackId::Dof(i),
396 erp_inv_dt,
397 cfm_coeff,
398 );
399 len += 1;
400 }
401 }
402
403 for i in DIM..SPATIAL_DIM {
404 if locked_axes & (1 << i) != 0 {
405 out[len] = builder.lock_angular(
406 params,
407 joint_id,
408 body1,
409 body2,
410 i - DIM,
411 WritebackId::Dof(i),
412 erp_inv_dt,
413 cfm_coeff,
414 );
415 len += 1;
416 }
417 }
418
419 JointConstraintHelper::finalize_constraints(&mut out[..len]);
420 len
421 }
422
423 pub fn solve(&mut self, solver_vels: &mut SolverBodies) {
424 let mut solver_vel1 = solver_vels.gather_vels(self.solver_vel1);
425 let mut solver_vel2 = solver_vels.gather_vels(self.solver_vel2);
426
427 self.solve_generic(&mut solver_vel1, &mut solver_vel2);
428
429 solver_vels.scatter_vels(self.solver_vel1, solver_vel1);
430 solver_vels.scatter_vels(self.solver_vel2, solver_vel2);
431 }
432
433 pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
434 let impulses: [_; SIMD_WIDTH] = self.impulse.into();
435
436 for ii in 0..SIMD_WIDTH {
438 let joint = &mut joints_all[self.joint_id[ii]].weight;
439 match self.writeback_id {
440 WritebackId::Dof(i) => joint.impulses[i] = impulses[ii],
441 WritebackId::Limit(i) => joint.data.limits[i].impulse = impulses[ii],
442 WritebackId::Motor(i) => joint.data.motors[i].impulse = impulses[ii],
443 }
444 }
445 }
446}