| @@ -1,7 +1,10 @@ | |||||
| use std::io::Error as IoError; | use std::io::Error as IoError; | ||||
| use glc::error::Error as GlcError; | 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 glyph_brush::ab_glyph::InvalidFont; | ||||
| use serde_json::Error as JsonError; | use serde_json::Error as JsonError; | ||||
| use space_crush_common::{misc::VfsError, Error as CommonError}; | use space_crush_common::{misc::VfsError, Error as CommonError}; | ||||
| @@ -24,6 +27,9 @@ pub enum Error { | |||||
| #[error("glutin Creation Error: {0}")] | #[error("glutin Creation Error: {0}")] | ||||
| GlutinCreationError(GlutinCreationError), | GlutinCreationError(GlutinCreationError), | ||||
| #[error("glutin External Error: {0}")] | |||||
| GlutinExternalError(GlutinExternalError), | |||||
| #[error("Invalid Font: {0}")] | #[error("Invalid Font: {0}")] | ||||
| InvalidFont(InvalidFont), | InvalidFont(InvalidFont), | ||||
| @@ -73,6 +79,12 @@ impl From<GlutinCreationError> for Error { | |||||
| } | } | ||||
| } | } | ||||
| impl From<GlutinExternalError> for Error { | |||||
| fn from(err: GlutinExternalError) -> Self { | |||||
| Self::GlutinExternalError(err) | |||||
| } | |||||
| } | |||||
| impl From<InvalidFont> for Error { | impl From<InvalidFont> for Error { | ||||
| fn from(err: InvalidFont) -> Self { | fn from(err: InvalidFont) -> Self { | ||||
| Self::InvalidFont(err) | Self::InvalidFont(err) | ||||
| @@ -26,7 +26,7 @@ pub struct App<'a, 'b> { | |||||
| impl<'a, 'b> App<'a, 'b> { | impl<'a, 'b> App<'a, 'b> { | ||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | pub fn new(world: &mut World) -> Result<Self, Error> { | ||||
| let config = Config::new(world)?; | let config = Config::new(world)?; | ||||
| let events = Events::new(world); | |||||
| let events = Events::new(world)?; | |||||
| let window = Window::new(events.handle(), &config)?; | let window = Window::new(events.handle(), &config)?; | ||||
| let state = State::default(); | let state = State::default(); | ||||
| @@ -67,7 +67,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| } | } | ||||
| pub fn process(&mut self, world: &World) -> Result<(), Error> { | pub fn process(&mut self, world: &World) -> Result<(), Error> { | ||||
| self.events.process(world); | |||||
| self.events.process(world, &self.window); | |||||
| self.dispatcher.dispatch(world); | self.dispatcher.dispatch(world); | ||||
| self.window.swap_buffers()?; | self.window.swap_buffers()?; | ||||
| @@ -7,26 +7,38 @@ use glutin::{ | |||||
| event_loop::{ControlFlow, EventLoop}, | event_loop::{ControlFlow, EventLoop}, | ||||
| platform::desktop::EventLoopExtDesktop, | platform::desktop::EventLoopExtDesktop, | ||||
| }; | }; | ||||
| use shrev::EventChannel; | |||||
| use shrev::{EventChannel, ReaderId}; | |||||
| use space_crush_common::misc::{LogResult, WorldHelper}; | |||||
| use specs::World; | use specs::World; | ||||
| use crate::{misc::Window, Error}; | |||||
| pub use glutin::event::{MouseButton, VirtualKeyCode}; | pub use glutin::event::{MouseButton, VirtualKeyCode}; | ||||
| pub struct Events { | pub struct Events { | ||||
| keys: HashSet<VirtualKeyCode>, | keys: HashSet<VirtualKeyCode>, | ||||
| event_loop: EventLoop<()>, | event_loop: EventLoop<()>, | ||||
| mouse_pos: Option<(f64, f64)>, | |||||
| mouse_lock: Option<(f64, f64)>, | |||||
| control_event_id: ReaderId<ControlEvent>, | |||||
| } | } | ||||
| impl Events { | impl Events { | ||||
| pub fn new(world: &mut World) -> Self { | |||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | |||||
| world.insert(EventChannel::<ControlEvent>::default()); | |||||
| world.insert(EventChannel::<MouseEvent>::default()); | world.insert(EventChannel::<MouseEvent>::default()); | ||||
| world.insert(EventChannel::<WindowEvent>::default()); | world.insert(EventChannel::<WindowEvent>::default()); | ||||
| world.insert(EventChannel::<KeyboardEvent>::default()); | world.insert(EventChannel::<KeyboardEvent>::default()); | ||||
| Self { | |||||
| let control_event_id = world.register_event_reader::<ControlEvent>()?; | |||||
| Ok(Self { | |||||
| keys: HashSet::new(), | keys: HashSet::new(), | ||||
| event_loop: EventLoop::new(), | event_loop: EventLoop::new(), | ||||
| } | |||||
| mouse_pos: None, | |||||
| mouse_lock: None, | |||||
| control_event_id, | |||||
| }) | |||||
| } | } | ||||
| } | } | ||||
| @@ -35,14 +47,36 @@ impl Events { | |||||
| &self.event_loop | &self.event_loop | ||||
| } | } | ||||
| pub fn process(&mut self, world: &World) { | |||||
| pub fn process(&mut self, world: &World, window: &Window) { | |||||
| let control_events = world.fetch::<EventChannel<ControlEvent>>(); | |||||
| let mut mouse_events = world.fetch_mut::<EventChannel<MouseEvent>>(); | let mut mouse_events = world.fetch_mut::<EventChannel<MouseEvent>>(); | ||||
| let mut window_events = world.fetch_mut::<EventChannel<WindowEvent>>(); | let mut window_events = world.fetch_mut::<EventChannel<WindowEvent>>(); | ||||
| let mut keyboard_events = world.fetch_mut::<EventChannel<KeyboardEvent>>(); | let mut keyboard_events = world.fetch_mut::<EventChannel<KeyboardEvent>>(); | ||||
| let keys = &mut self.keys; | let keys = &mut self.keys; | ||||
| let mouse_pos = &mut self.mouse_pos; | |||||
| let event_loop = &mut self.event_loop; | 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| { | event_loop.run_return(|event, _target, flow_control| { | ||||
| *flow_control = ControlFlow::Poll; | *flow_control = ControlFlow::Poll; | ||||
| @@ -53,8 +87,20 @@ impl Events { | |||||
| Event::WindowEvent { event, .. } => match event { | Event::WindowEvent { event, .. } => match event { | ||||
| /* Mouse Events */ | /* Mouse Events */ | ||||
| GlutinWindowEvent::CursorMoved { position, .. } => { | 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 { | GlutinWindowEvent::MouseInput { | ||||
| state: ElementState::Pressed, | state: ElementState::Pressed, | ||||
| @@ -82,6 +128,9 @@ impl Events { | |||||
| mouse_events.single_write(MouseEvent::ScrollY(y)); | mouse_events.single_write(MouseEvent::ScrollY(y)); | ||||
| } | } | ||||
| } | } | ||||
| GlutinWindowEvent::CursorLeft { .. } => { | |||||
| *mouse_pos = None; | |||||
| } | |||||
| /* Key Event */ | /* Key Event */ | ||||
| GlutinWindowEvent::KeyboardInput { | 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 { | pub enum WindowEvent { | ||||
| Resize(u32, u32), | Resize(u32, u32), | ||||
| Close, | Close, | ||||
| @@ -137,6 +198,7 @@ pub enum WindowEvent { | |||||
| pub enum MouseEvent { | pub enum MouseEvent { | ||||
| Move(f32, f32), | Move(f32, f32), | ||||
| Delta(f32, f32), | |||||
| ButtonDown(MouseButton), | ButtonDown(MouseButton), | ||||
| ButtonUp(MouseButton), | ButtonUp(MouseButton), | ||||
| ScrollX(f32), | ScrollX(f32), | ||||
| @@ -3,7 +3,9 @@ mod text; | |||||
| mod window; | mod window; | ||||
| mod world; | 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 text::{Text, TextCache, TextManager}; | ||||
| pub use window::Window; | pub use window::Window; | ||||
| pub use world::WorldHelper; | pub use world::WorldHelper; | ||||
| @@ -99,6 +99,7 @@ impl Window { | |||||
| (monitor_size.width - window_size.width) / 2, | (monitor_size.width - window_size.width) / 2, | ||||
| (monitor_size.height - window_size.height) / 2, | (monitor_size.height - window_size.height) / 2, | ||||
| )); | )); | ||||
| window.set_cursor_grab(true).unwrap(); | |||||
| let context = unsafe { context.make_current().unwrap() }; | let context = unsafe { context.make_current().unwrap() }; | ||||
| gl::load_with(|s| context.get_proc_address(s)); | gl::load_with(|s| context.get_proc_address(s)); | ||||
| @@ -116,4 +117,25 @@ impl Window { | |||||
| Ok(()) | 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(()) | |||||
| } | |||||
| } | } | ||||
| @@ -13,7 +13,7 @@ use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; | |||||
| use crate::{ | use crate::{ | ||||
| constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | ||||
| misc::{MouseEvent, WorldHelper}, | |||||
| misc::{ControlEvent, MouseButton, MouseEvent, WorldHelper}, | |||||
| resources::{Camera, Config, Geometry, State, Uniform}, | resources::{Camera, Config, Geometry, State, Uniform}, | ||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -52,6 +52,8 @@ impl Init { | |||||
| pub struct InitData<'a> { | pub struct InitData<'a> { | ||||
| camera: WriteExpect<'a, Camera>, | camera: WriteExpect<'a, Camera>, | ||||
| uniform: WriteExpect<'a, Uniform>, | uniform: WriteExpect<'a, Uniform>, | ||||
| control_events: WriteExpect<'a, EventChannel<ControlEvent>>, | |||||
| state: ReadExpect<'a, State>, | state: ReadExpect<'a, State>, | ||||
| global: ReadExpect<'a, Global>, | global: ReadExpect<'a, Global>, | ||||
| config: ReadExpect<'a, Config>, | config: ReadExpect<'a, Config>, | ||||
| @@ -66,6 +68,7 @@ impl<'a> System<'a> for Init { | |||||
| let InitData { | let InitData { | ||||
| mut camera, | mut camera, | ||||
| mut uniform, | mut uniform, | ||||
| mut control_events, | |||||
| state, | state, | ||||
| global, | global, | ||||
| config, | config, | ||||
| @@ -87,27 +90,43 @@ impl<'a> System<'a> for Init { | |||||
| /* zoom */ | /* zoom */ | ||||
| let events = mouse_events.read(&mut self.mouse_event_id); | let events = mouse_events.read(&mut self.mouse_event_id); | ||||
| for event in events { | 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 */ | /* 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 { | 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( | let translate = Vector3f::new( | ||||
| if left { s } else { 0.0 } + if right { -s } else { 0.0 }, | if left { s } else { 0.0 } + if right { -s } else { 0.0 }, | ||||
| if up { -s } else { 0.0 } + if down { s } else { 0.0 }, | if up { -s } else { 0.0 } + if down { s } else { 0.0 }, | ||||
| @@ -5,7 +5,10 @@ use smallvec::{smallvec, SmallVec}; | |||||
| use space_crush_common::misc::{Vfs, WorldHelper as _}; | use space_crush_common::misc::{Vfs, WorldHelper as _}; | ||||
| use specs::World; | use specs::World; | ||||
| use crate::{misc::VirtualKeyCode, Error}; | |||||
| use crate::{ | |||||
| misc::{MouseButton, VirtualKeyCode}, | |||||
| Error, | |||||
| }; | |||||
| #[derive(Debug, Default, Serialize, Deserialize)] | #[derive(Debug, Default, Serialize, Deserialize)] | ||||
| pub struct Config { | pub struct Config { | ||||
| @@ -30,19 +33,22 @@ pub struct Video { | |||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize)] | ||||
| pub struct Input { | 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]>; | type VirtualKeyCodes = SmallVec<[VirtualKeyCode; 2]>; | ||||
| @@ -80,13 +86,15 @@ impl Default for Video { | |||||
| impl Default for Input { | impl Default for Input { | ||||
| fn default() -> Self { | fn default() -> Self { | ||||
| 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 | true | ||||
| } | } | ||||
| pub fn key_camera_move_up() -> VirtualKeyCodes { | |||||
| pub fn camera_move_key_up() -> VirtualKeyCodes { | |||||
| smallvec![VirtualKeyCode::W, VirtualKeyCode::Up] | smallvec![VirtualKeyCode::W, VirtualKeyCode::Up] | ||||
| } | } | ||||
| pub fn key_camera_move_down() -> VirtualKeyCodes { | |||||
| pub fn camera_move_key_down() -> VirtualKeyCodes { | |||||
| smallvec![VirtualKeyCode::S, VirtualKeyCode::Down] | smallvec![VirtualKeyCode::S, VirtualKeyCode::Down] | ||||
| } | } | ||||
| pub fn key_camera_move_left() -> VirtualKeyCodes { | |||||
| pub fn camera_move_key_left() -> VirtualKeyCodes { | |||||
| smallvec![VirtualKeyCode::A, VirtualKeyCode::Left] | smallvec![VirtualKeyCode::A, VirtualKeyCode::Left] | ||||
| } | } | ||||
| pub fn key_camera_move_right() -> VirtualKeyCodes { | |||||
| pub fn camera_move_key_right() -> VirtualKeyCodes { | |||||
| smallvec![VirtualKeyCode::D, VirtualKeyCode::Right] | 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 | 500.0 | ||||
| } | } | ||||
| pub fn speed_camera_zoom() -> f32 { | |||||
| pub fn camera_zoom_speed() -> f32 { | |||||
| 100.0 | 100.0 | ||||
| } | } | ||||
| } | } | ||||