diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index fa8ab8a..ef71d55 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] +use std::borrow::Borrow; use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ops::{Deref, DerefMut, Mul}; @@ -432,7 +433,7 @@ where Matrix4::new( Vector4::new(two / (right - left), zero, zero, zero), Vector4::new(zero, two / (top - bottom), zero, zero), - Vector4::new(zero, zero, -zero / (far - near), zero), + Vector4::new(zero, zero, -two / (far - near), zero), Vector4::new( -(right + left) / (right - left), -(top + bottom) / (top - bottom), @@ -467,25 +468,15 @@ where } } -impl Mul> for Matrix4 +impl Mul for Matrix4 where T: Element, + M: Borrow>, { type Output = Self; - fn mul(self, rhs: Self) -> Self::Output { - self.multiply(&rhs) - } -} - -impl Mul> for Matrix4 -where - T: Element, -{ - type Output = Vector4; - - fn mul(self, rhs: Vector4) -> Self::Output { - self.transform(&rhs) + fn mul(self, rhs: M) -> Self::Output { + self.multiply(rhs.borrow()) } } diff --git a/glc/src/vector.rs b/glc/src/vector.rs index 05adab0..cfd2d75 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -135,6 +135,18 @@ macro_rules! define_vec { } } + impl From for $Name + where + T: Copy, + { + #[inline] + fn from(value: T) -> Self { + Self { + $($f: value,)+ + } + } + } + impl From<($($T,)+)> for $Name { #[inline] fn from(($($f,)+): ($($T,)+)) -> Self { diff --git a/space-crush/Cargo.toml b/space-crush/Cargo.toml index d004b8c..7f0fd18 100644 --- a/space-crush/Cargo.toml +++ b/space-crush/Cargo.toml @@ -16,7 +16,7 @@ num_cpus = "1.13" ordered-float = "2.0" rand = "0.7" serde_yaml = "0.8" -shred = "0.10" +shred = { version = "0.10", features = [ "shred-derive" ] } shrev = "1.1" specs = "0.16" thiserror = "1.0" diff --git a/space-crush/resources/shader/noise.frag b/space-crush/resources/shader/noise.frag index 5e4ea1d..c9311dc 100644 --- a/space-crush/resources/shader/noise.frag +++ b/space-crush/resources/shader/noise.frag @@ -1,27 +1,17 @@ #version 450 core -in vec4 gl_FragCoord; - -layout (std140, binding = 0) uniform Camera { - mat4 proj_world; - mat4 proj_ui; - mat4 view; -} camera; - out vec4 color; const float NOISE_RANGE = 0.05; const float NOISE_BASE = 0.05; -float random (vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); +float random(vec4 seed) { + return fract(sin(dot(seed, vec4(12.9898, 78.233, 45.164, 53.1324))) * 43758.5453); } void main() { - vec4 pos = inverse(camera.view) * gl_FragCoord; - - vec2 ipos = floor(pos.xy); - vec3 rbg = vec3(NOISE_BASE + NOISE_RANGE * random(ipos)); + float rnd = random(gl_FragCoord); + vec3 rgb = vec3(NOISE_BASE + NOISE_RANGE * rnd); - color = vec4(rbg, 1.0); + color = vec4(rgb, 1.0); } diff --git a/space-crush/resources/shader/quad.vert b/space-crush/resources/shader/quad.vert index 02bd7c9..bb58d6f 100644 --- a/space-crush/resources/shader/quad.vert +++ b/space-crush/resources/shader/quad.vert @@ -3,9 +3,9 @@ layout (location = 0) in vec3 position; layout (std140, binding = 0) uniform Camera { - mat4 proj_world; - mat4 proj_ui; + mat4 projection; mat4 view; + vec2 size; } camera; layout (location = 1) uniform mat4 model; @@ -16,5 +16,5 @@ out FragmentData { void main() { data.tex_coords = position.xy + vec2(0.5); - gl_Position = camera.proj_world * camera.view * model * vec4(position, 1.0); + gl_Position = camera.projection * camera.view * model * vec4(position, 1.0); } diff --git a/space-crush/resources/shader/text.vert b/space-crush/resources/shader/text.vert index a86a266..41fce6d 100644 --- a/space-crush/resources/shader/text.vert +++ b/space-crush/resources/shader/text.vert @@ -7,9 +7,9 @@ layout (location = 3) in vec2 tex_max; layout (location = 4) in vec4 color; layout (std140, binding = 0) uniform Camera { - mat4 proj_world; - mat4 proj_ui; + mat4 projection; mat4 view; + vec2 size; } camera; layout (location = 1) uniform mat4 model; @@ -45,7 +45,14 @@ void main() { break; } - gl_Position = camera.proj_ui * vec4(position, 0.0, 1.0); + mat4 ortho = mat4( + vec4(2.0 / camera.size.x, 0.0, 0.0, 0.0), + vec4(0.0, -2.0 / camera.size.y, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(-1.0, 1.0, 1.0, 1.0) + ); + + gl_Position = ortho * vec4(position, 0.0, 1.0); data.tex_coord = tex_coord; data.color = color; diff --git a/space-crush/src/app/misc/events.rs b/space-crush/src/app/misc/events.rs index ce23f8a..422f7d5 100644 --- a/space-crush/src/app/misc/events.rs +++ b/space-crush/src/app/misc/events.rs @@ -1,20 +1,30 @@ +use std::collections::HashSet; + use glutin::{ - event::{Event, WindowEvent as GlutinWindowEvent}, + event::{ + ElementState, Event, KeyboardInput, MouseScrollDelta, WindowEvent as GlutinWindowEvent, + }, event_loop::{ControlFlow, EventLoop}, platform::desktop::EventLoopExtDesktop, }; use shrev::EventChannel; use specs::World; +pub use glutin::event::{MouseButton, VirtualKeyCode}; + pub struct Events { + keys: HashSet, event_loop: EventLoop<()>, } impl Events { pub fn new(world: &mut World) -> Self { + world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); + world.insert(EventChannel::::default()); Self { + keys: HashSet::new(), event_loop: EventLoop::new(), } } @@ -26,9 +36,14 @@ impl Events { } pub fn process(&mut self, world: &World) { - let mut channel = world.fetch_mut::>(); + 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 event_loop = &mut self.event_loop; - self.event_loop.run_return(|event, _target, flow_control| { + event_loop.run_return(|event, _target, flow_control| { *flow_control = ControlFlow::Poll; match event { @@ -36,12 +51,77 @@ impl Events { *flow_control = ControlFlow::Exit; } Event::WindowEvent { event, .. } => match event { + /* Mouse Events */ + GlutinWindowEvent::CursorMoved { position, .. } => { + mouse_events + .single_write(MouseEvent::Move(position.x as _, position.y as _)); + } + GlutinWindowEvent::MouseInput { + state: ElementState::Pressed, + button, + .. + } => { + mouse_events.single_write(MouseEvent::ButtonDown(button)); + } + GlutinWindowEvent::MouseInput { + state: ElementState::Released, + button, + .. + } => { + mouse_events.single_write(MouseEvent::ButtonUp(button)); + } + GlutinWindowEvent::MouseWheel { + delta: MouseScrollDelta::LineDelta(x, y), + .. + } => { + if x != 0.0 { + mouse_events.single_write(MouseEvent::ScrollX(x)); + } + + if y != 0.0 { + mouse_events.single_write(MouseEvent::ScrollY(y)); + } + } + + /* Key Event */ + GlutinWindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(key), + .. + }, + .. + } => { + if keys.insert(key) { + keyboard_events.single_write(KeyboardEvent::KeyDown(key)); + } + + keyboard_events.single_write(KeyboardEvent::KeyPress(key)); + } + GlutinWindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Released, + virtual_keycode: Some(key), + .. + }, + .. + } => { + if keys.remove(&key) { + keyboard_events.single_write(KeyboardEvent::KeyUp(key)); + } + } + + /* Window Event */ GlutinWindowEvent::Resized(pos) => { - channel.single_write(WindowEvent::Resize(pos.width, pos.height)); + window_events.single_write(WindowEvent::Resize(pos.width, pos.height)); } GlutinWindowEvent::CloseRequested => { - channel.single_write(WindowEvent::Close); + window_events.single_write(WindowEvent::Close); } + + /* Ignore */ _ => (), }, _ => (), @@ -54,3 +134,17 @@ pub enum WindowEvent { Resize(u32, u32), Close, } + +pub enum MouseEvent { + Move(f32, f32), + ButtonDown(MouseButton), + ButtonUp(MouseButton), + ScrollX(f32), + ScrollY(f32), +} + +pub enum KeyboardEvent { + KeyUp(VirtualKeyCode), + KeyDown(VirtualKeyCode), + KeyPress(VirtualKeyCode), +} diff --git a/space-crush/src/app/misc/mod.rs b/space-crush/src/app/misc/mod.rs index f78883b..70eb8c3 100644 --- a/space-crush/src/app/misc/mod.rs +++ b/space-crush/src/app/misc/mod.rs @@ -3,7 +3,7 @@ mod text; mod window; mod world; -pub use events::{Events, WindowEvent}; +pub use events::{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/src/app/misc/window.rs b/space-crush/src/app/misc/window.rs index 350c60a..2922ccf 100644 --- a/space-crush/src/app/misc/window.rs +++ b/space-crush/src/app/misc/window.rs @@ -56,6 +56,7 @@ impl Window { gl::disable(gl::CULL_FACE); gl::disable(gl::DEPTH_TEST); + gl::clear_color(0.1, 0.1, 0.1, 1.0); gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1); Ok(Self { context }) diff --git a/space-crush/src/app/render/init.rs b/space-crush/src/app/render/init.rs index cb3352f..89d6fd8 100644 --- a/space-crush/src/app/render/init.rs +++ b/space-crush/src/app/render/init.rs @@ -1,20 +1,24 @@ use glc::{ + matrix::Matrix4f, misc::Bindable, shader::{Program, Type}, + vector::Vector3f, }; use log::error; -use specs::{ReadExpect, System, World, WriteExpect}; +use shrev::{EventChannel, ReaderId}; +use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; use crate::Error; use super::super::{ - misc::WorldHelper, + misc::{MouseEvent, VirtualKeyCode, WorldHelper}, resources::{Camera, Geometry, State}, }; pub struct Init { program: Program, resolution: (u32, u32), + mouse_event_id: ReaderId, } impl Init { @@ -23,22 +27,37 @@ impl Init { (Type::Vertex, "resources/shader/noise.vert"), (Type::Fragment, "resources/shader/noise.frag"), ])?; + let resolution = (0, 0); + let mouse_event_id = world.register_event_reader::()?; Ok(Self { program, - resolution: (0, 0), + resolution, + mouse_event_id, }) } } +#[derive(SystemData)] +pub struct InitData<'a> { + camera: WriteExpect<'a, Camera>, + state: ReadExpect<'a, State>, + geometry: ReadExpect<'a, Geometry>, + mouse_events: ReadExpect<'a, EventChannel>, +} + impl<'a> System<'a> for Init { - type SystemData = ( - WriteExpect<'a, Camera>, - ReadExpect<'a, State>, - ReadExpect<'a, Geometry>, - ); + type SystemData = InitData<'a>; - fn run(&mut self, (mut camera, state, geometry): Self::SystemData) { + fn run(&mut self, data: Self::SystemData) { + let InitData { + mut camera, + state, + geometry, + mouse_events, + } = data; + + /* screen size */ if self.resolution != state.resolution { self.resolution = state.resolution; @@ -46,12 +65,61 @@ impl<'a> System<'a> for Init { if let Err(err) = camera.resize(self.resolution.0 as _, self.resolution.1 as _) { error!("Error while updating camera: {}", err); - panic!("Error while updating camera: {}", err); } } + /* zoom */ + let events = mouse_events.read(&mut self.mouse_event_id); + for event in events { + if let MouseEvent::ScrollY(delta) = event { + let z = CAMERA_ZOOM_SPEED / (CAMERA_ZOOM_SPEED - 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()); + + if let Err(err) = camera.update_with(move |v| m * v) { + error!("Error while zooming camera: {}", err); + } + } + } + + /* move camera */ + let up = state.key_state(KEY_CAMERA_UP) || state.key_state(KEY_CAMERA_UP_ALT); + let down = state.key_state(KEY_CAMERA_DOWN) || state.key_state(KEY_CAMERA_DOWN_ALT); + let left = state.key_state(KEY_CAMERA_LEFT) || state.key_state(KEY_CAMERA_LEFT_ALT); + let right = state.key_state(KEY_CAMERA_RIGHT) || state.key_state(KEY_CAMERA_RIGHT_ALT); + + let translate = Vector3f::new( + if left { CAMERA_MOVE_SPEED_KEY } else { 0.0 } + + if right { -CAMERA_MOVE_SPEED_KEY } else { 0.0 }, + if up { -CAMERA_MOVE_SPEED_KEY } else { 0.0 } + + if down { CAMERA_MOVE_SPEED_KEY } else { 0.0 }, + 0.0, + ); + + if up || down || left || right { + let m = Matrix4f::translate(translate); + if let Err(err) = camera.update_with(move |v| m * v) { + error!("Error while moving camera: {}", err); + } + } + + /* render background */ self.program.bind(); geometry.render_quad(); self.program.unbind(); } } + +const CAMERA_MOVE_SPEED_KEY: f32 = 2.0; +const CAMERA_ZOOM_SPEED: f32 = 100.0; + +const KEY_CAMERA_UP: VirtualKeyCode = VirtualKeyCode::Up; +const KEY_CAMERA_DOWN: VirtualKeyCode = VirtualKeyCode::Down; +const KEY_CAMERA_LEFT: VirtualKeyCode = VirtualKeyCode::Left; +const KEY_CAMERA_RIGHT: VirtualKeyCode = VirtualKeyCode::Right; + +const KEY_CAMERA_UP_ALT: VirtualKeyCode = VirtualKeyCode::W; +const KEY_CAMERA_DOWN_ALT: VirtualKeyCode = VirtualKeyCode::S; +const KEY_CAMERA_LEFT_ALT: VirtualKeyCode = VirtualKeyCode::A; +const KEY_CAMERA_RIGHT_ALT: VirtualKeyCode = VirtualKeyCode::D; diff --git a/space-crush/src/app/resources/camera.rs b/space-crush/src/app/resources/camera.rs index 8a663e2..c4f0d07 100644 --- a/space-crush/src/app/resources/camera.rs +++ b/space-crush/src/app/resources/camera.rs @@ -4,6 +4,7 @@ use glc::{ array_buffer::{ArrayBuffer, Target, Usage}, error::Error, matrix::Matrix4f, + vector::Vector2f, }; pub struct Camera { @@ -16,9 +17,9 @@ impl Camera { buffer.buffer_data( Usage::StaticDraw, &[Data { - proj_world: Matrix4f::identity(), - proj_ui: Matrix4f::identity(), + projection: Matrix4f::identity(), view: Matrix4f::identity(), + size: Vector2f::default(), }], )?; @@ -28,8 +29,27 @@ impl Camera { pub fn resize(&mut self, w: f32, h: f32) -> Result<(), Error> { let mut data = self.buffer.map_mut::(true)?; - data[0].proj_world = Matrix4f::ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0); - data[0].proj_ui = Matrix4f::ortho(0.0, w, h, 0.0, -100.0, 100.0); + data[0].projection = Matrix4f::ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0); + data[0].size = (w, h).into(); + + Ok(()) + } + + pub fn update(&mut self, view: Matrix4f) -> Result<(), Error> { + let mut data = self.buffer.map_mut::(true)?; + + data[0].view = data[0].view * view; + + Ok(()) + } + + pub fn update_with(&mut self, f: F) -> Result<(), Error> + where + F: FnOnce(&Matrix4f) -> Matrix4f, + { + let mut data = self.buffer.map_mut::(true)?; + + data[0].view = f(&data[0].view); Ok(()) } @@ -41,7 +61,7 @@ impl Camera { #[repr(C, packed)] struct Data { - proj_world: Matrix4f, - proj_ui: Matrix4f, + projection: Matrix4f, view: Matrix4f, + size: Vector2f, } diff --git a/space-crush/src/app/resources/state.rs b/space-crush/src/app/resources/state.rs index 42ebc56..36464eb 100644 --- a/space-crush/src/app/resources/state.rs +++ b/space-crush/src/app/resources/state.rs @@ -1,6 +1,45 @@ +#![allow(dead_code)] + +use std::collections::HashSet; + +use super::super::misc::{MouseButton, VirtualKeyCode}; + #[derive(Default)] pub struct State { + pub mouse_pos: (f32, f32), pub resolution: (u32, u32), - pub close_requested: bool, + pub key_states: HashSet, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum KeyState { + KeyCode(VirtualKeyCode), + MouseButton(MouseButton), +} + +impl State { + pub fn key_state(&self, code: VirtualKeyCode) -> bool { + self.key_states.contains(&KeyState::KeyCode(code)) + } + + pub fn button_state(&self, button: MouseButton) -> bool { + self.key_states.contains(&KeyState::MouseButton(button)) + } + + pub fn set_key_state(&mut self, code: VirtualKeyCode, value: bool) { + if value { + self.key_states.insert(KeyState::KeyCode(code)); + } else { + self.key_states.remove(&KeyState::KeyCode(code)); + } + } + + pub fn set_button_state(&mut self, button: MouseButton, value: bool) { + if value { + self.key_states.insert(KeyState::MouseButton(button)); + } else { + self.key_states.remove(&KeyState::MouseButton(button)); + } + } } diff --git a/space-crush/src/app/systems/state_update.rs b/space-crush/src/app/systems/state_update.rs index fb9bb0e..1648f13 100644 --- a/space-crush/src/app/systems/state_update.rs +++ b/space-crush/src/app/systems/state_update.rs @@ -1,29 +1,67 @@ use shrev::{EventChannel, ReaderId}; -use specs::{ReadExpect, System, World, Write}; +use specs::{prelude::*, ReadExpect, System, World, Write}; use crate::Error; use super::super::{ - misc::{WindowEvent, WorldHelper}, + misc::{KeyboardEvent, MouseEvent, WindowEvent, WorldHelper}, resources::State, }; pub struct StateUpdate { + mouse_event_id: ReaderId, window_events_id: ReaderId, + keyboard_events_id: ReaderId, } impl StateUpdate { pub fn new(world: &mut World) -> Result { + let mouse_event_id = world.register_event_reader::()?; let window_events_id = world.register_event_reader::()?; + let keyboard_events_id = world.register_event_reader::()?; - Ok(Self { window_events_id }) + Ok(Self { + mouse_event_id, + window_events_id, + keyboard_events_id, + }) } } +#[derive(SystemData)] +pub struct StateUpdateData<'a> { + state: Write<'a, State>, + mouse_events: ReadExpect<'a, EventChannel>, + window_events: ReadExpect<'a, EventChannel>, + keyboard_events: ReadExpect<'a, EventChannel>, +} + impl<'a> System<'a> for StateUpdate { - type SystemData = (Write<'a, State>, ReadExpect<'a, EventChannel>); + type SystemData = StateUpdateData<'a>; + + fn run(&mut self, data: Self::SystemData) { + let StateUpdateData { + mut state, + mouse_events, + window_events, + keyboard_events, + } = data; + + let events = mouse_events.read(&mut self.mouse_event_id); + for event in events { + match event { + MouseEvent::Move(x, y) => { + let x = *x - state.resolution.0 as f32 / 2.0; + let y = state.resolution.1 as f32 / 2.0 - *y; + + state.mouse_pos = (x, y); + } + MouseEvent::ButtonUp(button) => state.set_button_state(*button, false), + MouseEvent::ButtonDown(button) => state.set_button_state(*button, true), + _ => (), + } + } - fn run(&mut self, (mut state, window_events): Self::SystemData) { let events = window_events.read(&mut self.window_events_id); for event in events { match event { @@ -35,5 +73,14 @@ impl<'a> System<'a> for StateUpdate { } } } + + let events = keyboard_events.read(&mut self.keyboard_events_id); + for event in events { + match event { + KeyboardEvent::KeyUp(code) => state.set_key_state(*code, false), + KeyboardEvent::KeyDown(code) => state.set_key_state(*code, true), + _ => (), + } + } } }