Refactor input handling

This commit is contained in:
Maximilian Ammann 2022-01-18 13:42:53 +01:00
parent 0cd5f8fc41
commit 8cede3df70
6 changed files with 235 additions and 173 deletions

View File

@ -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<Vector2<f64>>,
last_mouse_position: Option<Vector2<f64>>,
initial_camera: Option<Camera>,
initial_camera_position: Option<cgmath::Point3<f64>>,
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<f64>, 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 => {}
}
true
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)
}
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);
}
}

108
src/input/pan_handler.rs Normal file
View File

@ -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<Vector2<f64>>,
start_window_position: Option<Vector2<f64>>,
start_camera_position: Option<Vector3<f64>>,
reference_camera: Option<Camera>,
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<f64>, 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
}
}

View File

@ -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 {}
}
}

View File

@ -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<Vector3<f64>>,
pub struct ShiftHandler {
camera_translate: Vector3<f64>,
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<f64>,
initial_screen_position: Vector2<f64>,
current_screen_position: Vector2<f64>,
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(
&current_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;
}
}

20
src/input/tilt_handler.rs Normal file
View File

@ -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 }
}
}

View File

@ -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<RenderState> = if cfg!(target_os = "android") {
None
} else {