2019-01-07 21:10:31 +00:00
|
|
|
// Library
|
|
|
|
use vek::*;
|
2019-01-11 17:30:13 +00:00
|
|
|
use gfx::{
|
|
|
|
self,
|
2019-01-11 20:14:37 +00:00
|
|
|
traits::{Device, FactoryExt},
|
2019-01-07 21:10:31 +00:00
|
|
|
};
|
2019-01-30 12:11:34 +00:00
|
|
|
use image;
|
2019-01-07 21:10:31 +00:00
|
|
|
|
|
|
|
// Local
|
|
|
|
use super::{
|
2019-01-11 20:14:37 +00:00
|
|
|
consts::Consts,
|
2019-01-07 21:10:31 +00:00
|
|
|
mesh::Mesh,
|
2019-01-30 12:11:34 +00:00
|
|
|
model::Model,
|
|
|
|
texture::Texture,
|
2019-01-07 21:10:31 +00:00
|
|
|
Pipeline,
|
2019-01-11 23:18:34 +00:00
|
|
|
RenderError,
|
2019-01-11 17:30:13 +00:00
|
|
|
gfx_backend,
|
2019-01-11 20:14:37 +00:00
|
|
|
pipelines::{
|
|
|
|
Globals,
|
2019-01-13 20:53:55 +00:00
|
|
|
figure,
|
2019-01-11 20:14:37 +00:00
|
|
|
skybox,
|
2019-01-14 23:13:58 +00:00
|
|
|
terrain,
|
2019-01-30 12:11:34 +00:00
|
|
|
ui,
|
2019-01-11 20:14:37 +00:00
|
|
|
},
|
2019-01-07 21:10:31 +00:00
|
|
|
};
|
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// Represents the format of the window's color target.
|
2019-01-13 20:53:55 +00:00
|
|
|
pub type TgtColorFmt = gfx::format::Rgba8;
|
2019-01-11 23:18:34 +00:00
|
|
|
/// Represents the format of the window's depth target.
|
2019-01-11 17:30:13 +00:00
|
|
|
pub type TgtDepthFmt = gfx::format::DepthStencil;
|
2019-01-07 21:10:31 +00:00
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// A handle to a window color target.
|
2019-01-11 17:30:13 +00:00
|
|
|
pub type TgtColorView = gfx::handle::RenderTargetView<gfx_backend::Resources, TgtColorFmt>;
|
2019-01-11 23:18:34 +00:00
|
|
|
/// A handle to a window depth target.
|
2019-01-11 17:30:13 +00:00
|
|
|
pub type TgtDepthView = gfx::handle::DepthStencilView<gfx_backend::Resources, TgtDepthFmt>;
|
2019-01-07 21:10:31 +00:00
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// 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
|
2019-01-12 13:56:34 +00:00
|
|
|
/// objects (PSOs) needed to renderer different kinds of models to the screen.
|
2019-01-07 21:10:31 +00:00
|
|
|
pub struct Renderer {
|
2019-01-11 17:30:13 +00:00
|
|
|
device: gfx_backend::Device,
|
|
|
|
encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
|
|
|
factory: gfx_backend::Factory,
|
|
|
|
|
|
|
|
tgt_color_view: TgtColorView,
|
|
|
|
tgt_depth_view: TgtDepthView,
|
2019-01-11 20:14:37 +00:00
|
|
|
|
|
|
|
skybox_pipeline: GfxPipeline<skybox::pipe::Init<'static>>,
|
2019-01-13 20:53:55 +00:00
|
|
|
figure_pipeline: GfxPipeline<figure::pipe::Init<'static>>,
|
2019-01-14 23:13:58 +00:00
|
|
|
terrain_pipeline: GfxPipeline<terrain::pipe::Init<'static>>,
|
2019-01-30 12:11:34 +00:00
|
|
|
ui_pipeline: GfxPipeline<ui::pipe::Init<'static>>,
|
2019-01-07 21:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Renderer {
|
2019-01-12 01:14:58 +00:00
|
|
|
/// Create a new `Renderer` from a variety of backend-specific components and the window
|
|
|
|
/// targets.
|
2019-01-07 21:10:31 +00:00
|
|
|
pub fn new(
|
2019-01-11 17:30:13 +00:00
|
|
|
device: gfx_backend::Device,
|
|
|
|
mut factory: gfx_backend::Factory,
|
|
|
|
tgt_color_view: TgtColorView,
|
|
|
|
tgt_depth_view: TgtDepthView,
|
2019-01-11 23:18:34 +00:00
|
|
|
) -> Result<Self, RenderError> {
|
2019-01-11 20:14:37 +00:00
|
|
|
// Construct a pipeline for rendering skyboxes
|
2019-01-11 23:18:34 +00:00
|
|
|
let skybox_pipeline = create_pipeline(
|
2019-01-11 20:14:37 +00:00
|
|
|
&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")),
|
|
|
|
)?;
|
|
|
|
|
2019-01-13 20:53:55 +00:00
|
|
|
// Construct a pipeline for rendering figures
|
|
|
|
let figure_pipeline = create_pipeline(
|
2019-01-11 20:14:37 +00:00
|
|
|
&mut factory,
|
2019-01-13 20:53:55 +00:00
|
|
|
figure::pipe::new(),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/figure.vert")),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/figure.frag")),
|
2019-01-11 20:14:37 +00:00
|
|
|
)?;
|
|
|
|
|
2019-01-14 23:13:58 +00:00
|
|
|
// Construct a pipeline for rendering terrain
|
|
|
|
let terrain_pipeline = create_pipeline(
|
|
|
|
&mut factory,
|
|
|
|
terrain::pipe::new(),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/terrain.vert")),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/terrain.frag")),
|
|
|
|
)?;
|
|
|
|
|
2019-01-30 12:11:34 +00:00
|
|
|
// Construct a pipeline for rendering UI elements
|
|
|
|
let ui_pipeline = create_pipeline(
|
|
|
|
&mut factory,
|
|
|
|
ui::pipe::new(),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/ui.vert")),
|
|
|
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/ui.frag")),
|
|
|
|
)?;
|
|
|
|
|
2019-01-07 21:10:31 +00:00
|
|
|
Ok(Self {
|
2019-01-11 17:30:13 +00:00
|
|
|
device,
|
|
|
|
encoder: factory.create_command_buffer().into(),
|
2019-01-07 21:10:31 +00:00
|
|
|
factory,
|
2019-01-11 20:14:37 +00:00
|
|
|
|
2019-01-11 17:30:13 +00:00
|
|
|
tgt_color_view,
|
|
|
|
tgt_depth_view,
|
2019-01-11 20:14:37 +00:00
|
|
|
|
|
|
|
skybox_pipeline,
|
2019-01-13 20:53:55 +00:00
|
|
|
figure_pipeline,
|
2019-01-14 23:13:58 +00:00
|
|
|
terrain_pipeline,
|
2019-01-30 12:11:34 +00:00
|
|
|
ui_pipeline,
|
2019-01-07 21:10:31 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-01-23 22:39:31 +00:00
|
|
|
/// Get references to the internal render target views that get displayed directly by the window.
|
|
|
|
pub fn target_views(&self) -> (&TgtColorView, &TgtDepthView) {
|
|
|
|
(&self.tgt_color_view, &self.tgt_depth_view)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get mutable references to the internal render target views that get displayed directly by the window.
|
|
|
|
pub fn target_views_mut(&mut self) -> (&mut TgtColorView, &mut TgtDepthView) {
|
|
|
|
(&mut self.tgt_color_view, &mut self.tgt_depth_view)
|
|
|
|
}
|
|
|
|
|
2019-01-30 12:11:34 +00:00
|
|
|
/// Get the resolution of the render target.
|
|
|
|
pub fn get_resolution(&self) -> Vec2<u16> {
|
|
|
|
Vec2::new(
|
|
|
|
self.tgt_color_view.get_dimensions().0,
|
|
|
|
self.tgt_color_view.get_dimensions().1,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
|
2019-01-12 14:01:01 +00:00
|
|
|
/// TODO: Make a version of this that doesn't clear the colour target for speed
|
2019-01-07 21:10:31 +00:00
|
|
|
pub fn clear(&mut self, col: Rgba<f32>) {
|
2019-01-11 17:30:13 +00:00
|
|
|
self.encoder.clear(&self.tgt_color_view, col.into_array());
|
|
|
|
self.encoder.clear_depth(&self.tgt_depth_view, 1.0);
|
2019-01-07 21:10:31 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// Perform all queued draw calls for this frame and clean up discarded items.
|
2019-01-07 21:10:31 +00:00
|
|
|
pub fn flush(&mut self) {
|
2019-01-11 17:30:13 +00:00
|
|
|
self.encoder.flush(&mut self.device);
|
|
|
|
self.device.cleanup();
|
2019-01-07 21:10:31 +00:00
|
|
|
}
|
2019-01-11 20:14:37 +00:00
|
|
|
|
2019-01-13 20:53:55 +00:00
|
|
|
/// Create a new set of constants with the provided values.
|
|
|
|
pub fn create_consts<T: Copy + gfx::traits::Pod>(
|
2019-01-12 01:14:58 +00:00
|
|
|
&mut self,
|
2019-01-13 20:53:55 +00:00
|
|
|
vals: &[T],
|
2019-01-12 01:14:58 +00:00
|
|
|
) -> Result<Consts<T>, RenderError> {
|
2019-01-13 20:53:55 +00:00
|
|
|
let mut consts = Consts::new(&mut self.factory, vals.len());
|
|
|
|
consts.update(&mut self.encoder, vals)?;
|
2019-01-11 23:18:34 +00:00
|
|
|
Ok(consts)
|
2019-01-11 20:14:37 +00:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:53:55 +00:00
|
|
|
/// Update a set of constants with the provided values.
|
2019-01-12 01:14:58 +00:00
|
|
|
pub fn update_consts<T: Copy + gfx::traits::Pod>(
|
|
|
|
&mut self,
|
|
|
|
consts: &mut Consts<T>,
|
2019-01-13 20:53:55 +00:00
|
|
|
vals: &[T]
|
2019-01-12 01:14:58 +00:00
|
|
|
) -> Result<(), RenderError> {
|
2019-01-13 20:53:55 +00:00
|
|
|
consts.update(&mut self.encoder, vals)
|
2019-01-12 01:14:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
/// Create a new model from the provided mesh.
|
|
|
|
pub fn create_model<P: Pipeline>(&mut self, mesh: &Mesh<P>) -> Result<Model<P>, RenderError> {
|
2019-01-11 20:14:37 +00:00
|
|
|
Ok(Model::new(
|
|
|
|
&mut self.factory,
|
|
|
|
mesh,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2019-01-30 12:11:34 +00:00
|
|
|
/// Create a new texture from the provided image.
|
|
|
|
pub fn create_texture<P: Pipeline>(&mut self, image: &image::DynamicImage) -> Result<Texture<P>, RenderError> {
|
|
|
|
Texture::new(
|
|
|
|
&mut self.factory,
|
|
|
|
image,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-01-12 13:56:34 +00:00
|
|
|
/// Queue the rendering of the provided skybox model in the upcoming frame.
|
2019-01-11 20:14:37 +00:00
|
|
|
pub fn render_skybox(
|
|
|
|
&mut self,
|
|
|
|
model: &Model<skybox::SkyboxPipeline>,
|
|
|
|
globals: &Consts<Globals>,
|
2019-01-13 20:53:55 +00:00
|
|
|
locals: &Consts<skybox::Locals>,
|
2019-01-11 20:14:37 +00:00
|
|
|
) {
|
|
|
|
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(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2019-01-13 20:53:55 +00:00
|
|
|
|
|
|
|
/// Queue the rendering of the provided figure model in the upcoming frame.
|
|
|
|
pub fn render_figure(
|
|
|
|
&mut self,
|
|
|
|
model: &Model<figure::FigurePipeline>,
|
|
|
|
globals: &Consts<Globals>,
|
|
|
|
locals: &Consts<figure::Locals>,
|
|
|
|
bones: &Consts<figure::BoneData>,
|
|
|
|
) {
|
|
|
|
self.encoder.draw(
|
|
|
|
&model.slice,
|
|
|
|
&self.figure_pipeline.pso,
|
|
|
|
&figure::pipe::Data {
|
|
|
|
vbuf: model.vbuf.clone(),
|
|
|
|
locals: locals.buf.clone(),
|
|
|
|
globals: globals.buf.clone(),
|
|
|
|
bones: bones.buf.clone(),
|
|
|
|
tgt_color: self.tgt_color_view.clone(),
|
|
|
|
tgt_depth: self.tgt_depth_view.clone(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2019-01-14 23:13:58 +00:00
|
|
|
|
|
|
|
/// Queue the rendering of the provided terrain chunk model in the upcoming frame.
|
|
|
|
pub fn render_terrain_chunk(
|
|
|
|
&mut self,
|
|
|
|
model: &Model<terrain::TerrainPipeline>,
|
|
|
|
globals: &Consts<Globals>,
|
|
|
|
locals: &Consts<terrain::Locals>,
|
|
|
|
) {
|
|
|
|
self.encoder.draw(
|
|
|
|
&model.slice,
|
|
|
|
&self.terrain_pipeline.pso,
|
|
|
|
&terrain::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(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2019-01-30 12:11:34 +00:00
|
|
|
|
|
|
|
/// Queue the rendering of the provided UI element in the upcoming frame.
|
|
|
|
pub fn render_ui_element(
|
|
|
|
&mut self,
|
|
|
|
model: &Model<ui::UiPipeline>,
|
|
|
|
locals: &Consts<ui::Locals>,
|
|
|
|
tex: &Texture<ui::UiPipeline>,
|
|
|
|
) {
|
|
|
|
self.encoder.draw(
|
|
|
|
&model.slice,
|
|
|
|
&self.ui_pipeline.pso,
|
|
|
|
&ui::pipe::Data {
|
|
|
|
vbuf: model.vbuf.clone(),
|
|
|
|
locals: locals.buf.clone(),
|
|
|
|
tex: (tex.srv.clone(), tex.sampler.clone()),
|
|
|
|
tgt_color: self.tgt_color_view.clone(),
|
|
|
|
tgt_depth: self.tgt_depth_view.clone(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2019-01-11 20:14:37 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
struct GfxPipeline<P: gfx::pso::PipelineInit> {
|
2019-01-11 20:14:37 +00:00
|
|
|
pso: gfx::pso::PipelineState<gfx_backend::Resources, P::Meta>,
|
2019-01-07 21:10:31 +00:00
|
|
|
}
|
2019-01-11 23:18:34 +00:00
|
|
|
|
2019-01-12 13:56:34 +00:00
|
|
|
/// Create a new pipeline from the provided vertex shader and fragment shader.
|
2019-01-11 23:18:34 +00:00
|
|
|
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 {
|
2019-01-12 01:14:58 +00:00
|
|
|
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),
|
2019-01-11 23:18:34 +00:00
|
|
|
}))?,
|
|
|
|
})
|
|
|
|
}
|