use crate::{
atlas::*,
features::*,
vertex::*
};
#[cfg(feature = "mesh")]
use crate::mesh::*;
use std::io::{Cursor, Read};
use byteorder::*;
use log::*;
use thiserror::*;
#[derive(Debug, Error)]
pub enum Error {
#[error("failed to read mesh")]
Io(#[from] std::io::Error),
#[error("wrong mesh header")]
WrongHeader([u8; 5]),
#[error("unknown mesh version {0}")]
UnknownVersion(u8),
#[error("mesh contains banned features: {0:?}")]
BannedFeatures(MeshFeatures),
#[error("mesh does not contain required features: {0:?}")]
MissingFeatures(MeshFeatures),
#[error("non-utf8 texture name #{0}")]
InvalidTextureName(u8),
#[error("atlas does not contain texture '{0}'")]
MissingTexture(String),
#[error("object {0} uses an unknown texture index")]
UnknownTexture(u8),
#[error("unknown colour index {0}")]
UnknownColour(u16)
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct MeshReader<'a, A: Atlas> {
atlas: &'a A,
fallback: Option<&'a AtlasRegion>,
banned: MeshFeatures,
required: MeshFeatures
}
impl<'a, A: Atlas> MeshReader<'a, A> {
pub fn new<V: Vertex>(atlas: &'a A, fallback: Option<&str>) -> Self {
Self {
atlas,
fallback: match fallback.map(|name| atlas.get(name)) {
Some(region) => region,
None => {
error!("Atlas does not contain fallback texture, will error out if missing textures exist");
None
}
},
banned: MeshFeatures::default(),
required: V::features()
}
}
pub fn ban_feature(mut self, feature: MeshFeature) -> Self {
self.banned |= feature;
self
}
pub fn ban_features(mut self, features: MeshFeatures) -> Self {
self.banned |= features;
self
}
pub fn require_feature(mut self, feature: MeshFeature) -> Self {
self.required |= feature;
self
}
pub fn require_features(mut self, features: MeshFeatures) -> Self {
self.required |= features;
self
}
pub fn read<V: Vertex>(&self, bytes: &[u8]) -> Result<(MeshFeatures, Vec<V>, Vec<Index>)> {
let mut bytes = Cursor::new(bytes);
let bytes = &mut bytes;
let mut header = [0; 5];
bytes.read_exact(&mut header)?;
if &header != b"CHAIR" {
return Err(Error::WrongHeader(header));
}
let version = bytes.read_u8()?;
debug!("Mesh version is {version}");
if version != 0 {
return Err(Error::UnknownVersion(version));
}
let features = bytes.read_u8()?;
let features = unsafe {
MeshFeatures::from_bits_unchecked(features)
};
debug!("Mesh features are {features:?}");
let banned = features & self.banned;
if !banned.is_empty() {
return Err(Error::BannedFeatures(banned));
}
let missing = self.required & !features;
if !missing.is_empty() {
return Err(Error::MissingFeatures(missing));
}
let colours = if features.contains(MeshFeature::Coloured) {
let len = bytes.read_u16::<LE>()?;
debug!("Have {len} colours");
(0..len)
.map(|_| {
Ok([chan(bytes)?, chan(bytes)?, chan(bytes)?, chan(bytes)?])
})
.collect::<Result<_>>()?
} else {
vec![]
};
let textures = if features.contains(MeshFeature::Textured) {
let len = bytes.read_u8()?;
debug!("Have {len} textures");
(0..len)
.map(|n| {
let len = bytes.read_u8()?;
let mut data = vec![0; len as usize];
bytes.read_exact(&mut data)?;
String::from_utf8(data)
.map_err(|_| Error::InvalidTextureName(n))
})
.map(|name| match name {
Ok(name) => match self.atlas.get(&name) {
Some(region) => Ok(region),
None => {
warn!("Mesh uses missing texture {name}");
if let Some(fallback) = self.fallback {
Ok(fallback)
} else {
Err(Error::MissingTexture(name))
}
}
},
Err(e) => Err(e)
})
.collect::<Result<_>>()?
} else {
vec![]
};
let read_pos_comp = if features.contains(MeshFeature::ShortPos) {
read_pos_short
} else {
read_pos_long
};
let read_col_index = if colours.len() > 255 {
read_col_u16
} else {
read_col_u8
};
let read_normal = if features.contains(MeshFeature::ShortNormals) {
read_normal_short
} else {
read_f32
};
let read_uv = if features.contains(MeshFeature::ShortUvs) {
read_uv_short
} else {
read_f32
};
let object_count = bytes.read_u8()?;
debug!("Have {object_count} objects");
let vertices = (0..object_count)
.flat_map(|n| {
let vertex_count = bytes.read_u32::<LE>()?;
debug!("Object {n} has {vertex_count} vertices");
let (scale, offset) = if features.contains(MeshFeature::ShortPos) {
([
bytes.read_f32::<LE>()? / 128.0,
bytes.read_f32::<LE>()? / 128.0,
bytes.read_f32::<LE>()? / 128.0
], [
bytes.read_f32::<LE>()?,
bytes.read_f32::<LE>()?,
bytes.read_f32::<LE>()?
])
} else {
([1.0; 3], [0.0; 3])
};
debug!("Scale is {scale:?}, offset is {offset:?}");
let region = if features.contains(MeshFeature::Textured) {
textures.get(bytes.read_u8()? as usize)
.ok_or_else(|| Error::UnknownTexture(n))?
} else {
&AtlasRegion {
xy: [0.0; 2],
x2y: [0.0; 2],
x2y2: [0.0; 2],
xy2: [0.0; 2]
}
};
(0..vertex_count).map(|_| {
let pos = [
read_pos_comp(bytes, scale[0], offset[0])?,
read_pos_comp(bytes, scale[1], offset[1])?,
read_pos_comp(bytes, scale[2], offset[2])?
];
let col = if features.contains(MeshFeature::Coloured) {
let index = read_col_index(bytes)?;
colours.get(index as usize).copied()
.ok_or_else(|| Error::UnknownColour(index))?
} else {
[1.0; 4]
};
let normals = if features.contains(MeshFeature::Normals) {
[
read_normal(bytes)?,
read_normal(bytes)?,
read_normal(bytes)?
]
} else {
[0.0; 3]
};
let uvs = if features.contains(MeshFeature::Textured) {
region.map_uvs(read_uv(bytes)?, read_uv(bytes)?)
} else {
[0.0; 2]
};
Ok(V::new(pos, col, normals, uvs, ))
}).collect::<Result<Vec<_>>>()
})
.flatten()
.collect::<Vec<V>>();
let read_index = if vertices.len() > 65535 {
read_index_u32
} else if vertices.len() > 255 {
read_index_u16
} else {
read_index_u8
};
let index_count = bytes.read_u32::<LE>()?;
debug!("Have {index_count} indices");
let indices = (0..index_count)
.map(|_| read_index(bytes))
.collect::<Result<_>>()?;
Ok((features, vertices, indices))
}
#[cfg(feature = "mesh")]
pub fn create<V: Vertex>(&self, device: &wgpu::Device, bytes: &[u8]) -> Result<Mesh> {
let (_features, vertices, indices) = self.read::<V>(bytes)?;
Ok(Mesh::new(device, &vertices, &indices))
}
}
fn read_pos_short(bytes: &mut Cursor<&[u8]>, scale: f32, offset: f32) -> Result<f32> {
Ok(bytes.read_u8()? as f32 * scale + offset)
}
fn read_pos_long(bytes: &mut Cursor<&[u8]>, _: f32, _: f32) -> Result<f32> {
read_f32(bytes)
}
fn chan(bytes: &mut Cursor<&[u8]>) -> Result<f32> {
Ok(bytes.read_u8()? as f32 / 255.0)
}
fn read_col_u8(bytes: &mut Cursor<&[u8]>) -> Result<u16> {
Ok(bytes.read_u8()? as u16)
}
fn read_col_u16(bytes: &mut Cursor<&[u8]>) -> Result<u16> {
Ok(bytes.read_u16::<LE>()?)
}
fn read_normal_short(bytes: &mut Cursor<&[u8]>) -> Result<f32> {
Ok(NORMAL[bytes.read_u8()? as usize])
}
fn read_f32(bytes: &mut Cursor<&[u8]>) -> Result<f32> {
Ok(bytes.read_f32::<LE>()?)
}
fn read_uv_short(bytes: &mut Cursor<&[u8]>) -> Result<f32> {
Ok(bytes.read_u8()? as f32 / 128.0)
}
fn read_index_u32(bytes: &mut Cursor<&[u8]>) -> Result<Index> {
Ok(bytes.read_u32::<LE>()?)
}
fn read_index_u16(bytes: &mut Cursor<&[u8]>) -> Result<Index> {
Ok(bytes.read_u16::<LE>()? as Index)
}
fn read_index_u8(bytes: &mut Cursor<&[u8]>) -> Result<Index> {
Ok(bytes.read_u8()? as Index)
}
const NORMAL: [f32; 256] = include!(concat!(env!("OUT_DIR"), "/normals.rs"));