use crate::collision::{Capsule, PlaneResult, WorldCastOutput};
use crate::debug_draw::{DebugDraw, HexColor};
use crate::distance::ShapeProxy;
use crate::id::ShapeId;
use crate::math_functions::{mul_sv, offset_pos, Aabb, Pos, Vec2, WorldTransform, ROT_IDENTITY};
use crate::shape::{shape_get_aabb, shape_is_valid};
use crate::types::QueryFilter;
use crate::world::World;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RecQueryType {
#[default]
OverlapAabb,
OverlapShape,
CastRay,
CastShape,
CollideMover,
CastRayClosest,
CastMover,
ShapeTestPoint,
ShapeRayCast,
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct RecordedHit {
pub id: ShapeId,
pub point: Pos,
pub normal: Vec2,
pub fraction: f32,
pub plane: PlaneResult,
#[allow(dead_code)] pub user_return_f: f32,
#[allow(dead_code)]
pub user_return_b: bool,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct DrawQuery {
pub kind: RecQueryType,
pub filter: QueryFilter,
pub aabb: Aabb,
pub proxy: ShapeProxy,
pub mover: Capsule,
pub origin: Pos,
pub translation: Vec2,
pub bool_result: bool,
#[allow(dead_code)] pub cast_fraction: f32,
pub cast_out: WorldCastOutput,
pub shape: ShapeId,
pub hit_start: usize,
pub hit_count: usize,
}
#[derive(Default)]
pub(crate) struct QueryStash {
pub queries: Vec<DrawQuery>,
pub hits: Vec<RecordedHit>,
}
impl QueryStash {
pub fn clear(&mut self) {
self.queries.clear();
self.hits.clear();
}
pub fn begin(&mut self, kind: RecQueryType, hits: &[RecordedHit]) -> &mut DrawQuery {
let hit_start = self.hits.len();
self.hits.extend_from_slice(hits);
self.queries.push(DrawQuery {
kind,
hit_start,
hit_count: hits.len(),
..DrawQuery::default()
});
self.queries.last_mut().unwrap()
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RecQueryInfo {
pub type_: RecQueryType,
pub filter: QueryFilter,
pub aabb: Aabb,
pub origin: Pos,
pub translation: Vec2,
pub shape: ShapeId,
pub hit_count: i32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RecQueryHit {
pub shape: ShapeId,
pub point: Pos,
pub normal: Vec2,
pub fraction: f32,
}
fn draw_hit_aabbs(world: &World, stash: &QueryStash, q: &DrawQuery, draw: &mut dyn DebugDraw) {
for hit in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
if !shape_is_valid(world, hit.id) {
continue;
}
let b = shape_get_aabb(world, hit.id);
let lower = b.lower_bound;
let upper = b.upper_bound;
let vs = [
lower,
Vec2 {
x: upper.x,
y: lower.y,
},
upper,
Vec2 {
x: lower.x,
y: upper.y,
},
];
draw.draw_polygon(
crate::math_functions::WORLD_TRANSFORM_IDENTITY,
&vs,
HexColor::MAGENTA,
);
}
}
pub(crate) fn draw_stashed_queries(
world: &World,
stash: &QueryStash,
draw: &mut dyn DebugDraw,
query_index: i32,
) {
for (qi, q) in stash.queries.iter().enumerate() {
if query_index >= 0 && qi as i32 != query_index {
continue;
}
match q.kind {
RecQueryType::CastRay | RecQueryType::CastRayClosest => {
let origin = q.origin;
let end = offset_pos(origin, q.translation);
draw.draw_line(origin, end, HexColor::YELLOW);
for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
let point = h.point;
draw.draw_point(point, 4.0, HexColor::YELLOW);
let np = offset_pos(point, mul_sv(0.2, h.normal));
draw.draw_line(point, np, HexColor::LIGHT_YELLOW);
}
}
RecQueryType::CastShape => {
for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
let point = h.point;
draw.draw_point(point, 4.0, HexColor::SKY_BLUE);
let np = offset_pos(point, mul_sv(0.2, h.normal));
draw.draw_line(point, np, HexColor::LIGHT_SKY_BLUE);
}
}
RecQueryType::CastMover => {
let c1 = offset_pos(q.origin, q.mover.center1);
let c2 = offset_pos(q.origin, q.mover.center2);
draw.draw_solid_capsule(c1, c2, q.mover.radius, HexColor::LIGHT_SKY_BLUE);
}
RecQueryType::OverlapAabb => {
let lower = q.aabb.lower_bound;
let upper = q.aabb.upper_bound;
let vs = [
lower,
Vec2 {
x: upper.x,
y: lower.y,
},
upper,
Vec2 {
x: lower.x,
y: upper.y,
},
];
draw.draw_polygon(
WorldTransform {
p: q.origin,
q: ROT_IDENTITY,
},
&vs,
HexColor::LIME_GREEN,
);
draw_hit_aabbs(world, stash, q, draw);
}
RecQueryType::OverlapShape => {
if q.proxy.count == 1 {
draw.draw_circle(
offset_pos(q.origin, q.proxy.points[0]),
q.proxy.radius,
HexColor::LIME_GREEN,
);
} else if q.proxy.count >= 2 {
draw.draw_polygon(
WorldTransform {
p: q.origin,
q: ROT_IDENTITY,
},
&q.proxy.points[..q.proxy.count as usize],
HexColor::LIME_GREEN,
);
}
draw_hit_aabbs(world, stash, q, draw);
}
RecQueryType::CollideMover => {
let c1 = offset_pos(q.origin, q.mover.center1);
let c2 = offset_pos(q.origin, q.mover.center2);
draw.draw_solid_capsule(c1, c2, q.mover.radius, HexColor::TAN);
for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
if h.plane.hit {
let point = offset_pos(q.origin, h.plane.point);
let np = offset_pos(point, mul_sv(0.2, h.plane.plane.normal));
draw.draw_line(point, np, HexColor::ORANGE);
}
}
}
RecQueryType::ShapeTestPoint => {
let c = if q.bool_result {
HexColor::AQUA
} else {
HexColor::RED
};
draw.draw_point(q.origin, 6.0, c);
}
RecQueryType::ShapeRayCast => {
let origin = q.origin;
let end = offset_pos(origin, q.translation);
draw.draw_line(origin, end, HexColor::VIOLET);
if q.cast_out.hit {
draw.draw_point(q.cast_out.point, 4.0, HexColor::VIOLET);
}
}
}
}
}
pub(crate) fn stash_query_info(stash: &QueryStash, index: i32) -> RecQueryInfo {
if index < 0 || index as usize >= stash.queries.len() {
return RecQueryInfo::default();
}
let q = &stash.queries[index as usize];
RecQueryInfo {
type_: q.kind,
filter: q.filter,
aabb: q.aabb,
origin: q.origin,
translation: q.translation,
shape: q.shape,
hit_count: q.hit_count as i32,
}
}
pub(crate) fn stash_query_hit(stash: &QueryStash, query_index: i32, hit_index: i32) -> RecQueryHit {
if query_index < 0 || query_index as usize >= stash.queries.len() {
return RecQueryHit::default();
}
let q = &stash.queries[query_index as usize];
if hit_index < 0 || hit_index as usize >= q.hit_count {
return RecQueryHit::default();
}
let h = &stash.hits[q.hit_start + hit_index as usize];
RecQueryHit {
shape: h.id,
point: h.point,
normal: h.normal,
fraction: h.fraction,
}
}