use std::mem::offset_of; use bytemuck::{Pod, Zeroable, bytes_of, cast_slice}; use glam::{Mat4, Vec3}; use wgpu::util::DeviceExt as _; use crate::render::OUTPUT_FORMAT; #[derive(Debug, Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct LookParams { pub m: Mat4, } #[derive(Debug, Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct Vertex { pub pos: Vec3, pub color: Vec3, } impl Vertex { pub fn new(pos: Vec3, color: Vec3) -> Self { Self { pos, color } } } pub struct Mesh { vertex_buffer: wgpu::Buffer, vertex_count: u32, } impl Mesh { pub fn new(device: &wgpu::Device, vertices: &[Vertex]) -> Self { let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: None, usage: wgpu::BufferUsages::VERTEX, contents: cast_slice(vertices), }); Self { vertex_buffer, vertex_count: vertices.len().try_into().expect("too many vertices"), } } } pub struct Pipeline { look_buf: wgpu::Buffer, bindings: wgpu::BindGroup, pipeline: wgpu::RenderPipeline, } impl Pipeline { pub fn new(device: &wgpu::Device) -> Self { let look_buf = device.create_buffer(&wgpu::BufferDescriptor { label: None, size: size_of::() as u64, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(super::SIMPLE_SHADER.into()), }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: None, vertex: wgpu::VertexState { module: &shader, entry_point: None, compilation_options: wgpu::PipelineCompilationOptions::default(), buffers: &[wgpu::VertexBufferLayout { array_stride: size_of::() as u64, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[ wgpu::VertexAttribute { shader_location: 0, offset: offset_of!(Vertex, pos) as u64, format: wgpu::VertexFormat::Float32x3, }, wgpu::VertexAttribute { shader_location: 1, offset: offset_of!(Vertex, color) as u64, format: wgpu::VertexFormat::Float32x3, }, ], }], }, primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::LineList, ..Default::default() }, depth_stencil: None, multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false, }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: None, compilation_options: wgpu::PipelineCompilationOptions::default(), targets: &[Some(wgpu::ColorTargetState { format: OUTPUT_FORMAT, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, })], }), multiview: None, cache: None, }); let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &pipeline.get_bind_group_layout(0), entries: &[wgpu::BindGroupEntry { binding: 0, resource: look_buf.as_entire_binding(), }], }); Self { look_buf, bindings, pipeline, } } pub fn set_look(&self, queue: &wgpu::Queue, look: LookParams) { queue.write_buffer(&self.look_buf, 0, bytes_of(&look)); } pub fn render<'a>( &self, pass: &mut wgpu::RenderPass, meshes: impl IntoIterator, ) { pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &self.bindings, &[]); for mesh in meshes { pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); pass.draw(0..mesh.vertex_count, 0..1); } } }