315 lines
6.7 KiB
Rust
315 lines
6.7 KiB
Rust
use nom::{
|
|
IResult,
|
|
bytes::complete::{tag},
|
|
number::complete::{le_u32},
|
|
};
|
|
|
|
use std::convert::TryInto;
|
|
|
|
pub mod consts {
|
|
use iota::iota;
|
|
iota! {
|
|
pub const VERSION: usize = iota;
|
|
, FILESIZE
|
|
, FLAGS
|
|
, NUM_TEXT
|
|
, OFS_TEXT
|
|
, NUM_MESHES
|
|
, OFS_MESHES
|
|
, NUM_VERTEXARRAYS
|
|
, NUM_VERTEXES
|
|
, OFS_VERTEXARRAYS
|
|
, NUM_TRIANGLES
|
|
, OFS_TRIANGLES
|
|
, OFS_ADJACENCY
|
|
, NUM_JOINTS
|
|
, OFS_JOINTS
|
|
, NUM_POSES
|
|
, OFS_POSES
|
|
, NUM_ANIMS
|
|
, OFS_ANIMS
|
|
, NUM_FRAMES
|
|
, NUM_FRAMECHANNELS
|
|
, OFS_FRAMES
|
|
, OFS_BOUNDS
|
|
, NUM_COMMENT
|
|
, OFS_COMMENT
|
|
, NUM_EXTENSIONS
|
|
, OFS_EXTENSIONS
|
|
}
|
|
}
|
|
|
|
pub mod types {
|
|
iota! {
|
|
pub const POSITION: usize = iota;
|
|
, TEXCOORD
|
|
, NORMAL
|
|
, TANGENT
|
|
, BLENDINDEXES
|
|
, BLENDWEIGHTS
|
|
, COLOR
|
|
}
|
|
pub const CUSTOM: usize = 0x10;
|
|
}
|
|
|
|
pub mod formats {
|
|
iota! {
|
|
pub const BYTE: u32 = iota;
|
|
, UBYTE
|
|
, SHORT
|
|
, USHORT
|
|
, INT
|
|
, UINT
|
|
, HALF
|
|
, FLOAT
|
|
, DOUBLE
|
|
}
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct Mesh {
|
|
pub name: u32,
|
|
pub material: u32,
|
|
pub first_vertex: u32,
|
|
pub num_vertexes: u32,
|
|
pub first_triangle: u32,
|
|
pub num_triangles: u32,
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct Header {
|
|
pub fields: [u32; 27],
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct VertexArray {
|
|
va_type: u32,
|
|
va_flags: u32,
|
|
va_format: u32,
|
|
va_size: u32,
|
|
va_offset: u32,
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub struct Model <'a> {
|
|
data: &'a [u8],
|
|
|
|
pub header: Header,
|
|
text: Vec <u8>,
|
|
pub meshes: Vec <Mesh>,
|
|
vertexarrays: Vec <VertexArray>,
|
|
}
|
|
|
|
impl Header {
|
|
pub fn from_slice (input: &[u8]) -> IResult <&[u8], Header> {
|
|
let (input, _) = tag (b"INTERQUAKEMODEL\0")(input)?;
|
|
let (input, version) = le_u32 (input)?;
|
|
|
|
// I only know how to parse version 2
|
|
assert_eq! (version, 2);
|
|
|
|
let mut input = input;
|
|
let mut fields = [0; 27];
|
|
fields [0] = version;
|
|
|
|
for field in fields.iter_mut ().skip (1) {
|
|
let (i, h) = le_u32 (input)?;
|
|
input = i;
|
|
*field = h;
|
|
}
|
|
|
|
Ok ((input, Header {
|
|
fields,
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl Mesh {
|
|
pub fn from_slice (input: &[u8]) -> IResult <&[u8], Mesh> {
|
|
let mut result = Mesh {
|
|
name: 0,
|
|
material: 0,
|
|
first_vertex: 0,
|
|
num_vertexes: 0,
|
|
first_triangle: 0,
|
|
num_triangles: 0,
|
|
};
|
|
|
|
let mut input = input;
|
|
for field in [
|
|
&mut result.name,
|
|
&mut result.material,
|
|
&mut result.first_vertex,
|
|
&mut result.num_vertexes,
|
|
&mut result.first_triangle,
|
|
&mut result.num_triangles,
|
|
].iter_mut () {
|
|
let (i, f) = le_u32 (input)?;
|
|
input = i;
|
|
**field = f;
|
|
}
|
|
|
|
Ok ((input, result))
|
|
}
|
|
}
|
|
|
|
impl VertexArray {
|
|
pub fn from_slice (input: &[u8]) -> IResult <&[u8], VertexArray> {
|
|
let mut result = VertexArray {
|
|
va_type: 0,
|
|
va_flags: 0,
|
|
va_format: 0,
|
|
va_size: 0,
|
|
va_offset: 0,
|
|
};
|
|
|
|
let mut input = input;
|
|
for field in [
|
|
&mut result.va_type,
|
|
&mut result.va_flags,
|
|
&mut result.va_format,
|
|
&mut result.va_size,
|
|
&mut result.va_offset,
|
|
].iter_mut () {
|
|
let (i, f) = le_u32 (input)?;
|
|
input = i;
|
|
**field = f;
|
|
}
|
|
|
|
Ok ((input, result))
|
|
}
|
|
}
|
|
|
|
#[derive (Debug)]
|
|
pub enum ModelLoadErr {
|
|
ParseHeaderFailed,
|
|
ParseMeshFailed (usize),
|
|
ParseVertexArrayFailed (usize),
|
|
}
|
|
|
|
impl <'a> Model <'a> {
|
|
pub fn from_slice (data: &'a [u8]) -> Result <Model <'a>, ModelLoadErr> {
|
|
let header = match Header::from_slice (data) {
|
|
Ok ((_, h)) => h,
|
|
Err (_) => return Err (ModelLoadErr::ParseHeaderFailed),
|
|
};
|
|
|
|
let text = {
|
|
let offset: usize = header.fields [consts::OFS_TEXT].try_into ().unwrap ();
|
|
let num: usize = header.fields [consts::NUM_TEXT].try_into ().unwrap ();
|
|
Vec::from (&data [offset..offset + num])
|
|
};
|
|
|
|
let meshes = {
|
|
let num: usize = header.fields [consts::NUM_MESHES].try_into ().unwrap ();
|
|
let mut meshes = Vec::with_capacity (num);
|
|
let mesh_size = 6 * 4;
|
|
let meshes_offset: usize = header.fields [consts::OFS_MESHES].try_into ().unwrap ();
|
|
|
|
for i in 0..num {
|
|
let offset = meshes_offset + i * mesh_size;
|
|
let mesh_slice = &data [offset..offset + mesh_size];
|
|
|
|
let mesh = match Mesh::from_slice (mesh_slice) {
|
|
Ok ((_, m)) => m,
|
|
Err (_) => return Err (ModelLoadErr::ParseMeshFailed (i)),
|
|
};
|
|
|
|
meshes.push (mesh);
|
|
}
|
|
meshes
|
|
};
|
|
|
|
let vertexarrays = {
|
|
let num: usize = header.fields [consts::NUM_VERTEXARRAYS].try_into ().unwrap ();
|
|
let mut vertexarrays = Vec::with_capacity (num);
|
|
let vertexarray_size = 5 * 4;
|
|
let vertexarrays_offset: usize = header.fields [consts::OFS_VERTEXARRAYS].try_into ().unwrap ();
|
|
|
|
for i in 0..num {
|
|
let offset = vertexarrays_offset + i * vertexarray_size;
|
|
let vertexarray_slice = &data [offset..offset + vertexarray_size];
|
|
|
|
let vertexarray = match VertexArray::from_slice (vertexarray_slice) {
|
|
Ok ((_, va)) => va,
|
|
Err (_) => return Err (ModelLoadErr::ParseVertexArrayFailed (i)),
|
|
};
|
|
|
|
vertexarrays.push (vertexarray);
|
|
}
|
|
|
|
vertexarrays
|
|
};
|
|
|
|
Ok (Model {
|
|
data,
|
|
|
|
header,
|
|
text,
|
|
meshes,
|
|
vertexarrays,
|
|
})
|
|
}
|
|
|
|
pub fn get_vertex_slice (&self,
|
|
vertexarray_index: usize
|
|
) -> &[u8]
|
|
{
|
|
let vertexarray = &self.vertexarrays [vertexarray_index];
|
|
let bytes_per_float = 4;
|
|
let stride = bytes_per_float * vertexarray.va_size;
|
|
//assert_eq! (stride, 12);
|
|
|
|
let offset: usize = (vertexarray.va_offset).try_into ().unwrap ();
|
|
let num_bytes: usize = (stride * self.header.fields [consts::NUM_VERTEXES]).try_into ().unwrap ();
|
|
|
|
&self.data [offset..offset + num_bytes]
|
|
}
|
|
|
|
pub fn get_index_slice (&self, mesh_index: usize) -> &[u8] {
|
|
let mesh = &self.meshes [mesh_index];
|
|
let bytes_per_u32 = 4;
|
|
let indexes_per_tri = 3;
|
|
let stride = bytes_per_u32 * indexes_per_tri;
|
|
|
|
let offset: usize = (self.header.fields [consts::OFS_TRIANGLES] + stride * mesh.first_triangle).try_into ().unwrap ();
|
|
|
|
let num_bytes: usize = (stride * mesh.num_triangles).try_into ().unwrap ();
|
|
|
|
&self.data [offset..offset + num_bytes]
|
|
}
|
|
|
|
pub fn get_all_indexes (&self) -> &[u8] {
|
|
let bytes_per_u32 = 4;
|
|
let indexes_per_tri = 3;
|
|
let stride = bytes_per_u32 * indexes_per_tri;
|
|
|
|
let offset: usize = self.header.fields [consts::OFS_TRIANGLES].try_into ().unwrap ();
|
|
|
|
let num_bytes: usize = (stride * self.header.fields [consts::NUM_TRIANGLES]).try_into ().unwrap ();
|
|
|
|
&self.data [offset..offset + num_bytes]
|
|
}
|
|
|
|
// I don't think IQM makes any guarantees about UTF-8
|
|
// so I will only say that the slice has no NULs
|
|
|
|
pub fn get_mesh_name (&self, index: usize) -> &[u8] {
|
|
let mesh = &self.meshes [index];
|
|
|
|
let ofs: usize = (self.header.fields [consts::OFS_TEXT] + mesh.name).try_into ().unwrap ();
|
|
|
|
// There should be an easy way to do this with CString?
|
|
let mut nul_index = None;
|
|
for (j, c) in self.data [ofs..].iter ().enumerate () {
|
|
if *c == 0 {
|
|
nul_index = Some (j);
|
|
break;
|
|
}
|
|
}
|
|
let nul_index = nul_index.unwrap ();
|
|
|
|
&self.data [ofs..ofs + nul_index]
|
|
}
|
|
}
|