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