1use crate::collision::{Capsule, PlaneResult, WorldCastOutput};
9use crate::debug_draw::{DebugDraw, HexColor};
10use crate::distance::ShapeProxy;
11use crate::id::ShapeId;
12use crate::math_functions::{mul_sv, offset_pos, Aabb, Pos, Vec2, WorldTransform, ROT_IDENTITY};
13use crate::shape::{shape_get_aabb, shape_is_valid};
14use crate::types::QueryFilter;
15use crate::world::World;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub enum RecQueryType {
22 #[default]
23 OverlapAabb,
24 OverlapShape,
25 CastRay,
26 CastShape,
27 CollideMover,
28 CastRayClosest,
29 CastMover,
30 ShapeTestPoint,
31 ShapeRayCast,
32}
33
34#[derive(Debug, Clone, Copy, Default)]
37pub(crate) struct RecordedHit {
38 pub id: ShapeId,
39 pub point: Pos,
40 pub normal: Vec2,
41 pub fraction: f32,
42 pub plane: PlaneResult,
43 #[allow(dead_code)] pub user_return_f: f32,
45 #[allow(dead_code)]
46 pub user_return_b: bool,
47}
48
49#[derive(Debug, Clone, Default)]
51pub(crate) struct DrawQuery {
52 pub kind: RecQueryType,
53 pub filter: QueryFilter,
54 pub aabb: Aabb,
55 pub proxy: ShapeProxy,
56 pub mover: Capsule,
57 pub origin: Pos,
58 pub translation: Vec2,
59 pub bool_result: bool,
60 #[allow(dead_code)] pub cast_fraction: f32,
62 pub cast_out: WorldCastOutput,
63 pub shape: ShapeId,
64 pub hit_start: usize,
65 pub hit_count: usize,
66}
67
68#[derive(Default)]
72pub(crate) struct QueryStash {
73 pub queries: Vec<DrawQuery>,
74 pub hits: Vec<RecordedHit>,
75}
76
77impl QueryStash {
78 pub fn clear(&mut self) {
79 self.queries.clear();
80 self.hits.clear();
81 }
82
83 pub fn begin(&mut self, kind: RecQueryType, hits: &[RecordedHit]) -> &mut DrawQuery {
86 let hit_start = self.hits.len();
87 self.hits.extend_from_slice(hits);
88 self.queries.push(DrawQuery {
89 kind,
90 hit_start,
91 hit_count: hits.len(),
92 ..DrawQuery::default()
93 });
94 self.queries.last_mut().unwrap()
95 }
96}
97
98#[derive(Debug, Clone, Copy, Default)]
101pub struct RecQueryInfo {
102 pub type_: RecQueryType,
103 pub filter: QueryFilter,
105 pub aabb: Aabb,
107 pub origin: Pos,
109 pub translation: Vec2,
111 pub shape: ShapeId,
113 pub hit_count: i32,
115}
116
117#[derive(Debug, Clone, Copy, Default)]
119pub struct RecQueryHit {
120 pub shape: ShapeId,
121 pub point: Pos,
122 pub normal: Vec2,
123 pub fraction: f32,
124}
125
126fn draw_hit_aabbs(world: &World, stash: &QueryStash, q: &DrawQuery, draw: &mut dyn DebugDraw) {
130 for hit in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
131 if !shape_is_valid(world, hit.id) {
132 continue;
133 }
134 let b = shape_get_aabb(world, hit.id);
135 let lower = b.lower_bound;
136 let upper = b.upper_bound;
137 let vs = [
138 lower,
139 Vec2 {
140 x: upper.x,
141 y: lower.y,
142 },
143 upper,
144 Vec2 {
145 x: lower.x,
146 y: upper.y,
147 },
148 ];
149 draw.draw_polygon(
150 crate::math_functions::WORLD_TRANSFORM_IDENTITY,
151 &vs,
152 HexColor::MAGENTA,
153 );
154 }
155}
156
157pub(crate) fn draw_stashed_queries(
162 world: &World,
163 stash: &QueryStash,
164 draw: &mut dyn DebugDraw,
165 query_index: i32,
166) {
167 for (qi, q) in stash.queries.iter().enumerate() {
168 if query_index >= 0 && qi as i32 != query_index {
169 continue;
170 }
171
172 match q.kind {
173 RecQueryType::CastRay | RecQueryType::CastRayClosest => {
174 let origin = q.origin;
176 let end = offset_pos(origin, q.translation);
177 draw.draw_line(origin, end, HexColor::YELLOW);
178 for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
180 let point = h.point;
181 draw.draw_point(point, 4.0, HexColor::YELLOW);
182 let np = offset_pos(point, mul_sv(0.2, h.normal));
183 draw.draw_line(point, np, HexColor::LIGHT_YELLOW);
184 }
185 }
186 RecQueryType::CastShape => {
187 for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
189 let point = h.point;
190 draw.draw_point(point, 4.0, HexColor::SKY_BLUE);
191 let np = offset_pos(point, mul_sv(0.2, h.normal));
192 draw.draw_line(point, np, HexColor::LIGHT_SKY_BLUE);
193 }
194 }
195 RecQueryType::CastMover => {
196 let c1 = offset_pos(q.origin, q.mover.center1);
198 let c2 = offset_pos(q.origin, q.mover.center2);
199 draw.draw_solid_capsule(c1, c2, q.mover.radius, HexColor::LIGHT_SKY_BLUE);
200 }
201 RecQueryType::OverlapAabb => {
202 let lower = q.aabb.lower_bound;
204 let upper = q.aabb.upper_bound;
205 let vs = [
206 lower,
207 Vec2 {
208 x: upper.x,
209 y: lower.y,
210 },
211 upper,
212 Vec2 {
213 x: lower.x,
214 y: upper.y,
215 },
216 ];
217 draw.draw_polygon(
218 WorldTransform {
219 p: q.origin,
220 q: ROT_IDENTITY,
221 },
222 &vs,
223 HexColor::LIME_GREEN,
224 );
225 draw_hit_aabbs(world, stash, q, draw);
226 }
227 RecQueryType::OverlapShape => {
228 if q.proxy.count == 1 {
230 draw.draw_circle(
231 offset_pos(q.origin, q.proxy.points[0]),
232 q.proxy.radius,
233 HexColor::LIME_GREEN,
234 );
235 } else if q.proxy.count >= 2 {
236 draw.draw_polygon(
237 WorldTransform {
238 p: q.origin,
239 q: ROT_IDENTITY,
240 },
241 &q.proxy.points[..q.proxy.count as usize],
242 HexColor::LIME_GREEN,
243 );
244 }
245 draw_hit_aabbs(world, stash, q, draw);
246 }
247 RecQueryType::CollideMover => {
248 let c1 = offset_pos(q.origin, q.mover.center1);
251 let c2 = offset_pos(q.origin, q.mover.center2);
252 draw.draw_solid_capsule(c1, c2, q.mover.radius, HexColor::TAN);
253 for h in &stash.hits[q.hit_start..q.hit_start + q.hit_count] {
255 if h.plane.hit {
256 let point = offset_pos(q.origin, h.plane.point);
257 let np = offset_pos(point, mul_sv(0.2, h.plane.plane.normal));
258 draw.draw_line(point, np, HexColor::ORANGE);
259 }
260 }
261 }
262 RecQueryType::ShapeTestPoint => {
263 let c = if q.bool_result {
264 HexColor::AQUA
265 } else {
266 HexColor::RED
267 };
268 draw.draw_point(q.origin, 6.0, c);
269 }
270 RecQueryType::ShapeRayCast => {
271 let origin = q.origin;
272 let end = offset_pos(origin, q.translation);
273 draw.draw_line(origin, end, HexColor::VIOLET);
274 if q.cast_out.hit {
275 draw.draw_point(q.cast_out.point, 4.0, HexColor::VIOLET);
276 }
277 }
278 }
279 }
280}
281
282pub(crate) fn stash_query_info(stash: &QueryStash, index: i32) -> RecQueryInfo {
284 if index < 0 || index as usize >= stash.queries.len() {
285 return RecQueryInfo::default();
286 }
287 let q = &stash.queries[index as usize];
288 RecQueryInfo {
289 type_: q.kind,
290 filter: q.filter,
291 aabb: q.aabb,
292 origin: q.origin,
293 translation: q.translation,
294 shape: q.shape,
295 hit_count: q.hit_count as i32,
296 }
297}
298
299pub(crate) fn stash_query_hit(stash: &QueryStash, query_index: i32, hit_index: i32) -> RecQueryHit {
301 if query_index < 0 || query_index as usize >= stash.queries.len() {
302 return RecQueryHit::default();
303 }
304 let q = &stash.queries[query_index as usize];
305 if hit_index < 0 || hit_index as usize >= q.hit_count {
306 return RecQueryHit::default();
307 }
308 let h = &stash.hits[q.hit_start + hit_index as usize];
309 RecQueryHit {
310 shape: h.id,
311 point: h.point,
312 normal: h.normal,
313 fraction: h.fraction,
314 }
315}