1use std::collections::{HashMap, HashSet};
2
3use super::broadphase::SpatialHash;
4use super::constraints::solve_constraints;
5use super::integrate::integrate;
6use super::narrowphase::test_collision;
7use super::resolve::{initialize_contacts, resolve_contacts_position, resolve_contacts_velocity_iteration, warm_start_contacts};
8use super::sleep::update_sleep;
9use super::types::*;
10
11pub struct PhysicsWorld {
12 bodies: Vec<Option<RigidBody>>,
13 free_ids: Vec<BodyId>,
14 next_id: BodyId,
15 constraints: Vec<Constraint>,
16 next_constraint_id: ConstraintId,
17 gravity: (f32, f32),
18 fixed_dt: f32,
19 accumulator: f32,
20 contacts: Vec<Contact>,
21 frame_contacts: Vec<Contact>,
26 frame_contact_pairs: HashSet<(BodyId, BodyId)>,
28 broadphase: SpatialHash,
29 solver_iterations: usize,
30 warm_cache: HashMap<(BodyId, BodyId), (f32, f32)>,
32}
33
34impl PhysicsWorld {
35 pub fn new(gravity_x: f32, gravity_y: f32) -> Self {
36 Self {
37 bodies: Vec::new(),
38 free_ids: Vec::new(),
39 next_id: 0,
40 constraints: Vec::new(),
41 next_constraint_id: 0,
42 gravity: (gravity_x, gravity_y),
43 fixed_dt: 1.0 / 60.0,
44 accumulator: 0.0,
45 contacts: Vec::new(),
46 frame_contacts: Vec::new(),
47 frame_contact_pairs: HashSet::new(),
48 broadphase: SpatialHash::new(64.0),
49 solver_iterations: 6,
50 warm_cache: HashMap::new(),
51 }
52 }
53
54 pub fn step(&mut self, dt: f32) {
61 self.accumulator += dt;
62
63 self.frame_contacts.clear();
65 self.frame_contact_pairs.clear();
66
67 while self.accumulator >= self.fixed_dt {
68 let sub_dt = self.fixed_dt / 4.0;
69 for _ in 0..4 {
70 self.sub_step(sub_dt);
71
72 for contact in &self.contacts {
74 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
75 if self.frame_contact_pairs.insert(key) {
76 self.frame_contacts.push(contact.clone());
77 }
78 }
79 }
80 self.accumulator -= self.fixed_dt;
81 }
82 }
83
84 fn sub_step(&mut self, dt: f32) {
85 for body in self.bodies.iter_mut().flatten() {
87 integrate(body, self.gravity.0, self.gravity.1, dt);
88 }
89
90 self.broadphase.clear();
92 for body in self.bodies.iter().flatten() {
93 let (min_x, min_y, max_x, max_y) = get_shape_aabb(body);
94 self.broadphase.insert(body.id, min_x, min_y, max_x, max_y);
95 }
96 let pairs = self.broadphase.get_pairs();
97
98 self.contacts.clear();
100 for (id_a, id_b) in pairs {
101 let a_idx = id_a as usize;
102 let b_idx = id_b as usize;
103
104 let (layer_a, mask_a, sleeping_a) = match &self.bodies[a_idx] {
106 Some(b) => (b.layer, b.mask, b.sleeping),
107 None => continue,
108 };
109 let (layer_b, mask_b, sleeping_b) = match &self.bodies[b_idx] {
110 Some(b) => (b.layer, b.mask, b.sleeping),
111 None => continue,
112 };
113
114 if (layer_a & mask_b) == 0 || (layer_b & mask_a) == 0 {
116 continue;
117 }
118
119 if sleeping_a && sleeping_b {
121 continue;
122 }
123
124 let body_a = self.bodies[a_idx].as_ref().unwrap();
126 let body_b = self.bodies[b_idx].as_ref().unwrap();
127
128 if let Some(contact) = test_collision(body_a, body_b) {
129 self.contacts.push(contact);
130 }
131 }
132
133 self.contacts.sort_by(|a, b| {
135 let ay = self.bodies[a.body_a as usize]
136 .as_ref()
137 .map_or(0.0f32, |body| body.y)
138 .max(
139 self.bodies[a.body_b as usize]
140 .as_ref()
141 .map_or(0.0f32, |body| body.y),
142 );
143 let by = self.bodies[b.body_a as usize]
144 .as_ref()
145 .map_or(0.0f32, |body| body.y)
146 .max(
147 self.bodies[b.body_b as usize]
148 .as_ref()
149 .map_or(0.0f32, |body| body.y),
150 );
151 by.partial_cmp(&ay).unwrap_or(std::cmp::Ordering::Equal)
152 });
153
154 let gravity_mag = (self.gravity.0 * self.gravity.0 + self.gravity.1 * self.gravity.1).sqrt();
157 let restitution_threshold = gravity_mag * dt * 1.5;
158 initialize_contacts(&self.bodies, &mut self.contacts, restitution_threshold);
159
160 for contact in &mut self.contacts {
162 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
163 if let Some(&(jn, jt)) = self.warm_cache.get(&key) {
164 contact.accumulated_jn = jn * 0.95;
168 contact.accumulated_jt = jt * 0.95;
169 }
170 }
171 warm_start_contacts(&mut self.bodies, &self.contacts);
172
173 for i in 0..self.solver_iterations {
175 let reverse = i % 2 == 1;
176 resolve_contacts_velocity_iteration(&mut self.bodies, &mut self.contacts, reverse);
177 solve_constraints(&mut self.bodies, &self.constraints, dt);
178 }
179
180 self.warm_cache.clear();
182 for contact in &self.contacts {
183 let key = (contact.body_a.min(contact.body_b), contact.body_a.max(contact.body_b));
184 self.warm_cache.insert(key, (contact.accumulated_jn, contact.accumulated_jt));
185 }
186
187 for i in 0..3 {
192 for contact in &mut self.contacts {
193 let a = &self.bodies[contact.body_a as usize];
194 let b = &self.bodies[contact.body_b as usize];
195 if let (Some(a), Some(b)) = (a, b) {
196 if let Some(fresh) = test_collision(a, b) {
197 contact.penetration = fresh.penetration;
198 contact.normal = fresh.normal;
199 contact.contact_point = fresh.contact_point;
200 } else {
201 contact.penetration = 0.0;
202 }
203 }
204 }
205 resolve_contacts_position(&mut self.bodies, &self.contacts, i % 2 == 1);
206 }
207
208 for contact in &self.contacts {
216 let a_idx = contact.body_a as usize;
217 let b_idx = contact.body_b as usize;
218
219 let still_penetrating = match (&self.bodies[a_idx], &self.bodies[b_idx]) {
221 (Some(a), Some(b)) => test_collision(a, b).map_or(false, |c| c.penetration > 0.01),
222 _ => false,
223 };
224
225 if !still_penetrating {
226 continue;
227 }
228
229 let (nx, ny) = contact.normal;
230
231 if let Some(a) = &mut self.bodies[a_idx] {
233 if a.body_type == BodyType::Dynamic {
234 let vn = a.vx * nx + a.vy * ny;
235 if vn > 0.0 {
236 a.vx -= vn * nx;
237 a.vy -= vn * ny;
238 }
239 }
240 }
241 if let Some(b) = &mut self.bodies[b_idx] {
243 if b.body_type == BodyType::Dynamic {
244 let vn = b.vx * nx + b.vy * ny;
245 if vn < 0.0 {
246 b.vx -= vn * nx;
247 b.vy -= vn * ny;
248 }
249 }
250 }
251 }
252
253 update_sleep(&mut self.bodies, &self.contacts, dt);
255 }
256
257 pub fn add_body(
258 &mut self,
259 body_type: BodyType,
260 shape: Shape,
261 x: f32,
262 y: f32,
263 mass: f32,
264 material: Material,
265 layer: u16,
266 mask: u16,
267 ) -> BodyId {
268 let id = if let Some(recycled) = self.free_ids.pop() {
269 recycled
270 } else {
271 let id = self.next_id;
272 self.next_id += 1;
273 id
274 };
275
276 let (inv_mass, inertia, inv_inertia) = compute_mass_and_inertia(&shape, mass, body_type);
277
278 let body = RigidBody {
279 id,
280 body_type,
281 shape,
282 material,
283 x,
284 y,
285 angle: 0.0,
286 vx: 0.0,
287 vy: 0.0,
288 angular_velocity: 0.0,
289 fx: 0.0,
290 fy: 0.0,
291 torque: 0.0,
292 mass,
293 inv_mass,
294 inertia,
295 inv_inertia,
296 layer,
297 mask,
298 sleeping: false,
299 sleep_timer: 0.0,
300 };
301
302 let idx = id as usize;
303 if idx >= self.bodies.len() {
304 self.bodies.resize_with(idx + 1, || None);
305 }
306 self.bodies[idx] = Some(body);
307 id
308 }
309
310 pub fn remove_body(&mut self, id: BodyId) {
311 let idx = id as usize;
312 if idx < self.bodies.len() {
313 self.bodies[idx] = None;
314 self.free_ids.push(id);
315 }
316 }
317
318 pub fn get_body(&self, id: BodyId) -> Option<&RigidBody> {
319 self.bodies.get(id as usize)?.as_ref()
320 }
321
322 pub fn get_body_mut(&mut self, id: BodyId) -> Option<&mut RigidBody> {
323 self.bodies.get_mut(id as usize)?.as_mut()
324 }
325
326 pub fn set_velocity(&mut self, id: BodyId, vx: f32, vy: f32) {
327 if let Some(body) = self.get_body_mut(id) {
328 body.vx = vx;
329 body.vy = vy;
330 body.sleeping = false;
331 body.sleep_timer = 0.0;
332 }
333 }
334
335 pub fn set_angular_velocity(&mut self, id: BodyId, av: f32) {
336 if let Some(body) = self.get_body_mut(id) {
337 body.angular_velocity = av;
338 body.sleeping = false;
339 body.sleep_timer = 0.0;
340 }
341 }
342
343 pub fn apply_force(&mut self, id: BodyId, fx: f32, fy: f32) {
344 if let Some(body) = self.get_body_mut(id) {
345 body.fx += fx;
346 body.fy += fy;
347 body.sleeping = false;
348 body.sleep_timer = 0.0;
349 }
350 }
351
352 pub fn apply_impulse(&mut self, id: BodyId, ix: f32, iy: f32) {
353 if let Some(body) = self.get_body_mut(id) {
354 body.vx += ix * body.inv_mass;
355 body.vy += iy * body.inv_mass;
356 body.sleeping = false;
357 body.sleep_timer = 0.0;
358 }
359 }
360
361 pub fn set_position(&mut self, id: BodyId, x: f32, y: f32) {
362 if let Some(body) = self.get_body_mut(id) {
363 body.x = x;
364 body.y = y;
365 body.sleeping = false;
366 body.sleep_timer = 0.0;
367 }
368 }
369
370 pub fn set_collision_layers(&mut self, id: BodyId, layer: u16, mask: u16) {
371 if let Some(body) = self.get_body_mut(id) {
372 body.layer = layer;
373 body.mask = mask;
374 }
375 }
376
377 pub fn add_constraint(&mut self, constraint: Constraint) -> ConstraintId {
378 let id = self.next_constraint_id;
379 self.next_constraint_id += 1;
380
381 let constraint = match constraint {
382 Constraint::Distance {
383 body_a,
384 body_b,
385 distance,
386 anchor_a,
387 anchor_b,
388 ..
389 } => Constraint::Distance {
390 id,
391 body_a,
392 body_b,
393 distance,
394 anchor_a,
395 anchor_b,
396 },
397 Constraint::Revolute {
398 body_a,
399 body_b,
400 pivot,
401 ..
402 } => Constraint::Revolute {
403 id,
404 body_a,
405 body_b,
406 pivot,
407 },
408 };
409 self.constraints.push(constraint);
410 id
411 }
412
413 pub fn remove_constraint(&mut self, id: ConstraintId) {
414 self.constraints.retain(|c| c.id() != id);
415 }
416
417 pub fn query_aabb(&self, min_x: f32, min_y: f32, max_x: f32, max_y: f32) -> Vec<BodyId> {
418 let mut result = Vec::new();
419 for body in self.bodies.iter().flatten() {
420 let (bmin_x, bmin_y, bmax_x, bmax_y) = get_shape_aabb(body);
421 if bmax_x >= min_x && bmin_x <= max_x && bmax_y >= min_y && bmin_y <= max_y {
422 result.push(body.id);
423 }
424 }
425 result
426 }
427
428 pub fn raycast(
429 &self,
430 ox: f32,
431 oy: f32,
432 dx: f32,
433 dy: f32,
434 max_dist: f32,
435 ) -> Option<(BodyId, f32, f32, f32)> {
436 let dir_len = (dx * dx + dy * dy).sqrt();
437 if dir_len < 1e-8 {
438 return None;
439 }
440 let ndx = dx / dir_len;
441 let ndy = dy / dir_len;
442
443 let mut closest: Option<(BodyId, f32, f32, f32)> = None;
444
445 for body in self.bodies.iter().flatten() {
446 let t = match &body.shape {
447 Shape::Circle { radius } => {
448 ray_vs_circle(ox, oy, ndx, ndy, body.x, body.y, *radius)
449 }
450 Shape::AABB { half_w, half_h } => {
451 ray_vs_aabb(ox, oy, ndx, ndy, body.x, body.y, *half_w, *half_h)
452 }
453 Shape::Polygon { vertices } => {
454 ray_vs_polygon(ox, oy, ndx, ndy, body, vertices)
455 }
456 };
457
458 if let Some(t) = t {
459 if t >= 0.0 && t <= max_dist {
460 let hit_x = ox + ndx * t;
461 let hit_y = oy + ndy * t;
462 if closest.is_none() || t < closest.unwrap().3 {
463 closest = Some((body.id, hit_x, hit_y, t));
464 }
465 }
466 }
467 }
468 closest
469 }
470
471 pub fn get_contacts(&self) -> &[Contact] {
476 &self.frame_contacts
477 }
478
479 pub fn all_bodies(&self) -> Vec<&RigidBody> {
481 self.bodies.iter().filter_map(|b| b.as_ref()).collect()
482 }
483
484 pub fn gravity(&self) -> (f32, f32) {
486 self.gravity
487 }
488
489 pub fn body_count(&self) -> usize {
491 self.bodies.iter().filter(|b| b.is_some()).count()
492 }
493}
494
495fn ray_vs_circle(
496 ox: f32, oy: f32,
497 dx: f32, dy: f32,
498 cx: f32, cy: f32,
499 radius: f32,
500) -> Option<f32> {
501 let fx = ox - cx;
502 let fy = oy - cy;
503 let a = dx * dx + dy * dy;
504 let b = 2.0 * (fx * dx + fy * dy);
505 let c = fx * fx + fy * fy - radius * radius;
506 let discriminant = b * b - 4.0 * a * c;
507 if discriminant < 0.0 {
508 return None;
509 }
510 let sqrt_d = discriminant.sqrt();
511 let t1 = (-b - sqrt_d) / (2.0 * a);
512 let t2 = (-b + sqrt_d) / (2.0 * a);
513 if t1 >= 0.0 {
514 Some(t1)
515 } else if t2 >= 0.0 {
516 Some(t2)
517 } else {
518 None
519 }
520}
521
522fn ray_vs_aabb(
523 ox: f32, oy: f32,
524 dx: f32, dy: f32,
525 cx: f32, cy: f32,
526 hw: f32, hh: f32,
527) -> Option<f32> {
528 let min_x = cx - hw;
529 let max_x = cx + hw;
530 let min_y = cy - hh;
531 let max_y = cy + hh;
532
533 let (mut tmin, mut tmax) = if dx.abs() < 1e-8 {
534 if ox < min_x || ox > max_x {
535 return None;
536 }
537 (f32::MIN, f32::MAX)
538 } else {
539 let inv_dx = 1.0 / dx;
540 let t1 = (min_x - ox) * inv_dx;
541 let t2 = (max_x - ox) * inv_dx;
542 (t1.min(t2), t1.max(t2))
543 };
544
545 let (tymin, tymax) = if dy.abs() < 1e-8 {
546 if oy < min_y || oy > max_y {
547 return None;
548 }
549 (f32::MIN, f32::MAX)
550 } else {
551 let inv_dy = 1.0 / dy;
552 let t1 = (min_y - oy) * inv_dy;
553 let t2 = (max_y - oy) * inv_dy;
554 (t1.min(t2), t1.max(t2))
555 };
556
557 tmin = tmin.max(tymin);
558 tmax = tmax.min(tymax);
559
560 if tmin > tmax || tmax < 0.0 {
561 return None;
562 }
563
564 Some(if tmin >= 0.0 { tmin } else { tmax })
565}
566
567fn ray_vs_polygon(
568 ox: f32, oy: f32,
569 dx: f32, dy: f32,
570 body: &RigidBody,
571 vertices: &[(f32, f32)],
572) -> Option<f32> {
573 let cos = body.angle.cos();
574 let sin = body.angle.sin();
575 let n = vertices.len();
576 if n < 3 {
577 return None;
578 }
579
580 let mut closest_t: Option<f32> = None;
581
582 for i in 0..n {
583 let (vx0, vy0) = vertices[i];
584 let (vx1, vy1) = vertices[(i + 1) % n];
585
586 let ax = vx0 * cos - vy0 * sin + body.x;
588 let ay = vx0 * sin + vy0 * cos + body.y;
589 let bx = vx1 * cos - vy1 * sin + body.x;
590 let by = vx1 * sin + vy1 * cos + body.y;
591
592 if let Some(t) = ray_vs_segment(ox, oy, dx, dy, ax, ay, bx, by) {
593 if closest_t.is_none() || t < closest_t.unwrap() {
594 closest_t = Some(t);
595 }
596 }
597 }
598 closest_t
599}
600
601fn ray_vs_segment(
602 ox: f32, oy: f32,
603 dx: f32, dy: f32,
604 ax: f32, ay: f32,
605 bx: f32, by: f32,
606) -> Option<f32> {
607 let ex = bx - ax;
608 let ey = by - ay;
609 let denom = dx * ey - dy * ex;
610 if denom.abs() < 1e-8 {
611 return None;
612 }
613 let t = ((ax - ox) * ey - (ay - oy) * ex) / denom;
614 let u = ((ax - ox) * dy - (ay - oy) * dx) / denom;
615 if t >= 0.0 && u >= 0.0 && u <= 1.0 {
616 Some(t)
617 } else {
618 None
619 }
620}