mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added session, documented, improved structure
This commit is contained in:
parent
d559d633ab
commit
c9d877de7a
@ -25,3 +25,5 @@ vek = "0.9"
|
||||
glsl-include = "0.2"
|
||||
failure = "0.1"
|
||||
lazy_static = "1.1"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.3"
|
||||
|
25
voxygen/shaders/character.frag
Normal file
25
voxygen/shaders/character.frag
Normal 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);
|
||||
}
|
32
voxygen/shaders/character.vert
Normal file
32
voxygen/shaders/character.vert
Normal 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);
|
||||
}
|
@ -4,7 +4,7 @@ in vec3 f_pos;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
vec4 nul;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
@ -21,5 +21,5 @@ uniform u_globals {
|
||||
out vec4 tgt_color;
|
||||
|
||||
void main() {
|
||||
tgt_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
tgt_color = vec4(f_pos, 1.0);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ in vec3 v_pos;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
mat4 model_mat;
|
||||
vec4 nul;
|
||||
};
|
||||
|
||||
layout (std140)
|
||||
@ -26,5 +26,6 @@ void main() {
|
||||
gl_Position =
|
||||
proj_mat *
|
||||
view_mat *
|
||||
vec4(3000 * v_pos + cam_pos.xyz, 1);
|
||||
vec4(v_pos + cam_pos.xyz, 1);
|
||||
gl_Position.z = 0.0;
|
||||
}
|
||||
|
@ -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
22
voxygen/src/error.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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",
|
||||
}
|
||||
}
|
@ -1,37 +1,28 @@
|
||||
mod menu;
|
||||
mod render;
|
||||
mod window;
|
||||
pub mod error;
|
||||
pub mod menu;
|
||||
pub mod render;
|
||||
pub mod scene;
|
||||
pub mod session;
|
||||
pub mod window;
|
||||
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
|
||||
// Standard
|
||||
use std::{
|
||||
any,
|
||||
mem,
|
||||
};
|
||||
use std::mem;
|
||||
|
||||
// Library
|
||||
use glutin;
|
||||
use failure;
|
||||
use log;
|
||||
use pretty_env_logger;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
menu::title::TitleState,
|
||||
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
|
||||
pub struct GlobalState {
|
||||
window: Window,
|
||||
@ -42,40 +33,73 @@ pub struct GlobalState {
|
||||
pub enum PlayStateResult {
|
||||
/// Pop all play states in reverse order and shut down the program
|
||||
Shutdown,
|
||||
/// Close the current play state
|
||||
Close,
|
||||
/// Close the current play state and pop it from the play state stack
|
||||
Pop,
|
||||
/// Push a new play state onto the play state stack
|
||||
Push(Box<dyn PlayState>),
|
||||
/// Switch the current play state with a new play state
|
||||
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 {
|
||||
/// 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;
|
||||
|
||||
/// Get a descriptive name for this state type
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
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 {
|
||||
window: Window::new()
|
||||
.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)){
|
||||
// Implement state transfer logic
|
||||
match state_result {
|
||||
PlayStateResult::Shutdown => while states.last().is_some() {
|
||||
states.pop();
|
||||
PlayStateResult::Shutdown => {
|
||||
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 => {
|
||||
states.pop();
|
||||
PlayStateResult::Pop => {
|
||||
states.pop().map(|old_state| {
|
||||
log::info!("Popped state '{}'", old_state.name())
|
||||
});
|
||||
},
|
||||
PlayStateResult::Push(new_state) => {
|
||||
log::info!("Pushed state '{}'", new_state.name());
|
||||
states.push(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);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -7,32 +7,47 @@ use crate::{
|
||||
PlayStateResult,
|
||||
GlobalState,
|
||||
window::Event,
|
||||
render,
|
||||
session::SessionState,
|
||||
};
|
||||
|
||||
pub struct TitleState;
|
||||
|
||||
impl TitleState {
|
||||
/// Create a new `TitleState`
|
||||
pub fn new() -> 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 {
|
||||
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
|
||||
'eventloop: loop {
|
||||
// Process window events
|
||||
loop {
|
||||
// Handle window events
|
||||
for event in global_state.window.fetch_events() {
|
||||
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);
|
||||
|
||||
// Finish the frame
|
||||
global_state.window.renderer_mut().flush();
|
||||
global_state.window.display()
|
||||
.expect("Failed to display window");
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str { "Title" }
|
||||
}
|
||||
|
@ -6,28 +6,32 @@ use gfx::{
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
RenderErr,
|
||||
RenderError,
|
||||
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)]
|
||||
pub struct Consts<T: Copy + gfx::traits::Pod> {
|
||||
pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>,
|
||||
}
|
||||
|
||||
impl<T: Copy + gfx::traits::Pod> Consts<T> {
|
||||
/// Create a new `Const<T>`
|
||||
pub fn new(factory: &mut gfx_backend::Factory) -> Self {
|
||||
Self {
|
||||
buf: factory.create_constant_buffer(1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the GPU-side value represented by this constant handle.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
data: T,
|
||||
) -> Result<(), RenderErr> {
|
||||
encoder.update_buffer(&self.buf, &[data], 0)
|
||||
.map_err(|err| RenderErr::UpdateErr(err))
|
||||
val: T,
|
||||
) -> Result<(), RenderError> {
|
||||
encoder.update_buffer(&self.buf, &[val], 0)
|
||||
.map_err(|err| RenderError::UpdateError(err))
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,38 @@
|
||||
// Local
|
||||
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> {
|
||||
verts: Vec<P::Vertex>,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> Mesh<P> {
|
||||
/// Create a new `Mesh`
|
||||
pub fn new() -> Self {
|
||||
Self { verts: vec![] }
|
||||
}
|
||||
|
||||
/// Get a slice referencing the vertices of this mesh.
|
||||
pub fn vertices(&self) -> &[P::Vertex] {
|
||||
&self.verts
|
||||
}
|
||||
|
||||
/// Push a new vertex onto the end of this mesh.
|
||||
pub fn push(&mut self, vert: P::Vertex) {
|
||||
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>) {
|
||||
// A quad is composed of two triangles. The code below converts the former to the latter.
|
||||
|
||||
// Tri 1
|
||||
self.verts.push(quad.a.clone());
|
||||
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> {
|
||||
a: P::Vertex,
|
||||
b: P::Vertex,
|
||||
|
@ -1,8 +1,8 @@
|
||||
mod consts;
|
||||
mod mesh;
|
||||
mod model;
|
||||
mod pipelines;
|
||||
mod renderer;
|
||||
pub mod consts;
|
||||
pub mod mesh;
|
||||
pub mod model;
|
||||
pub mod pipelines;
|
||||
pub mod renderer;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
@ -11,8 +11,16 @@ pub use self::{
|
||||
model::Model,
|
||||
renderer::{Renderer, TgtColorFmt, TgtDepthFmt},
|
||||
pipelines::{
|
||||
character::CharacterPipeline,
|
||||
skybox::SkyboxPipeline,
|
||||
Globals,
|
||||
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
|
||||
#[derive(Debug)]
|
||||
pub enum RenderErr {
|
||||
PipelineErr(gfx::PipelineStateError<String>),
|
||||
UpdateErr(gfx::UpdateError<usize>),
|
||||
pub enum RenderError {
|
||||
PipelineError(gfx::PipelineStateError<String>),
|
||||
UpdateError(gfx::UpdateError<usize>),
|
||||
}
|
||||
|
||||
/// Used to represent a specific rendering configuration
|
||||
|
@ -11,7 +11,7 @@ use super::{
|
||||
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 vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
||||
pub slice: gfx::Slice<gfx_backend::Resources>,
|
||||
|
@ -23,6 +23,7 @@ use super::{
|
||||
gfx_defines! {
|
||||
vertex Vertex {
|
||||
pos: [f32; 3] = "v_pos",
|
||||
col: [f32; 3] = "v_col",
|
||||
bone: u8 = "v_bone",
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,10 @@ use gfx::{
|
||||
self,
|
||||
// Macros
|
||||
gfx_defines,
|
||||
gfx_vertex_struct_meta,
|
||||
gfx_constant_struct_meta,
|
||||
gfx_impl_struct_meta,
|
||||
gfx_pipeline,
|
||||
gfx_pipeline_inner,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
gfx_defines! {
|
||||
constant Globals {
|
||||
@ -24,3 +22,28 @@ gfx_defines! {
|
||||
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],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ gfx_defines! {
|
||||
}
|
||||
|
||||
constant Locals {
|
||||
model_mat: [[f32; 4]; 4] = "model_mat",
|
||||
nul: [f32; 4] = "nul",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
@ -36,7 +36,13 @@ gfx_defines! {
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
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] }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,16 +5,13 @@ use gfx::{
|
||||
traits::{Device, FactoryExt},
|
||||
};
|
||||
|
||||
// Crate
|
||||
use crate::VoxygenErr;
|
||||
|
||||
// Local
|
||||
use super::{
|
||||
consts::Consts,
|
||||
model::Model,
|
||||
mesh::Mesh,
|
||||
Pipeline,
|
||||
RenderErr,
|
||||
RenderError,
|
||||
gfx_backend,
|
||||
pipelines::{
|
||||
Globals,
|
||||
@ -23,12 +20,19 @@ use super::{
|
||||
},
|
||||
};
|
||||
|
||||
/// Represents the format of the window's color target.
|
||||
pub type TgtColorFmt = gfx::format::Srgba8;
|
||||
/// Represents the format of the window's depth target.
|
||||
pub type TgtDepthFmt = gfx::format::DepthStencil;
|
||||
|
||||
/// A handle to a window color target.
|
||||
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>;
|
||||
|
||||
/// 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 {
|
||||
device: gfx_backend::Device,
|
||||
encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
@ -38,18 +42,19 @@ pub struct Renderer {
|
||||
tgt_depth_view: TgtDepthView,
|
||||
|
||||
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
|
||||
//character_pipeline: GfxPipeline<character::pipe::Init<'static>>,
|
||||
character_pipeline: GfxPipeline<character::pipe::Init<'static>>,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
/// 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,
|
||||
tgt_color_view: TgtColorView,
|
||||
tgt_depth_view: TgtDepthView,
|
||||
) -> Result<Self, RenderErr> {
|
||||
) -> Result<Self, RenderError> {
|
||||
// Construct a pipeline for rendering skyboxes
|
||||
let skybox_pipeline = Self::create_pipeline(
|
||||
let skybox_pipeline = create_pipeline(
|
||||
&mut factory,
|
||||
skybox::pipe::new(),
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.vert")),
|
||||
@ -57,14 +62,12 @@ impl Renderer {
|
||||
)?;
|
||||
|
||||
// Construct a pipeline for rendering characters
|
||||
/*
|
||||
let character_pipeline = Self::new_pipeline(
|
||||
let character_pipeline = create_pipeline(
|
||||
&mut factory,
|
||||
character::pipe::new(),
|
||||
include_bytes!("shaders/character.vert"),
|
||||
include_bytes!("shaders/character.frag"),
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.vert")),
|
||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.frag")),
|
||||
)?;
|
||||
*/
|
||||
|
||||
Ok(Self {
|
||||
device,
|
||||
@ -75,57 +78,36 @@ impl Renderer {
|
||||
tgt_depth_view,
|
||||
|
||||
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>) {
|
||||
self.encoder.clear(&self.tgt_color_view, col.into_array());
|
||||
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) {
|
||||
self.encoder.flush(&mut self.device);
|
||||
self.device.cleanup();
|
||||
}
|
||||
|
||||
/// 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>, 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 set of constants.
|
||||
pub fn create_consts<T: Copy + gfx::traits::Pod>(&mut self) -> Result<Consts<T>, RenderError> {
|
||||
Ok(Consts::new(&mut self.factory))
|
||||
}
|
||||
|
||||
/// Create a new model from the provided mesh
|
||||
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderErr> {
|
||||
/// 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> {
|
||||
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(
|
||||
&mut self.factory,
|
||||
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>,
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
49
voxygen/src/scene/camera.rs
Normal file
49
voxygen/src/scene/camera.rs
Normal 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
56
voxygen/src/scene/mod.rs
Normal 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
61
voxygen/src/session.rs
Normal 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" }
|
||||
}
|
@ -4,7 +4,7 @@ use gfx_window_glutin;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
VoxygenErr,
|
||||
Error,
|
||||
render::{
|
||||
Renderer,
|
||||
TgtColorFmt,
|
||||
@ -20,14 +20,13 @@ pub struct Window {
|
||||
|
||||
|
||||
impl Window {
|
||||
pub fn new() -> Result<Window, VoxygenErr> {
|
||||
pub fn new() -> Result<Window, Error> {
|
||||
let events_loop = glutin::EventsLoop::new();
|
||||
|
||||
let win_builder = glutin::WindowBuilder::new()
|
||||
.with_title("Veloren (Voxygen)")
|
||||
.with_dimensions(glutin::dpi::LogicalSize::new(800.0, 500.0))
|
||||
.with_maximized(false)
|
||||
;
|
||||
.with_maximized(false);
|
||||
|
||||
let ctx_builder = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2)))
|
||||
@ -43,7 +42,7 @@ impl Window {
|
||||
win_builder,
|
||||
ctx_builder,
|
||||
&events_loop,
|
||||
).map_err(|err| VoxygenErr::BackendErr(Box::new(err)))?;
|
||||
).map_err(|err| Error::BackendError(Box::new(err)))?;
|
||||
|
||||
let tmp = Ok(Self {
|
||||
events_loop,
|
||||
@ -66,6 +65,7 @@ impl Window {
|
||||
self.events_loop.poll_events(|event| match event {
|
||||
glutin::Event::WindowEvent { event, .. } => match event {
|
||||
glutin::WindowEvent::CloseRequested => events.push(Event::Close),
|
||||
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
@ -73,12 +73,13 @@ impl Window {
|
||||
events
|
||||
}
|
||||
|
||||
pub fn display(&self) -> Result<(), VoxygenErr> {
|
||||
pub fn display(&self) -> Result<(), Error> {
|
||||
self.window.swap_buffers()
|
||||
.map_err(|err| VoxygenErr::BackendErr(Box::new(err)))
|
||||
.map_err(|err| Error::BackendError(Box::new(err)))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
Char(char),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user