use {
crate::{state::State, vertex, Vertex},
std::{borrow::Cow, error, fmt, marker::PhantomData, mem, slice},
wgpu::{Buffer, RenderPass},
};
type Face = [u16; 3];
#[derive(Clone)]
pub struct MeshData<'a, V> {
verts: &'a [V],
indxs: Option<Cow<'a, [Face]>>,
}
impl<'a, V> MeshData<'a, V> {
pub const fn from_verts(verts: &'a [V]) -> Self {
Self { verts, indxs: None }
}
pub fn new(verts: &'a [V], indxs: &'a [Face]) -> Result<Self, Error> {
let len: u16 = verts.len().try_into().map_err(|_| Error::TooManyVertices)?;
if let Some(index) = indxs.iter().flatten().copied().find(|&i| i >= len) {
return Err(Error::InvalidIndex { index });
}
let indxs = Some(Cow::Borrowed(indxs));
Ok(Self { verts, indxs })
}
pub fn from_quads(verts: &'a [[V; 4]]) -> Result<Self, TooManyVertices> {
let new_len = verts.len() * 4;
let verts = unsafe { slice::from_raw_parts(verts.as_ptr().cast(), new_len) };
let indxs = {
let len: u16 = new_len.try_into().map_err(|_| TooManyVertices)?;
Some(
(0..len)
.step_by(4)
.flat_map(|i| [[i, i + 1, i + 2], [i, i + 2, i + 3]])
.collect(),
)
};
Ok(Self { verts, indxs })
}
}
#[derive(Debug)]
pub enum Error {
TooManyVertices,
InvalidIndex { index: u16 },
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooManyVertices => write!(f, "too many vertices"),
Self::InvalidIndex { index } => write!(f, "invalid index: {index}"),
}
}
}
impl error::Error for Error {}
#[derive(Debug)]
pub struct TooManyVertices;
impl fmt::Display for TooManyVertices {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "too many vertices")
}
}
impl error::Error for TooManyVertices {}
pub struct Mesh<V> {
verts: Buffer,
indxs: Option<Buffer>,
ty: PhantomData<V>,
}
impl<V> Mesh<V> {
pub(crate) fn new(state: &State, data: &MeshData<V>) -> Self
where
V: Vertex,
{
use wgpu::{
util::{BufferInitDescriptor, DeviceExt},
BufferUsages,
};
let device = state.device();
let verts = {
let desc = BufferInitDescriptor {
label: None,
contents: vertex::verts_as_bytes(data.verts),
usage: BufferUsages::VERTEX,
};
device.create_buffer_init(&desc)
};
let indxs = data.indxs.as_deref().map(|indxs| {
let desc = BufferInitDescriptor {
label: None,
contents: bytemuck::cast_slice(indxs),
usage: BufferUsages::INDEX,
};
device.create_buffer_init(&desc)
});
Self {
verts,
indxs,
ty: PhantomData,
}
}
pub(crate) fn is_indexed(&self) -> bool {
self.indxs.is_some()
}
pub(crate) fn draw<'a>(&'a self, pass: &mut RenderPass<'a>, slot: u32, count: u32) {
use wgpu::IndexFormat;
pass.set_vertex_buffer(slot, self.verts.slice(..));
match &self.indxs {
Some(indxs) => {
pass.set_index_buffer(indxs.slice(..), IndexFormat::Uint16);
let len = indxs.size() as u32 / mem::size_of::<u16>() as u32;
pass.draw_indexed(0..len, 0, 0..count);
}
None => {
let len = self.verts.size() as u32 / mem::size_of::<V>() as u32;
pass.draw(0..len, 0..count);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_quads() {
let verts = [[0, 1, 2, 3], [4, 5, 6, 7]];
let data = MeshData::from_quads(&verts).expect("mesh data");
let indxs = data.indxs.expect("indices");
assert_eq!(data.verts.len(), 8);
assert_eq!(indxs.len(), 4);
assert_eq!([data.verts[0], data.verts[1], data.verts[2]], indxs[0]);
assert_eq!([data.verts[0], data.verts[2], data.verts[3]], indxs[1]);
assert_eq!([data.verts[4], data.verts[5], data.verts[6]], indxs[2]);
assert_eq!([data.verts[4], data.verts[6], data.verts[7]], indxs[3]);
}
}