use crate::render::{ Bound, Consts, DebugDrawer, DebugLocals, DebugVertex, Mesh, Model, Quad, Renderer, Tri, }; use common::util::srgba_to_linear; use hashbrown::{HashMap, HashSet}; use tracing::warn; use vek::*; #[derive(Debug)] pub enum DebugShape { Line([Vec3; 2]), Cylinder { radius: f32, height: f32, }, CapsulePrism { p0: Vec2, p1: Vec2, radius: f32, height: f32, }, } impl DebugShape { pub fn mesh(&self) -> Mesh { use core::f32::consts::PI; let mut mesh = Mesh::new(); let tri = |x: Vec3, y: Vec3, z: Vec3| { Tri::::new(x.into(), y.into(), z.into()) }; let quad = |x: Vec3, y: Vec3, z: Vec3, w: Vec3| { Quad::::new(x.into(), y.into(), z.into(), w.into()) }; match self { DebugShape::Line([a, b]) => { let h = Vec3::new(0.0, 1.0, 0.0); mesh.push_quad(quad(*a, a + h, b + h, *b)); }, DebugShape::Cylinder { radius, height } => { const SUBDIVISIONS: u8 = 16; for i in 0..SUBDIVISIONS { // dot on circle edge let to = |n: u8| { const FULL: f32 = 2.0 * PI; let angle = FULL * f32::from(n) / f32::from(SUBDIVISIONS); Vec3::new(radius * angle.cos(), radius * angle.sin(), 0.0) }; let origin = Vec3::zero(); let r0 = to(i); let r1 = to(i + 1); let h = Vec3::new(0.0, 0.0, *height); // Draw bottom sector mesh.push_tri(tri(r1, r0, origin)); // Draw face mesh.push_quad(quad(r0, r1, r1 + h, r0 + h)); // Draw top sector mesh.push_tri(tri(origin + h, r0 + h, r1 + h)); } }, DebugShape::CapsulePrism { p0, p1, radius, height, } => { // We split circle in two parts const HALF_SECTORS: u8 = 8; const TOTAL: u8 = HALF_SECTORS * 2; let offset = (p0 - p1).angle_between(Vec2::new(0.0, 1.0)); let h = Vec3::new(0.0, 0.0, *height); let draw_cylinder_sector = |mesh: &mut Mesh, origin: Vec3, from: u8, to: u8| { for i in from..to { // dot on circle edge let to = |n: u8| { const FULL: f32 = 2.0 * PI; let angle = offset + FULL * f32::from(n) / f32::from(TOTAL); let (x, y) = (radius * angle.cos(), radius * angle.sin()); let to_edge = Vec3::new(x, y, 0.0); origin + to_edge }; let r0 = to(i); let r1 = to(i + 1); // Draw bottom sector mesh.push_tri(tri(r1, r0, origin)); // Draw face mesh.push_quad(quad(r0, r1, r1 + h, r0 + h)); // Draw top sector mesh.push_tri(tri(origin + h, r0 + h, r1 + h)); } }; let p0 = Vec3::new(p0.x, p0.y, 0.0); let p1 = Vec3::new(p1.x, p1.y, 0.0); // 1) Draw first half-cylinder draw_cylinder_sector(&mut mesh, p0, 0, HALF_SECTORS); // 2) Draw cuboid in-between // get main line segment let a = p1 - p0; // normalize let a = a / a.magnitude(); // stretch to radius let a = a * *radius; // rotate to 90 degrees to get needed shift let ortoghonal = Quaternion::rotation_z(PI / 2.0); let shift = ortoghonal * a; // bottom points let a0 = p0 + shift; let b0 = p0 - shift; let c0 = p1 - shift; let d0 = p1 + shift; // top points let a1 = a0 + h; let b1 = b0 + h; let c1 = c0 + h; let d1 = d0 + h; // Bottom mesh.push_quad(quad(d0, c0, b0, a0)); // Faces // (we need only two of them, because other two are inside) mesh.push_quad(quad(d0, a0, a1, d1)); mesh.push_quad(quad(b0, c0, c1, b1)); // Top mesh.push_quad(quad(a1, b1, c1, d1)); // 3) Draw second half-cylinder draw_cylinder_sector(&mut mesh, p1, HALF_SECTORS, TOTAL); }, } mesh } } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct DebugShapeId(pub u64); pub struct Debug { next_shape_id: DebugShapeId, pending_shapes: HashMap, pending_locals: HashMap, pending_deletes: HashSet, models: HashMap, Bound>)>, } impl Debug { pub fn new() -> Debug { Debug { next_shape_id: DebugShapeId(0), pending_shapes: HashMap::new(), pending_locals: HashMap::new(), pending_deletes: HashSet::new(), models: HashMap::new(), } } pub fn add_shape(&mut self, shape: DebugShape) -> DebugShapeId { let id = DebugShapeId(self.next_shape_id.0); self.next_shape_id.0 += 1; self.pending_shapes.insert(id, shape); id } pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) { self.pending_locals.insert(id, (pos, color, ori)); } pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); } pub fn maintain(&mut self, renderer: &mut Renderer) { for (id, shape) in self.pending_shapes.drain() { if let Some(model) = renderer.create_model(&shape.mesh()) { let locals = renderer.create_debug_bound_locals(&[DebugLocals { pos: [0.0; 4], color: [1.0, 0.0, 0.0, 1.0], ori: [0.0, 0.0, 0.0, 1.0], }]); self.models.insert(id, (model, locals)); } else { warn!( "Failed to create model for debug shape {:?}: {:?}", id, shape ); } } for (id, (pos, color, ori)) in self.pending_locals.drain() { if let Some((_, locals)) = self.models.get_mut(&id) { let lc = srgba_to_linear(color.into()); let new_locals = [DebugLocals { pos, color: [lc.r, lc.g, lc.b, lc.a], ori, }]; renderer.update_consts(locals, &new_locals); } else { warn!( "Tried to update locals for nonexistent debug shape {:?}", id ); } } for id in self.pending_deletes.drain() { self.models.remove(&id); } } pub fn render<'a>(&'a self, drawer: &mut DebugDrawer<'_, 'a>) { for (model, locals) in self.models.values() { drawer.draw(model, locals); } } } impl Default for Debug { fn default() -> Debug { Debug::new() } }