use crate::GraphicsContext;
use glam::{Vec2, Vec3};
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VertexFormat {
Position,
PositionNormal,
PositionUv,
PositionNormalUv,
PositionNormalUvColor,
}
impl VertexFormat {
pub fn vertex_size(&self) -> u64 {
match self {
VertexFormat::Position => 12, VertexFormat::PositionNormal => 24, VertexFormat::PositionUv => 20, VertexFormat::PositionNormalUv => 32, VertexFormat::PositionNormalUvColor => 48, }
}
pub fn buffer_layout(&self) -> wgpu::VertexBufferLayout<'static> {
match self {
VertexFormat::Position => wgpu::VertexBufferLayout {
array_stride: 12,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
}],
},
VertexFormat::PositionNormal => wgpu::VertexBufferLayout {
array_stride: 24,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 12,
shader_location: 1,
},
],
},
VertexFormat::PositionUv => wgpu::VertexBufferLayout {
array_stride: 20,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: 12,
shader_location: 1,
},
],
},
VertexFormat::PositionNormalUv => wgpu::VertexBufferLayout {
array_stride: 32,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 12,
shader_location: 1,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
},
VertexFormat::PositionNormalUvColor => wgpu::VertexBufferLayout {
array_stride: 48,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 12,
shader_location: 1,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x4,
offset: 32,
shader_location: 3,
},
],
},
}
}
}
pub struct Mesh {
vertex_buffer: wgpu::Buffer,
index_buffer: Option<wgpu::Buffer>,
vertex_format: VertexFormat,
topology: wgpu::PrimitiveTopology,
index_format: Option<wgpu::IndexFormat>,
vertex_count: u32,
index_count: Option<u32>,
_context: Arc<GraphicsContext>,
}
impl Mesh {
pub fn draw<'a>(&'a self, pass: &mut wgpu::RenderPass<'a>) {
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
if let Some(ref index_buffer) = self.index_buffer {
let index_format = self.index_format.expect("Index format must be set");
pass.set_index_buffer(index_buffer.slice(..), index_format);
pass.draw_indexed(0..self.index_count.unwrap(), 0, 0..1);
} else {
pass.draw(0..self.vertex_count, 0..1);
}
}
pub fn draw_instanced<'a>(&'a self, pass: &mut wgpu::RenderPass<'a>, instances: u32) {
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
if let Some(ref index_buffer) = self.index_buffer {
let index_format = self.index_format.expect("Index format must be set");
pass.set_index_buffer(index_buffer.slice(..), index_format);
pass.draw_indexed(0..self.index_count.unwrap(), 0, 0..instances);
} else {
pass.draw(0..self.vertex_count, 0..instances);
}
}
pub fn vertex_format(&self) -> VertexFormat {
self.vertex_format
}
pub fn topology(&self) -> wgpu::PrimitiveTopology {
self.topology
}
pub fn vertex_count(&self) -> u32 {
self.vertex_count
}
pub fn index_count(&self) -> Option<u32> {
self.index_count
}
pub fn cube(ctx: Arc<GraphicsContext>, size: f32) -> Self {
let half = size / 2.0;
let positions = vec![
Vec3::new(-half, -half, half),
Vec3::new(half, -half, half),
Vec3::new(half, half, half),
Vec3::new(-half, half, half),
Vec3::new(-half, -half, -half),
Vec3::new(-half, half, -half),
Vec3::new(half, half, -half),
Vec3::new(half, -half, -half),
Vec3::new(-half, half, -half),
Vec3::new(-half, half, half),
Vec3::new(half, half, half),
Vec3::new(half, half, -half),
Vec3::new(-half, -half, -half),
Vec3::new(half, -half, -half),
Vec3::new(half, -half, half),
Vec3::new(-half, -half, half),
Vec3::new(half, -half, -half),
Vec3::new(half, half, -half),
Vec3::new(half, half, half),
Vec3::new(half, -half, half),
Vec3::new(-half, -half, -half),
Vec3::new(-half, -half, half),
Vec3::new(-half, half, half),
Vec3::new(-half, half, -half),
];
let normals = vec![
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.0, 0.0, 1.0),
Vec3::new(0.0, 0.0, -1.0),
Vec3::new(0.0, 0.0, -1.0),
Vec3::new(0.0, 0.0, -1.0),
Vec3::new(0.0, 0.0, -1.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, -1.0, 0.0),
Vec3::new(0.0, -1.0, 0.0),
Vec3::new(0.0, -1.0, 0.0),
Vec3::new(0.0, -1.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(-1.0, 0.0, 0.0),
Vec3::new(-1.0, 0.0, 0.0),
Vec3::new(-1.0, 0.0, 0.0),
Vec3::new(-1.0, 0.0, 0.0),
];
let uvs = vec![
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(0.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
Vec2::new(1.0, 0.0),
Vec2::new(1.0, 1.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
Vec2::new(0.0, 1.0),
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
];
#[rustfmt::skip]
let indices: Vec<u32> = vec![
0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8, 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20, ];
MeshBuilder::new()
.with_positions(positions)
.with_normals(normals)
.with_uvs(uvs)
.with_indices(indices)
.build(ctx)
}
pub fn plane(ctx: Arc<GraphicsContext>, width: f32, depth: f32) -> Self {
let hw = width / 2.0;
let hd = depth / 2.0;
let positions = vec![
Vec3::new(-hw, 0.0, -hd),
Vec3::new(hw, 0.0, -hd),
Vec3::new(hw, 0.0, hd),
Vec3::new(-hw, 0.0, hd),
];
let normals = vec![
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
];
let uvs = vec![
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
Vec2::new(1.0, 0.0),
Vec2::new(0.0, 0.0),
];
let indices = vec![0, 1, 2, 2, 3, 0];
MeshBuilder::new()
.with_positions(positions)
.with_normals(normals)
.with_uvs(uvs)
.with_indices(indices)
.build(ctx)
}
pub fn sphere(ctx: Arc<GraphicsContext>, radius: f32, segments: u32, rings: u32) -> Self {
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
let mut indices = Vec::new();
for ring in 0..=rings {
let theta = ring as f32 * std::f32::consts::PI / rings as f32;
let sin_theta = theta.sin();
let cos_theta = theta.cos();
for segment in 0..=segments {
let phi = segment as f32 * 2.0 * std::f32::consts::PI / segments as f32;
let sin_phi = phi.sin();
let cos_phi = phi.cos();
let x = sin_theta * cos_phi;
let y = cos_theta;
let z = sin_theta * sin_phi;
positions.push(Vec3::new(x * radius, y * radius, z * radius));
normals.push(Vec3::new(x, y, z));
uvs.push(Vec2::new(
segment as f32 / segments as f32,
ring as f32 / rings as f32,
));
}
}
for ring in 0..rings {
for segment in 0..segments {
let first = ring * (segments + 1) + segment;
let second = first + segments + 1;
indices.push(first);
indices.push(second);
indices.push(first + 1);
indices.push(second);
indices.push(second + 1);
indices.push(first + 1);
}
}
MeshBuilder::new()
.with_positions(positions)
.with_normals(normals)
.with_uvs(uvs)
.with_indices(indices)
.build(ctx)
}
}
pub struct MeshBuilder {
positions: Vec<Vec3>,
normals: Option<Vec<Vec3>>,
uvs: Option<Vec<Vec2>>,
colors: Option<Vec<[f32; 4]>>,
indices: Option<Vec<u32>>,
topology: wgpu::PrimitiveTopology,
}
impl MeshBuilder {
pub fn new() -> Self {
Self {
positions: Vec::new(),
normals: None,
uvs: None,
colors: None,
indices: None,
topology: wgpu::PrimitiveTopology::TriangleList,
}
}
pub fn with_positions(mut self, positions: Vec<Vec3>) -> Self {
self.positions = positions;
self
}
pub fn with_normals(mut self, normals: Vec<Vec3>) -> Self {
self.normals = Some(normals);
self
}
pub fn with_uvs(mut self, uvs: Vec<Vec2>) -> Self {
self.uvs = Some(uvs);
self
}
pub fn with_colors(mut self, colors: Vec<[f32; 4]>) -> Self {
self.colors = Some(colors);
self
}
pub fn with_indices(mut self, indices: Vec<u32>) -> Self {
self.indices = Some(indices);
self
}
pub fn with_topology(mut self, topology: wgpu::PrimitiveTopology) -> Self {
self.topology = topology;
self
}
pub fn generate_flat_normals(mut self) -> Self {
if self.indices.is_none() {
panic!("Cannot generate flat normals without indices");
}
let indices = self.indices.as_ref().unwrap();
let mut normals = vec![Vec3::ZERO; self.positions.len()];
for triangle in indices.chunks(3) {
let i0 = triangle[0] as usize;
let i1 = triangle[1] as usize;
let i2 = triangle[2] as usize;
let v0 = self.positions[i0];
let v1 = self.positions[i1];
let v2 = self.positions[i2];
let edge1 = v1 - v0;
let edge2 = v2 - v0;
let normal = edge1.cross(edge2).normalize();
normals[i0] = normal;
normals[i1] = normal;
normals[i2] = normal;
}
self.normals = Some(normals);
self
}
pub fn generate_smooth_normals(mut self) -> Self {
if self.indices.is_none() {
panic!("Cannot generate smooth normals without indices");
}
let indices = self.indices.as_ref().unwrap();
let mut normals = vec![Vec3::ZERO; self.positions.len()];
let mut counts = vec![0u32; self.positions.len()];
for triangle in indices.chunks(3) {
let i0 = triangle[0] as usize;
let i1 = triangle[1] as usize;
let i2 = triangle[2] as usize;
let v0 = self.positions[i0];
let v1 = self.positions[i1];
let v2 = self.positions[i2];
let edge1 = v1 - v0;
let edge2 = v2 - v0;
let normal = edge1.cross(edge2);
normals[i0] += normal;
normals[i1] += normal;
normals[i2] += normal;
counts[i0] += 1;
counts[i1] += 1;
counts[i2] += 1;
}
for (i, normal) in normals.iter_mut().enumerate() {
if counts[i] > 0 {
*normal = (*normal / counts[i] as f32).normalize();
}
}
self.normals = Some(normals);
self
}
pub fn build(self, ctx: Arc<GraphicsContext>) -> Mesh {
let vertex_format = match (&self.normals, &self.uvs, &self.colors) {
(None, None, None) => VertexFormat::Position,
(Some(_), None, None) => VertexFormat::PositionNormal,
(None, Some(_), None) => VertexFormat::PositionUv,
(Some(_), Some(_), None) => VertexFormat::PositionNormalUv,
(Some(_), Some(_), Some(_)) => VertexFormat::PositionNormalUvColor,
_ => panic!("Invalid vertex format combination"),
};
let mut vertex_data = Vec::new();
for i in 0..self.positions.len() {
vertex_data.extend_from_slice(bytemuck::bytes_of(&self.positions[i]));
if let Some(ref normals) = self.normals {
vertex_data.extend_from_slice(bytemuck::bytes_of(&normals[i]));
}
if let Some(ref uvs) = self.uvs {
vertex_data.extend_from_slice(bytemuck::bytes_of(&uvs[i]));
}
if let Some(ref colors) = self.colors {
vertex_data.extend_from_slice(bytemuck::bytes_of(&colors[i]));
}
}
let vertex_buffer = ctx.device().create_buffer(&wgpu::BufferDescriptor {
label: Some("Mesh Vertex Buffer"),
size: vertex_data.len() as u64,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
ctx.queue().write_buffer(&vertex_buffer, 0, &vertex_data);
let (index_buffer, index_format, index_count) = if let Some(ref indices) = self.indices {
let buffer = ctx.device().create_buffer(&wgpu::BufferDescriptor {
label: Some("Mesh Index Buffer"),
size: (indices.len() * std::mem::size_of::<u32>()) as u64,
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
ctx.queue()
.write_buffer(&buffer, 0, bytemuck::cast_slice(indices));
(
Some(buffer),
Some(wgpu::IndexFormat::Uint32),
Some(indices.len() as u32),
)
} else {
(None, None, None)
};
Mesh {
vertex_buffer,
index_buffer,
vertex_format,
topology: self.topology,
index_format,
vertex_count: self.positions.len() as u32,
index_count,
_context: ctx,
}
}
}
impl Default for MeshBuilder {
fn default() -> Self {
Self::new()
}
}