mirror of
https://codeberg.org/icewind/vmdl.git
synced 2026-06-03 16:44:11 +02:00
use bytemuck for most reading
This commit is contained in:
parent
526efa83dc
commit
473d6eecc5
7 changed files with 109 additions and 69 deletions
30
src/lib.rs
30
src/lib.rs
|
|
@ -9,11 +9,13 @@ use crate::mdl::Mdl;
|
|||
use crate::vtx::Vtx;
|
||||
use crate::vvd::{Vertex, Vvd};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
use bytemuck::{pod_read_unaligned, Pod};
|
||||
pub use error::*;
|
||||
pub use handle::Handle;
|
||||
pub use shared::*;
|
||||
use std::any::type_name;
|
||||
use std::io::Cursor;
|
||||
use std::mem::size_of;
|
||||
|
||||
pub struct Model {
|
||||
#[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>>(
|
||||
data: &'a [u8],
|
||||
indexes: I,
|
||||
) -> impl Iterator<Item = Result<T, ModelError>> + 'a
|
||||
where
|
||||
<<T as ReadRelative>::Header as BinRead>::Args: Default,
|
||||
{
|
||||
) -> impl Iterator<Item = Result<T, ModelError>> + 'a {
|
||||
indexes.map(|index| {
|
||||
let data = data.get(index..).ok_or_else(|| ModelError::OutOfBounds {
|
||||
data: type_name::<T>(),
|
||||
offset: index,
|
||||
})?;
|
||||
let mut reader = Cursor::new(data);
|
||||
let header = reader.read_le()?;
|
||||
let header = <T::Header as Readable>::read(data)?;
|
||||
T::read(data, header)
|
||||
})
|
||||
}
|
||||
|
|
@ -129,20 +127,28 @@ where
|
|||
fn read_relative<T: ReadRelative, I: Iterator<Item = usize>>(
|
||||
data: &[u8],
|
||||
indexes: I,
|
||||
) -> Result<Vec<T>, ModelError>
|
||||
where
|
||||
<<T as ReadRelative>::Header as BinRead>::Args: Default,
|
||||
{
|
||||
) -> Result<Vec<T>, ModelError> {
|
||||
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 {
|
||||
type Header: BinRead;
|
||||
type Header: Readable;
|
||||
|
||||
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;
|
||||
|
||||
fn read(_data: &[u8], header: Self::Header) -> Result<Self, ModelError> {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub use raw::header::*;
|
|||
pub use raw::header2::*;
|
||||
|
||||
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 std::io::Cursor;
|
||||
|
||||
|
|
@ -31,8 +31,7 @@ impl Mdl {
|
|||
data: "BodyPart",
|
||||
offset: index,
|
||||
})?;
|
||||
let mut reader = Cursor::new(data);
|
||||
let header = reader.read_le()?;
|
||||
let header = <BodyPartHeader as Readable>::read(data)?;
|
||||
BodyPart::read(data, header)
|
||||
})
|
||||
.collect::<Result<_>>()?,
|
||||
|
|
@ -73,7 +72,7 @@ impl ReadRelative for Model {
|
|||
fn read(data: &[u8], header: Self::Header) -> Result<Self> {
|
||||
Ok(Model {
|
||||
meshes: read_relative(data, header.mesh_indexes())?,
|
||||
name: header.name,
|
||||
name: header.name.try_into()?,
|
||||
ty: header.ty,
|
||||
bounding_radius: header.bounding_radius,
|
||||
vertex_offset: header.vertex_index,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{index_range, FixedString};
|
||||
use crate::{index_range, FixedString, Pod};
|
||||
use crate::{Quaternion, RadianEuler, Vector};
|
||||
use binrw::BinRead;
|
||||
use bitflags::bitflags;
|
||||
use bytemuck::Zeroable;
|
||||
use std::mem::size_of;
|
||||
|
||||
pub mod header;
|
||||
|
|
@ -62,7 +63,8 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct BodyPartHeader {
|
||||
pub name_index: 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)]
|
||||
pub struct ModelHeader {
|
||||
pub name: FixedString<64>,
|
||||
pub name: [u8; 64],
|
||||
pub ty: i32,
|
||||
pub bounding_radius: f32,
|
||||
mesh_count: i32,
|
||||
|
|
@ -99,7 +102,7 @@ pub struct ModelHeader {
|
|||
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 {
|
||||
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)]
|
||||
pub struct ModelVertexData {
|
||||
// these are pointers?
|
||||
|
|
@ -115,7 +119,8 @@ pub struct ModelVertexData {
|
|||
tangent_data: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
pub struct MeshHeader {
|
||||
material: i32,
|
||||
|
|
@ -132,7 +137,8 @@ pub struct MeshHeader {
|
|||
padding: [i32; 8],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
pub struct MeshVertexData {
|
||||
// these are pointers?
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use crate::{BinRead, StringError};
|
||||
use crate::{BinRead, ModelError, StringError};
|
||||
use arrayvec::ArrayString;
|
||||
use binrw::{BinResult, ReadOptions};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug, Clone, Copy, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, BinRead, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Vector {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
|
|
@ -71,6 +73,23 @@ pub struct RadianEuler {
|
|||
#[derive(Debug, Clone)]
|
||||
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> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
|
|
|
|||
|
|
@ -23,18 +23,7 @@ impl Vtx {
|
|||
let mut reader = Cursor::new(data);
|
||||
let header: VtxHeader = reader.read_le()?;
|
||||
Ok(Vtx {
|
||||
body_parts: header
|
||||
.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<_>>()?,
|
||||
body_parts: read_relative(data, header.body_indexes())?,
|
||||
header,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::index_range;
|
||||
use crate::{index_range, Pod};
|
||||
use binrw::BinRead;
|
||||
use bitflags::bitflags;
|
||||
use bytemuck::Zeroable;
|
||||
use std::mem::size_of;
|
||||
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 {
|
||||
model_count: 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 {
|
||||
lod_count: 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 {
|
||||
mesh_count: 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)]
|
||||
pub struct MeshHeader {
|
||||
strip_group_count: i32,
|
||||
|
|
@ -98,14 +103,16 @@ impl MeshHeader {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinRead, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct MeshFlags: u8 {
|
||||
const IS_TEETH = 0x01;
|
||||
const IS_EYES = 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
pub struct StripGroupHeader {
|
||||
vertex_count: i32,
|
||||
|
|
@ -143,7 +150,8 @@ impl StripGroupHeader {
|
|||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinRead, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct StripGroupFlags: u8 {
|
||||
const IS_FLEXED = 0x01;
|
||||
const IS_HWSKINNED = 0x02;
|
||||
|
|
@ -152,7 +160,8 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, BinRead)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
#[repr(packed)]
|
||||
pub struct StripHeader {
|
||||
index_count: i32,
|
||||
|
|
@ -168,7 +177,8 @@ pub struct StripHeader {
|
|||
static_assertions::const_assert_eq!(size_of::<StripHeader>(), 27);
|
||||
|
||||
bitflags! {
|
||||
#[derive(BinRead)]
|
||||
#[derive(BinRead, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct StripFlags: u8 {
|
||||
const IS_TRI_LIST = 0x01;
|
||||
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)]
|
||||
pub struct Vertex {
|
||||
pub bone_weight_indexes: [u8; 3],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
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;
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
|
|
@ -12,6 +14,7 @@ pub struct VvdHeader {
|
|||
fixup_count: i32,
|
||||
fixup_index: i32,
|
||||
vertex_index: i32,
|
||||
#[allow(dead_code)]
|
||||
tangent_index: i32,
|
||||
}
|
||||
|
||||
|
|
@ -39,31 +42,18 @@ impl VvdHeader {
|
|||
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 lod: i32,
|
||||
pub source_vertex_id: i32,
|
||||
pub vertex_count: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, BinRead)]
|
||||
#[derive(Debug, Clone, Zeroable, Pod, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Vertex {
|
||||
pub bone_weights: BoneWeight,
|
||||
pub position: Vector,
|
||||
|
|
@ -72,8 +62,27 @@ pub struct Vertex {
|
|||
}
|
||||
|
||||
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 weight: [f32; 3],
|
||||
pub bone: [u8; 3],
|
||||
|
|
@ -82,7 +91,8 @@ pub struct BoneWeight {
|
|||
|
||||
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 x: f32,
|
||||
pub y: f32,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue