| @@ -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<GlutinCreationError> for Error { | |||
| } | |||
| } | |||
| impl From<GlutinExternalError> for Error { | |||
| fn from(err: GlutinExternalError) -> Self { | |||
| Self::GlutinExternalError(err) | |||
| } | |||
| } | |||
| impl From<InvalidFont> for Error { | |||
| fn from(err: InvalidFont) -> Self { | |||
| Self::InvalidFont(err) | |||
| @@ -26,7 +26,7 @@ pub struct App<'a, 'b> { | |||
| impl<'a, 'b> App<'a, 'b> { | |||
| pub fn new(world: &mut World) -> Result<Self, Error> { | |||
| 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()?; | |||
| @@ -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<VirtualKeyCode>, | |||
| event_loop: EventLoop<()>, | |||
| mouse_pos: Option<(f64, f64)>, | |||
| mouse_lock: Option<(f64, f64)>, | |||
| control_event_id: ReaderId<ControlEvent>, | |||
| } | |||
| 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::<WindowEvent>::default()); | |||
| world.insert(EventChannel::<KeyboardEvent>::default()); | |||
| Self { | |||
| let control_event_id = world.register_event_reader::<ControlEvent>()?; | |||
| 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::<EventChannel<ControlEvent>>(); | |||
| let mut mouse_events = world.fetch_mut::<EventChannel<MouseEvent>>(); | |||
| let mut window_events = world.fetch_mut::<EventChannel<WindowEvent>>(); | |||
| let mut keyboard_events = world.fetch_mut::<EventChannel<KeyboardEvent>>(); | |||
| 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), | |||
| @@ -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; | |||
| @@ -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(()) | |||
| } | |||
| } | |||
| @@ -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<ControlEvent>>, | |||
| 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 }, | |||
| @@ -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 | |||
| } | |||
| } | |||