use crate::{
asset::manager::ResourceManager,
core::{
algebra::{Matrix4, Vector3},
color::Color,
math::Rect,
},
graphics::{
error::FrameworkError,
framebuffer::{Attachment, GpuFrameBuffer},
gpu_texture::{GpuTexture, PixelKind},
server::GraphicsServer,
},
renderer::{
bundle::{BundleRenderContext, RenderDataBundleStorage, RenderDataBundleStorageOptions},
cache::{
shader::ShaderCache, texture::TextureCache, uniform::UniformMemoryAllocator,
DynamicSurfaceCache,
},
observer::ObserverPosition,
resources::RendererResources,
settings::ShadowMapPrecision,
shadow::cascade_size,
GeometryCache, RenderPassStatistics, SPOT_SHADOW_PASS_NAME,
},
scene::{collider::BitMask, graph::Graph},
};
pub struct SpotShadowMapRenderer {
precision: ShadowMapPrecision,
cascades: [GpuFrameBuffer; 3],
size: usize,
}
impl SpotShadowMapRenderer {
pub fn new(
server: &dyn GraphicsServer,
size: usize,
precision: ShadowMapPrecision,
) -> Result<Self, FrameworkError> {
fn make_cascade(
server: &dyn GraphicsServer,
size: usize,
precision: ShadowMapPrecision,
) -> Result<GpuFrameBuffer, FrameworkError> {
let depth = server.create_2d_render_target(
"SpotShadowMapCascade",
match precision {
ShadowMapPrecision::Full => PixelKind::D32F,
ShadowMapPrecision::Half => PixelKind::D16,
},
size,
size,
)?;
server.create_frame_buffer(Some(Attachment::depth(depth)), vec![])
}
Ok(Self {
precision,
size,
cascades: [
make_cascade(server, cascade_size(size, 0), precision)?,
make_cascade(server, cascade_size(size, 1), precision)?,
make_cascade(server, cascade_size(size, 2), precision)?,
],
})
}
pub fn base_size(&self) -> usize {
self.size
}
pub fn precision(&self) -> ShadowMapPrecision {
self.precision
}
pub fn cascade_texture(&self, cascade: usize) -> &GpuTexture {
&self.cascades[cascade].depth_attachment().unwrap().texture
}
pub fn cascade_size(&self, cascade: usize) -> usize {
cascade_size(self.size, cascade)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn render(
&mut self,
server: &dyn GraphicsServer,
graph: &Graph,
render_mask: BitMask,
elapsed_time: f32,
light_position: Vector3<f32>,
light_view_matrix: Matrix4<f32>,
z_near: f32,
z_far: f32,
light_projection_matrix: Matrix4<f32>,
geom_cache: &mut GeometryCache,
cascade: usize,
shader_cache: &mut ShaderCache,
texture_cache: &mut TextureCache,
renderer_resources: &RendererResources,
uniform_memory_allocator: &mut UniformMemoryAllocator,
dynamic_surface_cache: &mut DynamicSurfaceCache,
resource_manager: &ResourceManager,
) -> Result<RenderPassStatistics, FrameworkError> {
let _debug_scope = server.begin_scope("SpotShadowMap");
let mut statistics = RenderPassStatistics::default();
let framebuffer = &self.cascades[cascade];
let cascade_size = cascade_size(self.size, cascade);
let viewport = Rect::new(0, 0, cascade_size as i32, cascade_size as i32);
framebuffer.clear(viewport, None, Some(1.0), None);
let bundle_storage = RenderDataBundleStorage::from_graph(
graph,
render_mask,
elapsed_time,
&ObserverPosition {
translation: light_position,
z_near,
z_far,
view_matrix: light_view_matrix,
projection_matrix: light_projection_matrix,
view_projection_matrix: light_projection_matrix * light_view_matrix,
},
SPOT_SHADOW_PASS_NAME.clone(),
RenderDataBundleStorageOptions {
collect_lights: false,
},
dynamic_surface_cache,
);
statistics += bundle_storage.render_to_frame_buffer(
server,
geom_cache,
shader_cache,
|_| true,
|_| true,
BundleRenderContext {
texture_cache,
render_pass_name: &SPOT_SHADOW_PASS_NAME,
frame_buffer: framebuffer,
viewport,
uniform_memory_allocator,
resource_manager,
use_pom: false,
light_position: &Default::default(),
renderer_resources,
ambient_light: Color::WHITE, scene_depth: None,
},
)?;
Ok(statistics)
}
}