From b53b19ee469801b2aaa02dc7ee244892269550c4 Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sun, 7 Feb 2021 22:48:48 +0100 Subject: [PATCH] rstnode: implement viewport shifting A bit of a rant: it's fucking ridiculous how much work it is to subtract two points from each other with mint + nalgebra. I ended up adding a complete `Vector2` implementation in this commit because I got fed up with it. Consider this the unofficial start of librgx (rust graphics x), which will basically become a libgdx in Rust, and the basis for the little game engine. Not to mention that ggez probably won't be maintained past version 0.6.0 unless some other maintainer takes over (I guess that could always happen). Anyway, I'm annoyed! --- games/rstnode/rst-client/src/graphics/mod.rs | 4 + .../rst-client/src/graphics/vector2.rs | 82 +++++++++++++++++++ games/rstnode/rst-client/src/input.rs | 23 ++++-- games/rstnode/rst-client/src/main.rs | 9 +- games/rstnode/rst-client/src/state.rs | 16 ++++ games/rstnode/rst-client/src/viewport.rs | 63 ++++++++++++++ 6 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 games/rstnode/rst-client/src/graphics/vector2.rs create mode 100644 games/rstnode/rst-client/src/viewport.rs diff --git a/games/rstnode/rst-client/src/graphics/mod.rs b/games/rstnode/rst-client/src/graphics/mod.rs index 095e66f1ad1..a7ba676ecc6 100644 --- a/games/rstnode/rst-client/src/graphics/mod.rs +++ b/games/rstnode/rst-client/src/graphics/mod.rs @@ -8,8 +8,12 @@ pub mod entities; pub mod ui; +mod vector2; +pub use vector2::*; + use crate::state::ClientState; use ggez::{Context, GameResult}; +use std::ops::{Add, Mul, Sub}; /// A utility module to include everything required to implement a /// graphics entity diff --git a/games/rstnode/rst-client/src/graphics/vector2.rs b/games/rstnode/rst-client/src/graphics/vector2.rs new file mode 100644 index 00000000000..5204e77fa44 --- /dev/null +++ b/games/rstnode/rst-client/src/graphics/vector2.rs @@ -0,0 +1,82 @@ +use std::fmt::{self, Display, Formatter}; +use std::ops::{Add, Mul, Sub, SubAssign}; + +/// Just a vector +#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)] +pub struct Vector2 { + pub x: f32, + pub y: f32, +} + +impl Display for Vector2 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "[{}, {}]", self.x, self.y) + } +} + +impl Vector2 { + pub fn new(x: f32, y: f32) -> Self { + Self { x, y } + } + + pub fn abs(self) -> Self { + Self { + x: self.x.abs(), + y: self.y.abs(), + } + } +} + +impl Sub for Vector2 { + type Output = Vector2; + + fn sub(self, o: Vector2) -> Self::Output { + Vector2 { + x: self.x - o.x, + y: self.y - o.y, + } + } +} + +impl SubAssign for Vector2 { + fn sub_assign(&mut self, o: Self) { + *self = Self { + x: self.x - o.x, + y: self.y - o.y, + } + } +} + +impl Add for Vector2 { + type Output = Vector2; + + fn add(self, o: Vector2) -> Self::Output { + Vector2 { + x: self.x + o.x, + y: self.y + o.y, + } + } +} + +impl Mul for Vector2 { + type Output = Vector2; + + fn mul(self, o: Vector2) -> Self::Output { + Vector2 { + x: self.x * o.x, + y: self.y * o.y, + } + } +} + +impl From for mint::Point2 { + fn from(v: Vector2) -> Self { + [v.x, v.y].into() + } +} + +impl From> for Vector2 { + fn from(v: mint::Point2) -> Self { + Self { x: v.x, y: v.y } + } +} diff --git a/games/rstnode/rst-client/src/input.rs b/games/rstnode/rst-client/src/input.rs index 4e901b8ade0..28138698643 100644 --- a/games/rstnode/rst-client/src/input.rs +++ b/games/rstnode/rst-client/src/input.rs @@ -1,50 +1,55 @@ //! Advanced input handler +use crate::{graphics::Vector2, viewport::Viewport}; use ggez::{ event::EventHandler, input::mouse::{self, MouseButton}, Context, GameResult, }; -use mint::Point2; pub struct InputHandle { /// The mouse position on the viewport - pub mouse_pos: Point2, + pub mouse_pos: Vector2, /// Whether the left mouse button is pressed this frame pub left_pressed: bool, + /// Whether the middle mouse button is pressed this frame + pub middle_pressed: bool, /// Whether the right mouse button is pressed this frame pub right_pressed: bool, /// Set when pressing left mouse and unset when releasing it - pub drag_point: Option>, + pub drag_point: Option, } impl InputHandle { pub fn new() -> Self { Self { - mouse_pos: [0.0, 0.0].into(), + mouse_pos: Vector2::new(0.0, 0.0), left_pressed: false, + middle_pressed: false, right_pressed: false, drag_point: None, } } /// Get the unprojected mouse coordinates - pub fn unproject(&self) -> Point2 { - self.mouse_pos.clone(); + pub fn unproject(&self, vp: &Viewport) -> Vector2 { + // self.mouse_pos.clone() - vp.start().clone() + todo!() } } impl EventHandler for InputHandle { fn update(&mut self, ctx: &mut Context) -> GameResult<()> { - self.mouse_pos = mouse::position(&ctx); + self.mouse_pos = mouse::position(&ctx).into(); self.left_pressed = mouse::button_pressed(ctx, MouseButton::Left); + self.middle_pressed = mouse::button_pressed(ctx, MouseButton::Middle); self.right_pressed = mouse::button_pressed(ctx, MouseButton::Right); // Only set the drag_point once and unset when we release Left button - if self.left_pressed && self.drag_point.is_none() { + if self.middle_pressed && self.drag_point.is_none() { self.drag_point = Some(self.mouse_pos.clone()); - } else if !self.left_pressed { + } else if !self.middle_pressed { self.drag_point = None; } diff --git a/games/rstnode/rst-client/src/main.rs b/games/rstnode/rst-client/src/main.rs index aa86de26c95..a6ba4a59cdc 100644 --- a/games/rstnode/rst-client/src/main.rs +++ b/games/rstnode/rst-client/src/main.rs @@ -14,6 +14,7 @@ mod input; mod log; mod settings; mod state; +mod viewport; mod window; #[allow(unused)] @@ -36,7 +37,13 @@ fn main() { // Load assets tree let assets = assets::load_tree(window.ctx(), &settings).unwrap_or_else(|e| fatal!("LoadError: {}!", e)); - let state = ClientState::new(settings, assets); + // Create the client state + let mut state = ClientState::new(settings, assets); + + // Initialise the viewport first! + state.viewport().init(window.ctx()); + + // Window goes brrrr window.run(state) } diff --git a/games/rstnode/rst-client/src/state.rs b/games/rstnode/rst-client/src/state.rs index b1b5e89f3d1..d3b5bbcc3d7 100644 --- a/games/rstnode/rst-client/src/state.rs +++ b/games/rstnode/rst-client/src/state.rs @@ -7,6 +7,7 @@ use crate::{ Renderer, }, input::InputHandle, + viewport::Viewport, GameSettings, }; use ggez::{event::EventHandler, graphics, Context, GameResult}; @@ -17,6 +18,7 @@ pub struct ClientState { assets: Assets, settings: GameSettings, input: InputHandle, + vp: Viewport, // Game state node: NodeRndr, @@ -27,6 +29,7 @@ impl ClientState { Self { assets, settings, + vp: Viewport::new(), input: InputHandle::new(), node: NodeRndr { loc: Coordinates(512.0, 512.0), @@ -49,6 +52,10 @@ impl ClientState { } } + pub fn viewport(&mut self) -> &mut Viewport { + &mut self.vp + } + pub fn assets(&self) -> &Assets { &self.assets } @@ -56,7 +63,16 @@ impl ClientState { impl EventHandler for ClientState { fn update(&mut self, ctx: &mut Context) -> GameResult<()> { + // TODO: get simulation updates + + // Get new input state self.input.update(ctx)?; + + // Apply inputs to viewpoirt + self.vp.apply(ctx, &self.input)?; + + // Update viewport + self.vp.update(ctx)?; Ok(()) } diff --git a/games/rstnode/rst-client/src/viewport.rs b/games/rstnode/rst-client/src/viewport.rs new file mode 100644 index 00000000000..3f9e803e387 --- /dev/null +++ b/games/rstnode/rst-client/src/viewport.rs @@ -0,0 +1,63 @@ +//! Viewport utilities + +use crate::{graphics::Vector2, input::InputHandle}; +use ggez::{ + error::GameResult, + graphics::{self, Rect}, + Context, +}; + +#[derive(Default)] +pub struct Viewport { + start: Vector2, + prev_start: Vector2, + size: Vector2, +} + +impl Viewport { + pub fn new() -> Self { + Self::default() + } + + pub fn start(&self) -> &Vector2 { + &self.start + } + + /// Must be called at least once before calling + /// [`update`](Self::update) + pub fn init(&mut self, ctx: &mut Context) { + let Rect { x, y, w, h } = graphics::screen_coordinates(&ctx); + self.start = Vector2::new(x, y); + self.prev_start = self.start; + self.size = Vector2::new(w, h); + } + + /// Update the game state with the curent viewport data + pub fn update(&self, ctx: &mut Context) -> GameResult<()> { + graphics::set_screen_coordinates( + ctx, + Rect { + x: self.start.x, + y: self.start.y, + w: self.size.x, + h: self.size.y, + }, + )?; + + Ok(()) + } + + /// Apply changes from the input handle to the viewport + pub fn apply(&mut self, _: &mut Context, input: &InputHandle) -> GameResult<()> { + if input.middle_pressed { + let drag = input.drag_point.as_ref().unwrap().clone(); + let pos = input.mouse_pos.clone(); + self.start = self.prev_start + (drag - pos); + debug!("Changing VP start: {}", self.start); + } else { + self.prev_start = self.start; + } + + Ok(()) + } +}