use glam::{Mat4, UVec2, Vec2, Vec3, Vec3A, Vec4};
use std::{
fmt::Debug,
hash::Hash,
marker::PhantomData,
mem,
num::NonZeroU32,
sync::{Arc, Weak},
};
use thiserror::Error;
pub use glam;
pub struct RawResourceHandle<T> {
pub idx: usize,
_phantom: PhantomData<T>,
}
impl<T> Debug for RawResourceHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RawResourceHandle").field("idx", &self.idx).finish()
}
}
impl<T> Copy for RawResourceHandle<T> {}
impl<T> Clone for RawResourceHandle<T> {
fn clone(&self) -> Self {
Self {
idx: self.idx,
_phantom: PhantomData,
}
}
}
impl<T> PartialEq for RawResourceHandle<T> {
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
impl<T> Eq for RawResourceHandle<T> {}
pub struct ResourceHandle<T> {
refcount: Arc<()>,
idx: usize,
_phantom: PhantomData<T>,
}
impl<T> Debug for ResourceHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceHandle")
.field("refcount", &Arc::strong_count(&self.refcount))
.field("idx", &self.idx)
.finish()
}
}
impl<T> Clone for ResourceHandle<T> {
fn clone(&self) -> Self {
Self {
refcount: self.refcount.clone(),
idx: self.idx,
_phantom: self._phantom,
}
}
}
impl<T> PartialEq for ResourceHandle<T> {
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
impl<T> Eq for ResourceHandle<T> {}
impl<T> Hash for ResourceHandle<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.idx.hash(state);
}
}
impl<T> ResourceHandle<T> {
pub fn new(idx: usize) -> Self {
Self {
refcount: Arc::new(()),
idx,
_phantom: PhantomData,
}
}
pub fn get_raw(&self) -> RawResourceHandle<T> {
RawResourceHandle {
idx: self.idx,
_phantom: PhantomData,
}
}
pub fn get_weak_refcount(&self) -> Weak<()> {
Arc::downgrade(&self.refcount)
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! declare_handle {
($($name:ident<$ty:ty>),*) => {$(
#[doc = concat!("Refcounted handle to a ", stringify!($ty) ,".")]
pub type $name = ResourceHandle<$ty>;
)*};
}
declare_handle!(
MeshHandle<Mesh>,
TextureHandle<Texture>,
MaterialHandle<MaterialTag>,
ObjectHandle<Object>,
DirectionalLightHandle<DirectionalLight>,
SkeletonHandle<Skeleton>
);
#[macro_export]
#[doc(hidden)]
macro_rules! declare_raw_handle {
($($name:ident<$ty:ty>),*) => {$(
#[doc = concat!("Internal non-owning handle to a ", stringify!($ty) ,".")]
pub type $name = RawResourceHandle<$ty>;
)*};
}
declare_raw_handle!(
RawMeshHandle<Mesh>,
RawTextureHandle<Texture>,
RawMaterialHandle<MaterialTag>,
RawObjectHandle<Object>,
RawDirectionalLightHandle<DirectionalLight>,
RawSkeletonHandle<Skeleton>
);
macro_rules! changeable_struct {
($(#[$outer:meta])* pub struct $name:ident <- $name_change:ident { $($(#[$inner:meta])* $field_vis:vis $field_name:ident : $field_type:ty),* $(,)? } ) => {
$(#[$outer])*
#[derive(Debug, Clone)]
pub struct $name {
$(
$(#[$inner])* $field_vis $field_name : $field_type
),*
}
impl $name {
pub fn update_from_changes(&mut self, change: $name_change) {
$(
if let Some(inner) = change.$field_name {
self.$field_name = inner;
}
);*
}
}
#[doc = concat!("Describes a modification to a ", stringify!($name), ".")]
#[derive(Debug, Default, Clone)]
pub struct $name_change {
$(
$field_vis $field_name : Option<$field_type>
),*
}
};
}
#[doc(inline)]
pub use wgt::{Backend, Backends, Color, DeviceType, PresentMode, TextureFormat, TextureUsages};
pub const MAX_VERTEX_COUNT: usize = 1 << 24;
#[derive(Debug, Copy, Clone)]
pub enum VertexBufferType {
Position,
Normal,
Tangent,
Uv0,
Uv1,
Colors,
}
#[derive(Debug, Error)]
pub enum MeshValidationError {
#[error("Mesh's {ty:?} buffer has {actual} vertices but the position buffer has {expected}")]
MismatchedVertexCount {
ty: VertexBufferType,
expected: usize,
actual: usize,
},
#[error("Mesh has {count} vertices when the vertex limit is {}", MAX_VERTEX_COUNT)]
ExceededMaxVertexCount { count: usize },
#[error("Mesh has {count} indices which is not a multiple of three. Meshes are always composed of triangles")]
IndexCountNotMultipleOfThree { count: usize },
#[error(
"Index at position {index} has the value {value} which is out of bounds for vertex buffers of {max} length"
)]
IndexOutOfBounds { index: usize, value: u32, max: usize },
}
#[derive(Debug, Default)]
pub struct MeshBuilder {
vertex_positions: Vec<Vec3>,
vertex_normals: Option<Vec<Vec3>>,
vertex_tangents: Option<Vec<Vec3>>,
vertex_uv0: Option<Vec<Vec2>>,
vertex_uv1: Option<Vec<Vec2>>,
vertex_colors: Option<Vec<[u8; 4]>>,
vertex_joint_indices: Option<Vec<[u16; 4]>>,
vertex_joint_weights: Option<Vec<Vec4>>,
vertex_count: usize,
indices: Option<Vec<u32>>,
without_validation: bool,
handedness: Handedness,
flip_winding_order: bool,
double_sided: bool,
}
impl MeshBuilder {
pub fn new(vertex_positions: Vec<Vec3>, handedness: Handedness) -> Self {
Self {
vertex_count: vertex_positions.len(),
vertex_positions,
handedness,
..Self::default()
}
}
pub fn with_vertex_normals(mut self, normals: Vec<Vec3>) -> Self {
self.vertex_normals = Some(normals);
self
}
pub fn with_vertex_tangents(mut self, tangents: Vec<Vec3>) -> Self {
self.vertex_tangents = Some(tangents);
self
}
pub fn with_vertex_uv0(mut self, uvs: Vec<Vec2>) -> Self {
self.vertex_uv0 = Some(uvs);
self
}
pub fn with_vertex_uv1(mut self, uvs: Vec<Vec2>) -> Self {
self.vertex_uv1 = Some(uvs);
self
}
pub fn with_vertex_colors(mut self, colors: Vec<[u8; 4]>) -> Self {
self.vertex_colors = Some(colors);
self
}
pub fn with_vertex_joint_indices(mut self, joint_indices: Vec<[u16; 4]>) -> Self {
self.vertex_joint_indices = Some(joint_indices);
self
}
pub fn with_vertex_joint_weights(mut self, joint_weights: Vec<Vec4>) -> Self {
self.vertex_joint_weights = Some(joint_weights);
self
}
pub fn with_indices(mut self, indices: Vec<u32>) -> Self {
self.indices = Some(indices);
self
}
pub fn with_flip_winding_order(mut self) -> Self {
self.flip_winding_order = true;
self
}
pub fn with_double_sided(mut self) -> Self {
self.double_sided = true;
self
}
pub unsafe fn without_validation(mut self) -> Self {
self.without_validation = true;
self
}
pub fn build(self) -> Result<Mesh, MeshValidationError> {
let length = self.vertex_count;
let has_normals = self.vertex_normals.is_some();
let has_tangents = self.vertex_tangents.is_some();
let has_uvs = self.vertex_uv0.is_some();
let mut mesh = Mesh {
vertex_positions: self.vertex_positions,
vertex_normals: self.vertex_normals.unwrap_or_else(|| vec![Vec3::ZERO; length]),
vertex_tangents: self.vertex_tangents.unwrap_or_else(|| vec![Vec3::ZERO; length]),
vertex_uv0: self.vertex_uv0.unwrap_or_else(|| vec![Vec2::ZERO; length]),
vertex_uv1: self.vertex_uv1.unwrap_or_else(|| vec![Vec2::ZERO; length]),
vertex_colors: self.vertex_colors.unwrap_or_else(|| vec![[255; 4]; length]),
vertex_joint_indices: self.vertex_joint_indices.unwrap_or_else(|| vec![[0; 4]; length]),
vertex_joint_weights: self.vertex_joint_weights.unwrap_or_else(|| vec![Vec4::ZERO; length]),
indices: self.indices.unwrap_or_else(|| (0..length as u32).collect()),
};
if !self.without_validation {
mesh.validate()?;
}
if self.flip_winding_order {
mesh.flip_winding_order();
}
if !has_normals {
unsafe { mesh.calculate_normals(self.handedness, true) };
}
if !has_tangents && has_uvs {
unsafe { mesh.calculate_tangents(true) };
}
Ok(mesh)
}
}
#[derive(Debug)]
pub struct Mesh {
pub vertex_positions: Vec<Vec3>,
pub vertex_normals: Vec<Vec3>,
pub vertex_tangents: Vec<Vec3>,
pub vertex_uv0: Vec<Vec2>,
pub vertex_uv1: Vec<Vec2>,
pub vertex_colors: Vec<[u8; 4]>,
pub vertex_joint_indices: Vec<[u16; 4]>,
pub vertex_joint_weights: Vec<Vec4>,
pub indices: Vec<u32>,
}
impl Clone for Mesh {
fn clone(&self) -> Self {
Self {
vertex_positions: self.vertex_positions.clone(),
vertex_normals: self.vertex_normals.clone(),
vertex_tangents: self.vertex_tangents.clone(),
vertex_uv0: self.vertex_uv0.clone(),
vertex_uv1: self.vertex_uv1.clone(),
vertex_colors: self.vertex_colors.clone(),
vertex_joint_indices: self.vertex_joint_indices.clone(),
vertex_joint_weights: self.vertex_joint_weights.clone(),
indices: self.indices.clone(),
}
}
}
impl Mesh {
pub fn validate(&self) -> Result<(), MeshValidationError> {
let position_length = self.vertex_positions.len();
let indices_length = self.indices.len();
if position_length > MAX_VERTEX_COUNT {
return Err(MeshValidationError::ExceededMaxVertexCount { count: position_length });
}
let first_different_length = [
(self.vertex_normals.len(), VertexBufferType::Normal),
(self.vertex_tangents.len(), VertexBufferType::Tangent),
(self.vertex_uv0.len(), VertexBufferType::Uv0),
(self.vertex_uv1.len(), VertexBufferType::Uv1),
(self.vertex_colors.len(), VertexBufferType::Colors),
]
.iter()
.find_map(|&(len, ty)| if len != position_length { Some((len, ty)) } else { None });
if let Some((len, ty)) = first_different_length {
return Err(MeshValidationError::MismatchedVertexCount {
ty,
actual: len,
expected: position_length,
});
}
if indices_length % 3 != 0 {
return Err(MeshValidationError::IndexCountNotMultipleOfThree { count: indices_length });
}
let first_oob_index = self.indices.iter().enumerate().find_map(|(idx, &i)| {
if (i as usize) >= position_length {
Some((idx, i))
} else {
None
}
});
if let Some((index, value)) = first_oob_index {
return Err(MeshValidationError::IndexOutOfBounds {
index,
value,
max: position_length,
});
}
Ok(())
}
pub unsafe fn calculate_normals(&mut self, handedness: Handedness, zeroed: bool) {
if handedness == Handedness::Left {
Self::calculate_normals_for_buffers::<true>(
&mut self.vertex_normals,
&self.vertex_positions,
&self.indices,
zeroed,
)
} else {
Self::calculate_normals_for_buffers::<false>(
&mut self.vertex_normals,
&self.vertex_positions,
&self.indices,
zeroed,
)
}
}
pub unsafe fn calculate_normals_for_buffers<const LEFT_HANDED: bool>(
normals: &mut [Vec3],
positions: &[Vec3],
indices: &[u32],
zeroed: bool,
) {
debug_assert_eq!(normals.len(), positions.len());
if !zeroed {
for norm in normals.iter_mut() {
*norm = Vec3::ZERO;
}
}
for idx in indices.chunks_exact(3) {
let (idx0, idx1, idx2) = match *idx {
[idx0, idx1, idx2] => (idx0, idx1, idx2),
_ => std::hint::unreachable_unchecked(),
};
let pos1 = *positions.get_unchecked(idx0 as usize);
let pos2 = *positions.get_unchecked(idx1 as usize);
let pos3 = *positions.get_unchecked(idx2 as usize);
let edge1 = pos2 - pos1;
let edge2 = pos3 - pos1;
let normal = if LEFT_HANDED {
edge1.cross(edge2)
} else {
edge2.cross(edge1)
};
*normals.get_unchecked_mut(idx0 as usize) += normal;
*normals.get_unchecked_mut(idx1 as usize) += normal;
*normals.get_unchecked_mut(idx2 as usize) += normal;
}
for normal in normals.iter_mut() {
*normal = normal.normalize_or_zero();
}
}
pub unsafe fn calculate_tangents(&mut self, zeroed: bool) {
Self::calculate_tangents_for_buffers(
&mut self.vertex_tangents,
&self.vertex_positions,
&self.vertex_normals,
&self.vertex_uv0,
&self.indices,
zeroed,
)
}
pub unsafe fn calculate_tangents_for_buffers(
tangents: &mut [Vec3],
positions: &[Vec3],
normals: &[Vec3],
uvs: &[Vec2],
indices: &[u32],
zeroed: bool,
) {
debug_assert_eq!(tangents.len(), positions.len());
debug_assert_eq!(uvs.len(), positions.len());
if !zeroed {
for tan in tangents.iter_mut() {
*tan = Vec3::ZERO;
}
}
for idx in indices.chunks_exact(3) {
let (idx0, idx1, idx2) = match *idx {
[idx0, idx1, idx2] => (idx0, idx1, idx2),
_ => std::hint::unreachable_unchecked(),
};
let pos1 = *positions.get_unchecked(idx0 as usize);
let pos2 = *positions.get_unchecked(idx1 as usize);
let pos3 = *positions.get_unchecked(idx2 as usize);
let tex1 = *uvs.get_unchecked(idx0 as usize);
let tex2 = *uvs.get_unchecked(idx1 as usize);
let tex3 = *uvs.get_unchecked(idx2 as usize);
let edge1 = pos2 - pos1;
let edge2 = pos3 - pos1;
let uv1 = tex2 - tex1;
let uv2 = tex3 - tex1;
let r = 1.0 / (uv1.x * uv2.y - uv1.y * uv2.x);
let tangent = Vec3::new(
((edge1.x * uv2.y) - (edge2.x * uv1.y)) * r,
((edge1.y * uv2.y) - (edge2.y * uv1.y)) * r,
((edge1.z * uv2.y) - (edge2.z * uv1.y)) * r,
);
*tangents.get_unchecked_mut(idx0 as usize) += tangent;
*tangents.get_unchecked_mut(idx1 as usize) += tangent;
*tangents.get_unchecked_mut(idx2 as usize) += tangent;
}
for (tan, norm) in tangents.iter_mut().zip(normals) {
let t = *tan - (*norm * norm.dot(*tan));
*tan = t.normalize_or_zero();
}
}
pub fn double_side(&mut self) {
let starting_len = self.indices.len();
let primative_count = starting_len / 3;
self.indices.reserve(starting_len);
let ptr = self.indices.as_mut_ptr();
#[allow(clippy::identity_op)]
unsafe {
for prim in (0..primative_count).rev() {
let i1 = *ptr.add(prim * 3 + 0);
let i2 = *ptr.add(prim * 3 + 1);
let i3 = *ptr.add(prim * 3 + 2);
ptr.add(prim * 6 + 0).write(i1);
ptr.add(prim * 6 + 1).write(i2);
ptr.add(prim * 6 + 2).write(i3);
ptr.add(prim * 6 + 3).write(i3);
ptr.add(prim * 6 + 4).write(i2);
ptr.add(prim * 6 + 5).write(i1);
}
self.indices.set_len(starting_len * 2);
}
}
pub fn flip_winding_order(&mut self) {
for indices in self.indices.chunks_exact_mut(3) {
if let [left, _, right] = indices {
mem::swap(left, right);
} else {
unsafe { std::hint::unreachable_unchecked() }
}
}
}
}
#[derive(Debug, Clone)]
pub enum MipmapCount {
Specific(NonZeroU32),
Maximum,
}
impl MipmapCount {
pub const ONE: Self = Self::Specific(unsafe { NonZeroU32::new_unchecked(1) });
}
#[derive(Debug, Clone)]
pub enum MipmapSource {
Uploaded,
Generated,
}
#[derive(Debug, Clone)]
pub struct Texture {
pub label: Option<String>,
pub data: Vec<u8>,
pub format: TextureFormat,
pub size: UVec2,
pub mip_count: MipmapCount,
pub mip_source: MipmapSource,
}
#[derive(Debug, Clone)]
pub struct TextureFromTexture {
pub label: Option<String>,
pub src: TextureHandle,
pub start_mip: u32,
pub mip_count: Option<NonZeroU32>,
}
#[doc(hidden)]
pub struct MaterialTag;
pub trait Material: Send + Sync + 'static {
const TEXTURE_COUNT: u32;
const DATA_SIZE: u32;
fn object_key(&self) -> u64;
fn to_textures<'a>(&'a self, slice: &mut [Option<&'a TextureHandle>]);
fn to_data(&self, slice: &mut [u8]);
}
#[derive(Clone, Debug)]
pub enum ObjectMeshKind {
Animated(SkeletonHandle),
Static(MeshHandle),
}
changeable_struct! {
pub struct Object <- ObjectChange {
pub mesh_kind: ObjectMeshKind,
pub material: MaterialHandle,
pub transform: Mat4,
}
}
#[derive(Debug, Default, Copy, Clone)]
pub struct Camera {
pub projection: CameraProjection,
pub view: Mat4,
}
#[derive(Debug, Copy, Clone)]
pub enum CameraProjection {
Orthographic {
size: Vec3A,
},
Perspective {
vfov: f32,
near: f32,
},
Raw(Mat4),
}
impl Default for CameraProjection {
fn default() -> Self {
Self::Perspective { vfov: 60.0, near: 0.1 }
}
}
changeable_struct! {
pub struct DirectionalLight <- DirectionalLightChange {
pub color: Vec3,
pub intensity: f32,
pub direction: Vec3,
pub distance: f32,
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SampleCount {
One = 1,
Four = 4,
}
impl Default for SampleCount {
fn default() -> Self {
Self::One
}
}
impl TryFrom<u8> for SampleCount {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
1 => Self::One,
4 => Self::Four,
v => return Err(v),
})
}
}
impl SampleCount {
pub fn needs_resolve(self) -> bool {
self != Self::One
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Handedness {
Left,
Right,
}
impl Default for Handedness {
fn default() -> Self {
Self::Left
}
}
#[derive(Debug, Clone)]
pub struct Skeleton {
pub joint_matrices: Vec<Mat4>,
pub mesh: MeshHandle,
}
impl Skeleton {
pub fn from_joint_transforms(
mesh: MeshHandle,
joint_global_transforms: &[Mat4],
inverse_bind_transforms: &[Mat4],
) -> Skeleton {
let joint_matrices = Self::compute_joint_matrices(joint_global_transforms, inverse_bind_transforms);
Skeleton { joint_matrices, mesh }
}
pub fn compute_joint_matrices(joint_global_transforms: &[Mat4], inverse_bind_transforms: &[Mat4]) -> Vec<Mat4> {
joint_global_transforms
.iter()
.zip(inverse_bind_transforms.iter())
.map(|(global_pos, inv_bind_pos)| (*global_pos) * (*inv_bind_pos))
.collect()
}
}