use bevy::{math::Mat2, prelude::*};
use crate::{physics_components::Transform2D, prelude::*};
pub struct CollPairKin(Entity, Entity);
pub struct CollPairStatic(Entity, Entity);
pub struct CollPairSensor(Entity, Entity);
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub fn broad_phase_2(
shapes: Query<&CollisionShape>,
kins: Query<(Entity, &Transform2D, &CollisionLayer),(/* Without<Vel>, */ Without<StaticBody>, Without<Sensor>)>,
statics: Query<(Entity, &Transform2D, &CollisionLayer),With<StaticBody>>,
sensors: Query<(Entity, &Transform2D, &CollisionLayer), With<Sensor>>,
mut pair_kin: EventWriter<CollPairKin>,
mut pair_static: EventWriter<CollPairStatic>,
mut pair_sensor: EventWriter<CollPairSensor>,
) {
for (i, (e1, t1, l1)) in kins.iter().enumerate() {
let aabb1 = match shapes.get(e1) {
Ok(s) => s.aabb(t1),
Err(_) => continue,
};
for (e2, t2, l2) in kins.iter().skip(i + 1) {
if l1.overlap(l2) {
let aabb2 = match shapes.get(e2) {
Ok(s) => s.aabb(t2),
Err(_) => continue,
};
if aabb1.collides(&aabb2) {
pair_kin.send(CollPairKin(e1,e2));
}
}
}
for (e2, t2, l2) in statics.iter() {
if l1.overlap(l2) {
let aabb2 = match shapes.get(e2) {
Ok(s) => s.aabb(t2),
Err(_) => continue,
};
if aabb1.collides(&aabb2) {
pair_static.send(CollPairStatic(e1,e2));
}
}
}
for (e2, t2, l2) in sensors.iter() {
if l1.overlap(l2) {
let aabb2 = match shapes.get(e2) {
Ok(s) => s.aabb(t2),
Err(_) => continue,
};
if aabb1.collides(&aabb2) {
pair_sensor.send(CollPairSensor(e1,e2));
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn narrow_phase_2(
shapes: Query<&CollisionShape>,
mut transforms: Query<&mut Transform2D>,
mut sensors: Query<&mut Sensor>,
mut vels: Query<&mut Vel>,
mut pair_kin: EventReader<CollPairKin>,
mut pair_static: EventReader<CollPairStatic>,
mut pair_sensor: EventReader<CollPairSensor>,
mut coll_writer: EventWriter<CollisionEvent>,
) {
for CollPairKin(e1, e2) in pair_kin.iter() {
let s1 = match shapes.get(*e1) {
Ok(s) => s,
Err(_) => continue,
};
let t1 = match transforms.get_component::<Transform2D>(*e1) {
Ok(t) => t,
Err(_) => continue,
};
let s2 = match shapes.get(*e2) {
Ok(s) => s,
Err(_) => continue,
};
let t2 = match transforms.get_component::<Transform2D>(*e2) {
Ok(t) => t,
Err(_) => continue,
};
let p = collide(s1,t1,s2,t2);
if let Some(pen) = p {
let normal = pen.normalize();
coll_writer.send(CollisionEvent {
entity_a: *e1,
entity_b: *e2,
is_b_static: false,
normal,
penetration: -pen,
});
if let Ok(mut t) = transforms.get_mut(*e1) {
t.add_translation(pen);
}
if let Ok(mut v) = vels.get_mut(*e1) {
if v.0.dot(normal) < 0.0 {
v.0 = v.0.slide(normal);
}
}
if let Ok(mut v) = vels.get_mut(*e2) {
if v.0.dot(-normal) < 0.0 {
v.0 = v.0.slide(normal);
}
}
}
}
for CollPairStatic(ek, es) in pair_static.iter() {
let sk = match shapes.get(*ek) {
Ok(s) => s,
Err(_) => continue,
};
let tk = match transforms.get_component::<Transform2D>(*ek) {
Ok(t) => t,
Err(_) => continue,
};
let ss = match shapes.get(*es) {
Ok(s) => s,
Err(_) => continue,
};
let ts = match transforms.get_component::<Transform2D>(*es) {
Ok(t) => t,
Err(_) => continue,
};
let p = collide(sk,tk,ss,ts);
if let Some(pen) = p {
coll_writer.send(CollisionEvent{
entity_a: *ek,
entity_b: *es,
is_b_static: true,
normal: pen.normalize(),
penetration: -pen,
});
if let Ok(mut t) = transforms.get_mut(*ek) {
t.add_translation(pen);
}
}
}
for CollPairSensor(ek, es) in pair_sensor.iter() {
let sk = match shapes.get(*ek) {
Ok(s) => s,
Err(_) => continue,
};
let tk = match transforms.get_component::<Transform2D>(*ek) {
Ok(t) => t,
Err(_) => continue,
};
let ss = match shapes.get(*es) {
Ok(s) => s,
Err(_) => continue,
};
let ts = match transforms.get_component::<Transform2D>(*es) {
Ok(t) => t,
Err(_) => continue,
};
let p = collide(sk,tk,ss,ts);
if p.is_some() {
if let Ok(mut sen) = sensors.get_mut(*es) {
if !sen.bodies.contains(ek) {
sen.bodies.push(*ek);
}
}
}
}
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub fn ray_phase(
trans: Query<&Transform2D>,
layers: Query<&CollisionLayer>,
mut rays: Query<(Entity, &mut RayCast)>,
kins: Query<(Entity, &CollisionShape),(Without<StaticBody>, Without<Sensor>)>,
stts: Query<(Entity, &CollisionShape),With<StaticBody>>,
) {
for (re, mut r) in rays.iter_mut() {
let rl = match layers.get(re) {
Ok(l) => l,
Err(_) => continue,
};
let rt = match trans.get(re) {
Ok(t) => t,
Err(_) => continue,
};
if r.collide_with_static {
let bodies_iter = kins.iter()
.chain(stts.iter())
.filter(|(e, ..)| layers.get(*e).unwrap_or(&CollisionLayer::ZERO).overlap(rl))
.filter(|(e,..)| trans.get(*e).is_ok())
.map(|(e, c)| (e, c, trans.get(e).unwrap()));
r.collision = collide_ray(&r, rt, bodies_iter);
}
else {
let bodies_iter = kins.iter()
.filter(|(e, ..)| layers.get(*e).unwrap_or(&CollisionLayer::ZERO).overlap(rl))
.filter(|(e,..)| trans.get(*e).is_ok())
.map(|(e, c)| (e, c, trans.get(e).unwrap()));
r.collision = collide_ray(&r, rt, bodies_iter);
}
}
}
pub fn collide_ray<'a,T>(
ray: &RayCast,
ray_trans: &Transform2D,
bodies: T,
) -> Option<RayCastCollision>
where
T: Iterator<Item = (Entity, &'a CollisionShape, &'a Transform2D)>
{
let r_rot = Mat2::from_angle(ray_trans.rotation());
let r_cast = r_rot * ray.cast;
let r_origin = ray_trans.translation() + r_rot * ray.offset;
let mut shortest = f32::INFINITY;
let mut short_entity = None;
for (be,bs, bt) in bodies {
let c = bs.ray(bt, r_origin, r_cast);
if let Some(c) = c {
if c > 0.0 && c < 1.0 && c < shortest {
shortest = c;
short_entity = Some(be);
}
}
}
short_entity.map(|e| RayCastCollision {
collision_point: shortest * r_cast + r_origin,
entity: e,
is_static: false,
})
}