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
67#[derive(Clone, Copy, Debug)]
68pub struct GizmoVertex {
69 pub position: [f32; 3],
70 pub color: [f32; 4],
71}
72
73#[repr(C)]
74#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
75pub struct GpuGizmoVertex {
76 pub position: [f32; 3],
77 pub color: [f32; 4],
78}
79
80impl GpuGizmoVertex {
81 pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
82 wgpu::VertexBufferLayout {
83 array_stride: std::mem::size_of::<GpuGizmoVertex>() as wgpu::BufferAddress,
84 step_mode: wgpu::VertexStepMode::Vertex,
85 attributes: &[
86 wgpu::VertexAttribute {
87 offset: 0,
88 shader_location: 0,
89 format: wgpu::VertexFormat::Float32x3,
90 },
91 wgpu::VertexAttribute {
92 offset: 12,
93 shader_location: 1,
94 format: wgpu::VertexFormat::Float32x4,
95 },
96 ],
97 }
98 }
99}
100
101pub struct GizmoRendererSystem {
102 pub pipeline: wgpu::RenderPipeline,
103 pub pipeline_no_depth: wgpu::RenderPipeline,
104 pub vertex_buffer: wgpu::Buffer,
105 pub max_vertices: u32,
106 pub index_count: u32,
107}
108
109impl GizmoRendererSystem {
110 pub fn new(
111 device: &wgpu::Device,
112 global_bind_group_layout: &wgpu::BindGroupLayout,
113 output_format: wgpu::TextureFormat,
114 depth_format: wgpu::TextureFormat,
115 ) -> Self {
116 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
117 label: Some("Debug Lines Shader"),
118 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/debug_lines.wgsl").into()),
119 });
120
121 let render_pipeline_layout =
122 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
123 label: Some("Debug Lines Pipeline Layout"),
124 bind_group_layouts: &[global_bind_group_layout],
125 push_constant_ranges: &[],
126 });
127
128 let mut desc = wgpu::RenderPipelineDescriptor {
129 label: Some("Debug Lines Pipeline"),
130 layout: Some(&render_pipeline_layout),
131 vertex: wgpu::VertexState {
132 module: &shader,
133 entry_point: "vs_main",
134 compilation_options: Default::default(),
135 buffers: &[GpuGizmoVertex::desc()],
136 },
137 fragment: Some(wgpu::FragmentState {
138 module: &shader,
139 entry_point: "fs_main",
140 compilation_options: Default::default(),
141 targets: &[Some(wgpu::ColorTargetState {
142 format: output_format,
143 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
144 write_mask: wgpu::ColorWrites::ALL,
145 })],
146 }),
147 primitive: wgpu::PrimitiveState {
148 topology: wgpu::PrimitiveTopology::LineList,
149 front_face: wgpu::FrontFace::Ccw,
150 cull_mode: None,
151 ..Default::default()
152 },
153 depth_stencil: Some(wgpu::DepthStencilState {
154 format: depth_format,
155 depth_write_enabled: false,
156 depth_compare: wgpu::CompareFunction::LessEqual,
157 stencil: wgpu::StencilState::default(),
158 bias: wgpu::DepthBiasState::default(),
159 }),
160 multisample: wgpu::MultisampleState::default(),
161 multiview: None,
162 };
163
164 let pipeline = device.create_render_pipeline(&desc);
165
166 desc.depth_stencil = Some(wgpu::DepthStencilState {
168 format: depth_format,
169 depth_write_enabled: false,
170 depth_compare: wgpu::CompareFunction::Always,
171 stencil: wgpu::StencilState::default(),
172 bias: wgpu::DepthBiasState::default(),
173 });
174 desc.label = Some("Debug Lines No-Depth Pipeline");
175 let pipeline_no_depth = device.create_render_pipeline(&desc);
176
177 let max_vertices = 200_000;
178 let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
179 label: Some("Gizmo Vertex Buffer"),
180 size: (max_vertices as usize * std::mem::size_of::<GpuGizmoVertex>())
181 as wgpu::BufferAddress,
182 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
183 mapped_at_creation: false,
184 });
185
186 Self {
187 pipeline,
188 pipeline_no_depth,
189 vertex_buffer,
190 max_vertices,
191 index_count: 0,
192 }
193 }
194
195 pub fn update(&mut self, queue: &wgpu::Queue, gizmos: &Gizmos) {
196 self.index_count = gizmos.lines.len() as u32;
197 if self.index_count > 0 {
198 let to_write = self.index_count.min(self.max_vertices) as usize;
199
200 let mut gpu_data = Vec::with_capacity(to_write);
202 for v in &gizmos.lines[0..to_write] {
203 gpu_data.push(GpuGizmoVertex {
204 position: v.position,
205 color: v.color,
206 });
207 }
208
209 queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&gpu_data));
210 }
211 }
212
213 pub fn render<'a>(
214 &'a self,
215 rpass: &mut wgpu::RenderPass<'a>,
216 global_bind_group: &'a wgpu::BindGroup,
217 depth_test: bool,
218 ) {
219 if self.index_count == 0 {
220 return;
221 }
222 let draw_count = self.index_count.min(self.max_vertices);
223 if depth_test {
224 rpass.set_pipeline(&self.pipeline);
225 } else {
226 rpass.set_pipeline(&self.pipeline_no_depth);
227 }
228 rpass.set_bind_group(0, global_bind_group, &[]);
229 rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
230 rpass.draw(0..draw_count, 0..1);
231 }
232}