Skip to content

Commit

Permalink
Mesh
Browse files Browse the repository at this point in the history
  • Loading branch information
nanoqsh committed Dec 26, 2023
1 parent 3adba38 commit e87f189
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 2 deletions.
3 changes: 2 additions & 1 deletion dunge/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

pub mod mesh;
mod state;
154 changes: 154 additions & 0 deletions dunge/src/mesh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use {
crate::state::State,
dunge_shader::vertex::{self, Vertex},
std::{borrow::Cow, marker::PhantomData, mem, slice},
wgpu::{Buffer, RenderPass},
};

type Face = [u16; 3];

#[derive(Clone)]
pub struct Data<'a, V> {
verts: &'a [V],
indxs: Option<Cow<'a, [Face]>>,
}

impl<'a, V> Data<'a, V> {
/// Creates a [mesh data](crate::mesh::Data) from given vertices.
pub fn from_verts(verts: &'a [V]) -> Self {
Self { verts, indxs: None }
}

/// Creates a [mesh data](crate::mesh::Data) from given vertices and indices.
///
/// # Errors
/// Returns an [error](crate::mesh::Error) if the passed data is incorrect.
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::WrongIndex { index });
}

let indxs = Some(Cow::Borrowed(indxs));
Ok(Self { verts, indxs })
}

/// Creates a [mesh data](crate::mesh::Data) from given quadrilaterals.
///
/// # Errors
/// Returns an [error](crate::mesh::TooManyVertices) if too many vertices are passed.
pub fn from_quads(verts: &'a [[V; 4]]) -> Result<Self, TooManyVertices> {
let new_len = verts.len() * 4;

// SAFETY:
// * It's ok to cast a slice of arrays to the flat slice.
// (TODO) This can be done safely when this function will be stable:
// https://doc.rust-lang.org/std/primitive.slice.html#method.flatten
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 })
}
}

/// An error returned from the [mesh data](crate::mesh::Data) constructors.
#[derive(Debug)]
pub enum Error {
/// Vertices length doesn't fit in [`u16`](std::u16) integer.
TooManyVertices,

/// The vertex index is out of bounds of the vertex slice.
WrongIndex { index: u16 },
}

/// Vertices length doesn't fit in [`u16`](std::u16) integer.
#[derive(Debug)]
pub struct TooManyVertices;

pub struct Mesh<V> {
verts: Buffer,
indxs: Option<Buffer>,
ty: PhantomData<V>,
}

impl<V> Mesh<V> {
pub(crate) fn new(state: &State, data: &Data<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 | BufferUsages::COPY_DST,
};

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 | BufferUsages::COPY_DST,
};

device.create_buffer_init(&desc)
});

Self {
verts,
indxs,
ty: PhantomData,
}
}

pub(crate) fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) {
use wgpu::IndexFormat;

pass.set_vertex_buffer(0, 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..1);
}
None => {
let len = self.verts.size() as u32 / mem::size_of::<V>() as u32;
pass.draw(0..len, 0..1);
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn from_quads() {
let verts = [[0, 1, 2, 3], [4, 5, 6, 7]];
let data = Data::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]);
}
}
16 changes: 16 additions & 0 deletions dunge/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use wgpu::{Device, Queue};

pub(crate) struct State {
device: Device,
queue: Queue,
}

impl State {
pub fn device(&self) -> &Device {
&self.device
}

pub fn queue(&self) -> &Queue {
&self.queue
}
}
11 changes: 10 additions & 1 deletion dunge_shader/src/vertex.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{iter, slice};
use std::{iter, mem, slice};

#[derive(Clone, Copy)]
pub enum VectorType {
Expand Down Expand Up @@ -37,6 +37,15 @@ pub unsafe trait Vertex {
const DECL: DeclareInput;
}

pub fn verts_as_bytes<V>(verts: &[V]) -> &[u8]
where
V: Vertex,
{
// SAFETY:
// * The `Vertex` invariant states converting a slice of vertices to bytes is safe
unsafe { slice::from_raw_parts(verts.as_ptr().cast(), mem::size_of_val(verts)) }
}

pub trait Projection {
fn projection(id: u32) -> Self;
}

0 comments on commit e87f189

Please sign in to comment.