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