1use crate::render::{
2 Bound, Consts, DebugDrawer, DebugLocals, DebugShadowDrawer, DebugVertex, Mesh, Model, Quad,
3 Renderer, Tri,
4};
5use common::util::srgba_to_linear;
6use hashbrown::{HashMap, HashSet};
7use tracing::warn;
8use vek::*;
9
10#[derive(Debug, PartialEq)]
11pub enum DebugShape {
12 Line([Vec3<f32>; 2], f32),
14 Cylinder {
15 radius: f32,
16 height: f32,
17 },
18 CapsulePrism {
19 p0: Vec2<f32>,
20 p1: Vec2<f32>,
21 radius: f32,
22 height: f32,
23 },
24 TrainTrack {
25 path: CubicBezier3<f32>,
26 rail_width: f32,
27 rail_sep: f32,
28 plank_width: f32,
29 plank_height: f32,
30 plank_sep: f32,
31 },
32}
33
34fn box_along_line(
48 line: LineSegment3<f32>,
49 width: f32,
50 height: f32,
51 color: [f32; 4],
52 mesh: &mut Mesh<DebugVertex>,
53) {
54 let dx = -Vec3::unit_z().cross(line.end - line.start).normalized();
57 let dz = dx.cross(line.end - line.start).normalized();
58 let w = width / 2.0;
59 let h = height / 2.0;
60 let LineSegment3 { start: q, end: r } = line;
61 let a = q - w * dx + h * dz;
62 let b = q + w * dx + h * dz;
63 let c = q - w * dx - h * dz;
64 let d = q + w * dx - h * dz;
65 let e = r - w * dx + h * dz;
66 let f = r + w * dx + h * dz;
67 let g = r - w * dx - h * dz;
68 let h = r + w * dx - h * dz;
69
70 let quad = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>, w: Vec3<f32>| {
71 let normal = (y - x).cross(z - y).normalized();
72 Quad::<DebugVertex>::new(
73 (x, color, normal).into(),
74 (y, color, normal).into(),
75 (z, color, normal).into(),
76 (w, color, normal).into(),
77 )
78 };
79
80 mesh.push_quad(quad(a, c, d, b));
81 mesh.push_quad(quad(a, b, f, e));
82 mesh.push_quad(quad(a, e, g, c));
83 mesh.push_quad(quad(b, d, h, f));
84 mesh.push_quad(quad(e, f, h, g));
85 mesh.push_quad(quad(d, c, g, h));
86}
87
88impl DebugShape {
89 pub fn mesh(&self) -> Mesh<DebugVertex> {
90 use core::f32::consts::{PI, TAU};
91 let mut mesh = Mesh::new();
92 let tri = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>| {
93 Tri::<DebugVertex>::new(x.into(), y.into(), z.into())
94 };
95 let quad = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>, w: Vec3<f32>| {
96 Quad::<DebugVertex>::new(x.into(), y.into(), z.into(), w.into())
97 };
98
99 match self {
100 DebugShape::Line([a, b], width) => {
101 box_along_line(
104 LineSegment3 { start: *a, end: *b },
105 *width,
106 *width,
107 [1.0; 4],
108 &mut mesh,
109 );
110 },
111 DebugShape::Cylinder { radius, height } => {
112 const SUBDIVISIONS: u8 = 16;
113 for i in 0..SUBDIVISIONS {
114 let to = |n: u8| {
116 let angle = TAU * f32::from(n) / f32::from(SUBDIVISIONS);
117
118 Vec3::new(radius * angle.cos(), radius * angle.sin(), 0.0)
119 };
120
121 let origin = Vec3::zero();
122 let r0 = to(i);
123 let r1 = to(i + 1);
124
125 let h = Vec3::new(0.0, 0.0, *height);
126
127 mesh.push_tri(tri(r1, r0, origin));
129 mesh.push_quad(quad(r0, r1, r1 + h, r0 + h));
131 mesh.push_tri(tri(origin + h, r0 + h, r1 + h));
133 }
134 },
135 DebugShape::CapsulePrism {
136 p0,
137 p1,
138 radius,
139 height,
140 } => {
141 const HALF_SECTORS: u8 = 8;
143 const TOTAL: u8 = HALF_SECTORS * 2;
144
145 let offset = (p0 - p1).angle_between(Vec2::new(0.0, 1.0));
146 let h = Vec3::new(0.0, 0.0, *height);
147
148 let draw_cylinder_sector =
149 |mesh: &mut Mesh<DebugVertex>, origin: Vec3<f32>, from: u8, to: u8| {
150 for i in from..to {
151 let to = |n: u8| {
153 let angle = offset + TAU * f32::from(n) / f32::from(TOTAL);
154 let (x, y) = (radius * angle.cos(), radius * angle.sin());
155 let to_edge = Vec3::new(x, y, 0.0);
156
157 origin + to_edge
158 };
159
160 let r0 = to(i);
161 let r1 = to(i + 1);
162
163 mesh.push_tri(tri(r1, r0, origin));
165 mesh.push_quad(quad(r0, r1, r1 + h, r0 + h));
167 mesh.push_tri(tri(origin + h, r0 + h, r1 + h));
169 }
170 };
171
172 let p0 = Vec3::new(p0.x, p0.y, 0.0);
173 let p1 = Vec3::new(p1.x, p1.y, 0.0);
174 draw_cylinder_sector(&mut mesh, p0, 0, HALF_SECTORS);
176
177 let a = p1 - p0;
180 let a = a / a.magnitude();
182 let a = a * *radius;
184 let orthogonal = Quaternion::rotation_z(PI / 2.0);
186 let shift = orthogonal * a;
187
188 let a0 = p0 + shift;
190 let b0 = p0 - shift;
191 let c0 = p1 - shift;
192 let d0 = p1 + shift;
193
194 let a1 = a0 + h;
196 let b1 = b0 + h;
197 let c1 = c0 + h;
198 let d1 = d0 + h;
199
200 mesh.push_quad(quad(d0, c0, b0, a0));
202
203 mesh.push_quad(quad(d0, a0, a1, d1));
206 mesh.push_quad(quad(b0, c0, c1, b1));
207
208 mesh.push_quad(quad(a1, b1, c1, d1));
210
211 draw_cylinder_sector(&mut mesh, p1, HALF_SECTORS, TOTAL);
213 },
214 DebugShape::TrainTrack {
215 path,
216 rail_width,
217 rail_sep,
218 plank_width,
219 plank_height,
220 plank_sep,
221 } => {
222 const STEEL_COLOR: [f32; 4] = [0.6, 0.6, 0.6, 1.0];
223 const WOOD_COLOR: [f32; 4] = [0.6, 0.2, 0.0, 1.0];
224 const SUBPLANK_LENGTH: usize = 5;
225 let length = path.length_by_discretization(100);
226 let num_planks = (length / (plank_sep + plank_width)).ceil() as usize;
227 let step_size = 1.0 / (SUBPLANK_LENGTH * num_planks) as f32;
228 for i in 0..(SUBPLANK_LENGTH * num_planks) {
229 let start = path.evaluate(i as f32 * step_size);
230 let end = path.evaluate((i + 1) as f32 * step_size);
231 let center = LineSegment3 { start, end };
232 let dx =
233 *rail_sep * -Vec3::unit_z().cross(center.end - center.start).normalized();
234 let dz = dx.cross(center.end - center.start).normalized();
235 let left = LineSegment3 {
236 start: center.start + dx,
237 end: center.end + dx,
238 };
239 let right = LineSegment3 {
240 start: center.start - dx,
241 end: center.end - dx,
242 };
243 box_along_line(left, *rail_width, *rail_width, STEEL_COLOR, &mut mesh);
244 box_along_line(right, *rail_width, *rail_width, STEEL_COLOR, &mut mesh);
245 if i % SUBPLANK_LENGTH == 0 {
247 let across = LineSegment3 {
248 start: center.start - 1.5 * dx - *rail_width * dz,
249 end: center.start + 1.5 * dx - *rail_width * dz,
250 };
251 box_along_line(across, *plank_width, *plank_height, WOOD_COLOR, &mut mesh);
252 }
253 }
254 },
255 }
256 mesh
257 }
258}
259
260#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
261pub struct DebugShapeId(pub u64);
262
263pub struct Debug {
264 next_shape_id: DebugShapeId,
265 shapes: HashMap<DebugShapeId, DebugShape>,
266 pending: HashSet<DebugShapeId>,
267 pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4], [f32; 4])>,
268 pending_deletes: HashSet<DebugShapeId>,
269 models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
270 casts_shadow: HashSet<DebugShapeId>,
271}
272
273impl Debug {
274 pub fn new() -> Debug {
275 Debug {
276 next_shape_id: DebugShapeId(0),
277 shapes: HashMap::new(),
278 pending: HashSet::new(),
279 pending_locals: HashMap::new(),
280 pending_deletes: HashSet::new(),
281 models: HashMap::new(),
282 casts_shadow: HashSet::new(),
283 }
284 }
285
286 pub fn add_shape(&mut self, shape: DebugShape) -> DebugShapeId {
287 let id = DebugShapeId(self.next_shape_id.0);
288 self.next_shape_id.0 += 1;
289 if matches!(shape, DebugShape::TrainTrack { .. }) {
290 self.casts_shadow.insert(id);
291 }
292 self.shapes.insert(id, shape);
293 self.pending.insert(id);
294 id
295 }
296
297 pub fn get_shape(&self, id: DebugShapeId) -> Option<&DebugShape> { self.shapes.get(&id) }
298
299 pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) {
300 self.pending_locals.insert(id, (pos, color, ori));
301 }
302
303 pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
304
305 pub fn maintain(&mut self, renderer: &mut Renderer) {
306 for id in self.pending.drain() {
307 if let Some(shape) = self.shapes.get(&id) {
308 if let Some(model) = renderer.create_model(&shape.mesh()) {
309 let locals = renderer.create_debug_bound_locals(&[DebugLocals {
310 pos: [0.0; 4],
311 color: [1.0, 0.0, 0.0, 1.0],
312 ori: [0.0, 0.0, 0.0, 1.0],
313 }]);
314 self.models.insert(id, (model, locals));
315 } else {
316 warn!(
317 "Failed to create model for debug shape {:?}: {:?}",
318 id, shape
319 );
320 }
321 }
322 }
323 for (id, (pos, color, ori)) in self.pending_locals.drain() {
324 if let Some((_, locals)) = self.models.get_mut(&id) {
325 let lc = srgba_to_linear(color.into());
326 let new_locals = [DebugLocals {
327 pos,
328 color: [lc.r, lc.g, lc.b, lc.a],
329 ori,
330 }];
331 renderer.update_consts(locals, &new_locals);
332 } else {
333 warn!(
334 "Tried to update locals for nonexistent debug shape {:?}",
335 id
336 );
337 }
338 }
339 for id in self.pending_deletes.drain() {
340 self.models.remove(&id);
341 self.shapes.remove(&id);
342 }
343 }
344
345 pub fn render<'a>(&'a self, drawer: &mut DebugDrawer<'_, 'a>) {
346 for (model, locals) in self.models.values() {
347 drawer.draw(model, locals);
348 }
349 }
350
351 pub fn render_shadows<'a>(&'a self, drawer: &mut DebugShadowDrawer<'_, 'a>) {
352 for id in self.casts_shadow.iter() {
353 if let Some((model, locals)) = self.models.get(id) {
354 drawer.draw(model, locals);
355 }
356 }
357 }
358}
359
360impl Default for Debug {
361 fn default() -> Debug { Debug::new() }
362}