From d841a0da126f6607dd86ae76a0c4e8b869590844 Mon Sep 17 00:00:00 2001 From: Marco Thomas Date: Wed, 25 Aug 2021 19:24:32 +0200 Subject: [PATCH] Add simple camera movement --- Cargo.toml | 2 +- src/camera.rs | 26 +++++++++++ src/camera_controller.rs | 96 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 ++ src/shader.wgsl | 10 +++- src/state.rs | 98 ++++++++++++++++++++++++++++++++++++---- src/uniform.rs | 18 ++++++++ 7 files changed, 242 insertions(+), 11 deletions(-) create mode 100644 src/camera.rs create mode 100644 src/camera_controller.rs create mode 100644 src/uniform.rs diff --git a/Cargo.toml b/Cargo.toml index 1cce915..0a1178b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ log = "0.4" wgpu = "0.9" pollster = "0.2" bytemuck = { version = "1.4", features = [ "derive" ] } -anyhow = "1.0" \ No newline at end of file +anyhow = "1.0" diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..9c15b2a --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,26 @@ +#[rustfmt::skip] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); + +pub struct Camera { + pub eye: cgmath::Point3, + pub target: cgmath::Point3, + pub up: cgmath::Vector3, + pub aspect: f32, + pub fovy: f32, + pub znear: f32, + pub zfar: f32, +} + +impl Camera { + pub fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up); + let projection = + cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + return OPENGL_TO_WGPU_MATRIX * projection * view; + } +} diff --git a/src/camera_controller.rs b/src/camera_controller.rs new file mode 100644 index 0000000..3cead26 --- /dev/null +++ b/src/camera_controller.rs @@ -0,0 +1,96 @@ +use winit::event::*; + +#[derive(Debug)] +struct IsPressed(bool); + +impl From for IsPressed { + fn from(item: bool) -> Self { + IsPressed(item) + } +} + +#[derive(Debug)] +enum ButtonPress { + Up(IsPressed), + Down(IsPressed), + Left(IsPressed), + Right(IsPressed), + Forward(IsPressed), + Backward(IsPressed), +} + +pub struct CameraController { + speed: f32, + button_press: Option, +} + +impl CameraController { + pub fn new(speed: f32) -> Self { + Self { + speed, + button_press: None, + } + } + + pub fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: + KeyboardInput { + state, + virtual_keycode: Some(key), + .. + }, + .. + } => { + let pressed = (*state == ElementState::Pressed).into(); + match key { + VirtualKeyCode::W => { + self.button_press = Some(ButtonPress::Forward(pressed)); + true + } + VirtualKeyCode::A => { + self.button_press = Some(ButtonPress::Left(pressed)); + true + } + VirtualKeyCode::S => { + self.button_press = Some(ButtonPress::Backward(pressed)); + true + } + VirtualKeyCode::D => { + self.button_press = Some(ButtonPress::Right(pressed)); + true + } + VirtualKeyCode::Space => { + self.button_press = Some(ButtonPress::Up(pressed)); + true + } + VirtualKeyCode::LShift => { + self.button_press = Some(ButtonPress::Down(pressed)); + true + } + _ => false, + } + } + _ => false, + } + } + + pub fn update_camera(&self, camera: &mut crate::camera::Camera) { + use cgmath::InnerSpace; + let forward = camera.target - camera.eye; + let forward_norm = forward.normalize(); + let forward_mag = forward.magnitude(); + + match self.button_press { + Some(ButtonPress::Forward(IsPressed(true))) if forward_mag > self.speed => { + camera.eye += forward_norm * self.speed; + } + Some(ButtonPress::Backward(IsPressed(true))) => { + camera.eye -= forward_norm * self.speed; + } + _ => {} + } + // TODO add rest of the movement + } +} diff --git a/src/main.rs b/src/main.rs index 9da3f32..9622cf3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,9 @@ use winit::{ mod state; mod vertex; mod texture; +mod camera; +mod uniform; +mod camera_controller; use crate::state::State; diff --git a/src/shader.wgsl b/src/shader.wgsl index bd6133b..8195e8e 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -1,4 +1,12 @@ // Vertex shader +[[block]] +struct Uniform { +view_projection: mat4x4; +}; + +[[group(1), binding(0)]] +var uniform: Uniform; + struct VertexInput { [[location(0)]] position: vec3; [[location(1)]] texture_coords: vec2; @@ -12,7 +20,7 @@ struct VertexOutput { [[stage(vertex)]] fn main(model: VertexInput) -> VertexOutput { var out: VertexOutput; - out.clip_coordinate = vec4(model.position, 1.0); + out.clip_coordinate = uniform.view_projection * vec4(model.position, 1.0); out.texture_coords = model.texture_coords; return out; } diff --git a/src/state.rs b/src/state.rs index 55e3e0c..58ecb3c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -7,8 +7,6 @@ use winit::{ window::Window, }; -use crate::{texture, vertex::Vertex}; - /// Hold state with important information pub struct State { surface: wgpu::Surface, @@ -19,7 +17,6 @@ pub struct State { swap_chain: wgpu::SwapChain, pub size: winit::dpi::PhysicalSize, - render_pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, @@ -27,7 +24,14 @@ pub struct State { num_indices: u32, aqua_bind_group: wgpu::BindGroup, - aqua_texture: texture::Texture, + aqua_texture: crate::texture::Texture, + + camera: crate::camera::Camera, + camera_controller: crate::camera_controller::CameraController, + + uniform: crate::uniform::Uniform, + uniform_buffer: wgpu::Buffer, + uniform_bind_group: wgpu::BindGroup, } impl State { @@ -79,7 +83,7 @@ impl State { let swap_chain = device.create_swap_chain(&surface, &sc_desc); let aqua_bytes = include_bytes!("../img/aqua.png"); - let aqua_texture = texture::Texture::from_bytes(&device, &queue, aqua_bytes, "aqua").unwrap(); + let aqua_texture = crate::texture::Texture::from_bytes(&device, &queue, aqua_bytes, "aqua").unwrap(); let texture_bind_group_layout = device.create_bind_group_layout( &wgpu::BindGroupLayoutDescriptor { @@ -127,6 +131,61 @@ impl State { } ); + let camera = crate::camera::Camera { + // x, y, z + // 1 up, 2 back + // +z is out of the screen + eye: (0.0, 1.0, 2.0).into(), + // look at the center + target: (0.0, 0.0, 0.0).into(), + // which way is up + up: cgmath::Vector3::unit_y(), + aspect: size.width as f32 / size.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + + let camera_controller = crate::camera_controller::CameraController::new(0.05); + + let mut uniform = crate::uniform::Uniform::new(); + uniform.update_view_proj(&camera); + + let uniform_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::cast_slice(&[uniform]), + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + } + ); + + let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("uniform_bind_group_layout"), + }); + + let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &uniform_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buffer.as_entire_binding(), + } + ], + label: Some("uniform_bind_group"), + }); + // load shader file let shader = device.create_shader_module( &wgpu::ShaderModuleDescriptor { @@ -139,7 +198,10 @@ impl State { let render_pipeline_layout = device.create_pipeline_layout( &wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layput"), - bind_group_layouts: &[&texture_bind_group_layout], + bind_group_layouts: &[ + &texture_bind_group_layout, + &uniform_bind_group_layout, + ], push_constant_ranges: &[], } ); @@ -154,7 +216,7 @@ impl State { // function name in shader.wgsl for [[stage(vertex)]] entry_point: "main", // specify memory layout - buffers: &[Vertex::desc()], + buffers: &[crate::vertex::Vertex::desc()], }, // needed to sotre color data to swap_chain @@ -212,15 +274,26 @@ impl State { surface, device, queue, + sc_desc, // saved, so we can create a new swap_chain later swap_chain, size, + render_pipeline, + vertex_buffer, index_buffer, num_indices, + aqua_bind_group, aqua_texture, + + camera, + camera_controller, + + uniform, + uniform_buffer, + uniform_bind_group, } } @@ -254,12 +327,18 @@ impl State { // self.use_pentagon = !self.use_pentagon; // true // } - _ => false + _ => self.camera_controller.process_events(event) } } - /// TODO + /// Update State before render() pub fn update(&mut self) { + // reposition camera + self.camera_controller.update_camera(&mut self.camera); + // update projection for uniform buffer + self.uniform.update_view_proj(&self.camera); + // write uniform buffer to queue + self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniform])); } /// Generate commands for gpu to render to frame @@ -296,6 +375,7 @@ impl State { render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); render_pass.set_bind_group(0, &self.aqua_bind_group, &[]); + render_pass.set_bind_group(1, &self.uniform_bind_group, &[]); // draw triangle render_pass.draw_indexed(0..self.num_indices, 0, 0..1); //render_pass.draw(0..self.num_vertices, 0..1); diff --git a/src/uniform.rs b/src/uniform.rs new file mode 100644 index 0000000..989c26c --- /dev/null +++ b/src/uniform.rs @@ -0,0 +1,18 @@ +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct Uniform { + view_projection: [[f32; 4]; 4], +} + +impl Uniform { + pub fn new() -> Self { + use cgmath::SquareMatrix; + Self { + view_projection: cgmath::Matrix4::identity().into(), + } + } + + pub fn update_view_proj(&mut self, camera: &crate::camera::Camera) { + self.view_projection = camera.build_view_projection_matrix().into(); + } +}