use bytemuck for most reading

This commit is contained in:
Robin Appelman 2022-03-19 16:34:57 +01:00
commit 473d6eecc5
7 changed files with 109 additions and 69 deletions

View file

@ -9,11 +9,13 @@ use crate::mdl::Mdl;
use crate::vtx::Vtx; use crate::vtx::Vtx;
use crate::vvd::{Vertex, Vvd}; use crate::vvd::{Vertex, Vvd};
use binrw::{BinRead, BinReaderExt}; use binrw::{BinRead, BinReaderExt};
use bytemuck::{pod_read_unaligned, Pod};
pub use error::*; pub use error::*;
pub use handle::Handle; pub use handle::Handle;
pub use shared::*; pub use shared::*;
use std::any::type_name; use std::any::type_name;
use std::io::Cursor; use std::io::Cursor;
use std::mem::size_of;
pub struct Model { pub struct Model {
#[allow(dead_code)] #[allow(dead_code)]
@ -111,17 +113,13 @@ fn index_range(index: i32, count: i32, size: usize) -> impl Iterator<Item = usiz
fn read_relative_iter<'a, T: ReadRelative, I: 'a + Iterator<Item = usize>>( fn read_relative_iter<'a, T: ReadRelative, I: 'a + Iterator<Item = usize>>(
data: &'a [u8], data: &'a [u8],
indexes: I, indexes: I,
) -> impl Iterator<Item = Result<T, ModelError>> + 'a ) -> impl Iterator<Item = Result<T, ModelError>> + 'a {
where
<<T as ReadRelative>::Header as BinRead>::Args: Default,
{
indexes.map(|index| { indexes.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds { let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: type_name::<T>(), data: type_name::<T>(),
offset: index, offset: index,
})?; })?;
let mut reader = Cursor::new(data); let header = <T::Header as Readable>::read(data)?;
let header = reader.read_le()?;
T::read(data, header) T::read(data, header)
}) })
} }
@ -129,20 +127,28 @@ where
fn read_relative<T: ReadRelative, I: Iterator<Item = usize>>( fn read_relative<T: ReadRelative, I: Iterator<Item = usize>>(
data: &[u8], data: &[u8],
indexes: I, indexes: I,
) -> Result<Vec<T>, ModelError> ) -> Result<Vec<T>, ModelError> {
where
<<T as ReadRelative>::Header as BinRead>::Args: Default,
{
read_relative_iter(data, indexes).collect() read_relative_iter(data, indexes).collect()
} }
trait Readable: Sized {
fn read(data: &[u8]) -> Result<Self, ModelError>;
}
impl<T: Pod> Readable for T {
fn read(data: &[u8]) -> Result<Self, ModelError> {
let data = &data[0..size_of::<Self>()];
Ok(pod_read_unaligned(data))
}
}
trait ReadRelative: Sized { trait ReadRelative: Sized {
type Header: BinRead; type Header: Readable;
fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError>; fn read(data: &[u8], header: Self::Header) -> Result<Self, ModelError>;
} }
impl<T: BinRead> ReadRelative for T { impl<T: Readable> ReadRelative for T {
type Header = T; type Header = T;
fn read(_data: &[u8], header: Self::Header) -> Result<Self, ModelError> { fn read(_data: &[u8], header: Self::Header) -> Result<Self, ModelError> {

View file

@ -4,7 +4,7 @@ pub use raw::header::*;
pub use raw::header2::*; pub use raw::header2::*;
use crate::mdl::raw::{BodyPartHeader, Bone, MeshHeader, ModelHeader}; use crate::mdl::raw::{BodyPartHeader, Bone, MeshHeader, ModelHeader};
use crate::{read_indexes, read_relative, FixedString, ModelError, ReadRelative}; use crate::{read_indexes, read_relative, FixedString, ModelError, ReadRelative, Readable};
use binrw::BinReaderExt; use binrw::BinReaderExt;
use std::io::Cursor; use std::io::Cursor;
@ -31,8 +31,7 @@ impl Mdl {
data: "BodyPart", data: "BodyPart",
offset: index, offset: index,
})?; })?;
let mut reader = Cursor::new(data); let header = <BodyPartHeader as Readable>::read(data)?;
let header = reader.read_le()?;
BodyPart::read(data, header) BodyPart::read(data, header)
}) })
.collect::<Result<_>>()?, .collect::<Result<_>>()?,
@ -73,7 +72,7 @@ impl ReadRelative for Model {
fn read(data: &[u8], header: Self::Header) -> Result<Self> { fn read(data: &[u8], header: Self::Header) -> Result<Self> {
Ok(Model { Ok(Model {
meshes: read_relative(data, header.mesh_indexes())?, meshes: read_relative(data, header.mesh_indexes())?,
name: header.name, name: header.name.try_into()?,
ty: header.ty, ty: header.ty,
bounding_radius: header.bounding_radius, bounding_radius: header.bounding_radius,
vertex_offset: header.vertex_index, vertex_offset: header.vertex_index,

View file

@ -1,7 +1,8 @@
use crate::{index_range, FixedString}; use crate::{index_range, FixedString, Pod};
use crate::{Quaternion, RadianEuler, Vector}; use crate::{Quaternion, RadianEuler, Vector};
use binrw::BinRead; use binrw::BinRead;
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::Zeroable;
use std::mem::size_of; use std::mem::size_of;
pub mod header; pub mod header;
@ -62,7 +63,8 @@ bitflags! {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct BodyPartHeader { pub struct BodyPartHeader {
pub name_index: i32, pub name_index: i32,
model_count: i32, model_count: i32,
@ -80,10 +82,11 @@ impl BodyPartHeader {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct ModelHeader { pub struct ModelHeader {
pub name: FixedString<64>, pub name: [u8; 64],
pub ty: i32, pub ty: i32,
pub bounding_radius: f32, pub bounding_radius: f32,
mesh_count: i32, mesh_count: i32,
@ -99,7 +102,7 @@ pub struct ModelHeader {
padding: [i32; 8], padding: [i32; 8],
} }
static_assertions::const_assert_eq!(size_of::<ModelHeader>() - size_of::<FixedString<0>>(), 148); static_assertions::const_assert_eq!(size_of::<ModelHeader>(), 148);
impl ModelHeader { impl ModelHeader {
pub fn mesh_indexes(&self) -> impl Iterator<Item = usize> { pub fn mesh_indexes(&self) -> impl Iterator<Item = usize> {
@ -107,7 +110,8 @@ impl ModelHeader {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct ModelVertexData { pub struct ModelVertexData {
// these are pointers? // these are pointers?
@ -115,7 +119,8 @@ pub struct ModelVertexData {
tangent_data: i32, tangent_data: i32,
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct MeshHeader { pub struct MeshHeader {
material: i32, material: i32,
@ -132,7 +137,8 @@ pub struct MeshHeader {
padding: [i32; 8], padding: [i32; 8],
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct MeshVertexData { pub struct MeshVertexData {
// these are pointers? // these are pointers?

View file

@ -1,11 +1,13 @@
use crate::{BinRead, StringError}; use crate::{BinRead, ModelError, StringError};
use arrayvec::ArrayString; use arrayvec::ArrayString;
use binrw::{BinResult, ReadOptions}; use binrw::{BinResult, ReadOptions};
use bytemuck::{Pod, Zeroable};
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Add; use std::ops::Add;
#[derive(Debug, Clone, Copy, BinRead)] #[derive(Debug, Clone, Copy, BinRead, Zeroable, Pod)]
#[repr(C)]
pub struct Vector { pub struct Vector {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
@ -71,6 +73,23 @@ pub struct RadianEuler {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FixedString<const LEN: usize>(ArrayString<LEN>); pub struct FixedString<const LEN: usize>(ArrayString<LEN>);
impl<const LEN: usize> TryFrom<[u8; LEN]> for FixedString<LEN> {
type Error = ModelError;
fn try_from(name_buf: [u8; LEN]) -> Result<Self, Self::Error> {
use std::str;
let zero_pos = name_buf
.iter()
.position(|c| *c == 0)
.ok_or(StringError::NotNullTerminated)?;
let name = &name_buf[..zero_pos];
Ok(FixedString(
ArrayString::from(str::from_utf8(name).map_err(StringError::NonUTF8)?).unwrap(),
))
}
}
impl<const N: usize> AsRef<str> for FixedString<N> { impl<const N: usize> AsRef<str> for FixedString<N> {
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
self.as_str() self.as_str()

View file

@ -23,18 +23,7 @@ impl Vtx {
let mut reader = Cursor::new(data); let mut reader = Cursor::new(data);
let header: VtxHeader = reader.read_le()?; let header: VtxHeader = reader.read_le()?;
Ok(Vtx { Ok(Vtx {
body_parts: header body_parts: read_relative(data, header.body_indexes())?,
.body_indexes()
.map(|index| {
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
data: "BodyPart",
offset: index,
})?;
let mut reader = Cursor::new(data);
let header = reader.read_le()?;
BodyPart::read(data, header)
})
.collect::<Result<_>>()?,
header, header,
}) })
} }

View file

@ -1,6 +1,7 @@
use crate::index_range; use crate::{index_range, Pod};
use binrw::BinRead; use binrw::BinRead;
use bitflags::bitflags; use bitflags::bitflags;
use bytemuck::Zeroable;
use std::mem::size_of; use std::mem::size_of;
use std::ops::Range; use std::ops::Range;
@ -30,7 +31,8 @@ impl VtxHeader {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct BodyPartHeader { pub struct BodyPartHeader {
model_count: i32, model_count: i32,
model_offset: i32, model_offset: i32,
@ -48,7 +50,8 @@ impl BodyPartHeader {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct ModelHeader { pub struct ModelHeader {
lod_count: i32, lod_count: i32,
lod_offset: i32, lod_offset: i32,
@ -62,7 +65,8 @@ impl ModelHeader {
} }
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct ModelLodHeader { pub struct ModelLodHeader {
mesh_count: i32, mesh_count: i32,
mesh_offset: i32, mesh_offset: i32,
@ -77,7 +81,8 @@ impl ModelLodHeader {
} }
} }
#[derive(Debug, Clone, Copy, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[repr(packed)] #[repr(packed)]
pub struct MeshHeader { pub struct MeshHeader {
strip_group_count: i32, strip_group_count: i32,
@ -98,14 +103,16 @@ impl MeshHeader {
} }
bitflags! { bitflags! {
#[derive(BinRead)] #[derive(BinRead, Zeroable, Pod)]
#[repr(C)]
pub struct MeshFlags: u8 { pub struct MeshFlags: u8 {
const IS_TEETH = 0x01; const IS_TEETH = 0x01;
const IS_EYES = 0x02; const IS_EYES = 0x02;
} }
} }
#[derive(Debug, Clone, Copy, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[repr(packed)] #[repr(packed)]
pub struct StripGroupHeader { pub struct StripGroupHeader {
vertex_count: i32, vertex_count: i32,
@ -143,7 +150,8 @@ impl StripGroupHeader {
} }
bitflags! { bitflags! {
#[derive(BinRead)] #[derive(BinRead, Zeroable, Pod)]
#[repr(C)]
pub struct StripGroupFlags: u8 { pub struct StripGroupFlags: u8 {
const IS_FLEXED = 0x01; const IS_FLEXED = 0x01;
const IS_HWSKINNED = 0x02; const IS_HWSKINNED = 0x02;
@ -152,7 +160,8 @@ bitflags! {
} }
} }
#[derive(Debug, Clone, Copy, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[repr(packed)] #[repr(packed)]
pub struct StripHeader { pub struct StripHeader {
index_count: i32, index_count: i32,
@ -168,7 +177,8 @@ pub struct StripHeader {
static_assertions::const_assert_eq!(size_of::<StripHeader>(), 27); static_assertions::const_assert_eq!(size_of::<StripHeader>(), 27);
bitflags! { bitflags! {
#[derive(BinRead)] #[derive(BinRead, Zeroable, Pod)]
#[repr(C)]
pub struct StripFlags: u8 { pub struct StripFlags: u8 {
const IS_TRI_LIST = 0x01; const IS_TRI_LIST = 0x01;
const IS_TRI_STRIP = 0x02; const IS_TRI_STRIP = 0x02;
@ -195,7 +205,8 @@ impl StripHeader {
} }
} }
#[derive(Debug, Clone, Copy, BinRead)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
#[repr(packed)] #[repr(packed)]
pub struct Vertex { pub struct Vertex {
pub bone_weight_indexes: [u8; 3], pub bone_weight_indexes: [u8; 3],

View file

@ -1,5 +1,7 @@
use crate::{index_range, Vector}; use crate::{index_range, Vector};
use binrw::BinRead; use binrw::{BinRead, BinResult, ReadOptions};
use bytemuck::{cast, Pod, Zeroable};
use std::io::{Read, Seek};
use std::mem::size_of; use std::mem::size_of;
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead)]
@ -12,6 +14,7 @@ pub struct VvdHeader {
fixup_count: i32, fixup_count: i32,
fixup_index: i32, fixup_index: i32,
vertex_index: i32, vertex_index: i32,
#[allow(dead_code)]
tangent_index: i32, tangent_index: i32,
} }
@ -39,31 +42,18 @@ impl VvdHeader {
None None
} }
} }
pub fn tangent_indexes(&self, lod: i32) -> Option<impl Iterator<Item = usize>> {
if lod > 0 && lod > self.fixup_count {
todo!("lod fixup not supported")
}
if lod < self.lod_count {
Some(index_range(
self.tangent_index,
self.lod_vertex_count[lod as usize],
size_of::<Vertex>(),
))
} else {
None
}
}
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)]
#[repr(C)]
pub struct VertexFileFixup { pub struct VertexFileFixup {
pub lod: i32, pub lod: i32,
pub source_vertex_id: i32, pub source_vertex_id: i32,
pub vertex_count: i32, pub vertex_count: i32,
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, Zeroable, Pod, Copy)]
#[repr(C)]
pub struct Vertex { pub struct Vertex {
pub bone_weights: BoneWeight, pub bone_weights: BoneWeight,
pub position: Vector, pub position: Vector,
@ -72,8 +62,27 @@ pub struct Vertex {
} }
static_assertions::const_assert_eq!(size_of::<Vertex>(), 48); static_assertions::const_assert_eq!(size_of::<Vertex>(), 48);
// binread_for_pod!(Vertex);
#[derive(Debug, Clone, BinRead)] impl BinRead for Vertex {
type Args = ();
fn read_options<R: Read + Seek>(
reader: &mut R,
_options: &ReadOptions,
_args: Self::Args,
) -> BinResult<Self> {
let mut bytes = unsafe {
std::mem::MaybeUninit::<[u8; std::mem::size_of::<Self>()]>::uninit().assume_init()
};
reader.read(&mut bytes)?;
Ok(cast(bytes))
}
}
#[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)]
#[repr(C)]
pub struct BoneWeight { pub struct BoneWeight {
pub weight: [f32; 3], pub weight: [f32; 3],
pub bone: [u8; 3], pub bone: [u8; 3],
@ -82,7 +91,8 @@ pub struct BoneWeight {
static_assertions::const_assert_eq!(size_of::<BoneWeight>(), 16); static_assertions::const_assert_eq!(size_of::<BoneWeight>(), 16);
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead, Zeroable, Pod, Copy)]
#[repr(C)]
pub struct Tangent { pub struct Tangent {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,