mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Refactor input handling
This commit is contained in:
parent
0cd5f8fc41
commit
8cede3df70
156
src/input/mod.rs
156
src/input/mod.rs
@ -7,32 +7,42 @@ use winit::event::{
|
|||||||
DeviceEvent, ElementState, KeyboardInput, MouseButton, TouchPhase, WindowEvent,
|
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::camera::Camera;
|
||||||
use crate::render::render_state::RenderState;
|
use crate::render::render_state::RenderState;
|
||||||
|
|
||||||
mod camera_controller;
|
mod pan_handler;
|
||||||
|
mod pinch_handler;
|
||||||
|
mod shift_handler;
|
||||||
|
mod tilt_handler;
|
||||||
|
|
||||||
pub struct InputHandler {
|
pub struct InputController {
|
||||||
camera_controller: CameraController,
|
pinch_handler: PinchHandler,
|
||||||
|
pan_handler: PanHandler,
|
||||||
mouse_position: Option<Vector2<f64>>,
|
tilt_handler: TiltHandler,
|
||||||
last_mouse_position: Option<Vector2<f64>>,
|
shift_handler: ShiftHandler,
|
||||||
initial_camera: Option<Camera>,
|
|
||||||
initial_camera_position: Option<cgmath::Point3<f64>>,
|
|
||||||
mouse_pressed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputHandler {
|
impl InputController {
|
||||||
pub fn new() -> Self {
|
/// Creates a new input controller.
|
||||||
let camera_controller = CameraController::new(5.0, 100.0);
|
///
|
||||||
|
/// # 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 {
|
Self {
|
||||||
initial_camera_position: None,
|
pinch_handler: PinchHandler::new(),
|
||||||
mouse_position: None,
|
pan_handler: PanHandler::new(),
|
||||||
last_mouse_position: None,
|
tilt_handler: TiltHandler::new(speed, sensitivity),
|
||||||
initial_camera: None,
|
shift_handler: ShiftHandler::new(speed, sensitivity),
|
||||||
mouse_pressed: false,
|
|
||||||
camera_controller,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,36 +52,12 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pan_camera(&mut self, position: Vector2<f64>, render_state: &mut RenderState) {
|
pub fn window_input(&mut self, event: &WindowEvent, render_state: &RenderState) -> bool {
|
||||||
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 {
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
if self.mouse_pressed {
|
let position: (f64, f64) = position.to_owned().into();
|
||||||
let mouse_position: (f64, f64) = position.to_owned().into();
|
self.pan_handler
|
||||||
self.pan_camera(Vector2::from(mouse_position), render_state);
|
.process_window_position(&Vector2::from(position), false)
|
||||||
self.mouse_position = Some(Vector2::from(mouse_position));
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
input:
|
input:
|
||||||
@ -82,67 +68,39 @@ impl InputHandler {
|
|||||||
},
|
},
|
||||||
..
|
..
|
||||||
} => match key {
|
} => match key {
|
||||||
_ => self.camera_controller.process_keyboard(*key, *state),
|
_ => self.shift_handler.process_key_press(*key, *state),
|
||||||
},
|
},
|
||||||
WindowEvent::Touch(touch) => {
|
WindowEvent::Touch(touch) => match touch.phase {
|
||||||
let touch_position: (f64, f64) = touch.location.to_owned().into();
|
TouchPhase::Started => self.pan_handler.process_touch_start(),
|
||||||
match touch.phase {
|
TouchPhase::Ended => self.pan_handler.process_touch_end(),
|
||||||
TouchPhase::Started => {
|
TouchPhase::Moved => {
|
||||||
self.last_mouse_position = Some(Vector2::from(touch_position));
|
let position: (f64, f64) = touch.location.to_owned().into();
|
||||||
self.initial_camera_position = Some(render_state.camera.position);
|
self.pan_handler
|
||||||
}
|
.process_window_position(&Vector2::from(position), true)
|
||||||
TouchPhase::Moved | TouchPhase::Ended => {
|
|
||||||
self.pan_camera(Vector2::from(touch_position), render_state);
|
|
||||||
}
|
|
||||||
TouchPhase::Cancelled => {}
|
|
||||||
}
|
}
|
||||||
|
TouchPhase::Cancelled => false,
|
||||||
true
|
},
|
||||||
}
|
|
||||||
WindowEvent::MouseWheel { delta, .. } => {
|
WindowEvent::MouseWheel { delta, .. } => {
|
||||||
self.camera_controller.process_scroll(delta);
|
self.shift_handler.process_scroll(delta);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
WindowEvent::MouseInput {
|
WindowEvent::MouseInput { button, state, .. } => {
|
||||||
button: MouseButton::Left, // Left Mouse Button
|
self.pan_handler.process_mouse_key_press(button, state)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_state(&mut self, state: &mut RenderState, dt: Duration) {
|
pub trait UpdateState {
|
||||||
self.camera_controller.update_camera(&mut state.camera, dt);
|
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
108
src/input/pan_handler.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/input/pinch_handler.rs
Normal file
17
src/input/pinch_handler.rs
Normal 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 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,28 +1,49 @@
|
|||||||
use cgmath::{EuclideanSpace, Matrix4, Point3, Vector2, Vector3, Vector4, Zero};
|
use super::UpdateState;
|
||||||
use log::info;
|
use crate::render::render_state::RenderState;
|
||||||
|
use cgmath::{Vector3, Zero};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::render::camera;
|
pub struct ShiftHandler {
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CameraController {
|
|
||||||
camera_position: Option<Vector3<f64>>,
|
|
||||||
camera_translate: Vector3<f64>,
|
camera_translate: Vector3<f64>,
|
||||||
|
|
||||||
speed: f64,
|
speed: f64,
|
||||||
sensitivity: 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 {
|
pub fn new(speed: f64, sensitivity: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
camera_position: None,
|
|
||||||
camera_translate: Vector3::zero(),
|
camera_translate: Vector3::zero(),
|
||||||
speed,
|
speed,
|
||||||
sensitivity,
|
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,
|
&mut self,
|
||||||
key: winit::event::VirtualKeyCode,
|
key: winit::event::VirtualKeyCode,
|
||||||
state: winit::event::ElementState,
|
state: winit::event::ElementState,
|
||||||
@ -52,66 +73,4 @@ impl CameraController {
|
|||||||
_ => false,
|
_ => 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(
|
|
||||||
¤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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
20
src/input/tilt_handler.rs
Normal file
20
src/input/tilt_handler.rs
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ use log::{error, info, trace};
|
|||||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
|
||||||
use crate::input::InputHandler;
|
use crate::input::{InputController, UpdateState};
|
||||||
use crate::io::worker_loop::WorkerLoop;
|
use crate::io::worker_loop::WorkerLoop;
|
||||||
use crate::platform::Instant;
|
use crate::platform::Instant;
|
||||||
use crate::render::render_state::RenderState;
|
use crate::render::render_state::RenderState;
|
||||||
@ -17,7 +17,7 @@ pub async fn setup(
|
|||||||
|
|
||||||
fetch_munich_tiles(worker_loop.as_ref());
|
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") {
|
let mut maybe_state: Option<RenderState> = if cfg!(target_os = "android") {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user