1use super::types::{BodyType, ContactManifold, ManifoldPoint, RigidBody};
2
3fn local_to_world(body: &RigidBody, local: (f32, f32)) -> (f32, f32) {
5 let cos = body.angle.cos();
6 let sin = body.angle.sin();
7 (
8 local.0 * cos - local.1 * sin + body.x,
9 local.0 * sin + local.1 * cos + body.y,
10 )
11}
12
13pub fn initialize_manifolds(
15 bodies: &[Option<RigidBody>],
16 manifolds: &mut [ContactManifold],
17 restitution_threshold: f32,
18) {
19 for manifold in manifolds.iter_mut() {
20 let id_a = manifold.body_a as usize;
21 let id_b = manifold.body_b as usize;
22
23 let a = match &bodies[id_a] {
24 Some(b) => b,
25 None => continue,
26 };
27 let b = match &bodies[id_b] {
28 Some(b) => b,
29 None => continue,
30 };
31
32 let (nx, ny) = manifold.normal;
33
34 manifold.tangent = (-ny, nx);
36
37 let mut total_vn = 0.0;
39 for point in &manifold.points {
40 let wa = local_to_world(a, point.local_a);
42 let wb = local_to_world(b, point.local_b);
43
44 let ra_x = wa.0 - a.x;
46 let ra_y = wa.1 - a.y;
47 let rb_x = wb.0 - b.x;
48 let rb_y = wb.1 - b.y;
49
50 let rel_vx = (b.vx + (-b.angular_velocity * rb_y)) - (a.vx + (-a.angular_velocity * ra_y));
52 let rel_vy = (b.vy + (b.angular_velocity * rb_x)) - (a.vy + (a.angular_velocity * ra_x));
53
54 let vn = rel_vx * nx + rel_vy * ny;
55 total_vn += vn;
56 }
57
58 let avg_vn = if manifold.points.is_empty() {
59 0.0
60 } else {
61 total_vn / manifold.points.len() as f32
62 };
63
64 let e = if -avg_vn < restitution_threshold {
66 0.0
67 } else {
68 a.material.restitution.max(b.material.restitution)
69 };
70 manifold.velocity_bias = e * (-avg_vn).max(0.0);
71 }
72}
73
74pub fn warm_start_manifolds(
76 bodies: &mut [Option<RigidBody>],
77 manifolds: &[ContactManifold],
78) {
79 for manifold in manifolds {
80 let id_a = manifold.body_a as usize;
81 let id_b = manifold.body_b as usize;
82
83 let (nx, ny) = manifold.normal;
84 let (tx, ty) = manifold.tangent;
85
86 for point in &manifold.points {
87 let jn = point.accumulated_jn;
88 let jt = point.accumulated_jt;
89
90 if jn == 0.0 && jt == 0.0 {
91 continue;
92 }
93
94 let px = jn * nx + jt * tx;
96 let py = jn * ny + jt * ty;
97
98 let (inv_ma, inv_ia, type_a, xa, ya, cos_a, sin_a) = match &bodies[id_a] {
100 Some(a) => (a.inv_mass, a.inv_inertia, a.body_type, a.x, a.y, a.angle.cos(), a.angle.sin()),
101 None => continue,
102 };
103 let (inv_mb, inv_ib, type_b, xb, yb, cos_b, sin_b) = match &bodies[id_b] {
104 Some(b) => (b.inv_mass, b.inv_inertia, b.body_type, b.x, b.y, b.angle.cos(), b.angle.sin()),
105 None => continue,
106 };
107
108 let wa_x = point.local_a.0 * cos_a - point.local_a.1 * sin_a + xa;
110 let wa_y = point.local_a.0 * sin_a + point.local_a.1 * cos_a + ya;
111 let wb_x = point.local_b.0 * cos_b - point.local_b.1 * sin_b + xb;
112 let wb_y = point.local_b.0 * sin_b + point.local_b.1 * cos_b + yb;
113
114 let ra_x = wa_x - xa;
116 let ra_y = wa_y - ya;
117 let rb_x = wb_x - xb;
118 let rb_y = wb_y - yb;
119
120 if let Some(a) = &mut bodies[id_a] {
121 if type_a == BodyType::Dynamic {
122 a.vx -= px * inv_ma;
123 a.vy -= py * inv_ma;
124 let ra_cross_p = ra_x * py - ra_y * px;
125 a.angular_velocity -= ra_cross_p * inv_ia;
126 }
127 }
128 if let Some(b) = &mut bodies[id_b] {
129 if type_b == BodyType::Dynamic {
130 b.vx += px * inv_mb;
131 b.vy += py * inv_mb;
132 let rb_cross_p = rb_x * py - rb_y * px;
133 b.angular_velocity += rb_cross_p * inv_ib;
134 }
135 }
136 }
137 }
138}
139
140pub fn resolve_manifolds_velocity_iteration(
143 bodies: &mut [Option<RigidBody>],
144 manifolds: &mut [ContactManifold],
145 reverse: bool,
146 sub_dt: f32,
147) {
148 let len = manifolds.len();
149 if reverse {
150 for i in (0..len).rev() {
151 resolve_manifold_velocity(bodies, &mut manifolds[i], sub_dt);
152 }
153 } else {
154 for i in 0..len {
155 resolve_manifold_velocity(bodies, &mut manifolds[i], sub_dt);
156 }
157 }
158}
159
160fn resolve_manifold_velocity(bodies: &mut [Option<RigidBody>], manifold: &mut ContactManifold, sub_dt: f32) {
163 let id_a = manifold.body_a as usize;
164 let id_b = manifold.body_b as usize;
165
166 let (inv_ma, inv_ia, fric_a, type_a, xa, ya, cos_a, sin_a) = {
168 let a = match &bodies[id_a] {
169 Some(b) => b,
170 None => return,
171 };
172 (
173 a.inv_mass, a.inv_inertia,
174 a.material.friction, a.body_type, a.x, a.y, a.angle.cos(), a.angle.sin(),
175 )
176 };
177 let (inv_mb, inv_ib, fric_b, type_b, xb, yb, cos_b, sin_b) = {
178 let b = match &bodies[id_b] {
179 Some(b) => b,
180 None => return,
181 };
182 (
183 b.inv_mass, b.inv_inertia,
184 b.material.friction, b.body_type, b.x, b.y, b.angle.cos(), b.angle.sin(),
185 )
186 };
187
188 if type_a != BodyType::Dynamic && type_b != BodyType::Dynamic {
189 return;
190 }
191
192 let (nx, ny) = manifold.normal;
193 let (tx, ty) = manifold.tangent;
194 let velocity_bias = manifold.velocity_bias;
195 let mu = (fric_a * fric_b).sqrt();
196 let num_points = manifold.points.len() as f32;
197
198 for point in &mut manifold.points {
200 let wa_x = point.local_a.0 * cos_a - point.local_a.1 * sin_a + xa;
202 let wa_y = point.local_a.0 * sin_a + point.local_a.1 * cos_a + ya;
203 let wb_x = point.local_b.0 * cos_b - point.local_b.1 * sin_b + xb;
204 let wb_y = point.local_b.0 * sin_b + point.local_b.1 * cos_b + yb;
205
206 let ra_x = wa_x - xa;
208 let ra_y = wa_y - ya;
209 let rb_x = wb_x - xb;
210 let rb_y = wb_y - yb;
211
212 let (vax, vay, ava) = bodies[id_a].as_ref().map_or((0.0, 0.0, 0.0), |b| (b.vx, b.vy, b.angular_velocity));
214 let (vbx, vby, avb) = bodies[id_b].as_ref().map_or((0.0, 0.0, 0.0), |b| (b.vx, b.vy, b.angular_velocity));
215
216 let rel_vx = (vbx + (-avb * rb_y)) - (vax + (-ava * ra_y));
218 let rel_vy = (vby + (avb * rb_x)) - (vay + (ava * ra_x));
219
220 let vn = rel_vx * nx + rel_vy * ny;
221
222 let ra_cross_n = ra_x * ny - ra_y * nx;
224 let rb_cross_n = rb_x * ny - rb_y * nx;
225 let inv_mass_sum = inv_ma + inv_mb
226 + ra_cross_n * ra_cross_n * inv_ia
227 + rb_cross_n * rb_cross_n * inv_ib;
228
229 if inv_mass_sum == 0.0 {
230 continue;
231 }
232
233 let per_point_bias = if point.penetration < 0.0 && sub_dt > 0.0 {
238 point.penetration / sub_dt
240 } else {
241 velocity_bias / num_points
243 };
244 let j_new = -(vn - per_point_bias) / inv_mass_sum;
245
246 let old_accumulated = point.accumulated_jn;
247 point.accumulated_jn = (old_accumulated + j_new).max(0.0);
248 let j = point.accumulated_jn - old_accumulated;
249
250 if j.abs() > 1e-8 {
251 let impulse_x = j * nx;
252 let impulse_y = j * ny;
253
254 if let Some(a) = &mut bodies[id_a] {
255 if a.body_type == BodyType::Dynamic {
256 a.vx -= impulse_x * inv_ma;
257 a.vy -= impulse_y * inv_ma;
258 a.angular_velocity -= ra_cross_n * j * inv_ia;
259 }
260 }
261 if let Some(b) = &mut bodies[id_b] {
262 if b.body_type == BodyType::Dynamic {
263 b.vx += impulse_x * inv_mb;
264 b.vy += impulse_y * inv_mb;
265 b.angular_velocity += rb_cross_n * j * inv_ib;
266 }
267 }
268 }
269
270 let ra_cross_t = ra_x * ty - ra_y * tx;
272 let rb_cross_t = rb_x * ty - rb_y * tx;
273 let inv_mass_sum_t = inv_ma + inv_mb
274 + ra_cross_t * ra_cross_t * inv_ia
275 + rb_cross_t * rb_cross_t * inv_ib;
276
277 if inv_mass_sum_t > 0.0 {
278 let (vax, vay, ava) = bodies[id_a].as_ref().map_or((0.0, 0.0, 0.0), |b| (b.vx, b.vy, b.angular_velocity));
280 let (vbx, vby, avb) = bodies[id_b].as_ref().map_or((0.0, 0.0, 0.0), |b| (b.vx, b.vy, b.angular_velocity));
281
282 let rel_vx = (vbx + (-avb * rb_y)) - (vax + (-ava * ra_y));
283 let rel_vy = (vby + (avb * rb_x)) - (vay + (ava * ra_x));
284
285 let vt = rel_vx * tx + rel_vy * ty;
286
287 let current_pos = ((wa_x + wb_x) * 0.5, (wa_y + wb_y) * 0.5);
290
291 if point.friction_anchor.is_none() {
293 point.friction_anchor = Some(current_pos);
294 }
295
296 let anchor = point.friction_anchor.unwrap();
298 let drift_x = current_pos.0 - anchor.0;
299 let drift_y = current_pos.1 - anchor.1;
300 let tangent_drift = drift_x * tx + drift_y * ty;
301
302 const FRICTION_BAUMGARTE: f32 = 0.1;
304 let correction_velocity = if sub_dt > 0.0 {
305 tangent_drift * FRICTION_BAUMGARTE / sub_dt
306 } else {
307 0.0
308 };
309
310 let vt_corrected = vt + correction_velocity;
312 let jt_new = -vt_corrected / inv_mass_sum_t;
313
314 let max_friction = point.accumulated_jn * mu;
316 let old_jt = point.accumulated_jt;
317 let new_jt_accumulated = (old_jt + jt_new).clamp(-max_friction, max_friction);
318
319 let requested_jt = old_jt + jt_new;
321 let is_sliding = (new_jt_accumulated - requested_jt).abs() > 1e-8;
322
323 if is_sliding {
324 point.friction_anchor = Some(current_pos);
326 }
327
328 point.accumulated_jt = new_jt_accumulated;
329 let jt = point.accumulated_jt - old_jt;
330
331 if jt.abs() > 1e-8 {
332 let friction_x = jt * tx;
333 let friction_y = jt * ty;
334
335 if let Some(a) = &mut bodies[id_a] {
336 if a.body_type == BodyType::Dynamic {
337 a.vx -= friction_x * inv_ma;
338 a.vy -= friction_y * inv_ma;
339 a.angular_velocity -= ra_cross_t * jt * inv_ia;
340 }
341 }
342 if let Some(b) = &mut bodies[id_b] {
343 if b.body_type == BodyType::Dynamic {
344 b.vx += friction_x * inv_mb;
345 b.vy += friction_y * inv_mb;
346 b.angular_velocity += rb_cross_t * jt * inv_ib;
347 }
348 }
349 }
350 }
351 }
352}
353
354pub fn resolve_manifolds_position(
356 bodies: &mut [Option<RigidBody>],
357 manifolds: &[ContactManifold],
358 reverse: bool,
359) {
360 if reverse {
361 for manifold in manifolds.iter().rev() {
362 for point in &manifold.points {
363 position_correction_manifold_point(bodies, manifold, point);
364 }
365 }
366 } else {
367 for manifold in manifolds {
368 for point in &manifold.points {
369 position_correction_manifold_point(bodies, manifold, point);
370 }
371 }
372 }
373}
374
375fn position_correction_manifold_point(
376 bodies: &mut [Option<RigidBody>],
377 manifold: &ContactManifold,
378 point: &ManifoldPoint,
379) {
380 let id_a = manifold.body_a as usize;
381 let id_b = manifold.body_b as usize;
382
383 let (inv_ma, type_a) = match &bodies[id_a] {
384 Some(b) => (b.inv_mass, b.body_type),
385 None => return,
386 };
387 let (inv_mb, type_b) = match &bodies[id_b] {
388 Some(b) => (b.inv_mass, b.body_type),
389 None => return,
390 };
391
392 if type_a != BodyType::Dynamic && type_b != BodyType::Dynamic {
393 return;
394 }
395
396 let slop = 0.005;
397 let max_correction = 0.2;
398 let baumgarte = 0.2;
399
400 let pen = (point.penetration - slop).max(0.0);
401 let inv_total = inv_ma + inv_mb;
402 if inv_total == 0.0 {
403 return;
404 }
405
406 let correction = (pen * baumgarte).min(max_correction) / inv_total;
407
408 let (nx, ny) = manifold.normal;
409
410 if let Some(a) = &mut bodies[id_a] {
411 if a.body_type == BodyType::Dynamic {
412 a.x -= correction * inv_ma * nx;
413 a.y -= correction * inv_ma * ny;
414 }
415 }
416 if let Some(b) = &mut bodies[id_b] {
417 if b.body_type == BodyType::Dynamic {
418 b.x += correction * inv_mb * nx;
419 b.y += correction * inv_mb * ny;
420 }
421 }
422}