diff --git a/src/app.rs b/src/app.rs index 471060d..cc944be 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; +use winit::event::Event; use winit::{window::Window, dpi::PhysicalSize, event::WindowEvent}; use wgpu::util::DeviceExt; use futures::executor::block_on; @@ -6,6 +8,7 @@ use crate::vertex::Vertex; use crate::texture::Texture; use crate::camera::Camera; use crate::uniforms::Uniforms; +use crate::camera_controller::CameraController; const VERTICES: &[Vertex] = &[ Vertex { position: [-1.0, 1.0, 0.0], uv: [0.0, 0.0] }, @@ -35,7 +38,9 @@ pub struct App { // Texture bind_group: wgpu::BindGroup, // Camera - camera: Camera + camera: Camera, + camera_controller: CameraController, + uniforms: Uniforms } impl App { @@ -46,7 +51,7 @@ impl App { format: adapter.get_swap_chain_preferred_format(&surface), width: size.width, height: size.height, - present_mode: wgpu::PresentMode::Fifo, + present_mode: wgpu::PresentMode::Mailbox, // Fifo = Vsync }; let swapchain = device.create_swap_chain(&surface, &swapchain_desc); (swapchain_desc, swapchain) @@ -209,9 +214,10 @@ impl App { // Cam let aspect = swapchain_desc.width as f32 / swapchain_desc.height as f32; - let camera = Camera::new((0.0, 1.0, 2.0).into(), (0.0, 0.0, 0.0).into(), aspect); + let camera = Camera::new((0.0, 0.0, 2.0).into(), (0.0, 0.0, -1.0).into(), aspect); let mut uniforms = Uniforms::new(); uniforms.update_projection_matrix(&camera); + let camera_controller = CameraController::new(2.0); // Buffers let vertex_buffer = Self::create_buffer(&device, wgpu::BufferUsage::VERTEX, bytemuck::cast_slice(VERTICES)); @@ -229,7 +235,7 @@ impl App { // Save values in app println!("Initialized"); - Self { surface, device, queue, swapchain_desc, swapchain, pipeline, vertex_buffer, index_buffer, uniform_buffer, bind_group, uniform_bind_group, camera, } + Self { surface, device, queue, swapchain_desc, swapchain, pipeline, vertex_buffer, index_buffer, uniform_buffer, bind_group, uniform_bind_group, camera, uniforms, camera_controller } } pub fn resize(&mut self, new_size: Option>) { @@ -239,20 +245,28 @@ impl App { // Recreate swapchain self.swapchain_desc.width = size.width; self.swapchain_desc.height = size.height; + // Update camera + self.camera.set_aspect(size.width as f32 / size.height as f32); } // Recreate swapchain self.swapchain = self.device.create_swap_chain(&self.surface, &self.swapchain_desc); } - pub fn input(&mut self, _event: &WindowEvent) -> bool { - false + pub fn input(&mut self, event: Arc>) -> bool { + self.camera_controller.handle_input(event.as_ref()) } - pub fn update(&mut self) { - // Nothing right now + pub fn update(&mut self, delta_time: f32) { + // Update camera + self.camera_controller.update_camera(&mut self.camera, delta_time); + + // Update projection matrix buffer + self.uniforms.update_projection_matrix(&self.camera); + self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); } pub fn render(&mut self) -> Result<(), wgpu::SwapChainError> { + // Get next frame to render to let frame = self.swapchain.get_current_frame()?.output; // Create encoder that will build command buffer for us diff --git a/src/camera.rs b/src/camera.rs index b233480..2017855 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -9,9 +9,9 @@ const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( ); pub struct Camera { - pos: Point3, - target: Point3, - up: Vector3, + pub position: Point3, + pub forward: Vector3, + pub up: Vector3, aspect: f32, fovy: f32, znear: f32, @@ -20,10 +20,10 @@ pub struct Camera { impl Camera { - pub fn new(pos: Point3, target: Point3, aspect: f32) -> Self { + pub fn new(position: Point3, forward: Vector3, aspect: f32) -> Self { Self { - pos, - target, + position, + forward, up: Vector3::unit_y(), aspect, fovy: 45.0, @@ -32,8 +32,12 @@ impl Camera { } } + pub fn set_aspect(&mut self, aspect: f32) { + self.aspect = aspect; + } + pub fn projection_matrix(&self) -> Matrix4 { - let view = Matrix4::look_at_rh(self.pos, self.target, self.up); + let view = Matrix4::look_to_rh(self.position, self.forward, self.up); let proj = cgmath::perspective(Deg(self.fovy), self.aspect, self.znear, self.zfar); return OPENGL_TO_WGPU_MATRIX * proj * view; } diff --git a/src/camera_controller.rs b/src/camera_controller.rs new file mode 100644 index 0000000..84bdd32 --- /dev/null +++ b/src/camera_controller.rs @@ -0,0 +1,92 @@ +use winit::event::Event; +use winit::event::{WindowEvent, DeviceEvent, KeyboardInput, ElementState, VirtualKeyCode}; +use cgmath::InnerSpace; +use crate::camera::Camera; + +#[derive(Default)] +struct CameraMovement { + pub up: bool, + pub down: bool, + pub fwd: bool, + pub back: bool, + pub left: bool, + pub right: bool, +} + +struct CameraRotation { + pub yaw: f32, // vertical axis + pub pitch: f32, // horizontal axis + pub last_update: Option +} + + +pub struct CameraController { + movement: CameraMovement, + rotation: CameraRotation, + speed: f32 +} + +impl CameraController { + pub fn new(speed: f32) -> Self { + let rotation = CameraRotation { yaw: 0.0, pitch: 0.0, last_update: None }; + Self { movement: CameraMovement::default(), rotation, speed } + } + + pub fn handle_input(&mut self, event: &Event<()>) -> bool { + match event { + Event::WindowEvent { ref event, .. } => { + match event { + WindowEvent::KeyboardInput { input: KeyboardInput { state, virtual_keycode: Some(keycode), .. }, .. } => { + let is_pressed = *state == ElementState::Pressed; + match keycode { + VirtualKeyCode::Space => { self.movement.up = is_pressed; true } + VirtualKeyCode::LShift => { self.movement.down = is_pressed; true } + VirtualKeyCode::W | VirtualKeyCode::Up => { self.movement.fwd = is_pressed; true } + VirtualKeyCode::A | VirtualKeyCode::Left => { self.movement.left = is_pressed; true } + VirtualKeyCode::S | VirtualKeyCode::Down => { self.movement.back = is_pressed; true } + VirtualKeyCode::D | VirtualKeyCode::Right => { self.movement.right = is_pressed; true } + _ => false, + } + } + _ => false + } + }, + Event::DeviceEvent { device_id: _, event } => { + match event { + DeviceEvent::MouseMotion { delta } => { + if let Some(last_update) = self.rotation.last_update { + let elapsed = last_update.elapsed().as_secs_f32(); + self.rotation.yaw += (delta.0 as f32) / elapsed; + self.rotation.pitch += (delta.1 as f32) / elapsed; + } + self.rotation.last_update = Some(std::time::Instant::now()); + true + } + _ => false + } + } + _ => false + } + } + + pub fn update_camera(&mut self, camera: &mut Camera, delta_time: f32) { + + // Movement amount + let amount = delta_time * self.speed; + + // Forward and backwards + if self.movement.fwd && camera.forward.magnitude() > amount { camera.position += camera.forward.normalize() * amount; } + if self.movement.back { camera.position -= camera.forward.normalize() * amount; } + + // Up and down + if self.movement.up { camera.position += camera.up.normalize() * amount; } + if self.movement.down { camera.position -= camera.up.normalize() * amount; } + + // Left and right + let right = camera.forward.cross(camera.up); + if self.movement.right { camera.position += right * amount; } + if self.movement.left { camera.position -= right * amount; } + + println!("Yaw: {}", self.rotation.yaw); + } +} diff --git a/src/main.rs b/src/main.rs index d7f2f06..f914360 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use winit::{ event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{ControlFlow, EventLoop}, @@ -10,6 +11,7 @@ mod texture; mod uniforms; mod camera; mod app; +mod camera_controller; use app::App; fn main() { @@ -28,12 +30,20 @@ fn main() { let mut fps_timer = std::time::Instant::now(); let mut fps_counter = 0; + let mut delta_timer = std::time::Instant::now(); // Run event loop events.run(move |e, _, c| { - match e { + let ev = Arc::new(e); + + if app.input(ev.clone()) { + // Event consumed + return; + } + + match ev.as_ref() { // Window event - Event::WindowEvent { ref event, .. } => if !app.input(event) { + Event::WindowEvent { ref event, .. } => { match event { // Window closed WindowEvent::CloseRequested => { @@ -69,18 +79,20 @@ fn main() { Event::RedrawRequested(_) => { // Display FPS - let elapsed = fps_timer.elapsed().as_millis(); - if elapsed >= 1000 { - let elapsed_sec = (elapsed as f32) / 1000.0; - let fps = (fps_counter as f32) / elapsed_sec; - println!("FPS: {}", fps); + let elapsed = fps_timer.elapsed().as_secs_f64(); + if elapsed >= 0.5 { + let fps = (fps_counter as f64) / elapsed; + println!("FPS: {}", (fps*100.0_f64).floor()/100.0); fps_timer = std::time::Instant::now(); fps_counter = 0; } fps_counter += 1; // Update and Render - app.update(); + app.update(delta_timer.elapsed().as_secs_f32()); + delta_timer = std::time::Instant::now(); + + // Render match app.render() { Ok(_) => { } // Recreate the swap_chain if lost diff --git a/src/texture.rs b/src/texture.rs index 3d2ac60..6d0ce00 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -47,7 +47,8 @@ impl Texture { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, + //mag_filter: wgpu::FilterMode::Linear, + mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, ..Default::default()