diff --git a/space-crush-app/src/error.rs b/space-crush-app/src/error.rs index e60a04d..51f2f36 100644 --- a/space-crush-app/src/error.rs +++ b/space-crush-app/src/error.rs @@ -1,7 +1,10 @@ use std::io::Error as IoError; use glc::error::Error as GlcError; -use glutin::{ContextError as GlutinContextError, CreationError as GlutinCreationError}; +use glutin::{ + error::ExternalError as GlutinExternalError, ContextError as GlutinContextError, + CreationError as GlutinCreationError, +}; use glyph_brush::ab_glyph::InvalidFont; use serde_json::Error as JsonError; use space_crush_common::{misc::VfsError, Error as CommonError}; @@ -24,6 +27,9 @@ pub enum Error { #[error("glutin Creation Error: {0}")] GlutinCreationError(GlutinCreationError), + #[error("glutin External Error: {0}")] + GlutinExternalError(GlutinExternalError), + #[error("Invalid Font: {0}")] InvalidFont(InvalidFont), @@ -73,6 +79,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: GlutinExternalError) -> Self { + Self::GlutinExternalError(err) + } +} + impl From for Error { fn from(err: InvalidFont) -> Self { Self::InvalidFont(err) diff --git a/space-crush-app/src/lib.rs b/space-crush-app/src/lib.rs index 23e9690..a92910a 100644 --- a/space-crush-app/src/lib.rs +++ b/space-crush-app/src/lib.rs @@ -26,7 +26,7 @@ pub struct App<'a, 'b> { impl<'a, 'b> App<'a, 'b> { pub fn new(world: &mut World) -> Result { let config = Config::new(world)?; - let events = Events::new(world); + let events = Events::new(world)?; let window = Window::new(events.handle(), &config)?; let state = State::default(); @@ -67,7 +67,7 @@ impl<'a, 'b> App<'a, 'b> { } pub fn process(&mut self, world: &World) -> Result<(), Error> { - self.events.process(world); + self.events.process(world, &self.window); self.dispatcher.dispatch(world); self.window.swap_buffers()?; diff --git a/space-crush-app/src/misc/events.rs b/space-crush-app/src/misc/events.rs index 422f7d5..1e69534 100644 --- a/space-crush-app/src/misc/events.rs +++ b/space-crush-app/src/misc/events.rs @@ -7,26 +7,38 @@ use glutin::{ event_loop::{ControlFlow, EventLoop}, platform::desktop::EventLoopExtDesktop, }; -use shrev::EventChannel; +use shrev::{EventChannel, ReaderId}; +use space_crush_common::misc::{LogResult, WorldHelper}; use specs::World; +use crate::{misc::Window, Error}; + pub use glutin::event::{MouseButton, VirtualKeyCode}; pub struct Events { keys: HashSet, event_loop: EventLoop<()>, + mouse_pos: Option<(f64, f64)>, + mouse_lock: Option<(f64, f64)>, + control_event_id: ReaderId, } impl Events { - pub fn new(world: &mut World) -> Self { + pub fn new(world: &mut World) -> Result { + world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); - Self { + let control_event_id = world.register_event_reader::()?; + + Ok(Self { keys: HashSet::new(), event_loop: EventLoop::new(), - } + mouse_pos: None, + mouse_lock: None, + control_event_id, + }) } } @@ -35,14 +47,36 @@ impl Events { &self.event_loop } - pub fn process(&mut self, world: &World) { + pub fn process(&mut self, world: &World, window: &Window) { + let control_events = world.fetch::>(); let mut mouse_events = world.fetch_mut::>(); let mut window_events = world.fetch_mut::>(); let mut keyboard_events = world.fetch_mut::>(); let keys = &mut self.keys; + let mouse_pos = &mut self.mouse_pos; let event_loop = &mut self.event_loop; + let mouse_lock = &mut self.mouse_lock; + + let events = control_events.read(&mut self.control_event_id); + for event in events { + match event { + ControlEvent::LockMouse => { + *mouse_lock = *mouse_pos; + window + .set_mouse_locked(true) + .warn("Unable to lock mouse to window"); + } + ControlEvent::UnlockMouse => { + *mouse_lock = None; + window + .set_mouse_locked(false) + .warn("Unable to lock mouse to window"); + } + } + } + let mut has_mouse_moved = false; event_loop.run_return(|event, _target, flow_control| { *flow_control = ControlFlow::Poll; @@ -53,8 +87,20 @@ impl Events { Event::WindowEvent { event, .. } => match event { /* Mouse Events */ GlutinWindowEvent::CursorMoved { position, .. } => { - mouse_events - .single_write(MouseEvent::Move(position.x as _, position.y as _)); + if mouse_lock.is_none() { + mouse_events + .single_write(MouseEvent::Move(position.x as _, position.y as _)); + } + + if let Some((x, y)) = mouse_pos { + let dx = position.x - *x; + let dy = position.y - *y; + + mouse_events.single_write(MouseEvent::Delta(dx as _, dy as _)); + } + + has_mouse_moved = true; + *mouse_pos = Some((position.x as _, position.y as _)); } GlutinWindowEvent::MouseInput { state: ElementState::Pressed, @@ -82,6 +128,9 @@ impl Events { mouse_events.single_write(MouseEvent::ScrollY(y)); } } + GlutinWindowEvent::CursorLeft { .. } => { + *mouse_pos = None; + } /* Key Event */ GlutinWindowEvent::KeyboardInput { @@ -127,9 +176,21 @@ impl Events { _ => (), } }); + + if let Some((x, y)) = self.mouse_lock { + if has_mouse_moved { + window.set_mouse_pos(x, y).warn("Unable to set mouse pos"); + self.mouse_pos = Some((x, y)); + } + } } } +pub enum ControlEvent { + LockMouse, + UnlockMouse, +} + pub enum WindowEvent { Resize(u32, u32), Close, @@ -137,6 +198,7 @@ pub enum WindowEvent { pub enum MouseEvent { Move(f32, f32), + Delta(f32, f32), ButtonDown(MouseButton), ButtonUp(MouseButton), ScrollX(f32), diff --git a/space-crush-app/src/misc/mod.rs b/space-crush-app/src/misc/mod.rs index 70eb8c3..62285a9 100644 --- a/space-crush-app/src/misc/mod.rs +++ b/space-crush-app/src/misc/mod.rs @@ -3,7 +3,9 @@ mod text; mod window; mod world; -pub use events::{Events, KeyboardEvent, MouseButton, MouseEvent, VirtualKeyCode, WindowEvent}; +pub use events::{ + ControlEvent, Events, KeyboardEvent, MouseButton, MouseEvent, VirtualKeyCode, WindowEvent, +}; pub use text::{Text, TextCache, TextManager}; pub use window::Window; pub use world::WorldHelper; diff --git a/space-crush-app/src/misc/window.rs b/space-crush-app/src/misc/window.rs index cca7ca8..0342a6c 100644 --- a/space-crush-app/src/misc/window.rs +++ b/space-crush-app/src/misc/window.rs @@ -99,6 +99,7 @@ impl Window { (monitor_size.width - window_size.width) / 2, (monitor_size.height - window_size.height) / 2, )); + window.set_cursor_grab(true).unwrap(); let context = unsafe { context.make_current().unwrap() }; gl::load_with(|s| context.get_proc_address(s)); @@ -116,4 +117,25 @@ impl Window { Ok(()) } + + pub fn set_mouse_locked(&self, value: bool) -> Result<(), Error> { + let window = self.context.window(); + + window.set_cursor_grab(value)?; + + if !value { + window.set_cursor_visible(true); + } + + Ok(()) + } + + pub fn set_mouse_pos(&self, x: f64, y: f64) -> Result<(), Error> { + let window = self.context.window(); + + window.set_cursor_visible(false); + window.set_cursor_position(PhysicalPosition { x, y })?; + + Ok(()) + } } diff --git a/space-crush-app/src/render/init.rs b/space-crush-app/src/render/init.rs index 454b9da..57ac90f 100644 --- a/space-crush-app/src/render/init.rs +++ b/space-crush-app/src/render/init.rs @@ -13,7 +13,7 @@ use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; use crate::{ constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, - misc::{MouseEvent, WorldHelper}, + misc::{ControlEvent, MouseButton, MouseEvent, WorldHelper}, resources::{Camera, Config, Geometry, State, Uniform}, Error, }; @@ -52,6 +52,8 @@ impl Init { pub struct InitData<'a> { camera: WriteExpect<'a, Camera>, uniform: WriteExpect<'a, Uniform>, + control_events: WriteExpect<'a, EventChannel>, + state: ReadExpect<'a, State>, global: ReadExpect<'a, Global>, config: ReadExpect<'a, Config>, @@ -66,6 +68,7 @@ impl<'a> System<'a> for Init { let InitData { mut camera, mut uniform, + mut control_events, state, global, config, @@ -87,27 +90,43 @@ impl<'a> System<'a> for Init { /* zoom */ let events = mouse_events.read(&mut self.mouse_event_id); for event in events { - if let MouseEvent::ScrollY(delta) = event { - let s = config.input.speed_camera_zoom; - let z = s / (s - delta); - let m = Matrix4f::translate((state.mouse_pos.0, state.mouse_pos.1, 0.0).into()) - * Matrix4f::scale(z.into()) - * Matrix4f::translate((-state.mouse_pos.0, -state.mouse_pos.1, 0.0).into()); - - camera - .update_with(move |v| m * v) - .error("Error while zooming camera"); + match event { + MouseEvent::ScrollY(delta) => { + let s = config.input.camera_zoom_speed; + let z = s / (s - delta); + let m = Matrix4f::translate((state.mouse_pos.0, state.mouse_pos.1, 0.0).into()) + * Matrix4f::scale(z.into()) + * Matrix4f::translate((-state.mouse_pos.0, -state.mouse_pos.1, 0.0).into()); + + camera + .update_with(move |v| m * v) + .error("Error while zooming camera"); + } + MouseEvent::Delta(x, y) if state.button_state(&[MouseButton::Right]) => { + let m = Matrix4f::translate((*x, -*y, 0.0).into()); + + camera + .update_with(move |v| m * v) + .error("Error while zooming camera"); + } + MouseEvent::ButtonDown(b) if b == &config.input.camera_move_button => { + control_events.single_write(ControlEvent::LockMouse); + } + MouseEvent::ButtonUp(b) if b == &config.input.camera_move_button => { + control_events.single_write(ControlEvent::UnlockMouse); + } + _ => (), } } /* move camera */ - let up = state.key_state(&config.input.key_camera_move_up); - let down = state.key_state(&config.input.key_camera_move_down); - let left = state.key_state(&config.input.key_camera_move_left); - let right = state.key_state(&config.input.key_camera_move_right); + let up = state.key_state(&config.input.camera_move_key_up); + let down = state.key_state(&config.input.camera_move_key_down); + let left = state.key_state(&config.input.camera_move_key_left); + let right = state.key_state(&config.input.camera_move_key_right); if up || down || left || right { - let s = config.input.speed_camera_move * global.delta; + let s = config.input.camera_move_speed * global.delta; let translate = Vector3f::new( if left { s } else { 0.0 } + if right { -s } else { 0.0 }, if up { -s } else { 0.0 } + if down { s } else { 0.0 }, diff --git a/space-crush-app/src/resources/config.rs b/space-crush-app/src/resources/config.rs index c0f14ae..2c2a43a 100644 --- a/space-crush-app/src/resources/config.rs +++ b/space-crush-app/src/resources/config.rs @@ -5,7 +5,10 @@ use smallvec::{smallvec, SmallVec}; use space_crush_common::misc::{Vfs, WorldHelper as _}; use specs::World; -use crate::{misc::VirtualKeyCode, Error}; +use crate::{ + misc::{MouseButton, VirtualKeyCode}, + Error, +}; #[derive(Debug, Default, Serialize, Deserialize)] pub struct Config { @@ -30,19 +33,22 @@ pub struct Video { #[derive(Debug, Serialize, Deserialize)] pub struct Input { - #[serde(default = "defaults::key_camera_move_up")] - pub key_camera_move_up: VirtualKeyCodes, - #[serde(default = "defaults::key_camera_move_down")] - pub key_camera_move_down: VirtualKeyCodes, - #[serde(default = "defaults::key_camera_move_left")] - pub key_camera_move_left: VirtualKeyCodes, - #[serde(default = "defaults::key_camera_move_right")] - pub key_camera_move_right: VirtualKeyCodes, - - #[serde(default = "defaults::speed_camera_move")] - pub speed_camera_move: f32, - #[serde(default = "defaults::speed_camera_zoom")] - pub speed_camera_zoom: f32, + #[serde(default = "defaults::camera_move_key_up")] + pub camera_move_key_up: VirtualKeyCodes, + #[serde(default = "defaults::camera_move_key_down")] + pub camera_move_key_down: VirtualKeyCodes, + #[serde(default = "defaults::camera_move_key_left")] + pub camera_move_key_left: VirtualKeyCodes, + #[serde(default = "defaults::camera_move_key_right")] + pub camera_move_key_right: VirtualKeyCodes, + + #[serde(default = "defaults::camera_move_button")] + pub camera_move_button: MouseButton, + + #[serde(default = "defaults::camera_move_speed")] + pub camera_move_speed: f32, + #[serde(default = "defaults::camera_zoom_speed")] + pub camera_zoom_speed: f32, } type VirtualKeyCodes = SmallVec<[VirtualKeyCode; 2]>; @@ -80,13 +86,15 @@ impl Default for Video { impl Default for Input { fn default() -> Self { Self { - key_camera_move_up: defaults::key_camera_move_up(), - key_camera_move_down: defaults::key_camera_move_down(), - key_camera_move_left: defaults::key_camera_move_left(), - key_camera_move_right: defaults::key_camera_move_right(), + camera_move_key_up: defaults::camera_move_key_up(), + camera_move_key_down: defaults::camera_move_key_down(), + camera_move_key_left: defaults::camera_move_key_left(), + camera_move_key_right: defaults::camera_move_key_right(), - speed_camera_move: defaults::speed_camera_move(), - speed_camera_zoom: defaults::speed_camera_zoom(), + camera_move_button: defaults::camera_move_button(), + + camera_move_speed: defaults::camera_move_speed(), + camera_zoom_speed: defaults::camera_zoom_speed(), } } } @@ -110,27 +118,31 @@ mod defaults { true } - pub fn key_camera_move_up() -> VirtualKeyCodes { + pub fn camera_move_key_up() -> VirtualKeyCodes { smallvec![VirtualKeyCode::W, VirtualKeyCode::Up] } - pub fn key_camera_move_down() -> VirtualKeyCodes { + pub fn camera_move_key_down() -> VirtualKeyCodes { smallvec![VirtualKeyCode::S, VirtualKeyCode::Down] } - pub fn key_camera_move_left() -> VirtualKeyCodes { + pub fn camera_move_key_left() -> VirtualKeyCodes { smallvec![VirtualKeyCode::A, VirtualKeyCode::Left] } - pub fn key_camera_move_right() -> VirtualKeyCodes { + pub fn camera_move_key_right() -> VirtualKeyCodes { smallvec![VirtualKeyCode::D, VirtualKeyCode::Right] } - pub fn speed_camera_move() -> f32 { + pub fn camera_move_button() -> MouseButton { + MouseButton::Right + } + + pub fn camera_move_speed() -> f32 { 500.0 } - pub fn speed_camera_zoom() -> f32 { + pub fn camera_zoom_speed() -> f32 { 100.0 } }