use glam::Mat4;
use std::num::NonZeroU64;
use wgpu::util::DeviceExt;
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
#[allow(clippy::pub_underscore_fields)]
pub struct ReflectionUniforms {
pub reflection_matrix: [[f32; 4]; 4],
pub intensity: f32,
pub ground_height: f32,
pub _padding: [f32; 2],
}
impl Default for ReflectionUniforms {
fn default() -> Self {
Self {
reflection_matrix: Mat4::IDENTITY.to_cols_array_2d(),
intensity: 0.25,
ground_height: 0.0,
_padding: [0.0; 2],
}
}
}
pub struct ReflectionPass {
uniform_buffer: wgpu::Buffer,
pub bind_group_layout: wgpu::BindGroupLayout,
bind_group: wgpu::BindGroup,
}
impl ReflectionPass {
#[must_use]
pub fn new(device: &wgpu::Device) -> Self {
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Reflection Uniform Buffer"),
contents: bytemuck::cast_slice(&[ReflectionUniforms::default()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Reflection 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: false,
min_binding_size: NonZeroU64::new(80),
},
count: None,
}],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Reflection Bind Group"),
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});
Self {
uniform_buffer,
bind_group_layout,
bind_group,
}
}
pub fn update_uniforms(
&self,
queue: &wgpu::Queue,
reflection_matrix: Mat4,
intensity: f32,
ground_height: f32,
) {
let uniforms = ReflectionUniforms {
reflection_matrix: reflection_matrix.to_cols_array_2d(),
intensity,
ground_height,
_padding: [0.0; 2],
};
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
}
#[must_use]
pub fn bind_group(&self) -> &wgpu::BindGroup {
&self.bind_group
}
#[must_use]
pub fn bind_group_layout(&self) -> &wgpu::BindGroupLayout {
&self.bind_group_layout
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reflection_uniforms_size() {
assert_eq!(
std::mem::size_of::<ReflectionUniforms>(),
64 + 4 + 4 + 8 );
}
}