Move input handler code to own module

This commit is contained in:
Maximilian Ammann 2021-12-28 16:25:46 +01:00
parent 05775e712e
commit d75c72f401
7 changed files with 301 additions and 271 deletions

View File

@ -0,0 +1,144 @@
use std::f32::consts::FRAC_PI_2;
use cgmath::InnerSpace;
const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
#[derive(Debug)]
pub struct CameraController {
amount_left: f32,
amount_right: f32,
amount_forward: f32,
amount_backward: f32,
amount_up: f32,
amount_down: f32,
rotate_horizontal: f32,
rotate_vertical: f32,
scroll: f32,
speed: f32,
sensitivity: f32,
}
impl CameraController {
pub fn new(speed: f32, sensitivity: f32) -> Self {
Self {
amount_left: 0.0,
amount_right: 0.0,
amount_forward: 0.0,
amount_backward: 0.0,
amount_up: 0.0,
amount_down: 0.0,
rotate_horizontal: 0.0,
rotate_vertical: 0.0,
scroll: 0.0,
speed,
sensitivity,
}
}
pub fn process_keyboard(
&mut self,
key: winit::event::VirtualKeyCode,
state: winit::event::ElementState,
) -> bool {
let amount = if state == winit::event::ElementState::Pressed {
1.0
} else {
0.0
};
match key {
winit::event::VirtualKeyCode::W | winit::event::VirtualKeyCode::Up => {
self.amount_forward = amount;
true
}
winit::event::VirtualKeyCode::S | winit::event::VirtualKeyCode::Down => {
self.amount_backward = amount;
true
}
winit::event::VirtualKeyCode::A | winit::event::VirtualKeyCode::Left => {
self.amount_left = amount;
true
}
winit::event::VirtualKeyCode::D | winit::event::VirtualKeyCode::Right => {
self.amount_right = amount;
true
}
winit::event::VirtualKeyCode::Space => {
self.amount_up = amount;
true
}
winit::event::VirtualKeyCode::LShift => {
self.amount_down = amount;
true
}
_ => false,
}
}
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
self.rotate_horizontal = mouse_dx as f32;
self.rotate_vertical = mouse_dy as f32;
}
pub fn process_touch(&mut self, touch_dx: f64, touch_dy: f64) {
self.amount_right += touch_dx as f32;
self.amount_up += touch_dy as f32;
}
pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) {
self.scroll = -match delta {
// I'm assuming a line is about 100 pixels
winit::event::MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition {
y: scroll,
..
}) => *scroll as f32,
};
}
pub fn update_camera(
&mut self,
camera: &mut crate::render::camera::Camera,
dt: std::time::Duration,
) {
let dt = dt.as_secs_f32();
// Move forward/backward and left/right
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
let forward = cgmath::Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
let right = cgmath::Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
// 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 (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
let scrollward =
cgmath::Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
self.scroll = 0.0;
// Move up/down. Since we don't use roll, we can just
// modify the y coordinate directly.
camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
// Rotate
camera.yaw += cgmath::Rad(self.rotate_horizontal) * self.sensitivity * dt;
camera.pitch += cgmath::Rad(-self.rotate_vertical) * self.sensitivity * dt;
// If process_mouse isn't called every frame, these values
// will not get set to zero, and the camera will rotate
// when moving in a non cardinal direction.
self.rotate_horizontal = 0.0;
self.rotate_vertical = 0.0;
// Keep the camera's angle from going too high/low.
if camera.pitch < -cgmath::Rad(SAFE_FRAC_PI_2) {
camera.pitch = -cgmath::Rad(SAFE_FRAC_PI_2);
} else if camera.pitch > cgmath::Rad(SAFE_FRAC_PI_2) {
camera.pitch = cgmath::Rad(SAFE_FRAC_PI_2);
}
}
}

124
src/input/mod.rs Normal file
View File

@ -0,0 +1,124 @@
use std::time::Duration;
use winit::event::{
DeviceEvent, ElementState, KeyboardInput, MouseButton, TouchPhase, WindowEvent,
};
use winit::window::Window;
use crate::input::camera_controller::CameraController;
use crate::render::state::{SceneParams, State};
mod camera_controller;
pub struct InputHandler {
camera_controller: CameraController,
last_touch: Option<(f64, f64)>,
mouse_pressed: bool,
target_stroke_width: f32,
}
impl InputHandler {
pub fn new() -> Self {
let camera_controller = CameraController::new(3000.0, 0.2);
Self {
target_stroke_width: 1.0,
last_touch: None,
mouse_pressed: false,
camera_controller,
}
}
pub fn device_input(&mut self, event: &DeviceEvent, window: &Window) -> bool {
match event {
DeviceEvent::MouseMotion { delta } => {
if self.mouse_pressed {
self.camera_controller.process_mouse(
delta.0 / window.scale_factor(),
delta.1 / window.scale_factor(),
);
}
true
}
_ => false,
}
}
pub fn window_input(&mut self, event: &WindowEvent, window: &Window) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(key),
..
},
..
} => match key {
winit::event::VirtualKeyCode::Z => {
self.target_stroke_width *= 1.2;
true
}
winit::event::VirtualKeyCode::H => {
self.target_stroke_width *= 0.8;
true
}
_ => self.camera_controller.process_keyboard(*key, *state),
},
WindowEvent::Touch(touch) => {
match touch.phase {
TouchPhase::Started => {
self.last_touch = Some((touch.location.x, touch.location.y))
}
TouchPhase::Moved | TouchPhase::Ended => {
if let Some(start) = self.last_touch {
let delta_x = start.0 - touch.location.x;
let delta_y = start.1 - touch.location.y;
self.camera_controller.process_touch(
delta_x / window.scale_factor(),
delta_y / window.scale_factor(),
);
}
self.last_touch = Some((touch.location.x, touch.location.y))
}
TouchPhase::Cancelled => {}
}
true
}
WindowEvent::MouseWheel { delta, .. } => {
self.camera_controller.process_scroll(delta);
true
}
WindowEvent::MouseInput {
button: MouseButton::Left, // Left Mouse Button
state,
..
} => {
self.mouse_pressed = *state == ElementState::Pressed;
true
}
_ => false,
}
}
pub fn update_state(&mut self, state: &mut State, dt: Duration) {
let scene = &mut state.scene;
self.camera_controller.update_camera(&mut state.camera, dt);
// Animate the stroke_width to match target_stroke_width
scene.stroke_width =
scene.stroke_width + (self.target_stroke_width - scene.stroke_width) / 5.0;
// Animate the strokes of primitive
scene.cpu_primitives[0 as usize].width = scene.stroke_width;
/*
scene.cpu_primitives[STROKE_PRIM_ID as usize].color = [
(time_secs * 0.8 - 1.6).sin() * 0.1 + 0.1,
(time_secs * 0.5 - 1.6).sin() * 0.1 + 0.1,
(time_secs - 1.6).sin() * 0.1 + 0.1,
1.0,
];
*/
}
}

View File

@ -1,4 +1,5 @@
mod fps_meter;
mod input;
mod io;
pub mod main_loop;
mod platform;

View File

@ -1,13 +1,15 @@
use log::{info, trace};
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::platform::Instant;
use crate::render::state::State;
pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>) {
info!("== mapr ==");
let mut input = InputHandler::new();
let mut state = State::new(&window).await;
let mut last_render_time = Instant::now();
@ -19,14 +21,14 @@ pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>) {
.. // We're not using device_id currently
} => {
trace!("{:?}", event);
state.device_input(event, &window);
input.device_input(event,&window);
}
Event::WindowEvent {
ref event,
window_id,
} if window_id == window.id() => {
if !state.window_input(event, &window) {
if !input.window_input(event, &window) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
@ -53,13 +55,19 @@ pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>) {
let now = Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
input.update_state( &mut state, dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if lost
Err(wgpu::SurfaceError::Lost) => state.resize(state.size),
Err(wgpu::SurfaceError::Lost) => {
error!("Surface Lost");
*control_flow = ControlFlow::Exit;
},
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
Err(wgpu::SurfaceError::OutOfMemory) => {
error!("Out of Memory");
*control_flow = ControlFlow::Exit;
},
// All other errors (Outdated, Timeout) should be resolved by the next frame
Err(e) => eprintln!("{:?}", e),
}

View File

@ -1,5 +1,3 @@
use std::f32::consts::FRAC_PI_2;
use cgmath::prelude::*;
use crate::render::shader_ffi::CameraUniform;
@ -12,13 +10,11 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
0.0, 0.0, 0.5, 1.0,
);
const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
#[derive(Debug)]
pub struct Camera {
pub position: cgmath::Point3<f32>,
yaw: cgmath::Rad<f32>,
pitch: cgmath::Rad<f32>,
pub yaw: cgmath::Rad<f32>,
pub pitch: cgmath::Rad<f32>,
}
impl Camera {
@ -46,6 +42,12 @@ impl Camera {
cgmath::Vector3::unit_y(),
)
}
pub fn create_camera_uniform(&self, projection: &Projection) -> CameraUniform {
CameraUniform::new(
(projection.calc_matrix() * self.calc_matrix()).into(),
self.position.to_homogeneous().into(),
)
}
}
pub struct Projection {
@ -79,145 +81,3 @@ impl Projection {
OPENGL_TO_WGPU_MATRIX * cgmath::perspective(self.fovy, self.aspect, self.znear, self.zfar)
}
}
#[derive(Debug)]
pub struct CameraController {
amount_left: f32,
amount_right: f32,
amount_forward: f32,
amount_backward: f32,
amount_up: f32,
amount_down: f32,
rotate_horizontal: f32,
rotate_vertical: f32,
scroll: f32,
speed: f32,
sensitivity: f32,
}
impl CameraController {
pub fn new(speed: f32, sensitivity: f32) -> Self {
Self {
amount_left: 0.0,
amount_right: 0.0,
amount_forward: 0.0,
amount_backward: 0.0,
amount_up: 0.0,
amount_down: 0.0,
rotate_horizontal: 0.0,
rotate_vertical: 0.0,
scroll: 0.0,
speed,
sensitivity,
}
}
pub fn process_keyboard(
&mut self,
key: winit::event::VirtualKeyCode,
state: winit::event::ElementState,
) -> bool {
let amount = if state == winit::event::ElementState::Pressed {
1.0
} else {
0.0
};
match key {
winit::event::VirtualKeyCode::W | winit::event::VirtualKeyCode::Up => {
self.amount_forward = amount;
true
}
winit::event::VirtualKeyCode::S | winit::event::VirtualKeyCode::Down => {
self.amount_backward = amount;
true
}
winit::event::VirtualKeyCode::A | winit::event::VirtualKeyCode::Left => {
self.amount_left = amount;
true
}
winit::event::VirtualKeyCode::D | winit::event::VirtualKeyCode::Right => {
self.amount_right = amount;
true
}
winit::event::VirtualKeyCode::Space => {
self.amount_up = amount;
true
}
winit::event::VirtualKeyCode::LShift => {
self.amount_down = amount;
true
}
_ => false,
}
}
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
self.rotate_horizontal = mouse_dx as f32;
self.rotate_vertical = mouse_dy as f32;
}
pub fn process_touch(&mut self, touch_dx: f64, touch_dy: f64) {
self.amount_right += touch_dx as f32;
self.amount_up += touch_dy as f32;
}
pub fn process_scroll(&mut self, delta: &winit::event::MouseScrollDelta) {
self.scroll = -match delta {
// I'm assuming a line is about 100 pixels
winit::event::MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
winit::event::MouseScrollDelta::PixelDelta(winit::dpi::PhysicalPosition {
y: scroll,
..
}) => *scroll as f32,
};
}
pub fn update_camera(&mut self, camera: &mut Camera, dt: std::time::Duration) {
let dt = dt.as_secs_f32();
// Move forward/backward and left/right
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
let forward = cgmath::Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
let right = cgmath::Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
// 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 (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
let scrollward =
cgmath::Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
self.scroll = 0.0;
// Move up/down. Since we don't use roll, we can just
// modify the y coordinate directly.
camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
// Rotate
camera.yaw += cgmath::Rad(self.rotate_horizontal) * self.sensitivity * dt;
camera.pitch += cgmath::Rad(-self.rotate_vertical) * self.sensitivity * dt;
// If process_mouse isn't called every frame, these values
// will not get set to zero, and the camera will rotate
// when moving in a non cardinal direction.
self.rotate_horizontal = 0.0;
self.rotate_vertical = 0.0;
// Keep the camera's angle from going too high/low.
if camera.pitch < -cgmath::Rad(SAFE_FRAC_PI_2) {
camera.pitch = -cgmath::Rad(SAFE_FRAC_PI_2);
} else if camera.pitch > cgmath::Rad(SAFE_FRAC_PI_2) {
camera.pitch = cgmath::Rad(SAFE_FRAC_PI_2);
}
}
pub fn create_camera_uniform(camera: &Camera, projection: &Projection) -> CameraUniform {
CameraUniform::new(
(projection.calc_matrix() * camera.calc_matrix()).into(),
camera.position.to_homogeneous().into(),
)
}
}

View File

@ -1,8 +1,9 @@
mod camera;
mod piplines;
mod shader_ffi;
mod tesselation;
mod texture;
mod shaders;
pub mod camera;
pub mod state;

View File

@ -16,7 +16,7 @@ use vector_tile::parse_tile_reader;
use crate::fps_meter::FPSMeter;
use crate::io::static_database;
use crate::render::camera::CameraController;
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
use crate::render::tesselation::TileMask;
use crate::render::{camera, shaders};
@ -24,23 +24,16 @@ use super::piplines::*;
use super::shader_ffi::*;
use super::tesselation::Tesselated;
use super::texture::Texture;
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
pub struct SceneParams {
stroke_width: f32,
target_stroke_width: f32,
last_touch: Option<(f64, f64)>,
cpu_primitives: Vec<PrimitiveUniform>,
pub stroke_width: f32,
pub cpu_primitives: Vec<PrimitiveUniform>,
}
impl Default for SceneParams {
fn default() -> Self {
SceneParams {
stroke_width: 1.0,
target_stroke_width: 1.0,
last_touch: None,
cpu_primitives: vec![],
}
}
@ -69,7 +62,7 @@ pub struct State {
surface_config: wgpu::SurfaceConfiguration,
suspended: bool,
pub size: winit::dpi::PhysicalSize<u32>,
size: winit::dpi::PhysicalSize<u32>,
render_pipeline: wgpu::RenderPipeline,
mask_pipeline: wgpu::RenderPipeline,
@ -95,12 +88,10 @@ pub struct State {
tile_mask_range: Range<u32>,
tile_mask_instances: wgpu::Buffer,
camera: camera::Camera,
pub(crate) camera: camera::Camera,
projection: camera::Projection,
camera_controller: camera::CameraController,
mouse_pressed: bool,
scene: SceneParams,
pub scene: SceneParams,
}
impl SceneParams {
@ -401,7 +392,6 @@ impl State {
0.1,
100000.0,
);
let camera_controller = camera::CameraController::new(3000.0, 0.2);
Self {
instance,
@ -431,8 +421,6 @@ impl State {
tile_mask_instances,
camera,
projection,
camera_controller,
mouse_pressed: false,
tile2_stroke_range,
suspended: false, // Initially the app is not suspended
}
@ -483,81 +471,6 @@ impl State {
}
}
pub fn device_input(&mut self, event: &DeviceEvent, window: &Window) -> bool {
match event {
DeviceEvent::MouseMotion { delta } => {
if self.mouse_pressed {
warn!("mouse {}", delta.0);
self.camera_controller.process_mouse(
delta.0 / window.scale_factor(),
delta.1 / window.scale_factor(),
);
}
true
}
_ => false,
}
}
pub fn window_input(&mut self, event: &WindowEvent, window: &Window) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state,
virtual_keycode: Some(key),
..
},
..
} => match key {
winit::event::VirtualKeyCode::Z => {
self.scene.target_stroke_width *= 1.2;
true
}
winit::event::VirtualKeyCode::H => {
self.scene.target_stroke_width *= 0.8;
true
}
_ => self.camera_controller.process_keyboard(*key, *state),
},
WindowEvent::Touch(touch) => {
match touch.phase {
TouchPhase::Started => {
self.scene.last_touch = Some((touch.location.x, touch.location.y))
}
TouchPhase::Moved | TouchPhase::Ended => {
if let Some(start) = self.scene.last_touch {
let delta_x = start.0 - touch.location.x;
let delta_y = start.1 - touch.location.y;
self.camera_controller.process_touch(
delta_x / window.scale_factor(),
delta_y / window.scale_factor(),
);
}
self.scene.last_touch = Some((touch.location.x, touch.location.y))
}
TouchPhase::Cancelled => {}
}
true
}
WindowEvent::MouseWheel { delta, .. } => {
self.camera_controller.process_scroll(delta);
true
}
WindowEvent::MouseInput {
button: MouseButton::Left, // Left Mouse Button
state,
..
} => {
self.mouse_pressed = *state == ElementState::Pressed;
true
}
_ => false,
}
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let frame = self.surface.get_current_texture()?;
let scene = &mut self.scene;
@ -570,7 +483,7 @@ impl State {
&self.globals_uniform_buffer,
0,
bytemuck::cast_slice(&[GlobalsUniform::new(
CameraController::create_camera_uniform(&self.camera, &self.projection),
self.camera.create_camera_uniform(&self.projection),
)]),
);
@ -662,31 +575,10 @@ impl State {
self.queue.submit(Some(encoder.finish()));
frame.present();
self.fps_meter.update_and_print();
Ok(())
}
pub fn update(&mut self, dt: std::time::Duration) {
let scene = &mut self.scene;
self.camera_controller.update_camera(&mut self.camera, dt);
// Animate the stroke_width to match target_stroke_width
scene.stroke_width =
scene.stroke_width + (scene.target_stroke_width - scene.stroke_width) / 5.0;
// Animate the strokes of primitive
scene.cpu_primitives[STROKE_PRIM_ID as usize].width = scene.stroke_width;
/*
scene.cpu_primitives[STROKE_PRIM_ID as usize].color = [
(time_secs * 0.8 - 1.6).sin() * 0.1 + 0.1,
(time_secs * 0.5 - 1.6).sin() * 0.1 + 0.1,
(time_secs - 1.6).sin() * 0.1 + 0.1,
1.0,
];
*/
self.fps_meter.update_and_print()
}
pub fn is_suspended(&self) -> bool {
self.suspended
}