diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index 8c64763..e7b7fe2 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -284,7 +284,8 @@ where } #[inline] - pub fn translate(v: Vector2) -> Self { + pub fn translate>>(v: V) -> Self { + let v = V::into(v); let one = T::one(); let zro = T::zero(); @@ -378,7 +379,8 @@ where T: Float, { #[inline] - pub fn translate(v: Vector3) -> Self { + pub fn translate>>(v: V) -> Self { + let v = V::into(v); let one = T::one(); let zero = T::zero(); @@ -391,7 +393,8 @@ where } #[inline] - pub fn scale(v: Vector3) -> Self { + pub fn scale>>(v: V) -> Self { + let v = V::into(v); let one = T::one(); let zero = T::zero(); @@ -405,8 +408,8 @@ where #[inline] #[allow(clippy::many_single_char_names)] - pub fn rotate(axis: Vector3, angle: Angle) -> Self { - let axis = axis.normalize(); + pub fn rotate>>(axis: V, angle: Angle) -> Self { + let axis = V::into(axis).normalize(); let x = axis.x; let y = axis.y; let z = axis.z; diff --git a/glc/src/vector.rs b/glc/src/vector.rs index 9ab2b58..5733bb4 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -2,7 +2,7 @@ use std::convert::{AsMut, AsRef}; use std::fmt::{Debug, Formatter, Result as FmtResult}; -use std::ops::{Add, Deref, DerefMut, Mul, Sub}; +use std::ops::{Add, Deref, DerefMut, Mul, Neg, Sub}; #[cfg(feature = "serde")] use serde::{ @@ -296,6 +296,21 @@ where } } +impl Neg for Vector2 +where + T: Numeric, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + Self { + x: -self.x, + y: -self.y, + } + } +} + impl Add for Vector2 where T: Numeric, @@ -350,6 +365,15 @@ where } } +impl From> for Vector2 +where + T: Numeric, +{ + fn from(vec3: Vector3) -> Self { + vec3.into_vec2() + } +} + impl From> for Vector2 where T: Numeric, @@ -408,6 +432,19 @@ where z: self.x * other.y - self.y * other.x, } } + + #[inline] + pub fn as_vec2(&self) -> &Vector2 { + unsafe { &*(self as *const Vector3 as *const Vector2) } + } + + #[inline] + pub fn into_vec2(self) -> Vector2 { + Vector2 { + x: self.x, + y: self.y, + } + } } impl Vector3 @@ -424,6 +461,22 @@ where } } +impl Neg for Vector3 +where + T: Numeric, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + Self { + x: -self.x, + y: -self.y, + z: -self.z, + } + } +} + impl Add for Vector3 where T: Numeric, @@ -480,6 +533,28 @@ where } } +impl From> for Vector3 +where + T: Numeric, +{ + fn from(vec2: Vector2) -> Self { + Self { + x: vec2.x, + y: vec2.y, + z: T::zero(), + } + } +} + +impl From> for Vector3 +where + T: Numeric, +{ + fn from(vec4: Vector4) -> Self { + vec4.into_vec3() + } +} + /* Vector4 */ impl Vector4 diff --git a/space-crush-app/src/debug/summary.rs b/space-crush-app/src/debug/summary.rs index fd48985..742fe8c 100644 --- a/space-crush-app/src/debug/summary.rs +++ b/space-crush-app/src/debug/summary.rs @@ -1,5 +1,6 @@ use std::string::ToString; +use glc::vector::Vector2f; use space_crush_common::{misc::LogResult, resources::Global}; use specs::{prelude::*, Entities, ReadExpect, System}; @@ -15,8 +16,8 @@ pub struct Summary { cache: TextCache, text: Text, fps: usize, - resolution: (u32, u32), - mouse_pos: (f32, f32), + resolution: Vector2f, + mouse_pos: Vector2f, entity_count: usize, } @@ -44,8 +45,8 @@ impl Summary { cache, text, fps: 0, - resolution: (0, 0), - mouse_pos: (0.0, 0.0), + resolution: Default::default(), + mouse_pos: Default::default(), entity_count: 0, }) } @@ -83,14 +84,14 @@ impl<'a> System<'a> for Summary { 4, &mut self.resolution, &input_state.resolution, - |(w, h)| format!("{} | {}", w, h), + |v| unsafe { format!("{:.0} | {:.0}", v.x, v.y) }, ); update_text( &mut self.text, 6, &mut self.mouse_pos, &input_state.mouse_pos, - |(x, y)| format!("{:.2} | {:.2}", x, y), + |v| unsafe { format!("{:.2} | {:.2}", v.x, v.y) }, ); update_text( &mut self.text, diff --git a/space-crush-app/src/render/init.rs b/space-crush-app/src/render/init.rs index a4a3ae7..fc6c8b7 100644 --- a/space-crush-app/src/render/init.rs +++ b/space-crush-app/src/render/init.rs @@ -2,7 +2,7 @@ use glc::{ matrix::Matrix4f, misc::Bindable, shader::{Program, Type}, - vector::Vector3f, + vector::{Vector2f, Vector3f}, }; use shrev::{EventChannel, ReaderId}; use space_crush_common::{ @@ -20,7 +20,7 @@ use crate::{ pub struct Init { program: Program, - resolution: (u32, u32), + resolution: Vector2f, mouse_event_id: ReaderId, } @@ -30,7 +30,7 @@ impl Init { (Type::Vertex, "resources/shader/noise/vert.glsl"), (Type::Fragment, "resources/shader/noise/frag.glsl"), ])?; - let resolution = (0, 0); + let resolution = Vector2f::default(); let mouse_event_id = world.register_event_reader::()?; world @@ -80,31 +80,30 @@ impl<'a> System<'a> for Init { if self.resolution != input_state.resolution { self.resolution = input_state.resolution; - gl::viewport(0, 0, self.resolution.0 as _, self.resolution.1 as _); + gl::viewport(0, 0, self.resolution.x as _, self.resolution.y as _); camera - .resize(self.resolution.0 as _, self.resolution.1 as _) + .resize(self.resolution.x, self.resolution.y) .error("Error while updating camera"); } /* zoom */ - let mouse_pos = &input_state.mouse_pos; let events = mouse_events.read(&mut self.mouse_event_id); for event in events { match event { MouseEvent::ScrollY(delta) => { let s = config.input.camera_zoom_speed; let z = s / (s - delta); - let m = Matrix4f::translate((mouse_pos.0, mouse_pos.1, 0.0).into()) - * Matrix4f::scale(z.into()) - * Matrix4f::translate((-mouse_pos.0, -mouse_pos.1, 0.0).into()); + let m = Matrix4f::translate(input_state.mouse_pos) + * Matrix4f::scale(z) + * Matrix4f::translate(-input_state.mouse_pos); camera .update_with(move |v| m * v) .error("Error while zooming camera"); } MouseEvent::Delta(x, y) if input_state.button_state(&[MouseButton::Right]) => { - let m = Matrix4f::translate((*x, -*y, 0.0).into()); + let m = Matrix4f::translate((*x, -*y, 0.0)); camera .update_with(move |v| m * v) diff --git a/space-crush-app/src/render/select_fleet.rs b/space-crush-app/src/render/select_fleet.rs index be2462d..e31b654 100644 --- a/space-crush-app/src/render/select_fleet.rs +++ b/space-crush-app/src/render/select_fleet.rs @@ -14,6 +14,7 @@ use space_crush_common::{ constants::VECTOR_2F_POS_X, misc::{LogResult, WorldHelper as _}, resources::Global, + return_if_none, }; use specs::{prelude::*, Entities, ReadExpect, ReadStorage, System, World}; @@ -44,9 +45,9 @@ pub struct SelectFleet { mouse_event_id: ReaderId, select_mode: SelectMode, camera_counter: usize, - camera_view_invert: Matrix4f, need_value_update: bool, values_changed_once: bool, + is_new_selection: bool, mouse_pos: Vector2f, count: ShipCount, @@ -69,7 +70,7 @@ enum SelectMode { } #[derive(SystemData)] -pub struct SelectFleetData<'a> { +pub struct FleetSelectData<'a> { player_state: WriteExpect<'a, PlayerState>, camera: ReadExpect<'a, Camera>, @@ -85,33 +86,24 @@ pub struct SelectFleetData<'a> { fleets: ReadStorage<'a, Fleet>, } -macro_rules! unwrap { - ($value:expr) => { - match $value { - Some(value) => value, - None => return, - } - }; -} - macro_rules! selection { (&$data:expr) => { - unwrap!(&$data.player_state.selection) + return_if_none!(&$data.player_state.selection) }; (&mut $data:expr) => { - unwrap!(&mut $data.player_state.selection) + return_if_none!(&mut $data.player_state.selection) }; } macro_rules! fleet_info { (&$data:expr, $id:expr) => { - unwrap!($data.fleet_infos.get($id)) + return_if_none!($data.fleet_infos.get($id)) }; } macro_rules! position { (&$data:expr, $id:expr) => { - unwrap!($data.positions.get($id)) + return_if_none!($data.positions.get($id)) }; } @@ -157,9 +149,9 @@ impl SelectFleet { mouse_event_id, select_mode, camera_counter: 0, - camera_view_invert: Default::default(), need_value_update: true, values_changed_once: false, + is_new_selection: true, mouse_pos: Default::default(), count: Default::default(), @@ -172,39 +164,46 @@ impl SelectFleet { }) } - fn update_camera(&mut self, d: &SelectFleetData<'_>) { + fn update_camera(&mut self, d: &FleetSelectData<'_>) { let camera_counter = d.camera.update_counter(); if self.camera_counter != camera_counter { self.camera_counter = camera_counter; self.need_value_update = true; - self.camera_view_invert = d.camera.view().invert(); } } - fn handle_events(&mut self, d: &mut SelectFleetData<'_>) { + fn handle_events(&mut self, d: &mut FleetSelectData<'_>) { let events = d.mouse_events.read(&mut self.mouse_event_id); for event in events { match event { MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => { - let pos = self.window_to_world(d.input_state.mouse_pos); + let pos = d.camera.view_to_world(d.input_state.mouse_pos); let selection = d.player_state.selection.take(); for (id, position, fleet) in (&d.entities, &d.positions, &d.fleets).join() { let r = fleet.orbit_max * fleet.orbit_max; if (position.pos - pos).length_sqr() <= r { d.player_state.selection = match selection { - Some(s) if s.fleet == id => Some(s), - _ => Some(Selection { - fleet: id, - count: ShipCount::all(), - }), + Some(s) if s.fleet == id => { + self.is_new_selection = false; + + Some(s) + } + _ => { + self.is_new_selection = true; + + Some(Selection { + fleet: id, + count: ShipCount::none(), + }) + } }; let selection = selection!(&d); let fleet_info = fleet_info!(&d, selection.fleet); let timeout = Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT; - self.mouse_pos = d.input_state.mouse_pos.into(); + self.mouse_pos = d.input_state.mouse_pos; self.select_mode = SelectMode::Init(timeout); self.values_changed_once = false; self.set_count(selection.count, &fleet_info.count); @@ -218,36 +217,41 @@ impl SelectFleet { SelectMode::Simple(progress) => { selection!(&mut d).count = self.count; - self.mouse_pos = d.input_state.mouse_pos.into(); + self.mouse_pos = d.input_state.mouse_pos; SelectMode::SimpleClose(progress) } SelectMode::Detail(progress) => { selection!(&mut d).count = self.count; - self.mouse_pos = d.input_state.mouse_pos.into(); + self.mouse_pos = d.input_state.mouse_pos; SelectMode::DetailClose(progress) } + SelectMode::Init(_) if self.is_new_selection => { + selection!(&mut d).count = ShipCount::all(); + + SelectMode::None + } _ => SelectMode::None, } } MouseEvent::Move(_, _) if self.select_mode.is_init() => { self.need_value_update = true; self.values_changed_once = false; - self.mouse_pos = d.input_state.mouse_pos.into(); + self.mouse_pos = d.input_state.mouse_pos; self.select_mode = SelectMode::Simple(0.0); } MouseEvent::Move(_, _) if self.select_mode.is_active() => { self.need_value_update = true; - self.mouse_pos = d.input_state.mouse_pos.into(); + self.mouse_pos = d.input_state.mouse_pos; } _ => (), } } } - fn update(&mut self, d: &SelectFleetData<'_>) { + fn update(&mut self, d: &FleetSelectData<'_>) { if !self.select_mode.is_active() { return; } @@ -261,7 +265,7 @@ impl SelectFleet { let position = position!(&d, selection.fleet); let fleet_info = fleet_info!(&d, selection.fleet); - self.marker = self.window_to_world(self.mouse_pos) - position.pos; + self.marker = d.camera.view_to_world(self.mouse_pos) - position.pos; self.zoom = d.camera.view().axis_x.as_vec3().length(); self.shape_size = position.shape.radius().unwrap_or(0.0); self.ring0 = self.shape_size + FLEET_SELECT_OFFSET / self.zoom; @@ -400,7 +404,7 @@ impl SelectFleet { } } - fn render(&mut self, progress: f32, d: &SelectFleetData<'_>) { + fn render(&mut self, progress: f32, d: &FleetSelectData<'_>) { /* select program */ let is_simple = self.select_mode.is_simple(); let program = if is_simple { @@ -500,15 +504,11 @@ impl SelectFleet { self.values[3] = sum / total_max; } - fn window_to_world>(&self, pos: T) -> Vector2f { - (self.camera_view_invert * T::into(pos).into_vec4()).into_vec2() - } - fn text_pos>( &self, dir: T, fleet_pos: Vector2f, - d: &SelectFleetData<'_>, + d: &FleetSelectData<'_>, ) -> Vector2f { let text_offset = self.ring1 + FLEET_SELECT_TEXT_OFFSET / self.zoom.sqrt(); let pos = fleet_pos + T::into(dir).normalize() * text_offset; @@ -518,7 +518,7 @@ impl SelectFleet { } impl<'a> System<'a> for SelectFleet { - type SystemData = SelectFleetData<'a>; + type SystemData = FleetSelectData<'a>; fn run(&mut self, mut data: Self::SystemData) { self.update_camera(&data); diff --git a/space-crush-app/src/resources/camera.rs b/space-crush-app/src/resources/camera.rs index e55fd52..b58a7d1 100644 --- a/space-crush-app/src/resources/camera.rs +++ b/space-crush-app/src/resources/camera.rs @@ -10,6 +10,8 @@ use glc::{ pub struct Camera { buffer: ArrayBuffer, data: Data, + view_invert: Matrix4f, + projection_invert: Matrix4f, update_counter: usize, } @@ -34,6 +36,8 @@ impl Camera { Ok(Self { buffer, data, + view_invert: Default::default(), + projection_invert: Default::default(), update_counter: 0, }) } @@ -42,10 +46,18 @@ impl Camera { &self.data.projection } + pub fn projection_invert(&self) -> &Matrix4f { + &self.projection_invert + } + pub fn view(&self) -> &Matrix4f { &self.data.view } + pub fn view_invert(&self) -> &Matrix4f { + &self.view_invert + } + pub fn update_counter(&self) -> usize { self.update_counter } @@ -63,6 +75,7 @@ impl Camera { data[0].size = self.data.size; self.update_counter = self.update_counter.wrapping_add(1); + self.projection_invert = self.data.projection.invert(); Ok(()) } @@ -81,6 +94,7 @@ impl Camera { data[0].view = self.data.view; self.update_counter = self.update_counter.wrapping_add(1); + self.view_invert = self.data.view.invert(); Ok(()) } @@ -89,11 +103,43 @@ impl Camera { Error::checked(|| self.buffer.bind_buffer_base(index)) } + pub fn world_to_view>(&self, pos: T) -> Vector2f { + self.data.view.transform(T::into(pos)).into() + } + + pub fn view_to_world>(&self, pos: T) -> Vector2f { + self.view_invert.transform(T::into(pos)).into() + } + + pub fn view_to_window>(&self, pos: T) -> Vector2f { + view_to_window(&self.data.size, pos) + } + + pub fn window_to_view>(&self, pos: T) -> Vector2f { + window_to_view(&self.data.size, pos) + } + pub fn world_to_window>(&self, pos: T) -> Vector2f { - let mut pos = self.data.view.transform(T::into(pos)).into_vec2(); - pos.x += self.data.size.x / 2.0; - pos.y = self.data.size.y / 2.0 - pos.y; + self.view_to_window(self.world_to_view(pos)) + } - pos + pub fn window_to_world>(&self, pos: T) -> Vector2f { + self.view_to_world(self.window_to_view(pos)) } } + +pub fn view_to_window>(res: &Vector2f, pos: T) -> Vector2f { + let mut pos = T::into(pos); + pos.x += res.x / 2.0; + pos.y = res.y / 2.0 - pos.y; + + pos +} + +pub fn window_to_view>(res: &Vector2f, pos: T) -> Vector2f { + let mut pos = T::into(pos); + pos.x -= res.x / 2.0; + pos.y = res.y / 2.0 - pos.y; + + pos +} diff --git a/space-crush-app/src/resources/input_state.rs b/space-crush-app/src/resources/input_state.rs index 3e41ed6..08a1b77 100644 --- a/space-crush-app/src/resources/input_state.rs +++ b/space-crush-app/src/resources/input_state.rs @@ -3,12 +3,14 @@ use std::collections::HashSet; use std::iter::IntoIterator; +use glc::vector::Vector2f; + use crate::misc::{MouseButton, VirtualKeyCode}; #[derive(Default)] pub struct InputState { - pub mouse_pos: (f32, f32), - pub resolution: (u32, u32), + pub mouse_pos: Vector2f, + pub resolution: Vector2f, pub close_requested: bool, pub key_states: HashSet, pub button_states: HashSet, diff --git a/space-crush-app/src/resources/mod.rs b/space-crush-app/src/resources/mod.rs index bab60da..4a7c387 100644 --- a/space-crush-app/src/resources/mod.rs +++ b/space-crush-app/src/resources/mod.rs @@ -1,9 +1,9 @@ -mod camera; -mod config; -mod geometry; -mod input_state; -mod player_state; -mod uniform; +pub mod camera; +pub mod config; +pub mod geometry; +pub mod input_state; +pub mod player_state; +pub mod uniform; pub use camera::Camera; pub use config::Config; diff --git a/space-crush-app/src/systems/state_update.rs b/space-crush-app/src/systems/state_update.rs index 9c245a7..ae165e5 100644 --- a/space-crush-app/src/systems/state_update.rs +++ b/space-crush-app/src/systems/state_update.rs @@ -4,7 +4,7 @@ use specs::{prelude::*, ReadExpect, System, World, Write}; use crate::{ misc::{KeyboardEvent, MouseEvent, WindowEvent}, - resources::InputState, + resources::{camera::window_to_view, InputState}, Error, }; @@ -51,10 +51,7 @@ impl<'a> System<'a> for StateUpdate { for event in events { match event { MouseEvent::Move(x, y) => { - let x = *x - input_state.resolution.0 as f32 / 2.0; - let y = input_state.resolution.1 as f32 / 2.0 - *y; - - input_state.mouse_pos = (x, y); + input_state.mouse_pos = window_to_view(&input_state.resolution, (*x, *y)) } MouseEvent::ButtonUp(button) => input_state.set_button_state(*button, false), MouseEvent::ButtonDown(button) => input_state.set_button_state(*button, true), @@ -66,7 +63,7 @@ impl<'a> System<'a> for StateUpdate { for event in events { match event { WindowEvent::Resize(w, h) => { - input_state.resolution = (*w, *h); + input_state.resolution = (*w as f32, *h as f32).into(); } WindowEvent::Close => { input_state.close_requested = true; diff --git a/space-crush-common/src/components/ship.rs b/space-crush-common/src/components/ship.rs index 9d3c558..32b5110 100644 --- a/space-crush-common/src/components/ship.rs +++ b/space-crush-common/src/components/ship.rs @@ -40,6 +40,14 @@ impl Count { } } + pub fn none() -> Self { + Self { + fighter: 0, + bomber: 0, + transporter: 0, + } + } + pub fn total(&self) -> usize { self.fighter .saturating_add(self.bomber) diff --git a/space-crush-common/src/lib.rs b/space-crush-common/src/lib.rs index d072147..232c20c 100644 --- a/space-crush-common/src/lib.rs +++ b/space-crush-common/src/lib.rs @@ -2,6 +2,7 @@ pub mod components; pub mod constants; pub mod dispatcher; pub mod error; +pub mod macros; pub mod misc; pub mod resources; pub mod systems; diff --git a/space-crush-common/src/macros.rs b/space-crush-common/src/macros.rs new file mode 100644 index 0000000..893317a --- /dev/null +++ b/space-crush-common/src/macros.rs @@ -0,0 +1,19 @@ +#[macro_export] +macro_rules! return_if_none { + ($value:expr) => { + match $value { + Some(value) => value, + None => return, + } + }; +} + +#[macro_export] +macro_rules! break_if_none { + ($value:expr) => { + match $value { + Some(value) => value, + None => break, + } + }; +}