1use super::materials::VisualizationMaterial;
6use cgmath::{Matrix4, Vector3};
7use wgpu::util::DeviceExt;
8use wgpu::*;
9
10#[repr(C)]
12#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
13pub struct VisualizationVertex {
14 pub position: [f32; 3],
15 pub tex_coords: [f32; 2],
16}
17
18impl VisualizationVertex {
19 const ATTRIBUTES: [VertexAttribute; 2] = [
20 VertexAttribute {
21 offset: 0,
22 shader_location: 0,
23 format: VertexFormat::Float32x3,
24 },
25 VertexAttribute {
26 offset: std::mem::size_of::<[f32; 3]>() as BufferAddress,
27 shader_location: 1,
28 format: VertexFormat::Float32x2,
29 },
30 ];
31
32 pub fn desc<'a>() -> VertexBufferLayout<'a> {
33 VertexBufferLayout {
34 array_stride: std::mem::size_of::<VisualizationVertex>() as BufferAddress,
35 step_mode: VertexStepMode::Vertex,
36 attributes: &Self::ATTRIBUTES,
37 }
38 }
39}
40
41#[repr(C)]
43#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
44pub struct VisualizationCameraUniform {
45 pub view_proj: [[f32; 4]; 4],
46}
47
48pub struct VisualizationItem {
50 pub vertices: Vec<VisualizationVertex>,
51 pub indices: Vec<u16>,
52 pub material: VisualizationMaterial,
53 pub transform: Matrix4<f32>,
54}
55
56impl VisualizationItem {
57 pub fn create_quad(position: Vector3<f32>, size: f32, material: VisualizationMaterial) -> Self {
59 let half_size = size * 0.5;
60
61 let vertices = vec![
62 VisualizationVertex {
63 position: [position.x - half_size, position.y - half_size, position.z],
64 tex_coords: [0.0, 1.0],
65 },
66 VisualizationVertex {
67 position: [position.x + half_size, position.y - half_size, position.z],
68 tex_coords: [1.0, 1.0],
69 },
70 VisualizationVertex {
71 position: [position.x + half_size, position.y + half_size, position.z],
72 tex_coords: [1.0, 0.0],
73 },
74 VisualizationVertex {
75 position: [position.x - half_size, position.y + half_size, position.z],
76 tex_coords: [0.0, 0.0],
77 },
78 ];
79
80 let indices = vec![0, 1, 2, 2, 3, 0];
81
82 Self {
83 vertices,
84 indices,
85 material,
86 transform: Matrix4::from_translation(position),
87 }
88 }
89}
90
91pub struct VisualizationRenderer {
93 render_pipeline: RenderPipeline,
94 camera_buffer: Buffer,
95 camera_bind_group: BindGroup,
96 vertex_buffer: Option<Buffer>,
97 index_buffer: Option<Buffer>,
98 vertex_count: u32,
99 index_count: u32,
100}
101
102impl VisualizationRenderer {
103 pub fn new(device: &Device, surface_format: TextureFormat) -> Self {
104 let shader = device.create_shader_module(ShaderModuleDescriptor {
106 label: Some("Visualization Shader"),
107 source: ShaderSource::Wgsl(super::shaders::VISUALIZATION_SHADER.into()),
108 });
109
110 let camera_buffer = device.create_buffer(&BufferDescriptor {
112 label: Some("Visualization Camera Buffer"),
113 size: std::mem::size_of::<VisualizationCameraUniform>() as BufferAddress,
114 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
115 mapped_at_creation: false,
116 });
117
118 let camera_bind_group_layout =
120 device.create_bind_group_layout(&BindGroupLayoutDescriptor {
121 label: Some("Visualization Camera Bind Group Layout"),
122 entries: &[BindGroupLayoutEntry {
123 binding: 0,
124 visibility: ShaderStages::VERTEX,
125 ty: BindingType::Buffer {
126 ty: BufferBindingType::Uniform,
127 has_dynamic_offset: false,
128 min_binding_size: None,
129 },
130 count: None,
131 }],
132 });
133
134 let camera_bind_group = device.create_bind_group(&BindGroupDescriptor {
136 label: Some("Visualization Camera Bind Group"),
137 layout: &camera_bind_group_layout,
138 entries: &[BindGroupEntry {
139 binding: 0,
140 resource: camera_buffer.as_entire_binding(),
141 }],
142 });
143
144 let material_bind_group_layout =
146 device.create_bind_group_layout(&BindGroupLayoutDescriptor {
147 label: Some("Visualization Material Bind Group Layout"),
148 entries: &[
149 BindGroupLayoutEntry {
150 binding: 0,
151 visibility: ShaderStages::FRAGMENT,
152 ty: BindingType::Texture {
153 multisampled: false,
154 view_dimension: TextureViewDimension::D2,
155 sample_type: TextureSampleType::Float { filterable: true },
156 },
157 count: None,
158 },
159 BindGroupLayoutEntry {
160 binding: 1,
161 visibility: ShaderStages::FRAGMENT,
162 ty: BindingType::Sampler(SamplerBindingType::Filtering),
163 count: None,
164 },
165 ],
166 });
167
168 let render_pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
170 label: Some("Visualization Render Pipeline Layout"),
171 bind_group_layouts: &[&camera_bind_group_layout, &material_bind_group_layout],
172 push_constant_ranges: &[],
173 });
174
175 let render_pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
177 label: Some("Visualization Render Pipeline"),
178 layout: Some(&render_pipeline_layout),
179 vertex: VertexState {
180 module: &shader,
181 entry_point: Some("vs_main"),
182 buffers: &[VisualizationVertex::desc()],
183 compilation_options: Default::default(),
184 },
185 fragment: Some(FragmentState {
186 module: &shader,
187 entry_point: Some("fs_main"),
188 targets: &[Some(ColorTargetState {
189 format: surface_format,
190 blend: Some(BlendState::ALPHA_BLENDING),
191 write_mask: ColorWrites::ALL,
192 })],
193 compilation_options: Default::default(),
194 }),
195 primitive: PrimitiveState {
196 topology: PrimitiveTopology::TriangleList,
197 strip_index_format: None,
198 front_face: FrontFace::Ccw,
199 cull_mode: None, unclipped_depth: false,
201 polygon_mode: PolygonMode::Fill,
202 conservative: false,
203 },
204 depth_stencil: Some(DepthStencilState {
205 format: TextureFormat::Depth32Float,
206 depth_write_enabled: true,
207 depth_compare: CompareFunction::Less,
208 stencil: StencilState::default(),
209 bias: DepthBiasState::default(),
210 }),
211 multisample: MultisampleState {
212 count: 1,
213 mask: !0,
214 alpha_to_coverage_enabled: false,
215 },
216 multiview: None,
217 cache: None,
218 });
219
220 Self {
221 render_pipeline,
222 camera_buffer,
223 camera_bind_group,
224 vertex_buffer: None,
225 index_buffer: None,
226 vertex_count: 0,
227 index_count: 0,
228 }
229 }
230
231 pub fn update_camera(&self, queue: &Queue, view_proj_matrix: Matrix4<f32>) {
233 let camera_uniform = VisualizationCameraUniform {
234 view_proj: view_proj_matrix.into(),
235 };
236 queue.write_buffer(
237 &self.camera_buffer,
238 0,
239 bytemuck::cast_slice(&[camera_uniform]),
240 );
241 }
242
243 pub fn update_buffers(&mut self, device: &Device, items: &[VisualizationItem]) {
245 let mut all_vertices = Vec::new();
246 let mut all_indices = Vec::new();
247 let mut index_offset = 0;
248
249 for item in items {
250 all_vertices.extend_from_slice(&item.vertices);
251 for &index in &item.indices {
252 all_indices.push(index + index_offset);
253 }
254 index_offset += item.vertices.len() as u16;
255 }
256
257 if !all_vertices.is_empty() {
258 self.vertex_buffer = Some(device.create_buffer_init(
260 &wgpu::util::BufferInitDescriptor {
261 label: Some("Visualization Vertex Buffer"),
262 contents: bytemuck::cast_slice(&all_vertices),
263 usage: BufferUsages::VERTEX,
264 },
265 ));
266 self.vertex_count = all_vertices.len() as u32;
267
268 self.index_buffer = Some(device.create_buffer_init(
270 &wgpu::util::BufferInitDescriptor {
271 label: Some("Visualization Index Buffer"),
272 contents: bytemuck::cast_slice(&all_indices),
273 usage: BufferUsages::INDEX,
274 },
275 ));
276 self.index_count = all_indices.len() as u32;
277 }
278 }
279
280 pub fn render(
282 &self,
283 encoder: &mut CommandEncoder,
284 color_attachment: &TextureView,
285 depth_attachment: &TextureView,
286 materials: &[&VisualizationMaterial],
287 ) {
288 if self.vertex_buffer.is_none() || self.index_buffer.is_none() {
289 return;
290 }
291
292 let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
293 label: Some("Visualization Render Pass"),
294 color_attachments: &[Some(RenderPassColorAttachment {
295 view: color_attachment,
296 resolve_target: None,
297 ops: Operations {
298 load: LoadOp::Load,
299 store: StoreOp::Store,
300 },
301 })],
302 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
303 view: depth_attachment,
304 depth_ops: Some(Operations {
305 load: LoadOp::Load,
306 store: StoreOp::Store,
307 }),
308 stencil_ops: None,
309 }),
310 occlusion_query_set: None,
311 timestamp_writes: None,
312 });
313
314 render_pass.set_pipeline(&self.render_pipeline);
315 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
316
317 if let (Some(vertex_buffer), Some(index_buffer)) = (&self.vertex_buffer, &self.index_buffer)
318 {
319 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
320 render_pass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint16);
321
322 for material in materials {
324 if let Some(bind_group) = &material.bind_group {
325 render_pass.set_bind_group(1, bind_group, &[]);
326 render_pass.draw_indexed(0..self.index_count, 0, 0..1);
327 }
328 }
329 }
330 }
331}