1use crate::body::{body_flags, BodyState, IDENTITY_BODY_STATE};
30use crate::contact::ContactSim;
31use crate::core::NULL_INDEX;
32use crate::math_functions::{
33 add, clamp_float, cross, cross_sv, dot, max_float, mul_add, mul_sub, mul_sv, right_perp,
34 rotate_vector, sub, Vec2, VEC2_ZERO,
35};
36use crate::solver::{Softness, StepContext};
37
38#[derive(Debug, Clone, Copy, PartialEq, Default)]
40pub struct ContactConstraintPoint {
41 pub anchor_a: Vec2,
42 pub anchor_b: Vec2,
43 pub base_separation: f32,
44 pub relative_velocity: f32,
45 pub normal_impulse: f32,
46 pub tangent_impulse: f32,
47 pub total_normal_impulse: f32,
48 pub normal_mass: f32,
49 pub tangent_mass: f32,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Default)]
54pub struct ContactConstraint {
55 pub index_a: i32,
57 pub index_b: i32,
58 pub points: [ContactConstraintPoint; 2],
59 pub normal: Vec2,
60 pub inv_mass_a: f32,
61 pub inv_mass_b: f32,
62 pub inv_i_a: f32,
63 pub inv_i_b: f32,
64 pub friction: f32,
65 pub restitution: f32,
66 pub tangent_speed: f32,
67 pub rolling_resistance: f32,
68 pub rolling_mass: f32,
69 pub rolling_impulse: f32,
70 pub softness: Softness,
71 pub point_count: i32,
72}
73
74pub fn prepare_contacts(
78 constraints: &mut [ContactConstraint],
79 contacts: &[ContactSim],
80 states: &[BodyState],
81 context: &StepContext,
82) {
83 debug_assert!(constraints.len() == contacts.len());
84
85 let contact_softness = context.contact_softness;
88 let static_softness = context.static_softness;
89
90 let warm_start_scale = if context.enable_warm_starting {
91 1.0
92 } else {
93 0.0
94 };
95
96 for (constraint, contact_sim) in constraints.iter_mut().zip(contacts.iter()) {
97 let manifold = &contact_sim.manifold;
98 let point_count = manifold.point_count;
99
100 debug_assert!(0 < point_count && point_count <= 2);
101
102 let index_a = contact_sim.body_sim_index_a;
103 let index_b = contact_sim.body_sim_index_b;
104
105 constraint.index_a = index_a + 1;
107 constraint.index_b = index_b + 1;
108 constraint.normal = manifold.normal;
109 constraint.friction = contact_sim.friction;
110 constraint.restitution = contact_sim.restitution;
111 constraint.rolling_resistance = contact_sim.rolling_resistance;
112 constraint.rolling_impulse = warm_start_scale * manifold.rolling_impulse;
113 constraint.tangent_speed = contact_sim.tangent_speed;
114 constraint.point_count = point_count;
115
116 let mut v_a = VEC2_ZERO;
117 let mut w_a = 0.0;
118 let m_a = contact_sim.inv_mass_a;
119 let i_a = contact_sim.inv_i_a;
120 if index_a != NULL_INDEX {
121 let state_a = &states[index_a as usize];
122 v_a = state_a.linear_velocity;
123 w_a = state_a.angular_velocity;
124 }
125
126 let mut v_b = VEC2_ZERO;
127 let mut w_b = 0.0;
128 let m_b = contact_sim.inv_mass_b;
129 let i_b = contact_sim.inv_i_b;
130 if index_b != NULL_INDEX {
131 let state_b = &states[index_b as usize];
132 v_b = state_b.linear_velocity;
133 w_b = state_b.angular_velocity;
134 }
135
136 if index_a == NULL_INDEX || index_b == NULL_INDEX {
137 constraint.softness = static_softness;
138 } else {
139 constraint.softness = contact_softness;
140 }
141
142 constraint.inv_mass_a = m_a;
144 constraint.inv_i_a = i_a;
145 constraint.inv_mass_b = m_b;
146 constraint.inv_i_b = i_b;
147
148 {
149 let k = i_a + i_b;
150 constraint.rolling_mass = if k > 0.0 { 1.0 / k } else { 0.0 };
151 }
152
153 let normal = constraint.normal;
154 let tangent = right_perp(constraint.normal);
155
156 for j in 0..point_count as usize {
157 let mp = &manifold.points[j];
158 let cp = &mut constraint.points[j];
159
160 cp.normal_impulse = warm_start_scale * mp.normal_impulse;
161 cp.tangent_impulse = warm_start_scale * mp.tangent_impulse;
162 cp.total_normal_impulse = 0.0;
163
164 let r_a = mp.anchor_a;
165 let r_b = mp.anchor_b;
166
167 cp.anchor_a = r_a;
168 cp.anchor_b = r_b;
169 cp.base_separation = mp.separation - dot(sub(r_b, r_a), normal);
170
171 let rn_a = cross(r_a, normal);
172 let rn_b = cross(r_b, normal);
173 let k_normal = m_a + m_b + i_a * rn_a * rn_a + i_b * rn_b * rn_b;
174 cp.normal_mass = if k_normal > 0.0 { 1.0 / k_normal } else { 0.0 };
175
176 let rt_a = cross(r_a, tangent);
177 let rt_b = cross(r_b, tangent);
178 let k_tangent = m_a + m_b + i_a * rt_a * rt_a + i_b * rt_b * rt_b;
179 cp.tangent_mass = if k_tangent > 0.0 {
180 1.0 / k_tangent
181 } else {
182 0.0
183 };
184
185 let vr_a = add(v_a, cross_sv(w_a, r_a));
187 let vr_b = add(v_b, cross_sv(w_b, r_b));
188 cp.relative_velocity = dot(normal, sub(vr_b, vr_a));
189 }
190 }
191}
192
193pub fn warm_start_contacts(constraints: &mut [ContactConstraint], states: &mut [BodyState]) {
195 for constraint in constraints.iter_mut() {
196 let index_a = constraint.index_a - 1;
197 let index_b = constraint.index_b - 1;
198
199 let mut state_a = if index_a == NULL_INDEX {
202 IDENTITY_BODY_STATE
203 } else {
204 states[index_a as usize]
205 };
206 let mut state_b = if index_b == NULL_INDEX {
207 IDENTITY_BODY_STATE
208 } else {
209 states[index_b as usize]
210 };
211
212 let mut v_a = state_a.linear_velocity;
213 let mut w_a = state_a.angular_velocity;
214 let mut v_b = state_b.linear_velocity;
215 let mut w_b = state_b.angular_velocity;
216
217 let m_a = constraint.inv_mass_a;
218 let i_a = constraint.inv_i_a;
219 let m_b = constraint.inv_mass_b;
220 let i_b = constraint.inv_i_b;
221
222 let normal = constraint.normal;
223 let tangent = right_perp(constraint.normal);
224 let point_count = constraint.point_count;
225
226 for j in 0..point_count as usize {
227 let cp = &mut constraint.points[j];
228
229 let r_a = cp.anchor_a;
231 let r_b = cp.anchor_b;
232
233 let p = add(
234 mul_sv(cp.normal_impulse, normal),
235 mul_sv(cp.tangent_impulse, tangent),
236 );
237
238 cp.total_normal_impulse += cp.normal_impulse;
239
240 w_a -= i_a * cross(r_a, p);
241 v_a = mul_add(v_a, -m_a, p);
242 w_b += i_b * cross(r_b, p);
243 v_b = mul_add(v_b, m_b, p);
244 }
245
246 w_a -= i_a * constraint.rolling_impulse;
247 w_b += i_b * constraint.rolling_impulse;
248
249 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
250 state_a.linear_velocity = v_a;
251 state_a.angular_velocity = w_a;
252 states[index_a as usize] = state_a;
253 }
254
255 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
256 state_b.linear_velocity = v_b;
257 state_b.angular_velocity = w_b;
258 states[index_b as usize] = state_b;
259 }
260 }
261}
262
263pub fn solve_contacts(
265 constraints: &mut [ContactConstraint],
266 states: &mut [BodyState],
267 context: &StepContext,
268 use_bias: bool,
269) {
270 let inv_h = context.inv_h;
271 let contact_speed = context.contact_speed;
272
273 for constraint in constraints.iter_mut() {
274 let m_a = constraint.inv_mass_a;
275 let i_a = constraint.inv_i_a;
276 let m_b = constraint.inv_mass_b;
277 let i_b = constraint.inv_i_b;
278
279 let index_a = constraint.index_a - 1;
280 let index_b = constraint.index_b - 1;
281
282 let mut state_a = if index_a == NULL_INDEX {
285 IDENTITY_BODY_STATE
286 } else {
287 states[index_a as usize]
288 };
289 let mut v_a = state_a.linear_velocity;
290 let mut w_a = state_a.angular_velocity;
291 let dq_a = state_a.delta_rotation;
292
293 let mut state_b = if index_b == NULL_INDEX {
294 IDENTITY_BODY_STATE
295 } else {
296 states[index_b as usize]
297 };
298 let mut v_b = state_b.linear_velocity;
299 let mut w_b = state_b.angular_velocity;
300 let dq_b = state_b.delta_rotation;
301
302 let dp = sub(state_b.delta_position, state_a.delta_position);
303
304 let normal = constraint.normal;
305 let tangent = right_perp(normal);
306 let friction = constraint.friction;
307 let softness = constraint.softness;
308
309 let point_count = constraint.point_count;
310 let mut total_normal_impulse = 0.0;
311
312 for j in 0..point_count as usize {
314 let cp = &mut constraint.points[j];
315
316 let r_a = cp.anchor_a;
318 let r_b = cp.anchor_b;
319
320 let ds = add(dp, sub(rotate_vector(dq_b, r_b), rotate_vector(dq_a, r_a)));
324 let s = cp.base_separation + dot(ds, normal);
325
326 let mut velocity_bias = 0.0;
327 let mut mass_scale = 1.0;
328 let mut impulse_scale = 0.0;
329 if s > 0.0 {
330 velocity_bias = s * inv_h;
332 } else if use_bias {
333 velocity_bias =
334 max_float(softness.mass_scale * softness.bias_rate * s, -contact_speed);
335 mass_scale = softness.mass_scale;
336 impulse_scale = softness.impulse_scale;
337 }
338
339 let vr_a = add(v_a, cross_sv(w_a, r_a));
341 let vr_b = add(v_b, cross_sv(w_b, r_b));
342 let vn = dot(sub(vr_b, vr_a), normal);
343
344 let mut impulse = -cp.normal_mass * (mass_scale * vn + velocity_bias)
346 - impulse_scale * cp.normal_impulse;
347
348 let new_impulse = max_float(cp.normal_impulse + impulse, 0.0);
350 impulse = new_impulse - cp.normal_impulse;
351 cp.normal_impulse = new_impulse;
352 cp.total_normal_impulse += impulse;
353
354 total_normal_impulse += new_impulse;
355
356 let p = mul_sv(impulse, normal);
358 v_a = mul_sub(v_a, m_a, p);
359 w_a -= i_a * cross(r_a, p);
360
361 v_b = mul_add(v_b, m_b, p);
362 w_b += i_b * cross(r_b, p);
363 }
364
365 if !use_bias {
366 for j in 0..point_count as usize {
368 let cp = &mut constraint.points[j];
369
370 let r_a = cp.anchor_a;
372 let r_b = cp.anchor_b;
373
374 let vr_b = add(v_b, cross_sv(w_b, r_b));
376 let vr_a = add(v_a, cross_sv(w_a, r_a));
377
378 let vt = dot(sub(vr_b, vr_a), tangent) - constraint.tangent_speed;
381
382 let mut impulse = cp.tangent_mass * (-vt);
384
385 let max_friction = friction * cp.normal_impulse;
387 let new_impulse =
388 clamp_float(cp.tangent_impulse + impulse, -max_friction, max_friction);
389 impulse = new_impulse - cp.tangent_impulse;
390 cp.tangent_impulse = new_impulse;
391
392 let p = mul_sv(impulse, tangent);
394 v_a = mul_sub(v_a, m_a, p);
395 w_a -= i_a * cross(r_a, p);
396 v_b = mul_add(v_b, m_b, p);
397 w_b += i_b * cross(r_b, p);
398 }
399
400 {
402 let mut delta_lambda = -constraint.rolling_mass * (w_b - w_a);
403 let lambda = constraint.rolling_impulse;
404 let max_lambda = constraint.rolling_resistance * total_normal_impulse;
405 constraint.rolling_impulse =
406 clamp_float(lambda + delta_lambda, -max_lambda, max_lambda);
407 delta_lambda = constraint.rolling_impulse - lambda;
408
409 w_a -= i_a * delta_lambda;
410 w_b += i_b * delta_lambda;
411 }
412 }
413
414 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
415 state_a.linear_velocity = v_a;
416 state_a.angular_velocity = w_a;
417 states[index_a as usize] = state_a;
418 }
419
420 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
421 state_b.linear_velocity = v_b;
422 state_b.angular_velocity = w_b;
423 states[index_b as usize] = state_b;
424 }
425 }
426}
427
428pub fn apply_restitution(
430 constraints: &mut [ContactConstraint],
431 states: &mut [BodyState],
432 context: &StepContext,
433) {
434 let threshold = context.restitution_threshold;
435
436 for constraint in constraints.iter_mut() {
437 let restitution = constraint.restitution;
438 if restitution == 0.0 {
439 continue;
440 }
441
442 let m_a = constraint.inv_mass_a;
443 let i_a = constraint.inv_i_a;
444 let m_b = constraint.inv_mass_b;
445 let i_b = constraint.inv_i_b;
446
447 let index_a = constraint.index_a - 1;
448 let index_b = constraint.index_b - 1;
449
450 let mut state_a = if index_a == NULL_INDEX {
452 IDENTITY_BODY_STATE
453 } else {
454 states[index_a as usize]
455 };
456 let mut v_a = state_a.linear_velocity;
457 let mut w_a = state_a.angular_velocity;
458
459 let mut state_b = if index_b == NULL_INDEX {
460 IDENTITY_BODY_STATE
461 } else {
462 states[index_b as usize]
463 };
464 let mut v_b = state_b.linear_velocity;
465 let mut w_b = state_b.angular_velocity;
466
467 let normal = constraint.normal;
468 let point_count = constraint.point_count;
469
470 for j in 0..point_count as usize {
473 let cp = &mut constraint.points[j];
474
475 if cp.relative_velocity > -threshold || cp.total_normal_impulse == 0.0 {
480 continue;
481 }
482
483 let r_a = cp.anchor_a;
485 let r_b = cp.anchor_b;
486
487 let vr_b = add(v_b, cross_sv(w_b, r_b));
489 let vr_a = add(v_a, cross_sv(w_a, r_a));
490 let vn = dot(sub(vr_b, vr_a), normal);
491
492 let mut impulse = -cp.normal_mass * (vn + restitution * cp.relative_velocity);
494
495 let new_impulse = max_float(cp.normal_impulse + impulse, 0.0);
497 impulse = new_impulse - cp.normal_impulse;
498 cp.normal_impulse = new_impulse;
499 cp.total_normal_impulse += impulse;
500
501 let p = mul_sv(impulse, normal);
503 v_a = mul_sub(v_a, m_a, p);
504 w_a -= i_a * cross(r_a, p);
505 v_b = mul_add(v_b, m_b, p);
506 w_b += i_b * cross(r_b, p);
507 }
508
509 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
510 state_a.linear_velocity = v_a;
511 state_a.angular_velocity = w_a;
512 states[index_a as usize] = state_a;
513 }
514
515 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
516 state_b.linear_velocity = v_b;
517 state_b.angular_velocity = w_b;
518 states[index_b as usize] = state_b;
519 }
520 }
521}
522
523pub fn store_impulses(constraints: &[ContactConstraint], contacts: &mut [ContactSim]) {
525 for (constraint, contact) in constraints.iter().zip(contacts.iter_mut()) {
526 let manifold = &mut contact.manifold;
527 let point_count = manifold.point_count;
528
529 for j in 0..point_count as usize {
530 manifold.points[j].normal_impulse = constraint.points[j].normal_impulse;
531 manifold.points[j].tangent_impulse = constraint.points[j].tangent_impulse;
532 manifold.points[j].total_normal_impulse = constraint.points[j].total_normal_impulse;
533 manifold.points[j].normal_velocity = constraint.points[j].relative_velocity;
534 }
535
536 manifold.rolling_impulse = constraint.rolling_impulse;
537 }
538}