use wgpu::{Buffer, BufferUsages, VertexBufferLayout, VertexStepMode, VertexAttribute, VertexFormat};
use bytemuck::{Pod, Zeroable};
use crate::renderer::RenderDevice;
pub trait Vertex: Pod + Zeroable {
fn layout() -> VertexBufferLayout<'static>;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct ColorVertex {
pub position: [f32; 3],
pub color: [f32; 3],
}
impl Vertex for ColorVertex {
fn layout() -> VertexBufferLayout<'static> {
const ATTRIBUTES: &[VertexAttribute] = &[
VertexAttribute {
offset: 0,
shader_location: 0,
format: VertexFormat::Float32x3,
},
VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as u64,
shader_location: 1,
format: VertexFormat::Float32x3,
},
];
VertexBufferLayout {
array_stride: std::mem::size_of::<ColorVertex>() as u64,
step_mode: VertexStepMode::Vertex,
attributes: ATTRIBUTES,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct MeshVertex {
pub position: [f32; 3],
pub normal: [f32; 3],
pub texcoord: [f32; 2],
}
impl Vertex for MeshVertex {
fn layout() -> VertexBufferLayout<'static> {
const ATTRIBUTES: &[VertexAttribute] = &[
VertexAttribute {
offset: 0,
shader_location: 0,
format: VertexFormat::Float32x3,
},
VertexAttribute {
offset: 12,
shader_location: 1,
format: VertexFormat::Float32x3,
},
VertexAttribute {
offset: 24,
shader_location: 2,
format: VertexFormat::Float32x2,
},
];
VertexBufferLayout {
array_stride: std::mem::size_of::<MeshVertex>() as u64,
step_mode: VertexStepMode::Vertex,
attributes: ATTRIBUTES,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct PbrVertex {
pub position: [f32; 3],
pub normal: [f32; 3],
pub texcoord: [f32; 2],
pub tangent: [f32; 4],
}
impl Vertex for PbrVertex {
fn layout() -> VertexBufferLayout<'static> {
const ATTRIBUTES: &[VertexAttribute] = &[
VertexAttribute {
offset: 0,
shader_location: 0,
format: VertexFormat::Float32x3,
},
VertexAttribute {
offset: 12,
shader_location: 1,
format: VertexFormat::Float32x3,
},
VertexAttribute {
offset: 24,
shader_location: 2,
format: VertexFormat::Float32x2,
},
VertexAttribute {
offset: 32,
shader_location: 3,
format: VertexFormat::Float32x4,
},
];
VertexBufferLayout {
array_stride: std::mem::size_of::<PbrVertex>() as u64,
step_mode: VertexStepMode::Vertex,
attributes: ATTRIBUTES,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
pub struct SkinnedVertex {
pub position: [f32; 3],
pub normal: [f32; 3],
pub texcoord: [f32; 2],
pub tangent: [f32; 4],
pub joint_indices: [u16; 4],
pub joint_weights: [f32; 4],
}
impl Vertex for SkinnedVertex {
fn layout() -> VertexBufferLayout<'static> {
const ATTRIBUTES: &[VertexAttribute] = &[
VertexAttribute { offset: 0, shader_location: 0, format: VertexFormat::Float32x3 },
VertexAttribute { offset: 12, shader_location: 1, format: VertexFormat::Float32x3 },
VertexAttribute { offset: 24, shader_location: 2, format: VertexFormat::Float32x2 },
VertexAttribute { offset: 32, shader_location: 3, format: VertexFormat::Float32x4 },
VertexAttribute { offset: 48, shader_location: 4, format: VertexFormat::Uint16x4 },
VertexAttribute { offset: 56, shader_location: 5, format: VertexFormat::Float32x4 },
];
VertexBufferLayout {
array_stride: std::mem::size_of::<SkinnedVertex>() as u64,
step_mode: VertexStepMode::Vertex,
attributes: ATTRIBUTES,
}
}
}
pub fn create_vertex_buffer<V: Vertex>(
device: &RenderDevice,
label: &str,
vertices: &[V],
) -> Buffer {
use wgpu::util::{BufferInitDescriptor, DeviceExt};
device.device().create_buffer_init(&BufferInitDescriptor {
label: Some(label),
contents: bytemuck::cast_slice(vertices),
usage: BufferUsages::VERTEX,
})
}
pub fn create_index_buffer(
device: &RenderDevice,
label: &str,
indices: &[u16],
) -> Buffer {
use wgpu::util::{BufferInitDescriptor, DeviceExt};
device.device().create_buffer_init(&BufferInitDescriptor {
label: Some(label),
contents: bytemuck::cast_slice(indices),
usage: BufferUsages::INDEX,
})
}
pub fn create_index_buffer_u32(
device: &RenderDevice,
label: &str,
indices: &[u32],
) -> Buffer {
use wgpu::util::{BufferInitDescriptor, DeviceExt};
device.device().create_buffer_init(&BufferInitDescriptor {
label: Some(label),
contents: bytemuck::cast_slice(indices),
usage: BufferUsages::INDEX,
})
}
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub const HDR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
pub const SHADOW_MAP_SIZE: u32 = 2048;
pub const MSAA_SAMPLE_COUNT: u32 = 4;
pub fn create_shadow_map(
device: &RenderDevice,
size: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width: size,
height: size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_shadow_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
device.device().create_sampler(&wgpu::SamplerDescriptor {
label: Some(label),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
compare: Some(wgpu::CompareFunction::LessEqual),
..Default::default()
})
}
pub fn create_uniform_buffer(
device: &RenderDevice,
label: &str,
contents: &[u8],
) -> Buffer {
use wgpu::util::{BufferInitDescriptor, DeviceExt};
device.device().create_buffer_init(&BufferInitDescriptor {
label: Some(label),
contents,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
})
}
pub fn create_depth_texture(
device: &RenderDevice,
width: u32,
height: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_hdr_render_target(
device: &RenderDevice,
width: u32,
height: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: HDR_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_depth_texture_msaa(
device: &RenderDevice,
width: u32,
height: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
mip_level_count: 1,
sample_count: MSAA_SAMPLE_COUNT,
dimension: wgpu::TextureDimension::D2,
format: DEPTH_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_hdr_msaa_texture(
device: &RenderDevice,
width: u32,
height: u32,
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
mip_level_count: 1,
sample_count: MSAA_SAMPLE_COUNT,
dimension: wgpu::TextureDimension::D2,
format: HDR_FORMAT,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_texture(
device: &RenderDevice,
width: u32,
height: u32,
data: &[u8],
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
device.queue().write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_texture_linear(
device: &RenderDevice,
width: u32,
height: u32,
data: &[u8],
label: &str,
) -> (wgpu::Texture, wgpu::TextureView) {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = device.device().create_texture(&wgpu::TextureDescriptor {
label: Some(label),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
device.queue().write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
pub fn create_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
device.device().create_sampler(&wgpu::SamplerDescriptor {
label: Some(label),
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::Repeat,
address_mode_w: wgpu::AddressMode::Repeat,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_vertex_size() {
assert_eq!(std::mem::size_of::<ColorVertex>(), 24); }
#[test]
fn test_color_vertex_layout() {
let layout = ColorVertex::layout();
assert_eq!(layout.array_stride, 24);
assert_eq!(layout.attributes.len(), 2);
assert_eq!(layout.attributes[0].offset, 0);
assert_eq!(layout.attributes[0].shader_location, 0);
assert_eq!(layout.attributes[1].offset, 12);
assert_eq!(layout.attributes[1].shader_location, 1);
}
#[test]
fn test_color_vertex_creation() {
let v = ColorVertex {
position: [1.0, 2.0, 3.0],
color: [0.5, 0.5, 0.5],
};
assert_eq!(v.position, [1.0, 2.0, 3.0]);
assert_eq!(v.color, [0.5, 0.5, 0.5]);
}
#[test]
fn test_color_vertex_bytemuck() {
let vertices = [
ColorVertex { position: [0.0, 0.5, 0.0], color: [1.0, 0.0, 0.0] },
ColorVertex { position: [-0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0] },
];
let bytes: &[u8] = bytemuck::cast_slice(&vertices);
assert_eq!(bytes.len(), 48); }
#[test]
fn test_depth_format() {
assert_eq!(DEPTH_FORMAT, wgpu::TextureFormat::Depth32Float);
}
#[test]
fn test_mesh_vertex_size() {
assert_eq!(std::mem::size_of::<MeshVertex>(), 32); }
#[test]
fn test_mesh_vertex_layout() {
let layout = MeshVertex::layout();
assert_eq!(layout.array_stride, 32);
assert_eq!(layout.attributes.len(), 3);
assert_eq!(layout.attributes[0].offset, 0); assert_eq!(layout.attributes[1].offset, 12); assert_eq!(layout.attributes[2].offset, 24); }
#[test]
fn test_pbr_vertex_size() {
assert_eq!(std::mem::size_of::<PbrVertex>(), 48); }
#[test]
fn test_pbr_vertex_layout() {
let layout = PbrVertex::layout();
assert_eq!(layout.array_stride, 48);
assert_eq!(layout.attributes.len(), 4);
assert_eq!(layout.attributes[0].offset, 0); assert_eq!(layout.attributes[1].offset, 12); assert_eq!(layout.attributes[2].offset, 24); assert_eq!(layout.attributes[3].offset, 32); }
}