diff --git a/voxygen/shaders/skybox.frag b/voxygen/shaders/skybox.frag new file mode 100644 index 0000000000..198b7a3f0e --- /dev/null +++ b/voxygen/shaders/skybox.frag @@ -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(1.0, 0.0, 0.0, 1.0); +} diff --git a/voxygen/shaders/skybox.vert b/voxygen/shaders/skybox.vert new file mode 100644 index 0000000000..a3e63adce3 --- /dev/null +++ b/voxygen/shaders/skybox.vert @@ -0,0 +1,30 @@ +#version 330 core + +in vec3 v_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 vec3 f_pos; + +void main() { + f_pos = v_pos; + + gl_Position = + proj_mat * + view_mat * + vec4(3000 * v_pos + cam_pos.xyz, 1); +} diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 60ae4fe4d3..adbeed6198 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -16,14 +16,22 @@ use failure; use crate::{ menu::title::TitleState, window::Window, + render::RenderErr, }; #[derive(Debug)] pub enum VoxygenErr { BackendErr(Box), + RenderErr(RenderErr), Other(failure::Error), } +impl From 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, diff --git a/voxygen/src/menu/title.rs b/voxygen/src/menu/title.rs index adbe185975..883f164693 100644 --- a/voxygen/src/menu/title.rs +++ b/voxygen/src/menu/title.rs @@ -17,6 +17,8 @@ impl TitleState { } } +const BG_COLOR: Rgba = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 }; + impl PlayState for TitleState { fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { 'eventloop: loop { @@ -27,12 +29,7 @@ impl PlayState for TitleState { } } - global_state.window.renderer_mut().clear(Rgba::new( - 0.0, - 0.3, - 1.0, - 1.0, - )); + global_state.window.renderer_mut().clear(BG_COLOR); global_state.window.renderer_mut().flush(); global_state.window.display() .expect("Failed to display window"); diff --git a/voxygen/src/render/consts.rs b/voxygen/src/render/consts.rs new file mode 100644 index 0000000000..b73f6859e3 --- /dev/null +++ b/voxygen/src/render/consts.rs @@ -0,0 +1,33 @@ +// Library +use gfx::{ + self, + traits::FactoryExt, +}; + +// Local +use super::{ + RenderErr, + gfx_backend, +}; + +#[derive(Clone)] +pub struct Consts { + pub buf: gfx::handle::Buffer, +} + +impl Consts { + pub fn new(factory: &mut gfx_backend::Factory) -> Self { + Self { + buf: factory.create_constant_buffer(1), + } + } + + pub fn update( + &mut self, + encoder: &mut gfx::Encoder, + data: T, + ) -> Result<(), RenderErr> { + encoder.update_buffer(&self.buf, &[data], 0) + .map_err(|err| RenderErr::UpdateErr(err)) + } +} diff --git a/voxygen/src/render/mesh.rs b/voxygen/src/render/mesh.rs index 352e1e92df..83f05037ad 100644 --- a/voxygen/src/render/mesh.rs +++ b/voxygen/src/render/mesh.rs @@ -7,15 +7,46 @@ pub struct Mesh { } impl Mesh

{ - pub fn empty() -> Self { + pub fn new() -> Self { Self { verts: vec![] } } - pub fn verts(&self) -> &[P::Vertex] { + pub fn vertices(&self) -> &[P::Vertex] { &self.verts } pub fn push(&mut self, vert: P::Vertex) { self.verts.push(vert); } + + pub fn push_quad(&mut self, quad: Quad

) { + // Tri 1 + self.verts.push(quad.a.clone()); + self.verts.push(quad.b); + self.verts.push(quad.c.clone()); + + // Tri 2 + self.verts.push(quad.c); + self.verts.push(quad.d); + self.verts.push(quad.a); + } +} + +/// Represents a quad +pub struct Quad { + a: P::Vertex, + b: P::Vertex, + c: P::Vertex, + d: P::Vertex, +} + +impl Quad

{ + pub fn new( + a: P::Vertex, + b: P::Vertex, + c: P::Vertex, + d: P::Vertex, + ) -> Self { + Self { a, b, c, d } + } } diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index 0e4c0badf1..ddac823f31 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -1,25 +1,38 @@ +mod consts; mod mesh; mod model; -mod renderer; mod pipelines; -mod shader_set; +mod renderer; // Reexports pub use self::{ - mesh::Mesh, + consts::Consts, + mesh::{Mesh, Quad}, model::Model, - shader_set::ShaderSet, renderer::{Renderer, TgtColorFmt, TgtDepthFmt}, + pipelines::{ + character::CharacterPipeline, + skybox::SkyboxPipeline, + }, }; #[cfg(feature = "gl")] use gfx_device_gl as gfx_backend; +// Library +use gfx; + /// Used to represent one of many possible errors that may be omitted by the rendering code #[derive(Debug)] -pub enum RenderErr {} +pub enum RenderErr { + PipelineErr(gfx::PipelineStateError), + UpdateErr(gfx::UpdateError), +} /// Used to represent a specific rendering configuration pub trait Pipeline { - type Vertex; + type Vertex: + Clone + + gfx::traits::Pod + + gfx::pso::buffer::Structure; } diff --git a/voxygen/src/render/model.rs b/voxygen/src/render/model.rs index b7aaf5d6f7..34394ea561 100644 --- a/voxygen/src/render/model.rs +++ b/voxygen/src/render/model.rs @@ -1,18 +1,36 @@ -// Standard -use std::marker::PhantomData; +// Library +use gfx::{ + self, + traits::FactoryExt, +}; // Local -use super::Pipeline; +use super::{ + mesh::Mesh, + Pipeline, + gfx_backend, +}; /// Represents a mesh that has been sent to the CPU pub struct Model { - phantom: PhantomData

, + pub vbuf: gfx::handle::Buffer, + pub slice: gfx::Slice, } impl Model

{ - pub fn new() -> Self { + pub fn new( + factory: &mut gfx_backend::Factory, + mesh: &Mesh

, + ) -> Self { Self { - phantom: PhantomData, + vbuf: factory.create_vertex_buffer(mesh.vertices()), + slice: gfx::Slice { + start: 0, + end: mesh.vertices().len() as u32, + base_vertex: 0, + instances: None, + buffer: gfx::IndexBuffer::Auto, + }, } } } diff --git a/voxygen/src/render/pipelines/character.rs b/voxygen/src/render/pipelines/character.rs index 72aa4d19a4..48c4edb3f5 100644 --- a/voxygen/src/render/pipelines/character.rs +++ b/voxygen/src/render/pipelines/character.rs @@ -1,11 +1,6 @@ // Library use gfx::{ self, - VertexBuffer, - ConstantBuffer, - RenderTarget, - DepthTarget, - preset::depth::LESS_EQUAL_WRITE, // Macros gfx_defines, gfx_vertex_struct_meta, @@ -16,8 +11,13 @@ use gfx::{ }; // Local -use super::super::{ - renderer::{TgtColorFmt, TgtDepthFmt}, +use super::{ + Globals, + super::{ + Pipeline, + TgtColorFmt, + TgtDepthFmt, + }, }; gfx_defines! { @@ -27,13 +27,20 @@ gfx_defines! { } constant Locals { - model: [[f32; 4]; 4] = "u_model", + model_mat: [[f32; 4]; 4] = "model_mat", } pipeline pipe { - vbuf: VertexBuffer = (), - locals: ConstantBuffer = "locals", - tgt_color: RenderTarget = "tgt", - tgt_depth: DepthTarget = LESS_EQUAL_WRITE, + vbuf: gfx::VertexBuffer = (), + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, } } + +pub struct CharacterPipeline; + +impl Pipeline for CharacterPipeline { + type Vertex = Vertex; +} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 771e7198e1..6cf5a9f803 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1 +1,26 @@ -mod character; +pub mod character; +pub mod skybox; + +// Library +use gfx::{ + self, + // Macros + gfx_defines, + gfx_vertex_struct_meta, + gfx_constant_struct_meta, + gfx_impl_struct_meta, + gfx_pipeline, + gfx_pipeline_inner, +}; + +gfx_defines! { + constant Globals { + view_mat: [[f32; 4]; 4] = "view_mat", + proj_mat: [[f32; 4]; 4] = "proj_mat", + cam_pos: [f32; 4] = "cam_pos", + focus_pos: [f32; 4] = "focus_pos", + view_distance: [f32; 4] = "view_distance", + tod: [f32; 4] = "tod", + time: [f32; 4] = "time", + } +} diff --git a/voxygen/src/render/pipelines/skybox.rs b/voxygen/src/render/pipelines/skybox.rs new file mode 100644 index 0000000000..888595316f --- /dev/null +++ b/voxygen/src/render/pipelines/skybox.rs @@ -0,0 +1,102 @@ +// Library +use gfx::{ + self, + // Macros + gfx_defines, + gfx_vertex_struct_meta, + gfx_constant_struct_meta, + gfx_impl_struct_meta, + gfx_pipeline, + gfx_pipeline_inner, +}; + +// Local +use super::{ + Globals, + super::{ + Pipeline, + TgtColorFmt, + TgtDepthFmt, + Mesh, + Quad, + }, +}; + +gfx_defines! { + vertex Vertex { + pos: [f32; 3] = "v_pos", + } + + constant Locals { + model_mat: [[f32; 4]; 4] = "model_mat", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + locals: gfx::ConstantBuffer = "u_locals", + globals: gfx::ConstantBuffer = "u_globals", + tgt_color: gfx::RenderTarget = "tgt_color", + tgt_depth: gfx::DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE, + } +} + +pub struct SkyboxPipeline; + +impl Pipeline for SkyboxPipeline { + type Vertex = Vertex; +} + +pub fn create_mesh() -> Mesh { + let mut mesh = Mesh::new(); + + // -x + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [-1.0, -1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0, 1.0] }, + Vertex { pos: [-1.0, -1.0, 1.0] }, + )); + // +x + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [ 1.0, -1.0, -1.0] }, + Vertex { pos: [ 1.0, 1.0, 1.0] }, + Vertex { pos: [ 1.0, 1.0, -1.0] }, + Vertex { pos: [ 1.0, -1.0, 1.0] }, + )); + // -y + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [ 1.0, -1.0, -1.0] }, + Vertex { pos: [-1.0, -1.0, -1.0] }, + Vertex { pos: [-1.0, -1.0, 1.0] }, + Vertex { pos: [ 1.0, -1.0, 1.0] }, + )); + // +y + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [ 1.0, 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0, 1.0] }, + Vertex { pos: [-1.0, 1.0, -1.0] }, + Vertex { pos: [ 1.0, 1.0, 1.0] }, + )); + // -z + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [-1.0, -1.0, -1.0] }, + Vertex { pos: [ 1.0, -1.0, -1.0] }, + Vertex { pos: [ 1.0, 1.0, -1.0] }, + Vertex { pos: [-1.0, 1.0, -1.0] }, + )); + // +z + #[rustfmt::skip] + mesh.push_quad(Quad::new( + Vertex { pos: [-1.0, -1.0, 1.0] }, + Vertex { pos: [ 1.0, 1.0, 1.0] }, + Vertex { pos: [ 1.0, -1.0, 1.0] }, + Vertex { pos: [-1.0, 1.0, 1.0] }, + )); + + mesh +} diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index d3da83f0c3..3e0111a911 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -2,7 +2,7 @@ use vek::*; use gfx::{ self, - traits::Device, + traits::{Device, FactoryExt}, }; // Crate @@ -10,12 +10,17 @@ use crate::VoxygenErr; // Local use super::{ + consts::Consts, model::Model, mesh::Mesh, - shader_set::ShaderSet, Pipeline, RenderErr, gfx_backend, + pipelines::{ + Globals, + character, + skybox, + }, }; pub type TgtColorFmt = gfx::format::Srgba8; @@ -31,6 +36,9 @@ pub struct Renderer { tgt_color_view: TgtColorView, tgt_depth_view: TgtDepthView, + + skybox_pipeline: GfxPipeline>, + //character_pipeline: GfxPipeline>, } impl Renderer { @@ -39,13 +47,35 @@ impl Renderer { mut factory: gfx_backend::Factory, tgt_color_view: TgtColorView, tgt_depth_view: TgtDepthView, - ) -> Result { + ) -> Result { + // Construct a pipeline for rendering skyboxes + let skybox_pipeline = Self::create_pipeline( + &mut factory, + skybox::pipe::new(), + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.vert")), + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/skybox.frag")), + )?; + + // Construct a pipeline for rendering characters + /* + let character_pipeline = Self::new_pipeline( + &mut factory, + character::pipe::new(), + include_bytes!("shaders/character.vert"), + include_bytes!("shaders/character.frag"), + )?; + */ + Ok(Self { device, encoder: factory.create_command_buffer().into(), factory, + tgt_color_view, tgt_depth_view, + + skybox_pipeline, + //character_pipeline, }) } @@ -54,8 +84,76 @@ impl Renderer { self.encoder.clear_depth(&self.tgt_depth_view, 1.0); } + /// 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, 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 + pub fn create_model(&mut self, mesh: &Mesh

) -> Result, RenderErr> { + Ok(Model::new( + &mut self.factory, + mesh, + )) + } + + /// Queue the rendering of the provided skybox model in the upcoming frame + pub fn render_skybox( + &mut self, + model: &Model, + locals: &Consts, + globals: &Consts, + ) { + self.encoder.draw( + &model.slice, + &self.skybox_pipeline.pso, + &skybox::pipe::Data { + vbuf: model.vbuf.clone(), + locals: locals.buf.clone(), + globals: globals.buf.clone(), + tgt_color: self.tgt_color_view.clone(), + tgt_depth: self.tgt_depth_view.clone(), + }, + ); + } +} + +pub struct GfxPipeline { + program: gfx::handle::Program, + pso: gfx::pso::PipelineState, } diff --git a/voxygen/src/render/shader_set.rs b/voxygen/src/render/shader_set.rs deleted file mode 100644 index d1e70d4cc4..0000000000 --- a/voxygen/src/render/shader_set.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Standard -use std::marker::PhantomData; - -// Local -use super::{ - Pipeline, - RenderErr -}; - -pub struct ShaderSet { - phantom: PhantomData

, -} - -impl ShaderSet

{ - pub fn new( - vs: &[u8], - fs: &[u8], - ) -> Result { - Ok(Self { - phantom: PhantomData, - }) - } -}