1use super::types::{BodyType, Contact, RigidBody};
2
3pub fn initialize_contacts(
7 bodies: &[Option<RigidBody>],
8 contacts: &mut [Contact],
9 restitution_threshold: f32,
10) {
11 for contact in contacts.iter_mut() {
12 let id_a = contact.body_a as usize;
13 let id_b = contact.body_b as usize;
14
15 let a = match &bodies[id_a] {
16 Some(b) => b,
17 None => continue,
18 };
19 let b = match &bodies[id_b] {
20 Some(b) => b,
21 None => continue,
22 };
23
24 let (nx, ny) = contact.normal;
25 let (cpx, cpy) = contact.contact_point;
26
27 let ra_x = cpx - a.x;
28 let ra_y = cpy - a.y;
29 let rb_x = cpx - b.x;
30 let rb_y = cpy - b.y;
31
32 let rel_vx = (b.vx + (-b.angular_velocity * rb_y)) - (a.vx + (-a.angular_velocity * ra_y));
34 let rel_vy = (b.vy + (b.angular_velocity * rb_x)) - (a.vy + (a.angular_velocity * ra_x));
35
36 let vn = rel_vx * nx + rel_vy * ny;
37
38 let e = if -vn < restitution_threshold {
40 0.0
41 } else {
42 a.material.restitution.min(b.material.restitution)
43 };
44 contact.velocity_bias = e * (-vn).max(0.0);
45
46 let tx = rel_vx - vn * nx;
48 let ty = rel_vy - vn * ny;
49 let t_len = (tx * tx + ty * ty).sqrt();
50 if t_len > 1e-8 {
51 contact.tangent = (tx / t_len, ty / t_len);
52 } else {
53 contact.tangent = (-ny, nx);
55 }
56 }
57}
58
59pub fn warm_start_contacts(
62 bodies: &mut [Option<RigidBody>],
63 contacts: &[Contact],
64) {
65 for contact in contacts {
66 let jn = contact.accumulated_jn;
67 let jt = contact.accumulated_jt;
68 if jn == 0.0 && jt == 0.0 {
69 continue;
70 }
71
72 let id_a = contact.body_a as usize;
73 let id_b = contact.body_b as usize;
74 let (nx, ny) = contact.normal;
75 let (tx, ty) = contact.tangent;
76
77 let px = jn * nx + jt * tx;
79 let py = jn * ny + jt * ty;
80
81 let (cpx, cpy) = contact.contact_point;
82
83 if let Some(a) = &mut bodies[id_a] {
84 if a.body_type == BodyType::Dynamic {
85 let ra_x = cpx - a.x;
86 let ra_y = cpy - a.y;
87 a.vx -= px * a.inv_mass;
88 a.vy -= py * a.inv_mass;
89 let ra_cross_p = ra_x * py - ra_y * px;
90 a.angular_velocity -= ra_cross_p * a.inv_inertia;
91 }
92 }
93 if let Some(b) = &mut bodies[id_b] {
94 if b.body_type == BodyType::Dynamic {
95 let rb_x = cpx - b.x;
96 let rb_y = cpy - b.y;
97 b.vx += px * b.inv_mass;
98 b.vy += py * b.inv_mass;
99 let rb_cross_p = rb_x * py - rb_y * px;
100 b.angular_velocity += rb_cross_p * b.inv_inertia;
101 }
102 }
103 }
104}
105
106pub fn resolve_contacts_velocity_iteration(
109 bodies: &mut [Option<RigidBody>],
110 contacts: &mut [Contact],
111 reverse: bool,
112) {
113 let len = contacts.len();
114 if reverse {
115 for i in (0..len).rev() {
116 resolve_single_accumulated(bodies, &mut contacts[i]);
117 }
118 } else {
119 for i in 0..len {
120 resolve_single_accumulated(bodies, &mut contacts[i]);
121 }
122 }
123}
124
125pub fn resolve_contacts_position(
128 bodies: &mut [Option<RigidBody>],
129 contacts: &[Contact],
130 reverse: bool,
131) {
132 if reverse {
133 for contact in contacts.iter().rev() {
134 position_correction(bodies, contact);
135 }
136 } else {
137 for contact in contacts {
138 position_correction(bodies, contact);
139 }
140 }
141}
142
143pub fn resolve_contacts(
145 bodies: &mut [Option<RigidBody>],
146 contacts: &mut [Contact],
147 iterations: usize,
148) {
149 let iterations = if iterations == 0 { 6 } else { iterations };
150
151 initialize_contacts(bodies, contacts, 0.0);
153
154 for i in 0..iterations {
155 resolve_contacts_velocity_iteration(bodies, contacts, i % 2 == 1);
156 }
157
158 resolve_contacts_position(bodies, contacts, false);
159}
160
161fn resolve_single_accumulated(bodies: &mut [Option<RigidBody>], contact: &mut Contact) {
166 let id_a = contact.body_a as usize;
167 let id_b = contact.body_b as usize;
168
169 let (inv_ma, inv_ia, vax, vay, ava, fric_a, type_a, xa, ya) = {
170 let a = match &bodies[id_a] {
171 Some(b) => b,
172 None => return,
173 };
174 (
175 a.inv_mass, a.inv_inertia, a.vx, a.vy, a.angular_velocity,
176 a.material.friction, a.body_type, a.x, a.y,
177 )
178 };
179 let (inv_mb, inv_ib, vbx, vby, avb, fric_b, type_b, xb, yb) = {
180 let b = match &bodies[id_b] {
181 Some(b) => b,
182 None => return,
183 };
184 (
185 b.inv_mass, b.inv_inertia, b.vx, b.vy, b.angular_velocity,
186 b.material.friction, b.body_type, b.x, b.y,
187 )
188 };
189
190 if type_a != BodyType::Dynamic && type_b != BodyType::Dynamic {
191 return;
192 }
193
194 let (nx, ny) = contact.normal;
195 let (cpx, cpy) = contact.contact_point;
196
197 let ra_x = cpx - xa;
198 let ra_y = cpy - ya;
199 let rb_x = cpx - xb;
200 let rb_y = cpy - yb;
201
202 let rel_vx = (vbx + (-avb * rb_y)) - (vax + (-ava * ra_y));
204 let rel_vy = (vby + (avb * rb_x)) - (vay + (ava * ra_x));
205
206 let vn = rel_vx * nx + rel_vy * ny;
207
208 let ra_cross_n = ra_x * ny - ra_y * nx;
209 let rb_cross_n = rb_x * ny - rb_y * nx;
210
211 let inv_mass_sum = inv_ma + inv_mb
212 + ra_cross_n * ra_cross_n * inv_ia
213 + rb_cross_n * rb_cross_n * inv_ib;
214
215 if inv_mass_sum == 0.0 {
216 return;
217 }
218
219 let j_new = -(vn - contact.velocity_bias) / inv_mass_sum;
223
224 let old_accumulated = contact.accumulated_jn;
226 contact.accumulated_jn = (old_accumulated + j_new).max(0.0);
227 let j = contact.accumulated_jn - old_accumulated; if j.abs() > 1e-8 {
230 let impulse_x = j * nx;
231 let impulse_y = j * ny;
232
233 if let Some(a) = &mut bodies[id_a] {
234 if a.body_type == BodyType::Dynamic {
235 a.vx -= impulse_x * inv_ma;
236 a.vy -= impulse_y * inv_ma;
237 a.angular_velocity -= ra_cross_n * j * inv_ia;
238 }
239 }
240 if let Some(b) = &mut bodies[id_b] {
241 if b.body_type == BodyType::Dynamic {
242 b.vx += impulse_x * inv_mb;
243 b.vy += impulse_y * inv_mb;
244 b.angular_velocity += rb_cross_n * j * inv_ib;
245 }
246 }
247 }
248
249 let (tx, ty) = contact.tangent;
252
253 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));
255 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));
256
257 let rel_vx = (vbx + (-avb * rb_y)) - (vax + (-ava * ra_y));
258 let rel_vy = (vby + (avb * rb_x)) - (vay + (ava * ra_x));
259
260 let ra_cross_t = ra_x * ty - ra_y * tx;
261 let rb_cross_t = rb_x * ty - rb_y * tx;
262
263 let inv_mass_sum_t = inv_ma + inv_mb
264 + ra_cross_t * ra_cross_t * inv_ia
265 + rb_cross_t * rb_cross_t * inv_ib;
266
267 if inv_mass_sum_t > 0.0 {
268 let vt = rel_vx * tx + rel_vy * ty;
269 let jt_new = -vt / inv_mass_sum_t;
270
271 let mu = (fric_a * fric_b).sqrt();
273 let max_friction = contact.accumulated_jn * mu;
274 let old_jt = contact.accumulated_jt;
275 contact.accumulated_jt = (old_jt + jt_new).clamp(-max_friction, max_friction);
276 let jt = contact.accumulated_jt - old_jt;
277
278 if jt.abs() > 1e-8 {
279 let friction_x = jt * tx;
280 let friction_y = jt * ty;
281
282 if let Some(a) = &mut bodies[id_a] {
283 if a.body_type == BodyType::Dynamic {
284 a.vx -= friction_x * inv_ma;
285 a.vy -= friction_y * inv_ma;
286 a.angular_velocity -= ra_cross_t * jt * inv_ia;
287 }
288 }
289 if let Some(b) = &mut bodies[id_b] {
290 if b.body_type == BodyType::Dynamic {
291 b.vx += friction_x * inv_mb;
292 b.vy += friction_y * inv_mb;
293 b.angular_velocity += rb_cross_t * jt * inv_ib;
294 }
295 }
296 }
297 }
298}
299
300fn position_correction(bodies: &mut [Option<RigidBody>], contact: &Contact) {
301 let id_a = contact.body_a as usize;
302 let id_b = contact.body_b as usize;
303
304 let (inv_ma, type_a) = match &bodies[id_a] {
305 Some(b) => (b.inv_mass, b.body_type),
306 None => return,
307 };
308 let (inv_mb, type_b) = match &bodies[id_b] {
309 Some(b) => (b.inv_mass, b.body_type),
310 None => return,
311 };
312
313 if type_a != BodyType::Dynamic && type_b != BodyType::Dynamic {
314 return;
315 }
316
317 let slop = 0.005;
318 let baumgarte = 0.4;
319
320 let correction = ((contact.penetration - slop).max(0.0) * baumgarte) / (inv_ma + inv_mb);
321
322 let (nx, ny) = contact.normal;
323
324 if let Some(a) = &mut bodies[id_a] {
325 if a.body_type == BodyType::Dynamic {
326 a.x -= correction * inv_ma * nx;
327 a.y -= correction * inv_ma * ny;
328 }
329 }
330 if let Some(b) = &mut bodies[id_b] {
331 if b.body_type == BodyType::Dynamic {
332 b.x += correction * inv_mb * nx;
333 b.y += correction * inv_mb * ny;
334 }
335 }
336}