From 9165d8e93f37934356739e532b00ffb14967eb3f Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 12 Jun 2022 21:47:11 +0200 Subject: [PATCH] [CAMERA] Add from tutorial --- engine_core/src/camera.rs | 139 ++++++++++++++++++++++++++++++++++++ engine_core/src/lib.rs | 1 + engine_core/src/shader.wgsl | 8 ++- engine_core/src/state.rs | 80 +++++++++++++++++++-- 4 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 engine_core/src/camera.rs diff --git a/engine_core/src/camera.rs b/engine_core/src/camera.rs new file mode 100644 index 0000000..fdcd82a --- /dev/null +++ b/engine_core/src/camera.rs @@ -0,0 +1,139 @@ +use winit::event::{WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}; + +#[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 { + fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { + let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up); + let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); + + return OPENGL_TO_WGPU_MATRIX * proj * view; + } +} + +// We need this for Rust to store our data correctly for the shaders +#[repr(C)] +// This is so we can store this in a buffer +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct CameraUniform { + // We can't use cgmath with bytemuck directly so we'll have + // to convert the Matrix4 into a 4x4 f32 array + view_proj: [[f32; 4]; 4], +} + +impl CameraUniform { + pub fn new() -> Self { + use cgmath::SquareMatrix; + Self { + view_proj: cgmath::Matrix4::identity().into(), + } + } + + pub fn update_view_proj(&mut self, camera: &Camera) { + self.view_proj = camera.build_view_projection_matrix().into(); + } +} + +pub struct CameraController { + speed: f32, + is_forward_pressed: bool, + is_backward_pressed: bool, + is_left_pressed: bool, + is_right_pressed: bool, +} + +impl CameraController { + pub fn new(speed: f32) -> Self { + Self { + speed, + is_forward_pressed: false, + is_backward_pressed: false, + is_left_pressed: false, + is_right_pressed: false, + } + } + + pub fn process_events(&mut self, event: &WindowEvent) -> bool { + match event { + WindowEvent::KeyboardInput { + input: + KeyboardInput { + state, + virtual_keycode: Some(keycode), + .. + }, + .. + } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::W | VirtualKeyCode::Up => { + self.is_forward_pressed = is_pressed; + true + } + VirtualKeyCode::A | VirtualKeyCode::Left => { + self.is_left_pressed = is_pressed; + true + } + VirtualKeyCode::S | VirtualKeyCode::Down => { + self.is_backward_pressed = is_pressed; + true + } + VirtualKeyCode::D | VirtualKeyCode::Right => { + self.is_right_pressed = is_pressed; + true + } + _ => false, + } + } + _ => false, + } + } + + pub fn update_camera(&self, camera: &mut Camera) { + use cgmath::InnerSpace; + let forward = camera.target - camera.eye; + let forward_norm = forward.normalize(); + let forward_mag = forward.magnitude(); + + // Prevents glitching when camera gets too close to the + // center of the scene. + if self.is_forward_pressed && forward_mag > self.speed { + camera.eye += forward_norm * self.speed; + } + if self.is_backward_pressed { + camera.eye -= forward_norm * self.speed; + } + + let right = forward_norm.cross(camera.up); + + // Redo radius calc in case the fowrard/backward is pressed. + let forward = camera.target - camera.eye; + let forward_mag = forward.magnitude(); + + if self.is_right_pressed { + // Rescale the distance between the target and eye so + // that it doesn't change. The eye therefore still + // lies on the circle made by the target and eye. + camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag; + } + if self.is_left_pressed { + camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag; + } + } +} diff --git a/engine_core/src/lib.rs b/engine_core/src/lib.rs index f1cd781..133eeca 100644 --- a/engine_core/src/lib.rs +++ b/engine_core/src/lib.rs @@ -1,6 +1,7 @@ mod state; pub(self) mod vertex; pub(self) mod texture; +pub(self) mod camera; use winit::{ event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, diff --git a/engine_core/src/shader.wgsl b/engine_core/src/shader.wgsl index 8a9d84f..38be381 100644 --- a/engine_core/src/shader.wgsl +++ b/engine_core/src/shader.wgsl @@ -1,3 +1,9 @@ +struct CameraUniform { + view_proj: mat4x4; +}; +[[group(1), binding(0)]] +var camera: CameraUniform; + struct VertexInput { [[location(0)]] position: vec3; [[location(1)]] tex_coords: vec2; @@ -14,7 +20,7 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; out.tex_coords = model.tex_coords; - out.clip_position = vec4(model.position, 1.0); + out.clip_position = camera.view_proj * vec4(model.position, 1.0); return out; } diff --git a/engine_core/src/state.rs b/engine_core/src/state.rs index 51ee97f..b88df44 100644 --- a/engine_core/src/state.rs +++ b/engine_core/src/state.rs @@ -35,6 +35,11 @@ pub struct State { pub size: winit::dpi::PhysicalSize, render_pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, + camera: super::camera::Camera, + camera_uniform: super::camera::CameraUniform, + camera_buffer: wgpu::Buffer, + camera_bind_group: wgpu::BindGroup, + camera_controller: super::camera::CameraController, // num_vertices: u32, index_buffer: wgpu::Buffer, num_indices: u32, @@ -160,10 +165,64 @@ impl State { source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), }); + let camera = super::camera::Camera { + // position the camera one unit up and 2 units back + // +z is out of the screen + eye: (0.0, 1.0, 2.0).into(), + // have it look at the origin + target: (0.0, 0.0, 0.0).into(), + // which way is "up" + up: cgmath::Vector3::unit_y(), + aspect: config.width as f32 / config.height as f32, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + }; + + let mut camera_uniform = super::camera::CameraUniform::new(); + camera_uniform.update_view_proj(&camera); + + let camera_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Camera Buffer"), + contents: bytemuck::cast_slice(&[camera_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + } + ); + + let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("camera_bind_group_layout"), + }); + + let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &camera_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera_buffer.as_entire_binding(), + } + ], + label: Some("camera_bind_group"), + }); + + let camera_controller = super::camera::CameraController::new(0.2); + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), - bind_group_layouts: &[&texture_bind_group_layout], + bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], push_constant_ranges: &[], }); @@ -203,7 +262,7 @@ impl State { alpha_to_coverage_enabled: false, }, multiview: None, - }); + }); Self { surface, @@ -213,6 +272,11 @@ impl State { size, render_pipeline, vertex_buffer, + camera, + camera_uniform, + camera_buffer, + camera_bind_group, + camera_controller, // num_vertices, index_buffer, num_indices, @@ -230,12 +294,15 @@ impl State { } } - pub fn input(&mut self, _event: &WindowEvent) -> bool { - // log::info!("{:#?}", event); - false + pub fn input(&mut self, event: &WindowEvent) -> bool { + self.camera_controller.process_events(event) } - pub fn update(&mut self) {} + pub fn update(&mut self) { + self.camera_controller.update_camera(&mut self.camera); + self.camera_uniform.update_view_proj(&self.camera); + self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[self.camera_uniform])); + } pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { let output = self.surface.get_current_texture()?; @@ -273,6 +340,7 @@ impl State { render_pass.set_pipeline(&self.render_pipeline); render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]); + render_pass.set_bind_group(1, &self.camera_bind_group, &[]); render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); // render_pass.draw(0..self.num_vertices, 0..1);