Added cursor trapping, more documentation

This commit is contained in:
Joshua Barretto 2019-01-12 01:14:58 +00:00
parent c9d877de7a
commit 45d5a0a396
15 changed files with 143 additions and 46 deletions

View File

@ -14,7 +14,7 @@ uniform u_globals {
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time_of_day;
vec4 time;
};

View File

@ -16,7 +16,7 @@ uniform u_globals {
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time_of_day;
vec4 time;
};

View File

@ -14,7 +14,7 @@ uniform u_globals {
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time_of_day;
vec4 time;
};

View File

@ -14,7 +14,7 @@ uniform u_globals {
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time_of_day;
vec4 time;
};

View File

@ -12,8 +12,6 @@ pub use crate::error::Error;
use std::mem;
// Library
use glutin;
use failure;
use log;
use pretty_env_logger;
@ -28,6 +26,12 @@ pub struct GlobalState {
window: Window,
}
impl GlobalState {
pub fn on_play_state_changed(&mut self) {
self.window.untrap_cursor();
}
}
// States can either close (and revert to a previous state), push a new state on top of themselves,
// or switch to a totally different state
pub enum PlayStateResult {
@ -82,23 +86,27 @@ fn main() {
log::info!("Shutting down all states...");
while states.last().is_some() {
states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name())
log::info!("Popped state '{}'", old_state.name());
global_state.on_play_state_changed();
});
}
},
PlayStateResult::Pop => {
states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name())
log::info!("Popped state '{}'", old_state.name());
global_state.on_play_state_changed();
});
},
PlayStateResult::Push(new_state) => {
log::info!("Pushed state '{}'", new_state.name());
states.push(new_state);
global_state.on_play_state_changed();
},
PlayStateResult::Switch(mut new_state) => {
states.last_mut().map(|old_state| {
log::info!("Switching to state '{}' from state '{}'", new_state.name(), old_state.name());
mem::swap(old_state, &mut new_state);
global_state.on_play_state_changed();
});
},
}

View File

@ -7,7 +7,6 @@ use crate::{
PlayStateResult,
GlobalState,
window::Event,
render,
session::SessionState,
};
@ -44,8 +43,9 @@ impl PlayState for TitleState {
// Finish the frame
global_state.window.renderer_mut().flush();
global_state.window.display()
.expect("Failed to display window");
global_state.window
.swap_buffers()
.expect("Failed to swap window buffers");
}
}

View File

@ -3,6 +3,7 @@ pub mod mesh;
pub mod model;
pub mod pipelines;
pub mod renderer;
mod util;
// Reexports
pub use self::{

View File

@ -11,6 +11,9 @@ use gfx::{
};
use vek::*;
// Local
use super::util::arr_to_mat;
gfx_defines! {
constant Globals {
view_mat: [[f32; 4]; 4] = "view_mat",
@ -18,32 +21,43 @@ gfx_defines! {
cam_pos: [f32; 4] = "cam_pos",
focus_pos: [f32; 4] = "focus_pos",
view_distance: [f32; 4] = "view_distance",
tod: [f32; 4] = "tod",
time_of_day: [f32; 4] = "time_of_day",
time: [f32; 4] = "time",
}
}
impl Globals {
pub fn new() -> Self {
// TODO: Get rid of this ugliness
#[rustfmt::skip]
fn f32_arr_to_mat(arr: [f32; 16]) -> [[f32; 4]; 4] {
[
[arr[ 0], arr[ 1], arr[ 2], arr[ 3]],
[arr[ 4], arr[ 5], arr[ 6], arr[ 7]],
[arr[ 8], arr[ 9], arr[10], arr[11]],
[arr[12], arr[13], arr[14], arr[15]],
]
}
/// Create global consts with default values.
pub fn default() -> Self {
Self {
view_mat: f32_arr_to_mat(Mat4::identity().into_col_array()),
proj_mat: f32_arr_to_mat(Mat4::identity().into_col_array()),
view_mat: arr_to_mat(Mat4::identity().into_col_array()),
proj_mat: arr_to_mat(Mat4::identity().into_col_array()),
cam_pos: [0.0; 4],
focus_pos: [0.0; 4],
view_distance: [0.0; 4],
tod: [0.0; 4],
time_of_day: [0.0; 4],
time: [0.0; 4],
}
}
/// Create global consts from the provided parameters.
pub fn new(
view_mat: Mat4<f32>,
proj_mat: Mat4<f32>,
cam_pos: Vec3<f32>,
focus_pos: Vec3<f32>,
view_distance: f32,
time_of_day: f32,
time: f32,
) -> Self {
Self {
view_mat: arr_to_mat(view_mat.into_col_array()),
proj_mat: arr_to_mat(proj_mat.into_col_array()),
cam_pos: Vec4::from(cam_pos).into_array(),
focus_pos: Vec4::from(focus_pos).into_array(),
view_distance: [view_distance; 4],
time_of_day: [time_of_day; 4],
time: [time; 4],
}
}
}

View File

@ -41,7 +41,7 @@ gfx_defines! {
}
impl Locals {
pub fn new() -> Self {
pub fn default() -> Self {
Self { nul: [0.0; 4] }
}
}

View File

@ -46,7 +46,8 @@ pub struct Renderer {
}
impl Renderer {
/// Create a new `Renderer` from a variety of backend-specific components and the window targets.
/// Create a new `Renderer` from a variety of backend-specific components and the window
/// targets.
pub fn new(
device: gfx_backend::Device,
mut factory: gfx_backend::Factory,
@ -99,13 +100,25 @@ impl Renderer {
Ok(Consts::new(&mut self.factory))
}
/// Create a new set of constants and update then with a value.
pub fn create_consts_with<T: Copy + gfx::traits::Pod>(&mut self, val: T) -> Result<Consts<T>, RenderError> {
/// Create a new set of constants with a value.
pub fn create_consts_with<T: Copy + gfx::traits::Pod>(
&mut self,
val: T
) -> Result<Consts<T>, RenderError> {
let mut consts = self.create_consts()?;
consts.update(&mut self.encoder, val)?;
Ok(consts)
}
/// Update a set of constants with a new value.
pub fn update_consts<T: Copy + gfx::traits::Pod>(
&mut self,
consts: &mut Consts<T>,
val: T
) -> Result<(), RenderError> {
consts.update(&mut self.encoder, val)
}
/// Create a new model from the provided mesh.
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderError> {
Ok(Model::new(
@ -136,7 +149,6 @@ impl Renderer {
}
struct GfxPipeline<P: gfx::pso::PipelineInit> {
program: gfx::handle::Program<gfx_backend::Resources>,
pso: gfx::pso::PipelineState<gfx_backend::Resources, P::Meta>,
}
@ -166,10 +178,12 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
)
// Do some funky things to work around an oddity in gfx's error ownership rules
.map_err(|err| RenderError::PipelineError(match err {
gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err),
gfx::PipelineStateError::DescriptorInit(err) => gfx::PipelineStateError::DescriptorInit(err.into()),
gfx::PipelineStateError::DeviceCreate(err) => gfx::PipelineStateError::DeviceCreate(err),
gfx::PipelineStateError::Program(err) =>
gfx::PipelineStateError::Program(err),
gfx::PipelineStateError::DescriptorInit(err) =>
gfx::PipelineStateError::DescriptorInit(err.into()),
gfx::PipelineStateError::DeviceCreate(err) =>
gfx::PipelineStateError::DeviceCreate(err),
}))?,
program,
})
}

View File

@ -0,0 +1,10 @@
// TODO: Get rid of this ugliness
#[rustfmt::skip]
pub fn arr_to_mat(arr: [f32; 16]) -> [[f32; 4]; 4] {
[
[arr[ 0], arr[ 1], arr[ 2], arr[ 3]],
[arr[ 4], arr[ 5], arr[ 6], arr[ 7]],
[arr[ 8], arr[ 9], arr[10], arr[11]],
[arr[12], arr[13], arr[14], arr[15]],
]
}

View File

@ -21,15 +21,15 @@ impl Camera {
Self {
focus: Vec3::zero(),
ori: Vec3::zero(),
dist: 10.0,
dist: 5.0,
fov: 1.3,
aspect: 1.618,
}
}
/// Compute the transformation matrices (view matrix and projection matrix) for the camera.
pub fn compute_matrices(&self) -> (Mat4<f32>, Mat4<f32>) {
let view = Mat4::<f32>::identity()
pub fn compute_dependents(&self) -> (Mat4<f32>, Mat4<f32>, Vec3<f32>) {
let view_mat = Mat4::<f32>::identity()
* Mat4::translation_3d(-Vec3::unit_z() * self.dist)
* Mat4::rotation_z(self.ori.z)
* Mat4::rotation_x(self.ori.y)
@ -37,13 +37,21 @@ impl Camera {
* Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x())
* Mat4::translation_3d(-self.focus);
let proj = Mat4::perspective_rh_no(
let proj_mat = Mat4::perspective_rh_no(
self.fov,
self.aspect,
NEAR_PLANE,
FAR_PLANE,
);
(view, proj)
// TODO: Make this more efficient
let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w());
(view_mat, proj_mat, cam_pos)
}
/// Get the focus position of the camera.
pub fn get_focus_pos(&self) -> Vec3<f32> { self.focus }
/// Set the focus position of the camera.
pub fn set_focus_pos(&mut self, focus: Vec3<f32>) { self.focus = focus; }
}

View File

@ -31,19 +31,36 @@ impl Scene {
Self {
camera: Camera::new(),
globals: renderer
.create_consts_with(Globals::new())
.create_consts_with(Globals::default())
.unwrap(),
skybox: Skybox {
model: renderer
.create_model(&create_skybox_mesh())
.unwrap(),
locals: renderer
.create_consts_with(SkyboxLocals::new())
.create_consts_with(SkyboxLocals::default())
.unwrap(),
},
}
}
pub fn maintain_gpu_data(&mut self, renderer: &mut Renderer) {
// Compute camera matrices
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents();
// Update global constants
renderer.update_consts(&mut self.globals, Globals::new(
view_mat,
proj_mat,
cam_pos,
self.camera.get_focus_pos(),
10.0,
0.0,
0.0,
))
.expect("Failed to update global constants");
}
/// Render the scene using the provided `Renderer`
pub fn render_to(&self, renderer: &mut Renderer) {
// Render the skybox first (it appears over everything else so must be rendered first)

View File

@ -31,6 +31,9 @@ const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 };
impl PlayState for SessionState {
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
// Trap the cursor
global_state.window.trap_cursor();
// Game loop
loop {
// Handle window events
@ -44,6 +47,9 @@ impl PlayState for SessionState {
}
}
// Maintain scene GPU data
self.scene.maintain_gpu_data(global_state.window.renderer_mut());
// Clear the screen
global_state.window.renderer_mut().clear(BG_COLOR);
@ -52,8 +58,9 @@ impl PlayState for SessionState {
// Finish the frame
global_state.window.renderer_mut().flush();
global_state.window.display()
.expect("Failed to display window");
global_state.window
.swap_buffers()
.expect("Failed to swap window buffers");
}
}

View File

@ -1,6 +1,7 @@
// Library
use glutin;
use gfx_window_glutin;
use vek::*;
// Crate
use crate::{
@ -73,13 +74,30 @@ impl Window {
events
}
pub fn display(&self) -> Result<(), Error> {
pub fn swap_buffers(&self) -> Result<(), Error> {
self.window.swap_buffers()
.map_err(|err| Error::BackendError(Box::new(err)))
}
pub fn trap_cursor(&mut self) {
self.window.hide_cursor(true);
self.window.grab_cursor(true)
.expect("Failed to grab cursor");
}
pub enum Event {
Close,
Char(char),
pub fn untrap_cursor(&mut self) {
self.window.hide_cursor(false);
self.window.grab_cursor(false)
.expect("Failed to ungrab cursor");
}
}
/// Represents an incoming event from the window
pub enum Event {
/// The window has been requested to close.
Close,
/// A key has been typed that corresponds to a specific character.
Char(char),
/// The cursor has been panned across the screen while trapped.
CursorPan(Vec2<f32>),
}