#![allow(dead_code)]
use crate::{
components::{FacesGPU, LightEmit, ModelMatrix, Name, PosLookat, Renderable, ShadowCaster, ShadowMap, ShadowMapDirty, VertsGPU, VisMesh},
scene::Scene,
};
use easy_wgpu::{
bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
buffer::Buffer,
gpu::Gpu,
pipeline::RenderPipelineDescBuilder,
};
use gloss_hecs::{Changed, CommandBuffer, Entity, Query, QueryBorrow};
use gloss_utils::numerical::{align, align_usz};
use log::debug;
use std::collections::HashMap;
pub struct ShadowPass {
render_pipeline: wgpu::RenderPipeline,
locals_uniform: LocalsUniform, iterator_light_uniform: IteratorLightUniform, command_buffer: CommandBuffer,
}
use super::upload_pass::PerFrameUniforms;
#[include_wgsl_oil::include_wgsl_oil("../../../shaders/shadow_map.wgsl")]
mod shader_code {}
impl ShadowPass {
pub fn new(gpu: &Gpu) -> Self {
let render_pipeline = RenderPipelineDescBuilder::new()
.label("shadow pass pipeline")
.shader_code(shader_code::SOURCE)
.add_bind_group_layout_desc(PerFrameUniforms::build_layout_desc())
.add_bind_group_layout_desc(IteratorLightUniform::layout_desc())
.add_bind_group_layout_desc(LocalsUniform::layout_desc())
.add_vertex_buffer_layout(VertsGPU::vertex_buffer_layout::<0>())
.depth_state(Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Greater,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}))
.multisample(wgpu::MultisampleState::default())
.build_pipeline(gpu.device());
let locals_uniform = LocalsUniform::new(gpu);
let iterator_light_uniform = IteratorLightUniform::new(gpu);
let command_buffer = CommandBuffer::new();
Self {
render_pipeline,
locals_uniform,
iterator_light_uniform,
command_buffer,
}
}
pub fn run(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &mut Scene) {
self.begin_pass();
self.render_shadows(gpu, per_frame_uniforms, scene);
self.end_pass(scene);
}
fn render_shadows(&mut self, gpu: &Gpu, per_frame_uniforms: &PerFrameUniforms, scene: &Scene) {
let shadow_map_requires_update = self.check_shadow_maps_dirty(scene);
debug!("shadow_map_requires_update {shadow_map_requires_update}");
if !shadow_map_requires_update {
return; }
let mut query_all_renderables = scene.world().query::<&Renderable>();
let mut query_meshes_for_shadow = scene.world().query::<(&VertsGPU, &FacesGPU, &VisMesh)>().with::<&Renderable>();
self.update_locals(gpu, &mut query_all_renderables, scene);
for (entity_light, shadow_map) in &mut scene.world().query::<&ShadowMap>().with::<(&LightEmit, &ShadowCaster)>() {
let mut encoder = gpu.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow pass encoder"),
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Shadow Pass"),
color_attachments: &[
],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &shadow_map.tex_depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
let light_name = scene.get_comp::<&Name>(&entity_light).unwrap().0.clone();
let light_idx = per_frame_uniforms.light2idx_ubo[&light_name];
self.iterator_light_uniform.set(gpu, light_idx); render_pass.set_bind_group(1, self.iterator_light_uniform.bind_group.bg(), &[]);
for (entity_mesh, (verts, faces, vis)) in query_meshes_for_shadow.iter() {
if !vis.show_mesh {
continue;
}
let name = scene.get_comp::<&Name>(&entity_mesh).unwrap().0.clone();
let (local_bg, offset) = &self.locals_uniform.mesh2local_bind[&name];
render_pass.set_bind_group(2, local_bg.bg(), &[*offset]);
render_pass.set_vertex_buffer(0, verts.buf.buffer.slice(..));
render_pass.set_index_buffer(faces.buf.buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..faces.nr_triangles * 3, 0, 0..1);
}
}
debug!("shadow encoder");
gpu.queue().submit(Some(encoder.finish()));
}
}
fn begin_pass(&mut self) {
self.locals_uniform.buffer.reset_chunks_offset_if_necessary();
}
fn end_pass(&mut self, scene: &mut Scene) {
{
let mut query_all_renderables = scene.world().query::<&Renderable>();
for (id, _comps) in query_all_renderables.iter() {
self.command_buffer.remove_one::<ShadowMapDirty>(id);
}
}
scene.world_mut().run_command_buffer(&mut self.command_buffer);
}
fn check_shadow_maps_dirty(&self, scene: &Scene) -> bool {
let mut query_for_shadow = scene
.world()
.query::<(Option<&ShadowMapDirty>, Changed<VertsGPU>, Changed<ModelMatrix>)>()
.with::<&Renderable>();
let mut shadow_map_requires_update = false;
for (_entity_mesh, (shadow_map_opt, changed_verts, changed_model_matrix)) in query_for_shadow.iter() {
shadow_map_requires_update = shadow_map_requires_update | changed_verts | changed_model_matrix | shadow_map_opt.is_some();
}
let mut query_for_lights = scene.world().query::<Changed<ShadowCaster>>();
for (_ent, changed_shadow_casting) in query_for_lights.iter() {
shadow_map_requires_update |= changed_shadow_casting;
}
let mut query_for_renderable = scene.world().query::<Changed<Renderable>>();
for (_entity_mesh, changed_renderable) in query_for_renderable.iter() {
shadow_map_requires_update |= changed_renderable;
}
if !scene.world().removed::<Renderable>().is_empty() {
shadow_map_requires_update = true;
}
let mut query_for_lights = scene.world().query::<(Changed<PosLookat>,)>().with::<&LightEmit>();
for (_entity_mesh, (changed_poslookat,)) in query_for_lights.iter() {
shadow_map_requires_update |= changed_poslookat;
}
shadow_map_requires_update
}
fn update_locals<Q: Query>(&mut self, gpu: &Gpu, query_state: &mut QueryBorrow<'_, Q>, scene: &Scene) {
for (id, _comps) in query_state.iter() {
let name = scene.get_comp::<&Name>(&id).unwrap().0.clone();
let locals = Locals::new(id, scene);
let offset_in_ubo = self.locals_uniform.buffer.push_cpu_chunk_aligned::<Locals>(&locals);
self.locals_uniform.update_bind_group(id, gpu, &name, offset_in_ubo, scene);
}
self.locals_uniform.buffer.upload_from_cpu_chunks(gpu.queue()); }
}
#[repr(C)]
#[derive(Clone, Copy, encase::ShaderType)]
struct Locals {
model_matrix: nalgebra::Matrix4<f32>,
}
impl Locals {
pub fn new(entity: Entity, scene: &Scene) -> Self {
let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap().0.to_homogeneous();
Locals { model_matrix }
}
}
struct LocalsUniform {
pub buffer: Buffer,
layout: wgpu::BindGroupLayout,
pub mesh2local_bind: HashMap<String, (BindGroupWrapper, u32)>,
}
impl LocalsUniform {
pub fn new(gpu: &Gpu) -> Self {
let size_bytes = 0x10000;
let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
let buffer = Buffer::new_empty(gpu.device(), usage, Some("local_buffer"), size_bytes);
let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
Self {
buffer,
layout,
mesh2local_bind: HashMap::default(),
}
}
pub fn layout_desc() -> BindGroupLayoutDesc {
BindGroupLayoutBuilder::new()
.label("shadow_pass_locals_layout")
.add_entry_uniform(
wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
true,
wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<Locals>()).unwrap(), 256))),
)
.build()
}
pub fn update_bind_group(
&mut self,
_entity: Entity,
gpu: &Gpu,
mesh_name: &str,
offset_in_ubo: u32,
_scene: &Scene,
) {
let entries = BindGroupBuilder::new().add_entry_buf_chunk::<Locals>(&self.buffer.buffer).build_entries();
if !self.mesh2local_bind.contains_key(mesh_name) || self.mesh2local_bind[mesh_name].0.is_stale(&entries) {
debug!("shadowmap_local_bg_recreating");
let bg = BindGroupDesc::new("shadow_local_bg", entries).into_bind_group_wrapper(gpu.device(), &self.layout);
let bg_and_offset = (bg, offset_in_ubo);
self.mesh2local_bind.insert(mesh_name.to_string(), bg_and_offset);
}
self.mesh2local_bind.entry(mesh_name.to_string()).and_modify(|r| r.1 = offset_in_ubo);
}
}
#[repr(C)]
#[derive(Clone, Copy, encase::ShaderType)]
struct IteratorLight {
light_idx: u32,
pad_0: u32,
pad_1: u32,
pad_2: u32,
}
impl IteratorLight {
pub fn new(light_idx: u32) -> Self {
IteratorLight {
light_idx,
pad_0: 0,
pad_1: 0,
pad_2: 0,
}
}
}
struct IteratorLightUniform {
pub buffer: Buffer,
pub bind_group: BindGroupWrapper,
}
impl IteratorLightUniform {
pub fn new(gpu: &Gpu) -> Self {
let size_bytes = align_usz(std::mem::size_of::<IteratorLight>(), 256);
let usage = wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM;
let buffer = Buffer::new_empty(gpu.device(), usage, Some("iterator_light_buffer"), size_bytes);
let layout = Self::layout_desc().into_bind_group_layout(gpu.device());
let bind_group = BindGroupBuilder::new().add_entry_buf(&buffer.buffer).build(gpu.device(), &layout);
Self {
buffer,
bind_group,
}
}
pub fn set(&mut self, gpu: &Gpu, idx: u32) {
let iterator = IteratorLight::new(idx);
self.buffer.reset_chunks_offset();
self.buffer.push_cpu_chunk_packed(&iterator);
self.buffer.upload_from_cpu_chunks(gpu.queue());
}
pub fn layout_desc() -> BindGroupLayoutDesc {
BindGroupLayoutBuilder::new()
.label("shadow_pass_iterator_layout")
.add_entry_uniform(
wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
false,
wgpu::BufferSize::new(u64::from(align(u32::try_from(std::mem::size_of::<IteratorLight>()).unwrap(), 256))),
)
.build()
}
}