// Library use vek::*; use gfx::{ self, traits::{Device, FactoryExt}, }; // Local use super::{ consts::Consts, model::Model, mesh::Mesh, Pipeline, RenderError, gfx_backend, pipelines::{ Globals, character, skybox, }, }; /// 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; /// A handle to a window depth target. pub type TgtDepthView = gfx::handle::DepthStencilView; /// 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, factory: gfx_backend::Factory, tgt_color_view: TgtColorView, tgt_depth_view: TgtDepthView, skybox_pipeline: GfxPipeline>, character_pipeline: GfxPipeline>, } 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 { // Construct a pipeline for rendering skyboxes let skybox_pipeline = 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 = create_pipeline( &mut factory, character::pipe::new(), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.vert")), include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/character.frag")), )?; Ok(Self { device, encoder: factory.create_command_buffer().into(), factory, tgt_color_view, tgt_depth_view, skybox_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) { 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. pub fn flush(&mut self) { self.encoder.flush(&mut self.device); self.device.cleanup(); } /// Create a new set of constants. pub fn create_consts(&mut self) -> Result, RenderError> { Ok(Consts::new(&mut self.factory)) } /// Create a new set of constants and update then with a value. pub fn create_consts_with(&mut self, val: T) -> Result, 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(&mut self, mesh: &Mesh

) -> Result, RenderError> { 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(), }, ); } } struct GfxPipeline { program: gfx::handle::Program, pso: gfx::pso::PipelineState, } /// 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, 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, }) }