use crate::{
palette::Srgb,
pipeline::{PipelineDescBuilder, PipelinesBuilder},
pod::IntoPod,
shape::Shape,
submodules::{DynamicUniform, FlatEnvironmentSub},
types::Backend,
util,
};
use amethyst_core::ecs::{Read, SystemData, World};
use derivative::Derivative;
use glsl_layout::{vec3, AsStd140};
use rendy::{
command::{QueueId, RenderPassEncoder},
factory::Factory,
graph::{
render::{PrepareResult, RenderGroup, RenderGroupDesc},
GraphContext, NodeBuffer, NodeImage,
},
hal::{self, device::Device, pso},
mesh::{AsVertex, Mesh, PosTex},
shader::Shader,
};
#[cfg(feature = "profiler")]
use thread_profiler::profile_scope;
#[derive(Clone, Debug, PartialEq)]
struct SkyboxSettings {
nadir_color: Srgb,
zenith_color: Srgb,
}
impl Default for SkyboxSettings {
fn default() -> Self {
Self {
nadir_color: Srgb::new(0.1, 0.3, 0.35),
zenith_color: Srgb::new(0.75, 1.0, 1.0),
}
}
}
#[derive(Clone, Debug, PartialEq, AsStd140)]
pub(crate) struct SkyboxUniform {
nadir_color: vec3,
zenith_color: vec3,
}
impl SkyboxSettings {
pub(crate) fn uniform(&self) -> <SkyboxUniform as AsStd140>::Std140 {
SkyboxUniform {
nadir_color: self.nadir_color.into_pod(),
zenith_color: self.zenith_color.into_pod(),
}
.std140()
}
}
#[derive(Clone, Debug, PartialEq, Derivative)]
#[derivative(Default(bound = ""))]
pub struct DrawSkyboxDesc {
default_settings: SkyboxSettings,
}
impl DrawSkyboxDesc {
pub fn new() -> Self {
Default::default()
}
pub fn with_colors(nadir_color: Srgb, zenith_color: Srgb) -> Self {
Self {
default_settings: SkyboxSettings {
nadir_color,
zenith_color,
},
}
}
}
impl<B: Backend> RenderGroupDesc<B, World> for DrawSkyboxDesc {
fn build(
self,
_ctx: &GraphContext<B>,
factory: &mut Factory<B>,
queue: QueueId,
_resources: &World,
framebuffer_width: u32,
framebuffer_height: u32,
subpass: hal::pass::Subpass<'_, B>,
_buffers: Vec<NodeBuffer>,
_images: Vec<NodeImage>,
) -> Result<Box<dyn RenderGroup<B, World>>, failure::Error> {
#[cfg(feature = "profiler")]
profile_scope!("build");
let env = FlatEnvironmentSub::new(factory)?;
let colors = DynamicUniform::new(factory, pso::ShaderStageFlags::FRAGMENT)?;
let mesh = Shape::Sphere(16, 16)
.generate::<Vec<PosTex>>(None)
.build(queue, factory)?;
let (pipeline, pipeline_layout) = build_skybox_pipeline(
factory,
subpass,
framebuffer_width,
framebuffer_height,
vec![env.raw_layout(), colors.raw_layout()],
)?;
Ok(Box::new(DrawSkybox::<B> {
pipeline,
pipeline_layout,
env,
colors,
mesh,
default_settings: self.default_settings,
}))
}
}
#[derive(Debug)]
pub struct DrawSkybox<B: Backend> {
pipeline: B::GraphicsPipeline,
pipeline_layout: B::PipelineLayout,
env: FlatEnvironmentSub<B>,
colors: DynamicUniform<B, SkyboxUniform>,
mesh: Mesh<B>,
default_settings: SkyboxSettings,
}
impl<B: Backend> RenderGroup<B, World> for DrawSkybox<B> {
fn prepare(
&mut self,
factory: &Factory<B>,
_queue: QueueId,
index: usize,
_subpass: hal::pass::Subpass<'_, B>,
resources: &World,
) -> PrepareResult {
#[cfg(feature = "profiler")]
profile_scope!("prepare");
let settings = <(Option<Read<'_, SkyboxSettings>>)>::fetch(resources)
.map(|s| s.uniform())
.unwrap_or_else(|| self.default_settings.uniform());
self.env.process(factory, index, resources);
let changed = self.colors.write(factory, index, settings);
if changed {
PrepareResult::DrawRecord
} else {
PrepareResult::DrawReuse
}
}
fn draw_inline(
&mut self,
mut encoder: RenderPassEncoder<'_, B>,
index: usize,
_subpass: hal::pass::Subpass<'_, B>,
_resources: &World,
) {
#[cfg(feature = "profiler")]
profile_scope!("draw");
encoder.bind_graphics_pipeline(&self.pipeline);
self.env.bind(index, &self.pipeline_layout, 0, &mut encoder);
self.colors
.bind(index, &self.pipeline_layout, 1, &mut encoder);
self.mesh
.bind(0, &[PosTex::vertex()], &mut encoder)
.unwrap();
unsafe {
encoder.draw(0..self.mesh.len(), 0..1);
}
}
fn dispose(self: Box<Self>, factory: &mut Factory<B>, _aux: &World) {
unsafe {
factory.device().destroy_graphics_pipeline(self.pipeline);
factory
.device()
.destroy_pipeline_layout(self.pipeline_layout);
}
}
}
fn build_skybox_pipeline<B: Backend>(
factory: &Factory<B>,
subpass: hal::pass::Subpass<'_, B>,
framebuffer_width: u32,
framebuffer_height: u32,
layouts: Vec<&B::DescriptorSetLayout>,
) -> Result<(B::GraphicsPipeline, B::PipelineLayout), failure::Error> {
let pipeline_layout = unsafe {
factory
.device()
.create_pipeline_layout(layouts, None as Option<(_, _)>)
}?;
let shader_vertex = unsafe { super::SKYBOX_VERTEX.module(factory).unwrap() };
let shader_fragment = unsafe { super::SKYBOX_FRAGMENT.module(factory).unwrap() };
let pipes = PipelinesBuilder::new()
.with_pipeline(
PipelineDescBuilder::new()
.with_vertex_desc(&[(PosTex::vertex(), pso::VertexInputRate::Vertex)])
.with_shaders(util::simple_shader_set(
&shader_vertex,
Some(&shader_fragment),
))
.with_layout(&pipeline_layout)
.with_subpass(subpass)
.with_framebuffer_size(framebuffer_width, framebuffer_height)
.with_depth_test(pso::DepthTest::On {
fun: pso::Comparison::LessEqual,
write: false,
})
.with_blend_targets(vec![pso::ColorBlendDesc(
pso::ColorMask::ALL,
pso::BlendState::Off,
)]),
)
.build(factory, None);
unsafe {
factory.destroy_shader_module(shader_vertex);
factory.destroy_shader_module(shader_fragment);
}
match pipes {
Err(e) => {
unsafe {
factory.device().destroy_pipeline_layout(pipeline_layout);
}
Err(e)
}
Ok(mut pipes) => Ok((pipes.remove(0), pipeline_layout)),
}
}