Added session, documented, improved structure

This commit is contained in:
Joshua Barretto 2019-01-11 23:18:34 +00:00
parent d559d633ab
commit c9d877de7a
22 changed files with 492 additions and 168 deletions

View File

@ -25,3 +25,5 @@ vek = "0.9"
glsl-include = "0.2" glsl-include = "0.2"
failure = "0.1" failure = "0.1"
lazy_static = "1.1" lazy_static = "1.1"
log = "0.4"
pretty_env_logger = "0.3"

View File

@ -0,0 +1,25 @@
#version 330 core
in vec3 f_pos;
layout (std140)
uniform u_locals {
mat4 model_mat;
};
layout (std140)
uniform u_globals {
mat4 view_mat;
mat4 proj_mat;
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time;
};
out vec4 tgt_color;
void main() {
tgt_color = vec4(f_pos, 1.0);
}

View File

@ -0,0 +1,32 @@
#version 330 core
in vec3 v_pos;
in vec3 v_col;
in uint v_bone;
layout (std140)
uniform u_locals {
mat4 model_mat;
};
layout (std140)
uniform u_globals {
mat4 view_mat;
mat4 proj_mat;
vec4 cam_pos;
vec4 focus_pos;
vec4 view_distance;
vec4 tod;
vec4 time;
};
out vec3 f_pos;
void main() {
f_pos = v_pos;
gl_Position =
proj_mat *
view_mat *
vec4(0.5 * v_pos + cam_pos.xyz, 1);
}

View File

@ -4,7 +4,7 @@ in vec3 f_pos;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
mat4 model_mat; vec4 nul;
}; };
layout (std140) layout (std140)
@ -21,5 +21,5 @@ uniform u_globals {
out vec4 tgt_color; out vec4 tgt_color;
void main() { void main() {
tgt_color = vec4(1.0, 0.0, 0.0, 1.0); tgt_color = vec4(f_pos, 1.0);
} }

View File

@ -4,7 +4,7 @@ in vec3 v_pos;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
mat4 model_mat; vec4 nul;
}; };
layout (std140) layout (std140)
@ -26,5 +26,6 @@ void main() {
gl_Position = gl_Position =
proj_mat * proj_mat *
view_mat * view_mat *
vec4(3000 * v_pos + cam_pos.xyz, 1); vec4(v_pos + cam_pos.xyz, 1);
gl_Position.z = 0.0;
} }

View File

@ -1,34 +0,0 @@
// Library
use gfx::{
handle::Buffer,
traits::{FactoryExt, Pod},
};
// Crate
use crate::render_ctx::{RenderCtx, Resources};
type ConstBuffer<T> = Buffer<Resources, T>;
#[derive(Clone)]
pub struct ConstHandle<T: Copy + Pod> {
buffer: ConstBuffer<T>,
}
impl<T: Copy + Pod> ConstHandle<T> {
pub fn new(render_ctx: &mut RenderCtx) -> Self {
Self {
buffer: render_ctx
.factory_mut()
.create_constant_buffer(1),
}
}
pub fn update(&self, render_ctx: &mut RenderCtx, consts: T) {
render_ctx
.encoder_mut()
.update_buffer(&self.buffer, &[consts], 0)
.unwrap();
}
pub fn buffer(&self) -> &ConstBuffer<T> { &self.buffer }
}

22
voxygen/src/error.rs Normal file
View File

@ -0,0 +1,22 @@
// Standard
use std::any;
// Crate
use crate::render::RenderError;
/// Represents any error that may be triggered by Voxygen
#[derive(Debug)]
pub enum Error {
/// A miscellaneous error relating to a backend dependency
BackendError(Box<any::Any>),
/// An error relating the rendering subsystem
RenderError(RenderError),
// A miscellaneous error with an unknown or unspecified source
Other(failure::Error),
}
impl From<RenderError> for Error {
fn from(err: RenderError) -> Self {
Error::RenderError(err)
}
}

View File

@ -1,18 +0,0 @@
// Library
use gfx::{
// Urgh
gfx_defines,
gfx_constant_struct_meta,
gfx_impl_struct_meta,
};
gfx_defines! {
constant GlobalConsts {
view_mat: [[f32; 4]; 4] = "view_mat",
proj_mat: [[f32; 4]; 4] = "proj_mat",
cam_origin: [f32; 4] = "cam_origin",
play_origin: [f32; 4] = "play_origin",
view_distance: [f32; 4] = "view_distance",
time: [f32; 4] = "time",
}
}

View File

@ -1,37 +1,28 @@
mod menu; pub mod error;
mod render; pub mod menu;
mod window; pub mod render;
pub mod scene;
pub mod session;
pub mod window;
// Reexports
pub use crate::error::Error;
// Standard // Standard
use std::{ use std::mem;
any,
mem,
};
// Library // Library
use glutin; use glutin;
use failure; use failure;
use log;
use pretty_env_logger;
// Crate // Crate
use crate::{ use crate::{
menu::title::TitleState, menu::title::TitleState,
window::Window, window::Window,
render::RenderErr,
}; };
#[derive(Debug)]
pub enum VoxygenErr {
BackendErr(Box<any::Any>),
RenderErr(RenderErr),
Other(failure::Error),
}
impl From<RenderErr> for VoxygenErr {
fn from(err: RenderErr) -> Self {
VoxygenErr::RenderErr(err)
}
}
// A type used to store state that is shared between all play states // A type used to store state that is shared between all play states
pub struct GlobalState { pub struct GlobalState {
window: Window, window: Window,
@ -42,40 +33,73 @@ pub struct GlobalState {
pub enum PlayStateResult { pub enum PlayStateResult {
/// Pop all play states in reverse order and shut down the program /// Pop all play states in reverse order and shut down the program
Shutdown, Shutdown,
/// Close the current play state /// Close the current play state and pop it from the play state stack
Close, Pop,
/// Push a new play state onto the play state stack /// Push a new play state onto the play state stack
Push(Box<dyn PlayState>), Push(Box<dyn PlayState>),
/// Switch the current play state with a new play state /// Switch the current play state with a new play state
Switch(Box<dyn PlayState>), Switch(Box<dyn PlayState>),
} }
/// A trait representing a playable game state. This may be a menu, a game session, the title
/// screen, etc.
pub trait PlayState { pub trait PlayState {
/// Play the state until some change of state is required (i.e: a menu is opened or the game
/// is closed).
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult; fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult;
/// Get a descriptive name for this state type
fn name(&self) -> &'static str;
} }
fn main() { fn main() {
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(TitleState::new())]; // Init logging
pretty_env_logger::init();
// Set up the initial play state
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(TitleState::new())];
states.last().map(|current_state| {
log::info!("Started game with state '{}'", current_state.name())
});
// Set up the global state
let mut global_state = GlobalState { let mut global_state = GlobalState {
window: Window::new() window: Window::new()
.expect("Failed to create window"), .expect("Failed to create window"),
}; };
// What's going on here?
// ---------------------
// The state system used by Voxygen allows for the easy development of stack-based menus.
// For example, you may want a "title" state that can push a "main menu" state on top of it,
// which can in turn push a "settings" state or a "game session" state on top of it.
// The code below manages the state transfer logic automatically so that we don't have to
// re-engineer it for each menu we decide to add to the game.
while let Some(state_result) = states.last_mut().map(|last| last.play(&mut global_state)){ while let Some(state_result) = states.last_mut().map(|last| last.play(&mut global_state)){
// Implement state transfer logic // Implement state transfer logic
match state_result { match state_result {
PlayStateResult::Shutdown => while states.last().is_some() { PlayStateResult::Shutdown => {
states.pop(); log::info!("Shutting down all states...");
while states.last().is_some() {
states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name())
});
}
}, },
PlayStateResult::Close => { PlayStateResult::Pop => {
states.pop(); states.pop().map(|old_state| {
log::info!("Popped state '{}'", old_state.name())
});
}, },
PlayStateResult::Push(new_state) => { PlayStateResult::Push(new_state) => {
log::info!("Pushed state '{}'", new_state.name());
states.push(new_state); states.push(new_state);
}, },
PlayStateResult::Switch(mut new_state) => { PlayStateResult::Switch(mut new_state) => {
states.last_mut().map(|old_state| mem::swap(old_state, &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);
});
}, },
} }
} }

View File

@ -7,32 +7,47 @@ use crate::{
PlayStateResult, PlayStateResult,
GlobalState, GlobalState,
window::Event, window::Event,
render,
session::SessionState,
}; };
pub struct TitleState; pub struct TitleState;
impl TitleState { impl TitleState {
/// Create a new `TitleState`
pub fn new() -> Self { pub fn new() -> Self {
Self Self
} }
} }
const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 }; // The background colour
const BG_COLOR: Rgba<f32> = Rgba { r: 0.8, g: 1.0, b: 0.8, a: 1.0 };
impl PlayState for TitleState { impl PlayState for TitleState {
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
'eventloop: loop { loop {
// Process window events // Handle window events
for event in global_state.window.fetch_events() { for event in global_state.window.fetch_events() {
match event { match event {
Event::Close => break 'eventloop PlayStateResult::Shutdown, Event::Close => return PlayStateResult::Shutdown,
// When space is pressed, start a session
Event::Char(' ') => return PlayStateResult::Push(
Box::new(SessionState::from_renderer(global_state.window.renderer_mut())),
),
// Ignore all other events
_ => {},
} }
} }
// Clear the screen
global_state.window.renderer_mut().clear(BG_COLOR); global_state.window.renderer_mut().clear(BG_COLOR);
// Finish the frame
global_state.window.renderer_mut().flush(); global_state.window.renderer_mut().flush();
global_state.window.display() global_state.window.display()
.expect("Failed to display window"); .expect("Failed to display window");
} }
} }
fn name(&self) -> &'static str { "Title" }
} }

View File

@ -6,28 +6,32 @@ use gfx::{
// Local // Local
use super::{ use super::{
RenderErr, RenderError,
gfx_backend, gfx_backend,
}; };
/// A handle to a series of constants sitting on the GPU. This is used to hold information used in
/// the rendering process that does not change throughout a single render pass.
#[derive(Clone)] #[derive(Clone)]
pub struct Consts<T: Copy + gfx::traits::Pod> { pub struct Consts<T: Copy + gfx::traits::Pod> {
pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>, pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>,
} }
impl<T: Copy + gfx::traits::Pod> Consts<T> { impl<T: Copy + gfx::traits::Pod> Consts<T> {
/// Create a new `Const<T>`
pub fn new(factory: &mut gfx_backend::Factory) -> Self { pub fn new(factory: &mut gfx_backend::Factory) -> Self {
Self { Self {
buf: factory.create_constant_buffer(1), buf: factory.create_constant_buffer(1),
} }
} }
/// Update the GPU-side value represented by this constant handle.
pub fn update( pub fn update(
&mut self, &mut self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
data: T, val: T,
) -> Result<(), RenderErr> { ) -> Result<(), RenderError> {
encoder.update_buffer(&self.buf, &[data], 0) encoder.update_buffer(&self.buf, &[val], 0)
.map_err(|err| RenderErr::UpdateErr(err)) .map_err(|err| RenderError::UpdateError(err))
} }
} }

View File

@ -1,25 +1,38 @@
// Local // Local
use super::Pipeline; use super::Pipeline;
/// Used to store vertex data on the CPU /// A `Vec`-based mesh structure used to store mesh data on the CPU.
pub struct Mesh<P: Pipeline> { pub struct Mesh<P: Pipeline> {
verts: Vec<P::Vertex>, verts: Vec<P::Vertex>,
} }
impl<P: Pipeline> Mesh<P> { impl<P: Pipeline> Mesh<P> {
/// Create a new `Mesh`
pub fn new() -> Self { pub fn new() -> Self {
Self { verts: vec![] } Self { verts: vec![] }
} }
/// Get a slice referencing the vertices of this mesh.
pub fn vertices(&self) -> &[P::Vertex] { pub fn vertices(&self) -> &[P::Vertex] {
&self.verts &self.verts
} }
/// Push a new vertex onto the end of this mesh.
pub fn push(&mut self, vert: P::Vertex) { pub fn push(&mut self, vert: P::Vertex) {
self.verts.push(vert); self.verts.push(vert);
} }
/// Push a new polygon onto the end of this mesh.
pub fn push_tri(&mut self, tri: Tri<P>) {
self.verts.push(tri.a);
self.verts.push(tri.b);
self.verts.push(tri.c);
}
/// Push a new quad onto the end of this mesh.
pub fn push_quad(&mut self, quad: Quad<P>) { pub fn push_quad(&mut self, quad: Quad<P>) {
// A quad is composed of two triangles. The code below converts the former to the latter.
// Tri 1 // Tri 1
self.verts.push(quad.a.clone()); self.verts.push(quad.a.clone());
self.verts.push(quad.b); self.verts.push(quad.b);
@ -32,7 +45,24 @@ impl<P: Pipeline> Mesh<P> {
} }
} }
/// Represents a quad /// Represents a triangle stored on the CPU.
pub struct Tri<P: Pipeline> {
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
}
impl<P: Pipeline> Tri<P> {
pub fn new(
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
) -> Self {
Self { a, b, c }
}
}
/// Represents a quad stored on the CPU.
pub struct Quad<P: Pipeline> { pub struct Quad<P: Pipeline> {
a: P::Vertex, a: P::Vertex,
b: P::Vertex, b: P::Vertex,

View File

@ -1,8 +1,8 @@
mod consts; pub mod consts;
mod mesh; pub mod mesh;
mod model; pub mod model;
mod pipelines; pub mod pipelines;
mod renderer; pub mod renderer;
// Reexports // Reexports
pub use self::{ pub use self::{
@ -11,8 +11,16 @@ pub use self::{
model::Model, model::Model,
renderer::{Renderer, TgtColorFmt, TgtDepthFmt}, renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
pipelines::{ pipelines::{
character::CharacterPipeline, Globals,
skybox::SkyboxPipeline, character::{
CharacterPipeline,
Locals as CharacterLocals,
},
skybox::{
create_mesh as create_skybox_mesh,
SkyboxPipeline,
Locals as SkyboxLocals,
},
}, },
}; };
@ -24,9 +32,9 @@ use gfx;
/// Used to represent one of many possible errors that may be omitted by the rendering code /// Used to represent one of many possible errors that may be omitted by the rendering code
#[derive(Debug)] #[derive(Debug)]
pub enum RenderErr { pub enum RenderError {
PipelineErr(gfx::PipelineStateError<String>), PipelineError(gfx::PipelineStateError<String>),
UpdateErr(gfx::UpdateError<usize>), UpdateError(gfx::UpdateError<usize>),
} }
/// Used to represent a specific rendering configuration /// Used to represent a specific rendering configuration

View File

@ -11,7 +11,7 @@ use super::{
gfx_backend, gfx_backend,
}; };
/// Represents a mesh that has been sent to the CPU /// Represents a mesh that has been sent to the GPU.
pub struct Model<P: Pipeline> { pub struct Model<P: Pipeline> {
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>, pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
pub slice: gfx::Slice<gfx_backend::Resources>, pub slice: gfx::Slice<gfx_backend::Resources>,

View File

@ -23,6 +23,7 @@ use super::{
gfx_defines! { gfx_defines! {
vertex Vertex { vertex Vertex {
pos: [f32; 3] = "v_pos", pos: [f32; 3] = "v_pos",
col: [f32; 3] = "v_col",
bone: u8 = "v_bone", bone: u8 = "v_bone",
} }

View File

@ -6,12 +6,10 @@ use gfx::{
self, self,
// Macros // Macros
gfx_defines, gfx_defines,
gfx_vertex_struct_meta,
gfx_constant_struct_meta, gfx_constant_struct_meta,
gfx_impl_struct_meta, gfx_impl_struct_meta,
gfx_pipeline,
gfx_pipeline_inner,
}; };
use vek::*;
gfx_defines! { gfx_defines! {
constant Globals { constant Globals {
@ -24,3 +22,28 @@ gfx_defines! {
time: [f32; 4] = "time", 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]],
]
}
Self {
view_mat: f32_arr_to_mat(Mat4::identity().into_col_array()),
proj_mat: f32_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: [0.0; 4],
}
}
}

View File

@ -28,7 +28,7 @@ gfx_defines! {
} }
constant Locals { constant Locals {
model_mat: [[f32; 4]; 4] = "model_mat", nul: [f32; 4] = "nul",
} }
pipeline pipe { pipeline pipe {
@ -36,7 +36,13 @@ gfx_defines! {
locals: gfx::ConstantBuffer<Locals> = "u_locals", locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals", globals: gfx::ConstantBuffer<Globals> = "u_globals",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color", tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE, tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::PASS_TEST,
}
}
impl Locals {
pub fn new() -> Self {
Self { nul: [0.0; 4] }
} }
} }

View File

@ -5,16 +5,13 @@ use gfx::{
traits::{Device, FactoryExt}, traits::{Device, FactoryExt},
}; };
// Crate
use crate::VoxygenErr;
// Local // Local
use super::{ use super::{
consts::Consts, consts::Consts,
model::Model, model::Model,
mesh::Mesh, mesh::Mesh,
Pipeline, Pipeline,
RenderErr, RenderError,
gfx_backend, gfx_backend,
pipelines::{ pipelines::{
Globals, Globals,
@ -23,12 +20,19 @@ use super::{
}, },
}; };
/// Represents the format of the window's color target.
pub type TgtColorFmt = gfx::format::Srgba8; pub type TgtColorFmt = gfx::format::Srgba8;
/// Represents the format of the window's depth target.
pub type TgtDepthFmt = gfx::format::DepthStencil; pub type TgtDepthFmt = gfx::format::DepthStencil;
/// A handle to a window color target.
pub type TgtColorView = gfx::handle::RenderTargetView<gfx_backend::Resources, TgtColorFmt>; pub type TgtColorView = gfx::handle::RenderTargetView<gfx_backend::Resources, TgtColorFmt>;
/// A handle to a window depth target.
pub type TgtDepthView = gfx::handle::DepthStencilView<gfx_backend::Resources, TgtDepthFmt>; pub type TgtDepthView = gfx::handle::DepthStencilView<gfx_backend::Resources, TgtDepthFmt>;
/// A type that encapsulates rendering state. `Renderer` is central to Voxygen's rendering
/// subsystem and contains any state necessary to interact with the GPU, along with pipeline state
/// objects (PSOs) needed to renderer different kinds of model to the screen.
pub struct Renderer { pub struct Renderer {
device: gfx_backend::Device, device: gfx_backend::Device,
encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
@ -38,18 +42,19 @@ pub struct Renderer {
tgt_depth_view: TgtDepthView, tgt_depth_view: TgtDepthView,
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>, skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
//character_pipeline: GfxPipeline<character::pipe::Init<'static>>, character_pipeline: GfxPipeline<character::pipe::Init<'static>>,
} }
impl Renderer { impl Renderer {
/// Create a new `Renderer` from a variety of backend-specific components and the window targets.
pub fn new( pub fn new(
device: gfx_backend::Device, device: gfx_backend::Device,
mut factory: gfx_backend::Factory, mut factory: gfx_backend::Factory,
tgt_color_view: TgtColorView, tgt_color_view: TgtColorView,
tgt_depth_view: TgtDepthView, tgt_depth_view: TgtDepthView,
) -> Result<Self, RenderErr> { ) -> Result<Self, RenderError> {
// Construct a pipeline for rendering skyboxes // Construct a pipeline for rendering skyboxes
let skybox_pipeline = Self::create_pipeline( let skybox_pipeline = create_pipeline(
&mut factory, &mut factory,
skybox::pipe::new(), skybox::pipe::new(),
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.vert")), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.vert")),
@ -57,14 +62,12 @@ impl Renderer {
)?; )?;
// Construct a pipeline for rendering characters // Construct a pipeline for rendering characters
/* let character_pipeline = create_pipeline(
let character_pipeline = Self::new_pipeline(
&mut factory, &mut factory,
character::pipe::new(), character::pipe::new(),
include_bytes!("shaders/character.vert"), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.vert")),
include_bytes!("shaders/character.frag"), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.frag")),
)?; )?;
*/
Ok(Self { Ok(Self {
device, device,
@ -75,57 +78,36 @@ impl Renderer {
tgt_depth_view, tgt_depth_view,
skybox_pipeline, skybox_pipeline,
//character_pipeline, character_pipeline,
}) })
} }
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
pub fn clear(&mut self, col: Rgba<f32>) { pub fn clear(&mut self, col: Rgba<f32>) {
self.encoder.clear(&self.tgt_color_view, col.into_array()); self.encoder.clear(&self.tgt_color_view, col.into_array());
self.encoder.clear_depth(&self.tgt_depth_view, 1.0); self.encoder.clear_depth(&self.tgt_depth_view, 1.0);
} }
/// Perform all queued draw calls for this frame and clean up discarded items /// Perform all queued draw calls for this frame and clean up discarded items.
pub fn flush(&mut self) { pub fn flush(&mut self) {
self.encoder.flush(&mut self.device); self.encoder.flush(&mut self.device);
self.device.cleanup(); self.device.cleanup();
} }
/// Create a new pipeline from the provided vertex shader and fragment shader /// Create a new set of constants.
fn create_pipeline<'a, P: gfx::pso::PipelineInit>( pub fn create_consts<T: Copy + gfx::traits::Pod>(&mut self) -> Result<Consts<T>, RenderError> {
factory: &mut gfx_backend::Factory, Ok(Consts::new(&mut self.factory))
pipe: P,
vs: &[u8],
fs: &[u8],
) -> Result<GfxPipeline<P>, RenderErr> {
let program = factory
.link_program(vs, fs)
.map_err(|err| RenderErr::PipelineErr(gfx::PipelineStateError::Program(err)))?;
Ok(GfxPipeline {
pso: factory.create_pipeline_from_program(
&program,
gfx::Primitive::TriangleList,
gfx::state::Rasterizer {
front_face: gfx::state::FrontFace::CounterClockwise,
cull_face: gfx::state::CullFace::Back,
method: gfx::state::RasterMethod::Fill,
offset: None,
samples: Some(gfx::state::MultiSample),
},
pipe,
)
// Do some funky things to work around an oddity in gfx's error ownership rules
.map_err(|err| RenderErr::PipelineErr(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),
}))?,
program,
})
} }
/// Create a new model from the provided mesh /// Create a new set of constants and update then with a value.
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderErr> { 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)
}
/// 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( Ok(Model::new(
&mut self.factory, &mut self.factory,
mesh, mesh,
@ -153,7 +135,41 @@ impl Renderer {
} }
} }
pub struct GfxPipeline<P: gfx::pso::PipelineInit> { struct GfxPipeline<P: gfx::pso::PipelineInit> {
program: gfx::handle::Program<gfx_backend::Resources>, program: gfx::handle::Program<gfx_backend::Resources>,
pso: gfx::pso::PipelineState<gfx_backend::Resources, P::Meta>, pso: gfx::pso::PipelineState<gfx_backend::Resources, P::Meta>,
} }
/// Create a new pipeline from the provided vertex shader and fragment shader
fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
factory: &mut gfx_backend::Factory,
pipe: P,
vs: &[u8],
fs: &[u8],
) -> Result<GfxPipeline<P>, RenderError> {
let program = factory
.link_program(vs, fs)
.map_err(|err| RenderError::PipelineError(gfx::PipelineStateError::Program(err)))?;
Ok(GfxPipeline {
pso: factory.create_pipeline_from_program(
&program,
gfx::Primitive::TriangleList,
gfx::state::Rasterizer {
front_face: gfx::state::FrontFace::CounterClockwise,
cull_face: gfx::state::CullFace::Back,
method: gfx::state::RasterMethod::Fill,
offset: None,
samples: Some(gfx::state::MultiSample),
},
pipe,
)
// 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),
}))?,
program,
})
}

View File

@ -0,0 +1,49 @@
// Standard
use std::f32::consts::PI;
// Library
use vek::*;
const NEAR_PLANE: f32 = 0.1;
const FAR_PLANE: f32 = 10000.0;
pub struct Camera {
focus: Vec3<f32>,
ori: Vec3<f32>,
dist: f32,
fov: f32,
aspect: f32,
}
impl Camera {
/// Create a new `Camera` with default parameters.
pub fn new() -> Self {
Self {
focus: Vec3::zero(),
ori: Vec3::zero(),
dist: 10.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()
* Mat4::translation_3d(-Vec3::unit_z() * self.dist)
* Mat4::rotation_z(self.ori.z)
* Mat4::rotation_x(self.ori.y)
* Mat4::rotation_y(self.ori.x)
* Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x())
* Mat4::translation_3d(-self.focus);
let proj = Mat4::perspective_rh_no(
self.fov,
self.aspect,
NEAR_PLANE,
FAR_PLANE,
);
(view, proj)
}
}

56
voxygen/src/scene/mod.rs Normal file
View File

@ -0,0 +1,56 @@
pub mod camera;
// Crate
use crate::render::{
Consts,
Globals,
Model,
Renderer,
SkyboxPipeline,
SkyboxLocals,
create_skybox_mesh,
};
// Local
use self::camera::Camera;
struct Skybox {
model: Model<SkyboxPipeline>,
locals: Consts<SkyboxLocals>,
}
pub struct Scene {
camera: Camera,
globals: Consts<Globals>,
skybox: Skybox,
}
impl Scene {
/// Create a new `Scene` with default parameters.
pub fn new(renderer: &mut Renderer) -> Self {
Self {
camera: Camera::new(),
globals: renderer
.create_consts_with(Globals::new())
.unwrap(),
skybox: Skybox {
model: renderer
.create_model(&create_skybox_mesh())
.unwrap(),
locals: renderer
.create_consts_with(SkyboxLocals::new())
.unwrap(),
},
}
}
/// 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)
renderer.render_skybox(
&self.skybox.model,
&self.skybox.locals,
&self.globals,
);
}
}

61
voxygen/src/session.rs Normal file
View File

@ -0,0 +1,61 @@
// Library
use vek::*;
// Crate
use crate::{
PlayState,
PlayStateResult,
GlobalState,
window::Event,
render::Renderer,
scene::Scene,
};
pub struct SessionState {
scene: Scene,
}
/// Represents an active game session (i.e: one that is being played)
impl SessionState {
/// Create a new `SessionState`
pub fn from_renderer(renderer: &mut Renderer) -> Self {
Self {
// Create a scene for this session. The scene handles visible elements of the game world
scene: Scene::new(renderer),
}
}
}
// The background colour
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 {
// Game loop
loop {
// Handle window events
for event in global_state.window.fetch_events() {
match event {
Event::Close => return PlayStateResult::Shutdown,
// When 'q' is pressed, exit the session
Event::Char('q') => return PlayStateResult::Pop,
// Ignore all other events
_ => {},
}
}
// Clear the screen
global_state.window.renderer_mut().clear(BG_COLOR);
// Render the screen using the global renderer
self.scene.render_to(global_state.window.renderer_mut());
// Finish the frame
global_state.window.renderer_mut().flush();
global_state.window.display()
.expect("Failed to display window");
}
}
fn name(&self) -> &'static str { "Session" }
}

View File

@ -4,7 +4,7 @@ use gfx_window_glutin;
// Crate // Crate
use crate::{ use crate::{
VoxygenErr, Error,
render::{ render::{
Renderer, Renderer,
TgtColorFmt, TgtColorFmt,
@ -20,14 +20,13 @@ pub struct Window {
impl Window { impl Window {
pub fn new() -> Result<Window, VoxygenErr> { pub fn new() -> Result<Window, Error> {
let events_loop = glutin::EventsLoop::new(); let events_loop = glutin::EventsLoop::new();
let win_builder = glutin::WindowBuilder::new() let win_builder = glutin::WindowBuilder::new()
.with_title("Veloren (Voxygen)") .with_title("Veloren (Voxygen)")
.with_dimensions(glutin::dpi::LogicalSize::new(800.0, 500.0)) .with_dimensions(glutin::dpi::LogicalSize::new(800.0, 500.0))
.with_maximized(false) .with_maximized(false);
;
let ctx_builder = glutin::ContextBuilder::new() let ctx_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2)))
@ -43,7 +42,7 @@ impl Window {
win_builder, win_builder,
ctx_builder, ctx_builder,
&events_loop, &events_loop,
).map_err(|err| VoxygenErr::BackendErr(Box::new(err)))?; ).map_err(|err| Error::BackendError(Box::new(err)))?;
let tmp = Ok(Self { let tmp = Ok(Self {
events_loop, events_loop,
@ -66,6 +65,7 @@ impl Window {
self.events_loop.poll_events(|event| match event { self.events_loop.poll_events(|event| match event {
glutin::Event::WindowEvent { event, .. } => match event { glutin::Event::WindowEvent { event, .. } => match event {
glutin::WindowEvent::CloseRequested => events.push(Event::Close), glutin::WindowEvent::CloseRequested => events.push(Event::Close),
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
_ => {}, _ => {},
}, },
_ => {}, _ => {},
@ -73,12 +73,13 @@ impl Window {
events events
} }
pub fn display(&self) -> Result<(), VoxygenErr> { pub fn display(&self) -> Result<(), Error> {
self.window.swap_buffers() self.window.swap_buffers()
.map_err(|err| VoxygenErr::BackendErr(Box::new(err))) .map_err(|err| Error::BackendError(Box::new(err)))
} }
} }
pub enum Event { pub enum Event {
Close, Close,
Char(char),
} }