use lin_alg::f32::{Quaternion, Vec3};
use winit::event::{DeviceEvent, ElementState, MouseButton, MouseScrollDelta, WindowEvent};
use winit::keyboard::{KeyCode, PhysicalKey::Code};
use crate::{
ScrollBehavior,
camera::Camera,
graphics::{FWD_VEC, RIGHT_VEC, UP_VEC},
types::InputSettings,
};
const EPS_MOUSE: f32 = 0.00001;
#[derive(Default, Debug)]
pub struct InputsCommanded {
pub fwd: bool,
pub back: bool,
pub left: bool,
pub right: bool,
pub up: bool,
pub down: bool,
pub roll_ccw: bool,
pub roll_cw: bool,
pub mouse_delta_x: f32,
pub mouse_delta_y: f32,
pub run: bool,
pub scroll_up: bool,
pub scroll_down: bool,
pub free_look: bool,
pub panning: bool, pub cursor_out_of_window: bool,
}
impl InputsCommanded {
pub fn inputs_present(&self) -> bool {
self.fwd
|| self.back
|| self.left
|| self.right
|| self.up
|| self.down
|| self.roll_ccw
|| self.roll_cw
|| self.mouse_delta_x.abs() > EPS_MOUSE
|| self.mouse_delta_y.abs() > EPS_MOUSE
|| self.scroll_up
|| self.scroll_down
}
}
pub(crate) fn add_input_cmd_device(
event: &DeviceEvent,
inputs: &mut InputsCommanded,
use_dev_events: bool,
) {
if inputs.cursor_out_of_window {
*inputs = InputsCommanded {
cursor_out_of_window: true,
..Default::default()
};
return;
}
match event {
DeviceEvent::Key(key) => {
if !use_dev_events {
return;
}
if let Code(key_code) = key.physical_key {
handle_physical_key(inputs, key_code, key.state);
};
}
DeviceEvent::Button { button, state } => {
if !use_dev_events {
return;
}
#[cfg(target_os = "linux")]
let button_ = match button {
1 => MouseButton::Left,
3 => MouseButton::Right,
2 => MouseButton::Middle,
_ => MouseButton::Other(0), };
#[cfg(not(target_os = "linux"))]
let button_ = match button {
0 => MouseButton::Left,
1 => MouseButton::Right,
2 => MouseButton::Middle,
_ => MouseButton::Other(0), };
handle_mouse_buttons(inputs, button_, *state);
}
DeviceEvent::MouseWheel { delta } => {
if !use_dev_events {
return;
}
handle_mouse_wheel(inputs, delta)
}
DeviceEvent::MouseMotion { delta } => {
inputs.mouse_delta_x += delta.0 as f32;
inputs.mouse_delta_y += delta.1 as f32;
}
_ => (),
}
}
pub(crate) fn add_input_cmd_window(
event_: &WindowEvent,
inputs: &mut InputsCommanded,
use_dev_events: bool,
) {
if use_dev_events {
return;
}
match event_ {
WindowEvent::KeyboardInput {
device_id: _,
event,
is_synthetic: _,
} => {
if let Code(code) = event.physical_key {
handle_physical_key(inputs, code, event.state)
}
}
WindowEvent::MouseInput {
device_id: _,
state,
button,
} => handle_mouse_buttons(inputs, *button, *state),
WindowEvent::MouseWheel {
device_id: _,
delta,
phase: _,
} => handle_mouse_wheel(inputs, delta),
_ => (),
}
}
fn handle_mouse_buttons(inputs: &mut InputsCommanded, button: MouseButton, state: ElementState) {
if button == MouseButton::Left {
inputs.free_look = match state {
ElementState::Pressed => true,
ElementState::Released => false,
}
}
}
fn handle_mouse_wheel(inputs: &mut InputsCommanded, delta: &MouseScrollDelta) {
match delta {
MouseScrollDelta::PixelDelta(_) => (),
MouseScrollDelta::LineDelta(_x, y) => {
if *y > 0. {
inputs.scroll_down = true;
} else {
inputs.scroll_up = true;
}
}
}
}
fn handle_physical_key(inputs: &mut InputsCommanded, code: KeyCode, state: ElementState) {
match state {
ElementState::Pressed => match code {
KeyCode::KeyW => {
inputs.fwd = true;
}
KeyCode::KeyS => {
inputs.back = true;
}
KeyCode::KeyA => {
inputs.left = true;
}
KeyCode::KeyD => {
inputs.right = true;
}
KeyCode::Space => {
inputs.up = true;
}
KeyCode::KeyC => {
inputs.down = true;
}
KeyCode::KeyQ => {
inputs.roll_ccw = true;
}
KeyCode::KeyE => {
inputs.roll_cw = true;
}
KeyCode::ShiftLeft => {
inputs.run = true;
}
_ => (),
},
ElementState::Released => match code {
KeyCode::KeyW => {
inputs.fwd = false;
}
KeyCode::KeyS => {
inputs.back = false;
}
KeyCode::KeyA => {
inputs.left = false;
}
KeyCode::KeyD => {
inputs.right = false;
}
KeyCode::Space => {
inputs.up = false;
}
KeyCode::KeyC => {
inputs.down = false;
}
KeyCode::KeyQ => {
inputs.roll_ccw = false;
}
KeyCode::KeyE => {
inputs.roll_cw = false;
}
KeyCode::ShiftLeft => {
inputs.run = false;
}
_ => (),
},
}
}
fn handle_scroll(
cam: &mut Camera,
inputs: &mut InputsCommanded,
input_settings: &InputSettings,
dt: f32,
movement_vec: &mut Vec3,
rotation: &mut Quaternion,
cam_moved: &mut bool,
cam_rotated: &mut bool,
) {
if inputs.scroll_down || inputs.scroll_up {
if let ScrollBehavior::MoveRoll {
move_amt,
rotate_amt,
} = input_settings.scroll_behavior
{
if inputs.free_look {
let fwd = cam.orientation.rotate_vec(FWD_VEC);
let mut rot_amt = -rotate_amt * dt;
if inputs.scroll_down {
rot_amt *= -1.; }
*rotation = Quaternion::from_axis_angle(fwd, rot_amt);
*cam_rotated = true;
} else {
let mut movement = Vec3::new(0., 0., move_amt);
if inputs.scroll_up {
movement *= -1.;
}
*movement_vec += movement;
*cam_moved = true;
}
}
inputs.scroll_down = false;
inputs.scroll_up = false;
}
}
pub fn arc_rotation(cam: &mut Camera, axis: Vec3, amt: f32, center: Vec3) {
let rotation = Quaternion::from_axis_angle(axis, amt);
cam.orientation = (rotation * cam.orientation).to_normalized();
let dist = (cam.position - center).magnitude();
cam.position = center - cam.orientation.rotate_vec(FWD_VEC) * dist;
}
pub fn adjust_camera_free(
cam: &mut Camera,
inputs: &mut InputsCommanded,
input_settings: &InputSettings,
dt: f32,
) -> bool {
let mut move_amt = input_settings.move_sens * dt;
let mut rotate_key_amt = input_settings.rotate_key_sens * dt;
let mut cam_moved = false;
let mut cam_rotated = false;
let mut movement_vec = Vec3::new_zero();
let mut rotation = Quaternion::new_identity();
if inputs.run {
move_amt *= input_settings.run_factor;
rotate_key_amt *= input_settings.run_factor;
}
if inputs.fwd {
movement_vec.z += move_amt;
cam_moved = true;
} else if inputs.back {
movement_vec.z -= move_amt;
cam_moved = true;
}
if inputs.right {
movement_vec.x += move_amt;
cam_moved = true;
} else if inputs.left {
movement_vec.x -= move_amt;
cam_moved = true;
}
if inputs.up {
movement_vec.y += move_amt;
cam_moved = true;
} else if inputs.down {
movement_vec.y -= move_amt;
cam_moved = true;
}
if inputs.roll_cw {
let fwd = cam.orientation.rotate_vec(FWD_VEC);
rotation = Quaternion::from_axis_angle(fwd, -rotate_key_amt);
cam_rotated = true;
} else if inputs.roll_ccw {
let fwd = cam.orientation.rotate_vec(FWD_VEC);
rotation = Quaternion::from_axis_angle(fwd, rotate_key_amt);
cam_rotated = true;
}
if inputs.free_look
&& (inputs.mouse_delta_x.abs() > EPS_MOUSE || inputs.mouse_delta_y.abs() > EPS_MOUSE)
{
let rotate_amt = input_settings.rotate_sens * dt;
let up = cam.orientation.rotate_vec(-UP_VEC);
let right = cam.orientation.rotate_vec(-RIGHT_VEC);
rotation = Quaternion::from_axis_angle(up, -inputs.mouse_delta_x * rotate_amt)
* Quaternion::from_axis_angle(right, -inputs.mouse_delta_y * rotate_amt)
* rotation;
cam_rotated = true;
}
handle_scroll(
cam,
inputs,
input_settings,
dt,
&mut movement_vec,
&mut rotation,
&mut cam_moved,
&mut cam_rotated,
);
if cam_rotated {
cam.orientation = (rotation * cam.orientation).to_normalized();
}
if cam_moved {
cam.position += cam.orientation.rotate_vec(movement_vec);
}
cam_moved || cam_rotated
}
pub fn adjust_camera_arc(
cam: &mut Camera,
inputs: &mut InputsCommanded,
input_settings: &InputSettings,
center: Vec3,
dt: f32,
) -> bool {
let mut cam_moved = false;
let mut cam_rotated = false;
let mut movement_vec = Vec3::new_zero();
let mut rotation = Quaternion::new_identity();
let rotate_key_amt = -input_settings.rotate_key_sens * dt;
if inputs.roll_cw {
let fwd = cam.orientation.rotate_vec(FWD_VEC);
rotation = Quaternion::from_axis_angle(fwd, -rotate_key_amt);
cam_rotated = true;
} else if inputs.roll_ccw {
let fwd = cam.orientation.rotate_vec(FWD_VEC);
rotation = Quaternion::from_axis_angle(fwd, rotate_key_amt);
cam_rotated = true;
}
let mut skip_move_vec = false;
if inputs.free_look
&& (inputs.mouse_delta_x.abs() > EPS_MOUSE || inputs.mouse_delta_y.abs() > EPS_MOUSE)
{
let rotate_amt = input_settings.rotate_sens * dt;
let up = cam.orientation.rotate_vec(-UP_VEC);
let right = cam.orientation.rotate_vec(-RIGHT_VEC);
rotation = Quaternion::from_axis_angle(up, -inputs.mouse_delta_x * rotate_amt)
* Quaternion::from_axis_angle(right, -inputs.mouse_delta_y * rotate_amt);
skip_move_vec = true;
cam_moved = true;
cam_rotated = true;
}
handle_scroll(
cam,
inputs,
input_settings,
dt,
&mut movement_vec,
&mut rotation,
&mut cam_moved,
&mut cam_rotated,
);
if cam_rotated {
cam.orientation = (rotation * cam.orientation).to_normalized();
}
if cam_moved && !skip_move_vec {
cam.position += cam.orientation.rotate_vec(movement_vec);
} else if cam_moved {
let dist = (cam.position - center).magnitude();
cam.position = center - cam.orientation.rotate_vec(FWD_VEC) * dist;
}
cam_moved || cam_rotated
}