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}