use super::raster::Dem; use { bytemuck::{Pod, Zeroable}, std::{borrow::Cow, rc::Rc}, wgpu::util::DeviceExt, }; pub struct DemRenderer { source: Rc, pipeline: wgpu::RenderPipeline, bind_group: wgpu::BindGroup, vertex_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer, index_count: usize, uniforms: Uniforms, animation_start: std::time::Instant, } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct Vertex { position: [f32; 4], } struct Uniforms { projection_matrix: glam::Mat4, view_matrix: glam::Mat4, buffer: wgpu::Buffer, } impl Uniforms { fn new(device: &wgpu::Device, aspect_ratio: f32) -> Self { let projection_matrix = glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0); let view_matrix = glam::Mat4::look_at_rh(glam::Vec3::ZERO, glam::Vec3::ZERO, glam::Vec3::Z); let camera_matrix = projection_matrix * view_matrix; let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref(); let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Uniform Buffer"), contents: bytemuck::cast_slice(camera_matrix_ref), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }); Self { projection_matrix, view_matrix, buffer, } } fn set_camera_position(&mut self, position: glam::Vec3) { self.view_matrix = glam::Mat4::look_at_rh(position, glam::Vec3::ZERO, glam::Vec3::Z); } fn set_aspect_ratio(&mut self, ratio: f32) { self.projection_matrix = glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, ratio, 1.0, 10.0); } fn update_buffer(&self, queue: &wgpu::Queue) { let camera_matrix = self.projection_matrix * self.view_matrix; let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref(); queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(camera_matrix_ref)) } fn binding(&self) -> wgpu::BindingResource<'_> { self.buffer.as_entire_binding() } } impl DemRenderer { pub fn new( source: Rc, device: &wgpu::Device, surface_config: &wgpu::SurfaceConfiguration, ) -> Self { let (vertex_data, index_data) = create_vertices(); let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("DemRenderer Vertex Buffer"), contents: bytemuck::cast_slice(&vertex_data), usage: wgpu::BufferUsages::VERTEX, }); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("DemRenderer Index Buffer"), contents: bytemuck::cast_slice(&index_data), usage: wgpu::BufferUsages::INDEX, }); let index_count = index_data.len(); let uniforms = Uniforms::new( device, surface_config.width as f32 / surface_config.height as f32, ); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::VERTEX, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: wgpu::BufferSize::new(64), }, count: None, }, /*wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { multisampled: false, sample_type: wgpu::TextureSampleType::Uint, view_dimension: wgpu::TextureViewDimension::D2, }, count: None, },*/ ], }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: uniforms.binding(), }], label: Some("DemRendererBindGroup"), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); let vertex_buffers = [wgpu::VertexBufferLayout { array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[wgpu::VertexAttribute { format: wgpu::VertexFormat::Float32x4, offset: 0, shader_location: 0, }], }]; let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("dem_renderer.wgsl"))), }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("DemRendererPipeline"), layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: Some("vs_main"), compilation_options: Default::default(), buffers: &vertex_buffers, }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: Some("fs_solid"), compilation_options: Default::default(), targets: &[Some(surface_config.view_formats[0].into())], }), primitive: wgpu::PrimitiveState { cull_mode: Some(wgpu::Face::Back), ..Default::default() }, depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, cache: None, }); DemRenderer { source, pipeline, bind_group, vertex_buffer, index_buffer, index_count, animation_start: std::time::Instant::now(), uniforms, } } pub fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("DemRendererCommandEncoder"), }); self.uniforms .set_camera_position(get_animated_camera_position(self.animation_start.elapsed())); self.uniforms.update_buffer(queue); { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("DemRendererRenderPass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, }); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &self.bind_group, &[]); rpass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); rpass.draw_indexed(0..self.index_count as u32, 0, 0..1); } queue.submit(Some(encoder.finish())); } } fn vertex(pos: [i8; 3]) -> Vertex { Vertex { position: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0], } } fn create_vertices() -> (Vec, Vec) { let vertex_data = [ // top (0, 0, 1) vertex([-1, -1, 1]), vertex([1, -1, 1]), vertex([1, 1, 1]), vertex([-1, 1, 1]), // bottom (0, 0, -1) vertex([-1, 1, -1]), vertex([1, 1, -1]), vertex([1, -1, -1]), vertex([-1, -1, -1]), // right (1, 0, 0) vertex([1, -1, -1]), vertex([1, 1, -1]), vertex([1, 1, 1]), vertex([1, -1, 1]), // left (-1, 0, 0) vertex([-1, -1, 1]), vertex([-1, 1, 1]), vertex([-1, 1, -1]), vertex([-1, -1, -1]), // front (0, 1, 0) vertex([1, 1, -1]), vertex([-1, 1, -1]), vertex([-1, 1, 1]), vertex([1, 1, 1]), // back (0, -1, 0) vertex([1, -1, 1]), vertex([-1, -1, 1]), vertex([-1, -1, -1]), vertex([1, -1, -1]), ]; let index_data: &[u16] = &[ 0, 1, 2, 2, 3, 0, // top 4, 5, 6, 6, 7, 4, // bottom 8, 9, 10, 10, 11, 8, // right 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // front 20, 21, 22, 22, 23, 20, // back ]; (vertex_data.to_vec(), index_data.to_vec()) } fn get_animated_camera_position(animation_time: std::time::Duration) -> glam::Vec3 { let animation_phase = 2.0 * std::f32::consts::PI * (animation_time.as_secs_f32() % 10.0) / 10.0; glam::Vec3::new( 5.0 * f32::sin(animation_phase), 5.0 * f32::cos(animation_phase), 3.0, ) }