|
1 use std::{ |
|
2 ops::RangeInclusive |
|
3 }; |
|
4 |
|
5 use crate::{ |
|
6 common::GearId, |
|
7 physics::PhysicsData, |
|
8 grid::Grid |
|
9 }; |
|
10 |
|
11 use fpnum::*; |
|
12 use integral_geometry::{ |
|
13 Point, Size, GridIndex |
|
14 }; |
|
15 use land2d::Land2D; |
|
16 |
|
17 pub fn fppoint_round(point: &FPPoint) -> Point { |
|
18 Point::new(point.x().round() as i32, point.y().round() as i32) |
|
19 } |
|
20 |
|
21 #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
|
22 pub struct CircleBounds { |
|
23 pub center: FPPoint, |
|
24 pub radius: FPNum |
|
25 } |
|
26 |
|
27 impl CircleBounds { |
|
28 pub fn intersects(&self, other: &CircleBounds) -> bool { |
|
29 (other.center - self.center).is_in_range(self.radius + other.radius) |
|
30 } |
|
31 |
|
32 pub fn rows(&self) -> impl Iterator<Item = (usize, RangeInclusive<usize>)> { |
|
33 let radius = self.radius.abs_round() as usize; |
|
34 let center = Point::from_fppoint(&self.center); |
|
35 (center.y as usize - radius..=center.y as usize + radius) |
|
36 .map(move |row| (row, center.x as usize - radius..=center.x as usize + radius)) |
|
37 } |
|
38 } |
|
39 |
|
40 #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
|
41 pub struct CollisionData { |
|
42 pub bounds: CircleBounds |
|
43 } |
|
44 |
|
45 #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
|
46 pub struct ContactData { |
|
47 pub elasticity: FPNum, |
|
48 pub friction: FPNum |
|
49 } |
|
50 |
|
51 struct EnabledCollisionsCollection { |
|
52 gear_ids: Vec<GearId>, |
|
53 collisions: Vec<CollisionData> |
|
54 } |
|
55 |
|
56 impl EnabledCollisionsCollection { |
|
57 fn push(&mut self, gear_id: GearId, collision: CollisionData) { |
|
58 self.gear_ids.push(gear_id); |
|
59 self.collisions.push(collision); |
|
60 } |
|
61 |
|
62 fn iter(&self) -> impl Iterator<Item = (GearId, &CollisionData)> { |
|
63 self.gear_ids.iter().cloned().zip(self.collisions.iter()) |
|
64 } |
|
65 } |
|
66 |
|
67 pub struct CollisionProcessor { |
|
68 grid: Grid, |
|
69 enabled_collisions: EnabledCollisionsCollection, |
|
70 |
|
71 detected_collisions: DetectedCollisions, |
|
72 } |
|
73 |
|
74 pub struct DetectedCollisions { |
|
75 pub pairs: Vec<(GearId, GearId)>, |
|
76 pub positions: Vec<Point> |
|
77 } |
|
78 |
|
79 impl DetectedCollisions { |
|
80 pub fn new(capacity: usize) -> Self { |
|
81 Self { |
|
82 pairs: Vec::with_capacity(capacity), |
|
83 positions: Vec::with_capacity(capacity), |
|
84 } |
|
85 } |
|
86 |
|
87 pub fn push(&mut self, contact_gear_id1: GearId, contact_gear_id2: GearId, position: &FPPoint) { |
|
88 self.pairs.push((contact_gear_id1, contact_gear_id2)); |
|
89 self.positions.push(fppoint_round(position)); |
|
90 } |
|
91 } |
|
92 |
|
93 impl CollisionProcessor { |
|
94 pub fn process(&mut self, land: &Land2D<u32>, updates: &crate::physics::PositionUpdate) { |
|
95 self.grid.check_collisions(&mut self.detected_collisions); |
|
96 |
|
97 for (gear_id, collision) in self.enabled_collisions.iter() { |
|
98 if collision.bounds.rows().any(|(y, r)| (&land[y][r]).iter().any(|v| *v != 0)) { |
|
99 self.detected_collisions.push(0, 0, &collision.bounds.center) |
|
100 } |
|
101 } |
|
102 } |
|
103 |
|
104 pub fn push(&mut self, gear_id: GearId, physics_data: PhysicsData, collision_data: CollisionData) { |
|
105 if physics_data.velocity.is_zero() { |
|
106 self.grid.insert_static(0, &physics_data.position, &collision_data.bounds); |
|
107 } else { |
|
108 self.grid.insert_dynamic(0, &physics_data.position, &collision_data.bounds); |
|
109 } |
|
110 } |
|
111 } |