1use bytemuck::{Pod, Zeroable};
7use glam::{Mat4, Vec3, Vec4};
8use std::sync::Arc;
9use wgpu::util::DeviceExt;
10
11#[repr(C)]
13#[derive(Clone, Copy, Debug, Pod, Zeroable)]
14pub struct Vertex {
15 pub position: [f32; 3],
16 pub color: [f32; 4],
17 pub normal: [f32; 3],
18 pub tex_coords: [f32; 2],
19}
20
21impl Vertex {
22 pub fn new(position: Vec3, color: Vec4) -> Self {
23 Self {
24 position: position.to_array(),
25 color: color.to_array(),
26 normal: [0.0, 0.0, 1.0], tex_coords: [0.0, 0.0], }
29 }
30
31 pub fn desc() -> wgpu::VertexBufferLayout<'static> {
32 let stride = std::mem::size_of::<Vertex>() as wgpu::BufferAddress;
33 println!(
34 "VERTEX: Struct size = {}, stride = {}",
35 std::mem::size_of::<Vertex>(),
36 stride
37 );
38 wgpu::VertexBufferLayout {
39 array_stride: stride,
40 step_mode: wgpu::VertexStepMode::Vertex,
41 attributes: &[
42 wgpu::VertexAttribute {
44 offset: 0,
45 shader_location: 0,
46 format: wgpu::VertexFormat::Float32x3,
47 },
48 wgpu::VertexAttribute {
50 offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
51 shader_location: 1,
52 format: wgpu::VertexFormat::Float32x4,
53 },
54 wgpu::VertexAttribute {
56 offset: std::mem::size_of::<[f32; 7]>() as wgpu::BufferAddress,
57 shader_location: 2,
58 format: wgpu::VertexFormat::Float32x3,
59 },
60 wgpu::VertexAttribute {
62 offset: std::mem::size_of::<[f32; 10]>() as wgpu::BufferAddress,
63 shader_location: 3,
64 format: wgpu::VertexFormat::Float32x2,
65 },
66 ],
67 }
68 }
69}
70
71#[repr(C)]
73#[derive(Clone, Copy, Debug, Pod, Zeroable)]
74pub struct Uniforms {
75 pub view_proj: [[f32; 4]; 4],
76 pub model: [[f32; 4]; 4],
77 pub normal_matrix: [[f32; 4]; 3], }
79
80#[repr(C)]
83#[derive(Clone, Copy, Debug, Pod, Zeroable)]
84pub struct DirectUniforms {
85 pub data_min: [f32; 2], pub data_max: [f32; 2], pub viewport_min: [f32; 2], pub viewport_max: [f32; 2], }
90
91impl Default for Uniforms {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97impl Uniforms {
98 pub fn new() -> Self {
99 Self {
100 view_proj: Mat4::IDENTITY.to_cols_array_2d(),
101 model: Mat4::IDENTITY.to_cols_array_2d(),
102 normal_matrix: [
103 [1.0, 0.0, 0.0, 0.0],
104 [0.0, 1.0, 0.0, 0.0],
105 [0.0, 0.0, 1.0, 0.0],
106 ],
107 }
108 }
109
110 pub fn update_view_proj(&mut self, view_proj: Mat4) {
111 self.view_proj = view_proj.to_cols_array_2d();
112 }
113
114 pub fn update_model(&mut self, model: Mat4) {
115 self.model = model.to_cols_array_2d();
116 let normal_mat = model.inverse().transpose();
118 self.normal_matrix = [
119 [
120 normal_mat.x_axis.x,
121 normal_mat.x_axis.y,
122 normal_mat.x_axis.z,
123 0.0,
124 ],
125 [
126 normal_mat.y_axis.x,
127 normal_mat.y_axis.y,
128 normal_mat.y_axis.z,
129 0.0,
130 ],
131 [
132 normal_mat.z_axis.x,
133 normal_mat.z_axis.y,
134 normal_mat.z_axis.z,
135 0.0,
136 ],
137 ];
138 }
139}
140
141impl DirectUniforms {
142 pub fn new(
143 data_min: [f32; 2],
144 data_max: [f32; 2],
145 viewport_min: [f32; 2],
146 viewport_max: [f32; 2],
147 ) -> Self {
148 Self {
149 data_min,
150 data_max,
151 viewport_min,
152 viewport_max,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum PipelineType {
160 Points,
161 Lines,
162 Triangles,
163 PointCloud,
164}
165
166pub struct WgpuRenderer {
168 pub device: Arc<wgpu::Device>,
169 pub queue: Arc<wgpu::Queue>,
170 pub surface_config: wgpu::SurfaceConfiguration,
171
172 point_pipeline: Option<wgpu::RenderPipeline>,
174 line_pipeline: Option<wgpu::RenderPipeline>,
175 triangle_pipeline: Option<wgpu::RenderPipeline>,
176
177 pub direct_line_pipeline: Option<wgpu::RenderPipeline>,
179
180 uniform_buffer: wgpu::Buffer,
182 uniform_bind_group: wgpu::BindGroup,
183 uniform_bind_group_layout: wgpu::BindGroupLayout,
184
185 direct_uniform_buffer: wgpu::Buffer,
187 pub direct_uniform_bind_group: wgpu::BindGroup,
188 direct_uniform_bind_group_layout: wgpu::BindGroupLayout,
189
190 uniforms: Uniforms,
192 direct_uniforms: DirectUniforms,
193}
194
195impl WgpuRenderer {
196 pub async fn new(
198 device: Arc<wgpu::Device>,
199 queue: Arc<wgpu::Queue>,
200 surface_config: wgpu::SurfaceConfiguration,
201 ) -> Self {
202 let uniforms = Uniforms::new();
204 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
205 label: Some("Uniform Buffer"),
206 contents: bytemuck::cast_slice(&[uniforms]),
207 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
208 });
209
210 let uniform_bind_group_layout =
212 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
213 entries: &[wgpu::BindGroupLayoutEntry {
214 binding: 0,
215 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
216 ty: wgpu::BindingType::Buffer {
217 ty: wgpu::BufferBindingType::Uniform,
218 has_dynamic_offset: false,
219 min_binding_size: None,
220 },
221 count: None,
222 }],
223 label: Some("uniform_bind_group_layout"),
224 });
225
226 let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
228 layout: &uniform_bind_group_layout,
229 entries: &[wgpu::BindGroupEntry {
230 binding: 0,
231 resource: uniform_buffer.as_entire_binding(),
232 }],
233 label: Some("uniform_bind_group"),
234 });
235
236 let direct_uniforms = DirectUniforms::new(
238 [0.0, 0.0], [1.0, 1.0], [-1.0, -1.0], [1.0, 1.0], );
243 let direct_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
244 label: Some("Direct Uniform Buffer"),
245 contents: bytemuck::cast_slice(&[direct_uniforms]),
246 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
247 });
248
249 let direct_uniform_bind_group_layout =
251 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
252 entries: &[wgpu::BindGroupLayoutEntry {
253 binding: 0,
254 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
255 ty: wgpu::BindingType::Buffer {
256 ty: wgpu::BufferBindingType::Uniform,
257 has_dynamic_offset: false,
258 min_binding_size: None,
259 },
260 count: None,
261 }],
262 label: Some("direct_uniform_bind_group_layout"),
263 });
264
265 let direct_uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
267 layout: &direct_uniform_bind_group_layout,
268 entries: &[wgpu::BindGroupEntry {
269 binding: 0,
270 resource: direct_uniform_buffer.as_entire_binding(),
271 }],
272 label: Some("direct_uniform_bind_group"),
273 });
274
275 Self {
276 device,
277 queue,
278 surface_config,
279 point_pipeline: None,
280 line_pipeline: None,
281 triangle_pipeline: None,
282 direct_line_pipeline: None,
283 uniform_buffer,
284 uniform_bind_group,
285 uniform_bind_group_layout,
286 direct_uniform_buffer,
287 direct_uniform_bind_group,
288 direct_uniform_bind_group_layout,
289 uniforms,
290 direct_uniforms,
291 }
292 }
293
294 pub fn create_vertex_buffer(&self, vertices: &[Vertex]) -> wgpu::Buffer {
296 self.device
297 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
298 label: Some("Vertex Buffer"),
299 contents: bytemuck::cast_slice(vertices),
300 usage: wgpu::BufferUsages::VERTEX,
301 })
302 }
303
304 pub fn create_index_buffer(&self, indices: &[u32]) -> wgpu::Buffer {
306 self.device
307 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
308 label: Some("Index Buffer"),
309 contents: bytemuck::cast_slice(indices),
310 usage: wgpu::BufferUsages::INDEX,
311 })
312 }
313
314 pub fn update_uniforms(&mut self, view_proj: Mat4, model: Mat4) {
316 self.uniforms.update_view_proj(view_proj);
317 self.uniforms.update_model(model);
318
319 self.queue.write_buffer(
320 &self.uniform_buffer,
321 0,
322 bytemuck::cast_slice(&[self.uniforms]),
323 );
324 }
325
326 pub fn get_uniform_bind_group(&self) -> &wgpu::BindGroup {
328 &self.uniform_bind_group
329 }
330
331 pub fn ensure_pipeline(&mut self, pipeline_type: PipelineType) {
333 match pipeline_type {
334 PipelineType::Points => {
335 if self.point_pipeline.is_none() {
336 self.point_pipeline = Some(self.create_point_pipeline());
337 }
338 }
339 PipelineType::Lines => {
340 if self.line_pipeline.is_none() {
341 self.line_pipeline = Some(self.create_line_pipeline());
342 }
343 }
344 PipelineType::Triangles => {
345 if self.triangle_pipeline.is_none() {
346 self.triangle_pipeline = Some(self.create_triangle_pipeline());
347 }
348 }
349 PipelineType::PointCloud => {
350 self.ensure_pipeline(PipelineType::Points);
352 }
353 }
354 }
355
356 pub fn get_pipeline(&self, pipeline_type: PipelineType) -> &wgpu::RenderPipeline {
358 match pipeline_type {
359 PipelineType::Points => self.point_pipeline.as_ref().unwrap(),
360 PipelineType::Lines => self.line_pipeline.as_ref().unwrap(),
361 PipelineType::Triangles => self.triangle_pipeline.as_ref().unwrap(),
362 PipelineType::PointCloud => self.get_pipeline(PipelineType::Points),
363 }
364 }
365
366 fn create_point_pipeline(&self) -> wgpu::RenderPipeline {
368 let shader = self
369 .device
370 .create_shader_module(wgpu::ShaderModuleDescriptor {
371 label: Some("Point Shader"),
372 source: wgpu::ShaderSource::Wgsl(
373 include_str!("../../shaders/vertex/point.wgsl").into(),
374 ),
375 });
376
377 let pipeline_layout = self
378 .device
379 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
380 label: Some("Point Pipeline Layout"),
381 bind_group_layouts: &[&self.uniform_bind_group_layout],
382 push_constant_ranges: &[],
383 });
384
385 self.device
386 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
387 label: Some("Point Pipeline"),
388 layout: Some(&pipeline_layout),
389 vertex: wgpu::VertexState {
390 module: &shader,
391 entry_point: "vs_main",
392 buffers: &[Vertex::desc()],
393 },
394 fragment: Some(wgpu::FragmentState {
395 module: &shader,
396 entry_point: "fs_main",
397 targets: &[Some(wgpu::ColorTargetState {
398 format: self.surface_config.format,
399 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
400 write_mask: wgpu::ColorWrites::ALL,
401 })],
402 }),
403 primitive: wgpu::PrimitiveState {
404 topology: wgpu::PrimitiveTopology::PointList,
405 strip_index_format: None,
406 front_face: wgpu::FrontFace::Ccw,
407 cull_mode: None,
408 polygon_mode: wgpu::PolygonMode::Fill,
409 unclipped_depth: false,
410 conservative: false,
411 },
412 depth_stencil: None, multisample: wgpu::MultisampleState {
414 count: 1,
415 mask: !0,
416 alpha_to_coverage_enabled: false,
417 },
418 multiview: None,
419 })
420 }
421
422 fn create_line_pipeline(&self) -> wgpu::RenderPipeline {
424 let shader = self
425 .device
426 .create_shader_module(wgpu::ShaderModuleDescriptor {
427 label: Some("Line Shader"),
428 source: wgpu::ShaderSource::Wgsl(
429 include_str!("../../shaders/vertex/line.wgsl").into(),
430 ),
431 });
432
433 let pipeline_layout = self
434 .device
435 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
436 label: Some("Line Pipeline Layout"),
437 bind_group_layouts: &[&self.uniform_bind_group_layout],
438 push_constant_ranges: &[],
439 });
440
441 self.device
442 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
443 label: Some("Line Pipeline"),
444 layout: Some(&pipeline_layout),
445 vertex: wgpu::VertexState {
446 module: &shader,
447 entry_point: "vs_main",
448 buffers: &[Vertex::desc()],
449 },
450 fragment: Some(wgpu::FragmentState {
451 module: &shader,
452 entry_point: "fs_main",
453 targets: &[Some(wgpu::ColorTargetState {
454 format: self.surface_config.format,
455 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
456 write_mask: wgpu::ColorWrites::ALL,
457 })],
458 }),
459 primitive: wgpu::PrimitiveState {
460 topology: wgpu::PrimitiveTopology::LineList,
461 strip_index_format: None,
462 front_face: wgpu::FrontFace::Ccw,
463 cull_mode: None,
464 polygon_mode: wgpu::PolygonMode::Fill,
465 unclipped_depth: false,
466 conservative: false,
467 },
468 depth_stencil: None, multisample: wgpu::MultisampleState {
470 count: 1,
471 mask: !0,
472 alpha_to_coverage_enabled: false,
473 },
474 multiview: None,
475 })
476 }
477
478 fn create_direct_line_pipeline(&self) -> wgpu::RenderPipeline {
480 let shader = self
481 .device
482 .create_shader_module(wgpu::ShaderModuleDescriptor {
483 label: Some("Direct Line Shader"),
484 source: wgpu::ShaderSource::Wgsl(
485 include_str!("../../shaders/vertex/line_direct.wgsl").into(),
486 ),
487 });
488
489 let pipeline_layout = self
490 .device
491 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
492 label: Some("Direct Line Pipeline Layout"),
493 bind_group_layouts: &[&self.direct_uniform_bind_group_layout],
494 push_constant_ranges: &[],
495 });
496
497 self.device
498 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
499 label: Some("Direct Line Pipeline"),
500 layout: Some(&pipeline_layout),
501 vertex: wgpu::VertexState {
502 module: &shader,
503 entry_point: "vs_main",
504 buffers: &[Vertex::desc()],
505 },
506 fragment: Some(wgpu::FragmentState {
507 module: &shader,
508 entry_point: "fs_main",
509 targets: &[Some(wgpu::ColorTargetState {
510 format: self.surface_config.format,
511 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
512 write_mask: wgpu::ColorWrites::ALL,
513 })],
514 }),
515 primitive: wgpu::PrimitiveState {
516 topology: wgpu::PrimitiveTopology::LineList,
517 strip_index_format: None,
518 front_face: wgpu::FrontFace::Ccw,
519 cull_mode: None,
520 polygon_mode: wgpu::PolygonMode::Fill,
521 unclipped_depth: false,
522 conservative: false,
523 },
524 depth_stencil: None, multisample: wgpu::MultisampleState {
526 count: 1,
527 mask: !0,
528 alpha_to_coverage_enabled: false,
529 },
530 multiview: None,
531 })
532 }
533
534 fn create_triangle_pipeline(&self) -> wgpu::RenderPipeline {
536 let shader = self
537 .device
538 .create_shader_module(wgpu::ShaderModuleDescriptor {
539 label: Some("Triangle Shader"),
540 source: wgpu::ShaderSource::Wgsl(
541 include_str!("../../shaders/vertex/triangle.wgsl").into(),
542 ),
543 });
544
545 let pipeline_layout = self
546 .device
547 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
548 label: Some("Triangle Pipeline Layout"),
549 bind_group_layouts: &[&self.uniform_bind_group_layout],
550 push_constant_ranges: &[],
551 });
552
553 self.device
554 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
555 label: Some("Triangle Pipeline"),
556 layout: Some(&pipeline_layout),
557 vertex: wgpu::VertexState {
558 module: &shader,
559 entry_point: "vs_main",
560 buffers: &[Vertex::desc()],
561 },
562 fragment: Some(wgpu::FragmentState {
563 module: &shader,
564 entry_point: "fs_main",
565 targets: &[Some(wgpu::ColorTargetState {
566 format: self.surface_config.format,
567 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
568 write_mask: wgpu::ColorWrites::ALL,
569 })],
570 }),
571 primitive: wgpu::PrimitiveState {
572 topology: wgpu::PrimitiveTopology::TriangleList,
573 strip_index_format: None,
574 front_face: wgpu::FrontFace::Ccw,
575 cull_mode: None, polygon_mode: wgpu::PolygonMode::Fill,
577 unclipped_depth: false,
578 conservative: false,
579 },
580 depth_stencil: None, multisample: wgpu::MultisampleState {
582 count: 1,
583 mask: !0,
584 alpha_to_coverage_enabled: false,
585 },
586 multiview: None,
587 })
588 }
589
590 pub fn begin_render_pass<'a>(
592 &'a self,
593 encoder: &'a mut wgpu::CommandEncoder,
594 view: &'a wgpu::TextureView,
595 _depth_view: &'a wgpu::TextureView,
596 ) -> wgpu::RenderPass<'a> {
597 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
598 label: Some("Render Pass"),
599 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
600 view,
601 resolve_target: None,
602 ops: wgpu::Operations {
603 load: wgpu::LoadOp::Clear(wgpu::Color {
604 r: 0.1,
605 g: 0.1,
606 b: 0.1,
607 a: 1.0,
608 }),
609 store: wgpu::StoreOp::Store,
610 },
611 })],
612 depth_stencil_attachment: None, occlusion_query_set: None,
614 timestamp_writes: None,
615 })
616 }
617
618 pub fn render_vertices<'a>(
620 &'a mut self,
621 render_pass: &mut wgpu::RenderPass<'a>,
622 pipeline_type: PipelineType,
623 vertex_buffer: &'a wgpu::Buffer,
624 vertex_count: u32,
625 index_buffer: Option<(&'a wgpu::Buffer, u32)>,
626 ) {
627 self.ensure_pipeline(pipeline_type);
629
630 let pipeline = self.get_pipeline(pipeline_type);
632 render_pass.set_pipeline(pipeline);
633 render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
634 render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
635
636 match index_buffer {
637 Some((indices, index_count)) => {
638 render_pass.set_index_buffer(indices.slice(..), wgpu::IndexFormat::Uint32);
639 render_pass.draw_indexed(0..index_count, 0, 0..1);
640 }
641 None => {
642 render_pass.draw(0..vertex_count, 0..1);
643 }
644 }
645 }
646
647 pub fn ensure_direct_line_pipeline(&mut self) {
649 if self.direct_line_pipeline.is_none() {
650 self.direct_line_pipeline = Some(self.create_direct_line_pipeline());
651 }
652 }
653
654 pub fn update_direct_uniforms(
656 &mut self,
657 data_min: [f32; 2],
658 data_max: [f32; 2],
659 viewport_min: [f32; 2],
660 viewport_max: [f32; 2],
661 ) {
662 self.direct_uniforms = DirectUniforms::new(data_min, data_max, viewport_min, viewport_max);
663 self.queue.write_buffer(
664 &self.direct_uniform_buffer,
665 0,
666 bytemuck::cast_slice(&[self.direct_uniforms]),
667 );
668 }
669}
670
671pub mod vertex_utils {
673 use super::*;
674
675 pub fn create_line(start: Vec3, end: Vec3, color: Vec4) -> Vec<Vertex> {
677 vec![Vertex::new(start, color), Vertex::new(end, color)]
678 }
679
680 pub fn create_triangle(p1: Vec3, p2: Vec3, p3: Vec3, color: Vec4) -> Vec<Vertex> {
682 vec![
683 Vertex::new(p1, color),
684 Vertex::new(p2, color),
685 Vertex::new(p3, color),
686 ]
687 }
688
689 pub fn create_point_cloud(points: &[Vec3], colors: &[Vec4]) -> Vec<Vertex> {
691 points
692 .iter()
693 .zip(colors.iter())
694 .map(|(&pos, &color)| Vertex::new(pos, color))
695 .collect()
696 }
697
698 pub fn create_line_plot(x_data: &[f64], y_data: &[f64], color: Vec4) -> Vec<Vertex> {
700 let mut vertices = Vec::new();
701
702 for i in 1..x_data.len() {
703 let start = Vec3::new(x_data[i - 1] as f32, y_data[i - 1] as f32, 0.0);
704 let end = Vec3::new(x_data[i] as f32, y_data[i] as f32, 0.0);
705 vertices.extend(create_line(start, end, color));
706 }
707
708 vertices
709 }
710
711 pub fn create_scatter_plot(x_data: &[f64], y_data: &[f64], color: Vec4) -> Vec<Vertex> {
713 x_data
714 .iter()
715 .zip(y_data.iter())
716 .map(|(&x, &y)| Vertex::new(Vec3::new(x as f32, y as f32, 0.0), color))
717 .collect()
718 }
719}