diff --git a/src/input/mod.rs b/src/input/mod.rs index b46e56e5..144011a5 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -7,32 +7,42 @@ use winit::event::{ DeviceEvent, ElementState, KeyboardInput, MouseButton, TouchPhase, WindowEvent, }; -use crate::input::camera_controller::CameraController; +use crate::input::pan_handler::PanHandler; +use crate::input::pinch_handler::PinchHandler; +use crate::input::shift_handler::ShiftHandler; +use crate::input::tilt_handler::TiltHandler; use crate::render::camera::Camera; use crate::render::render_state::RenderState; -mod camera_controller; +mod pan_handler; +mod pinch_handler; +mod shift_handler; +mod tilt_handler; -pub struct InputHandler { - camera_controller: CameraController, - - mouse_position: Option>, - last_mouse_position: Option>, - initial_camera: Option, - initial_camera_position: Option>, - mouse_pressed: bool, +pub struct InputController { + pinch_handler: PinchHandler, + pan_handler: PanHandler, + tilt_handler: TiltHandler, + shift_handler: ShiftHandler, } -impl InputHandler { - pub fn new() -> Self { - let camera_controller = CameraController::new(5.0, 100.0); +impl InputController { + /// Creates a new input controller. + /// + /// # Arguments + /// + /// * `speed`: How fast animation should go. Default is 1.0. 2.0 is a speedup of 2. + /// * `sensitivity`: How much impact an action has. Default is 10px for pressing the forward + /// key for example. + /// + /// returns: InputController + /// + pub fn new(speed: f64, sensitivity: f64) -> Self { Self { - initial_camera_position: None, - mouse_position: None, - last_mouse_position: None, - initial_camera: None, - mouse_pressed: false, - camera_controller, + pinch_handler: PinchHandler::new(), + pan_handler: PanHandler::new(), + tilt_handler: TiltHandler::new(speed, sensitivity), + shift_handler: ShiftHandler::new(speed, sensitivity), } } @@ -42,36 +52,12 @@ impl InputHandler { } } - fn pan_camera(&mut self, position: Vector2, render_state: &mut RenderState) { - if let (Some(last_mouse_position), Some(initial_camera_position), Some(initial_camera)) = ( - self.last_mouse_position, - self.initial_camera_position, - self.initial_camera.as_ref(), - ) { - self.camera_controller.pan_camera( - initial_camera_position, - last_mouse_position, - position, - initial_camera, - &render_state.camera, - &render_state.perspective, - ); - } else { - self.last_mouse_position = Some(position); - self.initial_camera_position = Some(render_state.camera.position); - self.initial_camera = Some(render_state.camera.clone()); - } - } - - pub fn window_input(&mut self, event: &WindowEvent, render_state: &mut RenderState) -> bool { + pub fn window_input(&mut self, event: &WindowEvent, render_state: &RenderState) -> bool { match event { WindowEvent::CursorMoved { position, .. } => { - if self.mouse_pressed { - let mouse_position: (f64, f64) = position.to_owned().into(); - self.pan_camera(Vector2::from(mouse_position), render_state); - self.mouse_position = Some(Vector2::from(mouse_position)); - } - true + let position: (f64, f64) = position.to_owned().into(); + self.pan_handler + .process_window_position(&Vector2::from(position), false) } WindowEvent::KeyboardInput { input: @@ -82,67 +68,39 @@ impl InputHandler { }, .. } => match key { - _ => self.camera_controller.process_keyboard(*key, *state), + _ => self.shift_handler.process_key_press(*key, *state), }, - WindowEvent::Touch(touch) => { - let touch_position: (f64, f64) = touch.location.to_owned().into(); - match touch.phase { - TouchPhase::Started => { - self.last_mouse_position = Some(Vector2::from(touch_position)); - self.initial_camera_position = Some(render_state.camera.position); - } - TouchPhase::Moved | TouchPhase::Ended => { - self.pan_camera(Vector2::from(touch_position), render_state); - } - TouchPhase::Cancelled => {} + WindowEvent::Touch(touch) => match touch.phase { + TouchPhase::Started => self.pan_handler.process_touch_start(), + TouchPhase::Ended => self.pan_handler.process_touch_end(), + TouchPhase::Moved => { + let position: (f64, f64) = touch.location.to_owned().into(); + self.pan_handler + .process_window_position(&Vector2::from(position), true) } - - true - } + TouchPhase::Cancelled => false, + }, WindowEvent::MouseWheel { delta, .. } => { - self.camera_controller.process_scroll(delta); + self.shift_handler.process_scroll(delta); true } - WindowEvent::MouseInput { - button: MouseButton::Left, // Left Mouse Button - state, - .. - } => { - self.mouse_pressed = *state == ElementState::Pressed; - - if !self.mouse_pressed { - /*if let ( - Some(last_mouse_position), - Some(initial_camera_position), - Some(initial_camera), - Some(mouse_position), - ) = ( - self.last_mouse_position, - self.initial_camera_position, - self.initial_camera.as_ref(), - self.mouse_position, - ) { - self.camera_controller.pan_camera( - initial_camera_position, - last_mouse_position, - mouse_position, - initial_camera, - &render_state.camera, - &render_state.perspective, - ); - }*/ - - self.last_mouse_position = None; - self.initial_camera_position = None; - self.initial_camera = None; - } - true + WindowEvent::MouseInput { button, state, .. } => { + self.pan_handler.process_mouse_key_press(button, state) } _ => false, } } +} - pub fn update_state(&mut self, state: &mut RenderState, dt: Duration) { - self.camera_controller.update_camera(&mut state.camera, dt); +pub trait UpdateState { + fn update_state(&mut self, state: &mut RenderState, dt: Duration); +} + +impl UpdateState for InputController { + fn update_state(&mut self, state: &mut RenderState, dt: Duration) { + self.pan_handler.update_state(state, dt); + self.pinch_handler.update_state(state, dt); + self.tilt_handler.update_state(state, dt); + self.shift_handler.update_state(state, dt); } } diff --git a/src/input/pan_handler.rs b/src/input/pan_handler.rs new file mode 100644 index 00000000..01515120 --- /dev/null +++ b/src/input/pan_handler.rs @@ -0,0 +1,108 @@ +use super::UpdateState; +use crate::render::camera::{Camera, Perspective}; +use crate::render::render_state::RenderState; +use cgmath::{EuclideanSpace, Point3, Vector2, Vector3}; +use std::time::Duration; +use winit::event::{ElementState, MouseButton}; + +pub struct PanHandler { + window_position: Option>, + start_window_position: Option>, + start_camera_position: Option>, + reference_camera: Option, + is_panning: bool, +} + +impl UpdateState for PanHandler { + fn update_state(&mut self, state: &mut RenderState, _dt: Duration) { + if !self.is_panning { + return; + } + + if let Some(reference_camera) = &self.reference_camera { + if let (Some(window_position), Some(start_window_position)) = + (self.window_position, self.start_window_position) + { + let perspective = &state.perspective; + let view_proj = reference_camera.calc_view_proj(perspective); + + let start = + reference_camera.project_screen_to_world(&start_window_position, &view_proj); + let current = + reference_camera.project_screen_to_world(&window_position, &view_proj); + let delta = start - current; + + if self.start_camera_position.is_none() { + self.start_camera_position = Some(state.camera.position.to_vec()); + } + + if let Some(start_camera_position) = self.start_camera_position { + state.camera.position = Point3::from_vec( + start_camera_position + Vector3::new(delta.x, delta.y, 0.0), + ); + } + } + } else { + self.reference_camera = Some(state.camera.clone()); + } + } +} + +impl PanHandler { + pub fn new() -> Self { + Self { + window_position: None, + start_window_position: None, + start_camera_position: None, + reference_camera: None, + is_panning: false, + } + } + + pub fn process_touch_start(&mut self) -> bool { + self.is_panning = true; + true + } + + pub fn process_touch_end(&mut self) -> bool { + self.start_camera_position = None; + self.start_window_position = None; + self.window_position = None; + self.reference_camera = None; + self.is_panning = false; + true + } + + pub fn process_window_position(&mut self, window_position: &Vector2, touch: bool) -> bool { + if !self.is_panning && !touch { + self.start_window_position = Some(*window_position); + self.window_position = Some(*window_position); + } else { + self.window_position = Some(*window_position); + } + + true + } + pub fn process_mouse_key_press(&mut self, key: &MouseButton, state: &ElementState) -> bool { + if *key != MouseButton::Left { + return false; + } + + if !self.is_panning { + // starting to pan + } + + if *state == ElementState::Pressed { + // currently panning or starting to pan + self.is_panning = true; + } else { + // finished panning + self.start_camera_position = None; + self.start_window_position = None; + self.window_position = None; + self.reference_camera = None; + self.is_panning = false; + } + true + } +} diff --git a/src/input/pinch_handler.rs b/src/input/pinch_handler.rs new file mode 100644 index 00000000..5ebcb6fb --- /dev/null +++ b/src/input/pinch_handler.rs @@ -0,0 +1,17 @@ +use super::UpdateState; +use crate::render::render_state::RenderState; +use std::time::Duration; + +pub struct PinchHandler {} + +impl UpdateState for PinchHandler { + fn update_state(&mut self, state: &mut RenderState, dt: Duration) { + // TODO + } +} + +impl PinchHandler { + pub fn new() -> Self { + Self {} + } +} diff --git a/src/input/camera_controller.rs b/src/input/shift_handler.rs similarity index 52% rename from src/input/camera_controller.rs rename to src/input/shift_handler.rs index 179df087..1ff89ac6 100644 --- a/src/input/camera_controller.rs +++ b/src/input/shift_handler.rs @@ -1,28 +1,49 @@ -use cgmath::{EuclideanSpace, Matrix4, Point3, Vector2, Vector3, Vector4, Zero}; -use log::info; +use super::UpdateState; +use crate::render::render_state::RenderState; +use cgmath::{Vector3, Zero}; +use std::time::Duration; -use crate::render::camera; - -#[derive(Debug)] -pub struct CameraController { - camera_position: Option>, +pub struct ShiftHandler { camera_translate: Vector3, speed: f64, sensitivity: f64, } -impl CameraController { +impl UpdateState for ShiftHandler { + fn update_state(&mut self, state: &mut RenderState, dt: Duration) { + let dt = dt.as_secs_f64() * (1.0 / self.speed); + + // Move in/out (aka. "zoom") + // Note: this isn't an actual zoom. The camera's position + // changes when zooming. I've added this to make it easier + // to get closer to an object you want to focus on. + let delta = self.camera_translate * dt; + state.camera.position += delta; + self.camera_translate -= delta; + } +} + +impl ShiftHandler { pub fn new(speed: f64, sensitivity: f64) -> Self { Self { - camera_position: None, camera_translate: Vector3::zero(), speed, sensitivity, } } - pub fn process_keyboard( + pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) { + self.camera_translate.z -= match delta { + winit::event::MouseScrollDelta::LineDelta(_, scroll) => *scroll as f64 * 50.0, + winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition { + y: scroll, + .. + }) => *scroll, + } * self.sensitivity; + } + + pub fn process_key_press( &mut self, key: winit::event::VirtualKeyCode, state: winit::event::ElementState, @@ -52,66 +73,4 @@ impl CameraController { _ => false, } } - - pub fn pan_camera( - &mut self, - initial_camera_position: cgmath::Point3, - initial_screen_position: Vector2, - current_screen_position: Vector2, - intial_camera: &camera::Camera, - camera: &camera::Camera, - perspective: &camera::Perspective, - ) { - let view_proj = camera.calc_view_proj(perspective); - let initial = camera.project_screen_to_world( - &initial_screen_position, - &intial_camera.calc_view_proj(perspective), - ); - let current = camera.project_screen_to_world( - ¤t_screen_position, - &intial_camera.calc_view_proj(perspective), - ); - let delta = initial - current; - - info!("initial: {:?}", initial); - info!("current: {:?}", current); - info!("delta: {:?}", delta); - - self.camera_position = - Some(initial_camera_position.to_vec() + Vector3::new(delta.x, delta.y, 0.0)); - //self.camera_position = Some(Vector3::new(-delta.x, delta.y, 0.0)) - } - - pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) { - self.camera_translate.z -= match delta { - winit::event::MouseScrollDelta::LineDelta(_, scroll) => *scroll as f64 * 50.0, - winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition { - y: scroll, - .. - }) => *scroll, - } * self.sensitivity; - } - - pub fn update_camera( - &mut self, - camera: &mut crate::render::camera::Camera, - dt: std::time::Duration, - ) { - let dt = dt.as_secs_f64() * self.speed; - - if let Some(position) = self.camera_position { - info!("position: {:?}", position); - camera.position = Point3::from_vec(position); - //camera.translation = Matrix4::from_translation(position); - self.camera_position = None; - } - - // Move in/out (aka. "zoom") - // Note: this isn't an actual zoom. The camera's position - // changes when zooming. I've added this to make it easier - // to get closer to an object you want to focus on. - let delta = self.camera_translate * dt; - camera.position += delta; - self.camera_translate -= delta; - } } diff --git a/src/input/tilt_handler.rs b/src/input/tilt_handler.rs new file mode 100644 index 00000000..9474f5e5 --- /dev/null +++ b/src/input/tilt_handler.rs @@ -0,0 +1,20 @@ +use super::UpdateState; +use crate::render::render_state::RenderState; +use std::time::Duration; + +pub struct TiltHandler { + speed: f64, + sensitivity: f64, +} + +impl UpdateState for TiltHandler { + fn update_state(&mut self, state: &mut RenderState, dt: Duration) { + // TODO + } +} + +impl TiltHandler { + pub fn new(speed: f64, sensitivity: f64) -> Self { + Self { speed, sensitivity } + } +} diff --git a/src/main_loop.rs b/src/main_loop.rs index 11c1ed33..1b09d1f2 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -3,7 +3,7 @@ use log::{error, info, trace}; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; -use crate::input::InputHandler; +use crate::input::{InputController, UpdateState}; use crate::io::worker_loop::WorkerLoop; use crate::platform::Instant; use crate::render::render_state::RenderState; @@ -17,7 +17,7 @@ pub async fn setup( fetch_munich_tiles(worker_loop.as_ref()); - let mut input = InputHandler::new(); + let mut input = InputController::new(0.2, 100.0); let mut maybe_state: Option = if cfg!(target_os = "android") { None } else {