bevy_physimple/narrow.rs
1use crate::{
2 bodies::*,
3 broad::ConBroadData,
4 physics_components::{
5 Transform2D,
6 Vel
7 },
8 plugin::CollisionEvent,
9 prelude::VecOp,
10 shapes::*,
11};
12use bevy::prelude::*;
13
14#[allow(clippy::too_many_arguments)]
15pub fn narrow_phase_system(
16 shapes: Query<&CollisionShape>,
17 mut vels: Query<&mut Vel>,
18 mut transforms: Query<&mut Transform2D>,
19 mut sensors: Query<&mut Sensor>,
20 mut broad_data: EventReader<ConBroadData>,
21 // Writer to throw collision events
22 mut collision_writer: EventWriter<CollisionEvent>,
23) {
24 // Loop over kinematic bodies
25 // Capture their sensor/static surroundings
26 // Move all kinematic bodies to where they need to be moved
27 // check collision pairs between kinematic bodies
28
29 // We need to transfer it into a Vec(or other iterable stuff) because the EventReader.iter is a 1 time consuming thingy
30 let broad_data = broad_data.iter().collect::<Vec<_>>();
31
32 for &broad in broad_data.iter() {
33 let k_entity = broad.entity;
34
35 // TODO normal error messages would be better i guess?
36 let mut k_trans = match transforms.get_component::<Transform2D>(k_entity) {
37 Ok(t) => t.clone(),
38 Err(_) => continue,
39 };
40
41 let k_shape = match shapes.get(k_entity) {
42 Ok(s) => s,
43 Err(_) => continue, // Add debug stuff
44 };
45
46 let mut iter_amount = 5; // Maximum number of collision detection - should probably be configureable
47 let mut movement = broad.inst_vel; // Current movement to check for
48
49 loop {
50 if iter_amount == 0 {
51 break;
52 }
53 iter_amount -= 1;
54
55 let mut normal = Vec2::ZERO;
56 let mut remainder = Vec2::ZERO;
57 let mut coll_entity: Option<Entity> = None;
58
59 for (s_entity, _) in broad.area.iter() {
60 let cmove = movement - remainder; // Basically only the movement left without the "recorded" collisions
61
62 // Get the obb shape thingy
63 let s_shape = match shapes.get(*s_entity) {
64 Ok(s) => s,
65 Err(_) => continue,
66 };
67
68 let s_trans = match transforms.get_component::<Transform2D>(*s_entity) {
69 Ok(t) => t,
70 Err(_) => continue,
71 };
72
73 let coll_position = s_shape.ray(s_trans, k_trans.translation(), cmove);
74 let coll_position = coll_position.unwrap_or(1.0);
75
76 let coll_pos = Transform2D::new(
77 k_trans.translation() + cmove * coll_position,
78 k_trans.rotation(),
79 k_trans.scale()
80 );
81
82 let dis = collide(k_shape, &coll_pos, s_shape, s_trans);
83
84 if let Some(dis) = dis {
85 let new_pos = coll_pos.translation() + dis;
86 normal = dis.normalize();
87
88 let moved = new_pos - k_trans.translation();
89 remainder = movement - moved;
90
91 coll_entity = Some(*s_entity);
92 }
93
94 } // out of the surroindings for loop
95 // We gonna check here for sensors, as we dont want to include it in our "main loop"
96 // and we want to check only when we know exactly how much we go further to avoid ghost triggers
97 for (se, _) in broad.sensors.iter() { // SENSOR LOOP!!!!
98 // this was pretty mostly copied from above
99 let cmove = movement - remainder; // Basically only the movement left without the "recorded" collisions
100 // let cmove_ray = (cmove.normalize(), cmove.length());
101
102 // Get the obb shape thingy
103 let s_shape = match shapes.get(*se) {
104 Ok(s) => s,
105 Err(_) => continue,
106 };
107
108 let s_trans = match transforms.get_component::<Transform2D>(*se) {
109 Ok(t) => t,
110 Err(_) => continue,
111 };
112
113 let coll_position = s_shape.ray(s_trans, k_trans.translation(), cmove);
114 let coll_position = coll_position.unwrap_or(1.0);
115
116 let coll_pos = Transform2D::new(
117 k_trans.translation() + cmove * coll_position,
118 k_trans.rotation(),
119 k_trans.scale()
120 );
121
122 let dis = collide(k_shape, &coll_pos, s_shape, s_trans);
123
124 // we dont really care how far we are penetrating, only that we indeed are penetrating
125 if dis.is_some() {
126 // we indeed collide
127 if let Ok(mut sensor) = sensors.get_mut(*se) {
128 if !sensor.bodies.contains(&k_entity) {
129 sensor.bodies.push(k_entity);
130 }
131 }
132 // TODO maybe also fire an event?
133 }
134 }
135
136 if let Some(se) = coll_entity {
137 // Supposedly to get the staticbody bounceness data
138 // let staticbody = match statics.get(se) {
139 // Ok(s) => s,
140 // Err(_) => {
141 // continue;
142 // }
143 // };
144
145 // Get the vel
146 let mut vel = match vels.get_mut(broad.entity) {
147 Ok(v) => v,
148 Err(_) => {
149 break;
150 }
151 };
152
153 let move_proj = vel.0.project(normal);
154 let move_slide = vel.0 - move_proj;
155
156 vel.0 = move_slide; // Redo bounciness + stiffness
157 // - move_proj * staticbody.bounciness.max(kin.bounciness) * kin.stiffness;
158 k_trans.add_translation(movement - remainder);
159
160 let rem_proj = remainder.project(normal);
161 let rem_slide = remainder - rem_proj;
162
163 // basically what we still need to move
164 movement = rem_slide; // same thing as 147
165 // - rem_proj * staticbody.bounciness.max(kin.bounciness) * kin.stiffness;
166
167
168 // Throw an event
169 collision_writer.send(CollisionEvent {
170 entity_a: k_entity,
171 entity_b: se,
172 is_b_static: true, // we only collide with static bodies here
173 normal,
174 penetration: Vec2::ZERO,
175 });
176 }
177 else {
178 // There was no collisions here so we can break
179 k_trans.add_translation(movement); // need to move whatever left to move with
180 break;
181 }
182 } // out of loop(line 94)
183
184 // We cloned the body's Transform2D to avoid mutability issues, so now we reapply it
185 if let Ok(mut t) = transforms.get_mut(k_entity) {
186 *t = k_trans;
187 }
188 } // out of kin_obb for loop
189}