use crate::ecs::mesh::components::{SkinnedVertex, Vertex};
use crate::ecs::prefab::resources::mesh_cache_iter;
use crate::ecs::skin::systems::GpuSkinData;
use std::collections::HashMap;
use wgpu::util::DeviceExt;
const MAX_BUFFER_SIZE: u64 = 256 * 1024 * 1024;
use super::types::{
CASCADE_SPLIT_DISTANCES, CascadeData, INITIAL_MAX_SKINS, INITIAL_MAX_TOTAL_BONES,
MAX_JOINTS_PER_SKIN, MAX_POINT_LIGHT_SHADOWS, NUM_SHADOW_CASCADES, POINT_SHADOW_FACE_SIZE,
POINT_SHADOW_NUM_FACES, PointLightShadowData, PointLightShadowSlot, PointShadowUniforms,
ShadowUniforms, SkinnedShadowObjectData, SpotlightShadowData, SpotlightShadowSlot,
};
pub struct ShadowDepthPass {
pub(super) pipeline: wgpu::RenderPipeline,
pub(super) uniform_buffer: wgpu::Buffer,
pub(super) uniform_bind_group_layout: wgpu::BindGroupLayout,
pub(super) transform_buffer: wgpu::Buffer,
pub(super) transform_bind_group: wgpu::BindGroup,
pub(super) transform_bind_group_layout: wgpu::BindGroupLayout,
pub(super) transform_buffer_size: usize,
pub(super) vertex_buffer: wgpu::Buffer,
pub(super) index_buffer: wgpu::Buffer,
pub(super) vertex_buffer_size: u64,
pub(super) index_buffer_size: u64,
pub(super) meshes: HashMap<String, (u32, u32, u32, u32)>,
pub(super) current_vertex_offset: u32,
pub(super) current_index_offset: u32,
pub(super) shadow_caster_entities: Vec<crate::ecs::world::Entity>,
pub(super) skinned_pipeline: wgpu::RenderPipeline,
pub(super) skinned_object_buffer: wgpu::Buffer,
pub(super) skinned_object_buffer_size: usize,
pub(super) skinned_bind_group: wgpu::BindGroup,
pub(super) skinned_bind_group_layout: wgpu::BindGroupLayout,
pub(super) joint_matrices_buffer: wgpu::Buffer,
pub(super) bone_transforms_buffer: wgpu::Buffer,
pub(super) inverse_bind_matrices_buffer: wgpu::Buffer,
pub(super) skin_data_buffer: wgpu::Buffer,
pub(super) bone_transforms_buffer_size: usize,
pub(super) inverse_bind_matrices_buffer_size: usize,
pub(super) joint_matrices_buffer_size: usize,
pub(super) skin_data_buffer_size: usize,
pub(super) skinning_compute_pipeline: wgpu::ComputePipeline,
pub(super) skinning_compute_bind_group: wgpu::BindGroup,
pub(super) skinning_compute_bind_group_layout: wgpu::BindGroupLayout,
pub(super) skinned_vertex_buffer: wgpu::Buffer,
pub(super) skinned_index_buffer: wgpu::Buffer,
pub(super) skinned_vertex_buffer_size: u64,
pub(super) skinned_index_buffer_size: u64,
pub(super) skinned_meshes: HashMap<String, (u32, u32, u32, u32)>,
pub(super) current_skinned_vertex_offset: u32,
pub(super) current_skinned_index_offset: u32,
pub(super) skinned_shadow_caster_entities: Vec<crate::ecs::world::Entity>,
pub(super) total_joints_to_dispatch: u32,
pub(super) spotlight_shadow_slots: Vec<SpotlightShadowSlot>,
pub(super) spotlight_shadow_data: Vec<SpotlightShadowData>,
pub(super) spotlight_atlas_slot_size: u32,
pub(super) point_light_pipeline: wgpu::RenderPipeline,
pub point_light_cubemap: wgpu::Texture,
pub(super) point_light_cubemap_view: wgpu::TextureView,
pub(super) point_light_face_views: Vec<wgpu::TextureView>,
pub point_light_depth_texture: wgpu::Texture,
pub(super) point_light_depth_view: wgpu::TextureView,
pub(super) point_light_uniform_buffer: wgpu::Buffer,
pub(super) point_light_uniform_bind_group: wgpu::BindGroup,
pub(super) point_light_shadow_slots: Vec<PointLightShadowSlot>,
pub(super) point_light_shadow_data: Vec<PointLightShadowData>,
pub(super) cascade_data: CascadeData,
pub(super) cascade_uniform_buffers: [wgpu::Buffer; NUM_SHADOW_CASCADES],
pub(super) cascade_uniform_bind_groups: [wgpu::BindGroup; NUM_SHADOW_CASCADES],
}
impl ShadowDepthPass {
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue) -> Self {
let shader =
device.create_shader_module(wgpu::include_wgsl!("../../shaders/shadow_depth.wgsl"));
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Uniform Buffer"),
size: std::mem::size_of::<ShadowUniforms>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Shadow Uniform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let initial_cascade_uniforms = ShadowUniforms {
light_view_projection: [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
],
_padding: [[0.0; 4]; 4],
_padding2: [[0.0; 4]; 4],
_padding3: [[0.0; 4]; 4],
_padding4: [0.0; 4],
};
let cascade_uniform_buffers: [wgpu::Buffer; NUM_SHADOW_CASCADES] =
std::array::from_fn(|_index| {
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Cascade Uniform Buffer"),
contents: bytemuck::cast_slice(&[initial_cascade_uniforms]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
})
});
let cascade_uniform_bind_groups: [wgpu::BindGroup; NUM_SHADOW_CASCADES] =
std::array::from_fn(|index| {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(&format!("Cascade {} Uniform Bind Group", index)),
layout: &uniform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: cascade_uniform_buffers[index].as_entire_binding(),
}],
})
});
let initial_transform_count = 1000;
let transform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Transform Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * initial_transform_count) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let transform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Shadow Transform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shadow Transform Bind Group"),
layout: &transform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: transform_buffer.as_entire_binding(),
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Shadow Pipeline Layout"),
bind_group_layouts: &[&uniform_bind_group_layout, &transform_bind_group_layout],
push_constant_ranges: &[],
});
let vertex_attributes = wgpu::vertex_attr_array![
0 => Float32x3,
1 => Float32x3,
2 => Float32x2,
];
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Shadow Depth Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vertex_main"),
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &vertex_attributes,
}],
compilation_options: Default::default(),
},
fragment: None,
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
let initial_vertex_count = 10000;
let initial_index_count = 30000;
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Vertex Buffer"),
size: (std::mem::size_of::<Vertex>() * initial_vertex_count) as u64,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Index Buffer"),
size: (std::mem::size_of::<u32>() * initial_index_count) as u64,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let bone_transforms_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Bone Transforms Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * INITIAL_MAX_TOTAL_BONES) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let inverse_bind_matrices_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Inverse Bind Matrices Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS)
as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let joint_matrices_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Joint Matrices Buffer"),
size: (std::mem::size_of::<[[f32; 4]; 4]>() * MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS)
as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let skin_data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skin Data Buffer"),
size: (std::mem::size_of::<GpuSkinData>() * INITIAL_MAX_SKINS) as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let skinning_compute_shader =
device.create_shader_module(wgpu::include_wgsl!("../../shaders/skinning_compute.wgsl"));
let skinning_compute_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Shadow Skinning Compute Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let initial_joint_matrices_size = MAX_JOINTS_PER_SKIN * INITIAL_MAX_SKINS;
let skinning_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shadow Skinning Compute Bind Group"),
layout: &skinning_compute_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: bone_transforms_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: inverse_bind_matrices_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: skin_data_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: joint_matrices_buffer.as_entire_binding(),
},
],
});
let skinning_compute_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Shadow Skinning Compute Pipeline Layout"),
bind_group_layouts: &[&skinning_compute_bind_group_layout],
push_constant_ranges: &[],
});
let skinning_compute_pipeline =
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("Shadow Skinning Compute Pipeline"),
layout: Some(&skinning_compute_pipeline_layout),
module: &skinning_compute_shader,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
let initial_skinned_object_count = 100;
let skinned_object_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skinned Object Buffer"),
size: (std::mem::size_of::<SkinnedShadowObjectData>() * initial_skinned_object_count)
as u64,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let skinned_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Shadow Skinned Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let skinned_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shadow Skinned Bind Group"),
layout: &skinned_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: skinned_object_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: joint_matrices_buffer.as_entire_binding(),
},
],
});
let skinned_shadow_shader = device.create_shader_module(wgpu::include_wgsl!(
"../../shaders/skinned_shadow_depth.wgsl"
));
let skinned_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skinned Shadow Pipeline Layout"),
bind_group_layouts: &[&uniform_bind_group_layout, &skinned_bind_group_layout],
push_constant_ranges: &[],
});
let skinned_vertex_layout = wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<SkinnedVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: 12,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: 24,
shader_location: 2,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: 32,
shader_location: 3,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: 40,
shader_location: 4,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: 56,
shader_location: 5,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: 72,
shader_location: 6,
format: wgpu::VertexFormat::Uint32x4,
},
wgpu::VertexAttribute {
offset: 88,
shader_location: 7,
format: wgpu::VertexFormat::Float32x4,
},
],
};
let skinned_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skinned Shadow Depth Pipeline"),
layout: Some(&skinned_pipeline_layout),
vertex: wgpu::VertexState {
module: &skinned_shadow_shader,
entry_point: Some("vs_main"),
buffers: &[skinned_vertex_layout],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &skinned_shadow_shader,
entry_point: Some("fs_main"),
targets: &[],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
let initial_skinned_vertex_count = 10000;
let initial_skinned_index_count = 30000;
let skinned_vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skinned Vertex Buffer"),
size: (std::mem::size_of::<SkinnedVertex>() * initial_skinned_vertex_count) as u64,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let skinned_index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skinned Index Buffer"),
size: (std::mem::size_of::<u32>() * initial_skinned_index_count) as u64,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let point_light_cubemap = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Point Light Shadow Cubemap Array"),
size: wgpu::Extent3d {
width: POINT_SHADOW_FACE_SIZE,
height: POINT_SHADOW_FACE_SIZE,
depth_or_array_layers: (POINT_SHADOW_NUM_FACES * MAX_POINT_LIGHT_SHADOWS) as u32,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::R32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let point_light_cubemap_view =
point_light_cubemap.create_view(&wgpu::TextureViewDescriptor {
label: Some("Point Light Shadow Cubemap Array View"),
format: Some(wgpu::TextureFormat::R32Float),
dimension: Some(wgpu::TextureViewDimension::CubeArray),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: Some(1),
base_array_layer: 0,
array_layer_count: Some((POINT_SHADOW_NUM_FACES * MAX_POINT_LIGHT_SHADOWS) as u32),
usage: Some(wgpu::TextureUsages::TEXTURE_BINDING),
});
let mut point_light_face_views =
Vec::with_capacity(POINT_SHADOW_NUM_FACES * MAX_POINT_LIGHT_SHADOWS);
for light_index in 0..MAX_POINT_LIGHT_SHADOWS {
for face_index in 0..POINT_SHADOW_NUM_FACES {
let layer = (light_index * POINT_SHADOW_NUM_FACES + face_index) as u32;
let view = point_light_cubemap.create_view(&wgpu::TextureViewDescriptor {
label: Some(&format!(
"Point Light {} Face {} View",
light_index, face_index
)),
format: Some(wgpu::TextureFormat::R32Float),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: Some(1),
base_array_layer: layer,
array_layer_count: Some(1),
usage: Some(wgpu::TextureUsages::RENDER_ATTACHMENT),
});
point_light_face_views.push(view);
}
}
let point_light_depth_texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Point Light Shadow Depth Texture"),
size: wgpu::Extent3d {
width: POINT_SHADOW_FACE_SIZE,
height: POINT_SHADOW_FACE_SIZE,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let point_light_depth_view =
point_light_depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
let point_uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Point Light Uniform Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: wgpu::BufferSize::new(std::mem::size_of::<
PointShadowUniforms,
>() as u64),
},
count: None,
}],
});
let total_point_uniforms = MAX_POINT_LIGHT_SHADOWS * POINT_SHADOW_NUM_FACES;
let point_light_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Point Light Shadow Uniform Buffer"),
size: (std::mem::size_of::<PointShadowUniforms>() * total_point_uniforms) as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let point_light_uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Point Light Shadow Uniform Bind Group"),
layout: &point_uniform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &point_light_uniform_buffer,
offset: 0,
size: wgpu::BufferSize::new(std::mem::size_of::<PointShadowUniforms>() as u64),
}),
}],
});
let point_shadow_shader = device
.create_shader_module(wgpu::include_wgsl!("../../shaders/point_shadow_depth.wgsl"));
let point_light_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Point Light Shadow Pipeline Layout"),
bind_group_layouts: &[
&point_uniform_bind_group_layout,
&transform_bind_group_layout,
],
push_constant_ranges: &[],
});
let point_light_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Point Light Shadow Pipeline"),
layout: Some(&point_light_pipeline_layout),
vertex: wgpu::VertexState {
module: &point_shadow_shader,
entry_point: Some("vs_main"),
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &vertex_attributes,
}],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &point_shadow_shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::R32Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Cw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
let mut pass = Self {
pipeline,
uniform_buffer,
uniform_bind_group_layout,
transform_buffer,
transform_bind_group,
transform_bind_group_layout,
transform_buffer_size: initial_transform_count,
vertex_buffer,
index_buffer,
vertex_buffer_size: (std::mem::size_of::<Vertex>() * initial_vertex_count) as u64,
index_buffer_size: (std::mem::size_of::<u32>() * initial_index_count) as u64,
meshes: HashMap::new(),
current_vertex_offset: 0,
current_index_offset: 0,
shadow_caster_entities: Vec::new(),
skinned_pipeline,
skinned_object_buffer,
skinned_object_buffer_size: initial_skinned_object_count,
skinned_bind_group,
skinned_bind_group_layout,
joint_matrices_buffer,
bone_transforms_buffer,
inverse_bind_matrices_buffer,
skin_data_buffer,
bone_transforms_buffer_size: INITIAL_MAX_TOTAL_BONES,
inverse_bind_matrices_buffer_size: initial_joint_matrices_size,
joint_matrices_buffer_size: initial_joint_matrices_size,
skin_data_buffer_size: INITIAL_MAX_SKINS,
skinning_compute_pipeline,
skinning_compute_bind_group,
skinning_compute_bind_group_layout,
skinned_vertex_buffer,
skinned_index_buffer,
skinned_vertex_buffer_size: (std::mem::size_of::<SkinnedVertex>()
* initial_skinned_vertex_count) as u64,
skinned_index_buffer_size: (std::mem::size_of::<u32>() * initial_skinned_index_count)
as u64,
skinned_meshes: HashMap::new(),
current_skinned_vertex_offset: 0,
current_skinned_index_offset: 0,
skinned_shadow_caster_entities: Vec::new(),
total_joints_to_dispatch: 0,
spotlight_shadow_slots: Vec::new(),
spotlight_shadow_data: Vec::new(),
spotlight_atlas_slot_size: 0,
point_light_pipeline,
point_light_cubemap,
point_light_cubemap_view,
point_light_face_views,
point_light_depth_texture,
point_light_depth_view,
point_light_uniform_buffer,
point_light_uniform_bind_group,
point_light_shadow_slots: Vec::new(),
point_light_shadow_data: Vec::new(),
cascade_data: CascadeData {
view_projections: [[[0.0; 4]; 4]; NUM_SHADOW_CASCADES],
split_distances: CASCADE_SPLIT_DISTANCES,
atlas_offsets: [[0.0, 0.0], [0.5, 0.0], [0.0, 0.5], [0.5, 0.5]],
atlas_scale: [0.5, 0.5],
_padding: [0.0, 0.0],
},
cascade_uniform_buffers,
cascade_uniform_bind_groups,
};
use crate::ecs::mesh::components::{
create_cone_mesh, create_cube_mesh, create_cylinder_mesh, create_plane_mesh,
create_sphere_mesh, create_subdivided_plane_mesh, create_torus_mesh,
};
let cube_mesh = create_cube_mesh();
pass.add_mesh(
device,
queue,
"Cube".to_string(),
&cube_mesh.vertices,
&cube_mesh.indices,
);
let sphere_mesh = create_sphere_mesh(1.0, 16);
pass.add_mesh(
device,
queue,
"Sphere".to_string(),
&sphere_mesh.vertices,
&sphere_mesh.indices,
);
let torus_mesh = create_torus_mesh(1.0, 0.3, 16, 16);
pass.add_mesh(
device,
queue,
"Torus".to_string(),
&torus_mesh.vertices,
&torus_mesh.indices,
);
let plane_mesh = create_plane_mesh(2.0);
pass.add_mesh(
device,
queue,
"Plane".to_string(),
&plane_mesh.vertices,
&plane_mesh.indices,
);
let subdivided_plane_mesh = create_subdivided_plane_mesh(2.0, 20);
pass.add_mesh(
device,
queue,
"SubdividedPlane".to_string(),
&subdivided_plane_mesh.vertices,
&subdivided_plane_mesh.indices,
);
let cylinder_mesh = create_cylinder_mesh(0.5, 1.0, 16);
pass.add_mesh(
device,
queue,
"Cylinder".to_string(),
&cylinder_mesh.vertices,
&cylinder_mesh.indices,
);
let cone_mesh = create_cone_mesh(0.5, 1.0, 16);
pass.add_mesh(
device,
queue,
"Cone".to_string(),
&cone_mesh.vertices,
&cone_mesh.indices,
);
pass
}
pub fn add_mesh(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
name: String,
vertices: &[Vertex],
indices: &[u32],
) {
let vertex_size = std::mem::size_of_val(vertices) as u64;
let index_size = std::mem::size_of_val(indices) as u64;
let required_vertex_size = (self.current_vertex_offset as u64
* std::mem::size_of::<Vertex>() as u64)
+ vertex_size;
let required_index_size =
(self.current_index_offset as u64 * std::mem::size_of::<u32>() as u64) + index_size;
if required_vertex_size > MAX_BUFFER_SIZE || required_index_size > MAX_BUFFER_SIZE {
tracing::warn!(
"Shadow mesh {} exceeds maximum buffer size, skipping shadow rendering",
name
);
return;
}
if required_vertex_size > self.vertex_buffer_size {
let new_size = (required_vertex_size * 2)
.max(self.vertex_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Vertex Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Vertex Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.vertex_buffer,
0,
&new_buffer,
0,
self.vertex_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.vertex_buffer = new_buffer;
self.vertex_buffer_size = new_size;
}
if required_index_size > self.index_buffer_size {
let new_size = (required_index_size * 2)
.max(self.index_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Index Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Index Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.index_buffer,
0,
&new_buffer,
0,
self.index_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.index_buffer = new_buffer;
self.index_buffer_size = new_size;
}
queue.write_buffer(
&self.vertex_buffer,
self.current_vertex_offset as u64 * std::mem::size_of::<Vertex>() as u64,
bytemuck::cast_slice(vertices),
);
queue.write_buffer(
&self.index_buffer,
self.current_index_offset as u64 * std::mem::size_of::<u32>() as u64,
bytemuck::cast_slice(indices),
);
self.meshes.insert(
name,
(
self.current_vertex_offset,
vertices.len() as u32,
self.current_index_offset,
indices.len() as u32,
),
);
self.current_vertex_offset += vertices.len() as u32;
self.current_index_offset += indices.len() as u32;
}
pub fn sync_meshes(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh_cache: &crate::ecs::prefab::resources::MeshCache,
) {
for (name, mesh) in mesh_cache_iter(mesh_cache) {
if mesh.skin_data.is_some() {
continue;
}
if !self.meshes.contains_key(name.as_str()) {
let vertex_size = (mesh.vertices.len() * std::mem::size_of::<Vertex>()) as u64;
let index_size = (mesh.indices.len() * std::mem::size_of::<u32>()) as u64;
let required_vertex_size = (self.current_vertex_offset as u64
* std::mem::size_of::<Vertex>() as u64)
+ vertex_size;
let required_index_size = (self.current_index_offset as u64
* std::mem::size_of::<u32>() as u64)
+ index_size;
if required_vertex_size > MAX_BUFFER_SIZE || required_index_size > MAX_BUFFER_SIZE {
tracing::warn!(
"Shadow mesh {} exceeds maximum buffer size, skipping shadow rendering",
name
);
continue;
}
if required_vertex_size > self.vertex_buffer_size {
let new_size = (required_vertex_size * 2)
.max(self.vertex_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Vertex Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Vertex Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.vertex_buffer,
0,
&new_buffer,
0,
self.vertex_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.vertex_buffer = new_buffer;
self.vertex_buffer_size = new_size;
}
if required_index_size > self.index_buffer_size {
let new_size = (required_index_size * 2)
.max(self.index_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Index Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Index Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.index_buffer,
0,
&new_buffer,
0,
self.index_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.index_buffer = new_buffer;
self.index_buffer_size = new_size;
}
queue.write_buffer(
&self.vertex_buffer,
self.current_vertex_offset as u64 * std::mem::size_of::<Vertex>() as u64,
bytemuck::cast_slice(&mesh.vertices),
);
queue.write_buffer(
&self.index_buffer,
self.current_index_offset as u64 * std::mem::size_of::<u32>() as u64,
bytemuck::cast_slice(&mesh.indices),
);
self.meshes.insert(
name.clone(),
(
self.current_vertex_offset,
mesh.vertices.len() as u32,
self.current_index_offset,
mesh.indices.len() as u32,
),
);
self.current_vertex_offset += mesh.vertices.len() as u32;
self.current_index_offset += mesh.indices.len() as u32;
}
}
}
pub fn sync_skinned_meshes(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh_cache: &crate::ecs::prefab::resources::MeshCache,
) {
for (name, mesh) in mesh_cache_iter(mesh_cache) {
if mesh.skin_data.is_none() {
continue;
}
if self.skinned_meshes.contains_key(name.as_str()) {
continue;
}
let skin_data = mesh.skin_data.as_ref().unwrap();
let skinned_vertices = &skin_data.skinned_vertices;
let vertex_size =
(skinned_vertices.len() * std::mem::size_of::<SkinnedVertex>()) as u64;
let index_size = (mesh.indices.len() * std::mem::size_of::<u32>()) as u64;
let required_vertex_size = (self.current_skinned_vertex_offset as u64
* std::mem::size_of::<SkinnedVertex>() as u64)
+ vertex_size;
let required_index_size = (self.current_skinned_index_offset as u64
* std::mem::size_of::<u32>() as u64)
+ index_size;
if required_vertex_size > MAX_BUFFER_SIZE || required_index_size > MAX_BUFFER_SIZE {
tracing::warn!(
"Shadow skinned mesh {} exceeds maximum buffer size, skipping shadow rendering",
name
);
continue;
}
if required_vertex_size > self.skinned_vertex_buffer_size {
let new_size = (required_vertex_size * 2)
.max(self.skinned_vertex_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skinned Vertex Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::VERTEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Skinned Vertex Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.skinned_vertex_buffer,
0,
&new_buffer,
0,
self.skinned_vertex_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.skinned_vertex_buffer = new_buffer;
self.skinned_vertex_buffer_size = new_size;
}
if required_index_size > self.skinned_index_buffer_size {
let new_size = (required_index_size * 2)
.max(self.skinned_index_buffer_size * 2)
.min(MAX_BUFFER_SIZE);
let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Shadow Skinned Index Buffer (Resized)"),
size: new_size,
usage: wgpu::BufferUsages::INDEX
| wgpu::BufferUsages::COPY_DST
| wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Shadow Skinned Index Buffer Copy"),
});
encoder.copy_buffer_to_buffer(
&self.skinned_index_buffer,
0,
&new_buffer,
0,
self.skinned_index_buffer_size,
);
queue.submit(Some(encoder.finish()));
self.skinned_index_buffer = new_buffer;
self.skinned_index_buffer_size = new_size;
}
queue.write_buffer(
&self.skinned_vertex_buffer,
self.current_skinned_vertex_offset as u64
* std::mem::size_of::<SkinnedVertex>() as u64,
bytemuck::cast_slice(skinned_vertices),
);
queue.write_buffer(
&self.skinned_index_buffer,
self.current_skinned_index_offset as u64 * std::mem::size_of::<u32>() as u64,
bytemuck::cast_slice(&mesh.indices),
);
self.skinned_meshes.insert(
name.clone(),
(
self.current_skinned_vertex_offset,
skinned_vertices.len() as u32,
self.current_skinned_index_offset,
mesh.indices.len() as u32,
),
);
self.current_skinned_vertex_offset += skinned_vertices.len() as u32;
self.current_skinned_index_offset += mesh.indices.len() as u32;
}
}
pub fn rebuild_skinned_bind_group(&mut self, device: &wgpu::Device) {
self.skinned_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shadow Skinned Bind Group"),
layout: &self.skinned_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.skinned_object_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.joint_matrices_buffer.as_entire_binding(),
},
],
});
}
pub fn rebuild_skinning_compute_bind_group(&mut self, device: &wgpu::Device) {
self.skinning_compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Shadow Skinning Compute Bind Group"),
layout: &self.skinning_compute_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.bone_transforms_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.inverse_bind_matrices_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: self.skin_data_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: self.joint_matrices_buffer.as_entire_binding(),
},
],
});
}
pub fn spotlight_shadow_slots(&self) -> &[SpotlightShadowSlot] {
&self.spotlight_shadow_slots
}
pub fn spotlight_shadow_data(&self) -> &[SpotlightShadowData] {
&self.spotlight_shadow_data
}
pub fn cascade_data(&self) -> &CascadeData {
&self.cascade_data
}
pub fn point_light_shadow_slots(&self) -> &[PointLightShadowSlot] {
&self.point_light_shadow_slots
}
pub fn point_light_shadow_data(&self) -> &[PointLightShadowData] {
&self.point_light_shadow_data
}
pub fn point_light_cubemap_view(&self) -> &wgpu::TextureView {
&self.point_light_cubemap_view
}
}