1use gizmo_math::Vec3;
2#[derive(Clone, Default)]
3pub struct Gizmos {
4 pub lines: Vec<GizmoVertex>,
5 pub depth_test: bool,
6}
7
8impl Gizmos {
9 pub fn clear(&mut self) {
10 self.lines.clear();
11 }
12
13 pub fn draw_line(&mut self, start: Vec3, end: Vec3, color: [f32; 4]) {
14 if !start.x.is_finite()
16 || !start.y.is_finite()
17 || !start.z.is_finite()
18 || !end.x.is_finite()
19 || !end.y.is_finite()
20 || !end.z.is_finite()
21 {
22 return;
23 }
24
25 if start.distance_squared(end) < 1e-8 {
27 return;
28 }
29
30 self.lines.push(GizmoVertex {
31 position: start.to_array(),
32 color,
33 });
34 self.lines.push(GizmoVertex {
35 position: end.to_array(),
36 color,
37 });
38 }
39
40 pub fn draw_box(&mut self, min: Vec3, max: Vec3, color: [f32; 4]) {
41 let p0 = Vec3::new(min.x, min.y, min.z);
42 let p1 = Vec3::new(max.x, min.y, min.z);
43 let p2 = Vec3::new(max.x, max.y, min.z);
44 let p3 = Vec3::new(min.x, max.y, min.z);
45 let p4 = Vec3::new(min.x, min.y, max.z);
46 let p5 = Vec3::new(max.x, min.y, max.z);
47 let p6 = Vec3::new(max.x, max.y, max.z);
48 let p7 = Vec3::new(min.x, max.y, max.z);
49 self.draw_line(p0, p1, color);
51 self.draw_line(p1, p2, color);
52 self.draw_line(p2, p3, color);
53 self.draw_line(p3, p0, color);
54 self.draw_line(p4, p5, color);
56 self.draw_line(p5, p6, color);
57 self.draw_line(p6, p7, color);
58 self.draw_line(p7, p4, color);
59 self.draw_line(p0, p4, color);
61 self.draw_line(p1, p5, color);
62 self.draw_line(p2, p6, color);
63 self.draw_line(p3, p7, color);
64 }
65
66 pub fn draw_aabb(&mut self, aabb: gizmo_math::Aabb, color: [f32; 4]) {
67 self.draw_box(aabb.min.into(), aabb.max.into(), color);
68 }
69
70 pub fn draw_frustum(&mut self, view_proj: gizmo_math::Mat4, color: [f32; 4]) {
71 let inv = view_proj.inverse();
72
73 let corners_ndc = [
74 Vec3::new(-1.0, -1.0, 1.0), Vec3::new(1.0, -1.0, 1.0), Vec3::new(1.0, 1.0, 1.0), Vec3::new(-1.0, 1.0, 1.0), Vec3::new(-1.0, -1.0, 0.0), Vec3::new(1.0, -1.0, 0.0), Vec3::new(1.0, 1.0, 0.0), Vec3::new(-1.0, 1.0, 0.0), ];
83
84 let mut corners_world = [Vec3::ZERO; 8];
85 for i in 0..8 {
86 let p = inv.project_point3(corners_ndc[i]);
87 corners_world[i] = p;
88 }
89
90 self.draw_line(corners_world[0], corners_world[1], color);
92 self.draw_line(corners_world[1], corners_world[2], color);
93 self.draw_line(corners_world[2], corners_world[3], color);
94 self.draw_line(corners_world[3], corners_world[0], color);
95
96 self.draw_line(corners_world[4], corners_world[5], color);
98 self.draw_line(corners_world[5], corners_world[6], color);
99 self.draw_line(corners_world[6], corners_world[7], color);
100 self.draw_line(corners_world[7], corners_world[4], color);
101
102 self.draw_line(corners_world[0], corners_world[4], color);
104 self.draw_line(corners_world[1], corners_world[5], color);
105 self.draw_line(corners_world[2], corners_world[6], color);
106 self.draw_line(corners_world[3], corners_world[7], color);
107 }
108}
109
110#[derive(Clone, Copy, Debug)]
111pub struct GizmoVertex {
112 pub position: [f32; 3],
113 pub color: [f32; 4],
114}
115
116#[repr(C)]
117#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
118pub struct GpuGizmoVertex {
119 pub position: [f32; 3],
120 pub color: [f32; 4],
121}
122
123impl GpuGizmoVertex {
124 pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
125 wgpu::VertexBufferLayout {
126 array_stride: std::mem::size_of::<GpuGizmoVertex>() as wgpu::BufferAddress,
127 step_mode: wgpu::VertexStepMode::Vertex,
128 attributes: &[
129 wgpu::VertexAttribute {
130 offset: 0,
131 shader_location: 0,
132 format: wgpu::VertexFormat::Float32x3,
133 },
134 wgpu::VertexAttribute {
135 offset: 12,
136 shader_location: 1,
137 format: wgpu::VertexFormat::Float32x4,
138 },
139 ],
140 }
141 }
142}
143
144pub struct GizmoRendererSystem {
145 pub pipeline: wgpu::RenderPipeline,
146 pub pipeline_no_depth: wgpu::RenderPipeline,
147 pub vertex_buffer: wgpu::Buffer,
148 pub max_vertices: u32,
149 pub index_count: u32,
150}
151
152impl GizmoRendererSystem {
153 pub fn new(
154 device: &wgpu::Device,
155 global_bind_group_layout: &wgpu::BindGroupLayout,
156 output_format: wgpu::TextureFormat,
157 depth_format: wgpu::TextureFormat,
158 ) -> Self {
159 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
160 label: Some("Debug Lines Shader"),
161 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/debug_lines.wgsl").into()),
162 });
163
164 let render_pipeline_layout =
165 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
166 label: Some("Debug Lines Pipeline Layout"),
167 bind_group_layouts: &[global_bind_group_layout],
168 push_constant_ranges: &[],
169 });
170
171 let mut desc = wgpu::RenderPipelineDescriptor {
172 label: Some("Debug Lines Pipeline"),
173 layout: Some(&render_pipeline_layout),
174 vertex: wgpu::VertexState {
175 module: &shader,
176 entry_point: "vs_main",
177 compilation_options: Default::default(),
178 buffers: &[GpuGizmoVertex::desc()],
179 },
180 fragment: Some(wgpu::FragmentState {
181 module: &shader,
182 entry_point: "fs_main",
183 compilation_options: Default::default(),
184 targets: &[Some(wgpu::ColorTargetState {
185 format: output_format,
186 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
187 write_mask: wgpu::ColorWrites::ALL,
188 })],
189 }),
190 primitive: wgpu::PrimitiveState {
191 topology: wgpu::PrimitiveTopology::LineList,
192 front_face: wgpu::FrontFace::Ccw,
193 cull_mode: None,
194 ..Default::default()
195 },
196 depth_stencil: Some(wgpu::DepthStencilState {
197 format: depth_format,
198 depth_write_enabled: false,
199 depth_compare: wgpu::CompareFunction::Always, stencil: wgpu::StencilState::default(),
201 bias: wgpu::DepthBiasState::default(),
202 }),
203 multisample: wgpu::MultisampleState::default(),
204 multiview: None,
205 };
206
207 let pipeline = device.create_render_pipeline(&desc);
208
209 desc.depth_stencil = Some(wgpu::DepthStencilState {
211 format: depth_format,
212 depth_write_enabled: false,
213 depth_compare: wgpu::CompareFunction::Always,
214 stencil: wgpu::StencilState::default(),
215 bias: wgpu::DepthBiasState::default(),
216 });
217 desc.label = Some("Debug Lines No-Depth Pipeline");
218 let pipeline_no_depth = device.create_render_pipeline(&desc);
219
220 let max_vertices = 200_000;
221 let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
222 label: Some("Gizmo Vertex Buffer"),
223 size: (max_vertices as usize * std::mem::size_of::<GpuGizmoVertex>())
224 as wgpu::BufferAddress,
225 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
226 mapped_at_creation: false,
227 });
228
229 Self {
230 pipeline,
231 pipeline_no_depth,
232 vertex_buffer,
233 max_vertices,
234 index_count: 0,
235 }
236 }
237
238 pub fn update(&mut self, queue: &wgpu::Queue, gizmos: &Gizmos) {
239 self.index_count = gizmos.lines.len() as u32;
240 if self.index_count > 0 {
241 let to_write = self.index_count.min(self.max_vertices) as usize;
242
243 let mut gpu_data = Vec::with_capacity(to_write);
245 for v in &gizmos.lines[0..to_write] {
246 gpu_data.push(GpuGizmoVertex {
247 position: v.position,
248 color: v.color,
249 });
250 }
251
252 queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&gpu_data));
253 }
254 }
255
256 pub fn render<'a>(
257 &'a self,
258 rpass: &mut wgpu::RenderPass<'a>,
259 global_bind_group: &'a wgpu::BindGroup,
260 depth_test: bool,
261 ) {
262 if self.index_count == 0 {
263 return;
264 }
265 let draw_count = self.index_count.min(self.max_vertices);
266 if depth_test {
267 rpass.set_pipeline(&self.pipeline);
268 } else {
269 rpass.set_pipeline(&self.pipeline_no_depth);
270 }
271 rpass.set_bind_group(0, global_bind_group, &[]);
272 rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
273 rpass.draw(0..draw_count, 0..1);
274 }
275}