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
399fn ray_vs_circle(
400 ox: f32, oy: f32,
401 dx: f32, dy: f32,
402 cx: f32, cy: f32,
403 radius: f32,
404) -> Option<f32> {
405 let fx = ox - cx;
406 let fy = oy - cy;
407 let a = dx * dx + dy * dy;
408 let b = 2.0 * (fx * dx + fy * dy);
409 let c = fx * fx + fy * fy - radius * radius;
410 let discriminant = b * b - 4.0 * a * c;
411 if discriminant < 0.0 {
412 return None;
413 }
414 let sqrt_d = discriminant.sqrt();
415 let t1 = (-b - sqrt_d) / (2.0 * a);
416 let t2 = (-b + sqrt_d) / (2.0 * a);
417 if t1 >= 0.0 {
418 Some(t1)
419 } else if t2 >= 0.0 {
420 Some(t2)
421 } else {
422 None
423 }
424}
425
426fn ray_vs_aabb(
427 ox: f32, oy: f32,
428 dx: f32, dy: f32,
429 cx: f32, cy: f32,
430 hw: f32, hh: f32,
431) -> Option<f32> {
432 let min_x = cx - hw;
433 let max_x = cx + hw;
434 let min_y = cy - hh;
435 let max_y = cy + hh;
436
437 let (mut tmin, mut tmax) = if dx.abs() < 1e-8 {
438 if ox < min_x || ox > max_x {
439 return None;
440 }
441 (f32::MIN, f32::MAX)
442 } else {
443 let inv_dx = 1.0 / dx;
444 let t1 = (min_x - ox) * inv_dx;
445 let t2 = (max_x - ox) * inv_dx;
446 (t1.min(t2), t1.max(t2))
447 };
448
449 let (tymin, tymax) = if dy.abs() < 1e-8 {
450 if oy < min_y || oy > max_y {
451 return None;
452 }
453 (f32::MIN, f32::MAX)
454 } else {
455 let inv_dy = 1.0 / dy;
456 let t1 = (min_y - oy) * inv_dy;
457 let t2 = (max_y - oy) * inv_dy;
458 (t1.min(t2), t1.max(t2))
459 };
460
461 tmin = tmin.max(tymin);
462 tmax = tmax.min(tymax);
463
464 if tmin > tmax || tmax < 0.0 {
465 return None;
466 }
467
468 Some(if tmin >= 0.0 { tmin } else { tmax })
469}
470
471fn ray_vs_polygon(
472 ox: f32, oy: f32,
473 dx: f32, dy: f32,
474 body: &RigidBody,
475 vertices: &[(f32, f32)],
476) -> Option<f32> {
477 let cos = body.angle.cos();
478 let sin = body.angle.sin();
479 let n = vertices.len();
480 if n < 3 {
481 return None;
482 }
483
484 let mut closest_t: Option<f32> = None;
485
486 for i in 0..n {
487 let (vx0, vy0) = vertices[i];
488 let (vx1, vy1) = vertices[(i + 1) % n];
489
490 let ax = vx0 * cos - vy0 * sin + body.x;
492 let ay = vx0 * sin + vy0 * cos + body.y;
493 let bx = vx1 * cos - vy1 * sin + body.x;
494 let by = vx1 * sin + vy1 * cos + body.y;
495
496 if let Some(t) = ray_vs_segment(ox, oy, dx, dy, ax, ay, bx, by) {
497 if closest_t.is_none() || t < closest_t.unwrap() {
498 closest_t = Some(t);
499 }
500 }
501 }
502 closest_t
503}
504
505fn ray_vs_segment(
506 ox: f32, oy: f32,
507 dx: f32, dy: f32,
508 ax: f32, ay: f32,
509 bx: f32, by: f32,
510) -> Option<f32> {
511 let ex = bx - ax;
512 let ey = by - ay;
513 let denom = dx * ey - dy * ex;
514 if denom.abs() < 1e-8 {
515 return None;
516 }
517 let t = ((ax - ox) * ey - (ay - oy) * ex) / denom;
518 let u = ((ax - ox) * dy - (ay - oy) * dx) / denom;
519 if t >= 0.0 && u >= 0.0 && u <= 1.0 {
520 Some(t)
521 } else {
522 None
523 }
524}