inter_quake_model/src/lib.rs

294 lines
6.7 KiB
Rust

#[macro_use]
extern crate iota;
use std::convert::TryInto;
use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt};
pub mod names;
pub mod zero_copy;
pub use crate::names::consts;
pub use crate::names::types;
pub use zero_copy::Error;
use zero_copy::usize_from;
#[derive (Debug, Default)]
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, Default)]
pub struct Header {
pub fields: [u32; 27],
}
#[derive (Debug, Default)]
pub struct VertexArray {
va_type: u32,
va_flags: u32,
va_format: u32,
va_size: u32,
va_offset: u32,
}
#[derive (Debug)]
pub struct Model <'data> {
zc: zero_copy::Model <'data>,
pub header: Header,
text: Vec <u8>,
pub meshes: Vec <Mesh>,
vertexarrays: Vec <VertexArray>,
}
impl Header {
pub fn from_zc (zc: &zero_copy::Model) -> Header {
let mut header = Header::default ();
for (i, value) in zc.header_fields ().enumerate () {
header.fields [i] = value;
}
header
}
}
impl Mesh {
pub fn from_zc (zc: &zero_copy::Mesh) -> Mesh {
use crate::names::consts::*;
Mesh {
name: zc.field (MESH_NAME),
material: zc.field (MESH_MATERIAL),
first_vertex: zc.field (MESH_FIRST_VERTEX),
num_vertexes: zc.field (MESH_NUM_VERTEXES),
first_triangle: zc.field (MESH_FIRST_TRIANGLE),
num_triangles: zc.field (MESH_NUM_TRIANGLES),
}
}
}
impl VertexArray {
pub fn from_slice (input: &[u8]) -> Result <VertexArray, Error> {
let mut va = VertexArray::default ();
let mut rdr = Cursor::new (input);
for field in [
&mut va.va_type,
&mut va.va_flags,
&mut va.va_format,
&mut va.va_size,
&mut va.va_offset,
].iter_mut () {
**field = rdr.read_u32::<LittleEndian> ().map_err (|_| Error::ParseVertexArrayFailed)?;
}
Ok (va)
}
pub fn from_zc (zc: &zero_copy::VertexArray) -> VertexArray {
use crate::names::consts::*;
VertexArray {
va_type: zc.field (VA_TYPE),
va_flags: zc.field (VA_FLAGS),
va_format: zc.field (VA_FORMAT),
va_size: zc.field (VA_SIZE),
va_offset: zc.field (VA_OFFSET),
}
}
}
impl <'a> Model <'a> {
pub fn from_slice (data: &'a [u8]) -> Result <Model <'a>, Error> {
let zc = zero_copy::Model::get_root (data)?;
let header = Header::from_zc (&zc);
let text = {
let offset = usize_from (zc.header_field (consts::OFS_TEXT));
let num = usize_from (zc.header_field (consts::NUM_TEXT));
Vec::from (&data [offset..offset + num])
};
let meshes = zc.meshes ().map (|zc_mesh| {
Mesh::from_zc (&zc_mesh)
}).collect ();
let vertexarrays = zc.vertexarrays ().map (|zc_va| {
VertexArray::from_zc (&zc_va)
}).collect ();
Ok (Model {
zc,
header,
text,
meshes,
vertexarrays,
})
}
pub fn get_vertex_slice (&self, idx: usize) -> &[u8] {
self.zc.vertexarray (idx).get_vertex_slice ()
}
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.zc.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.zc.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.zc.data [ofs..].iter ().enumerate () {
if *c == 0 {
nul_index = Some (j);
break;
}
}
let nul_index = nul_index.unwrap ();
&self.zc.data [ofs..ofs + nul_index]
}
}
#[cfg (test)]
mod borrow_test {
struct Model <'data> {
data: &'data [u8],
}
struct VertexArray <'model: 'data, 'data> {
model: &'model Model <'data>,
}
impl <'data> Model <'data> {
pub fn va (&self) -> VertexArray {
VertexArray {
model: self,
}
}
pub fn slice (&'data self) -> &'data [u8] {
let va = self.va ();
va.slice ()
}
}
impl <'model: 'data, 'data> VertexArray <'model, 'data> {
pub fn slice (&self) -> &'data [u8] {
self.model.data
}
}
#[test]
fn nothing () {
let data = [1, 1, 2, 3, 5, 8];
let model = Model {
data: &data,
};
let va = model.va ();
let slice = model.va ().slice ();
let slice = model.slice ();
}
}
#[cfg (test)]
mod test {
use std::path::Path;
fn load_file <P: AsRef <Path>> (filename: P) -> Vec <u8> {
use std::convert::TryInto;
use std::fs::File;
use std::io::Read;
let mut f = File::open (filename).unwrap ();
let mut data = vec! [0u8; f.metadata ().unwrap ().len ().try_into ().unwrap ()];
f.read_exact (&mut data).unwrap ();
data
}
#[test]
pub fn load_models () {
use super::*;
let airplane_bytes = load_file ("airplane.iqm");
let arrow_bytes = load_file ("arrow.iqm");
let truk_bytes = load_file ("truk.iqm");
let airplane = Model::from_slice (&airplane_bytes).unwrap ();
let arrow = Model::from_slice (&arrow_bytes).unwrap ();
let truk = Model::from_slice (&truk_bytes).unwrap ();
use crate::names::consts::*;
assert_eq! (airplane.header.fields [NUM_VERTEXES], 304);
assert_eq! (airplane.header.fields [NUM_TRIANGLES], 156);
assert_eq! (airplane.meshes.len (), 1);
assert_eq! (airplane.vertexarrays.len (), 4);
assert_eq! (arrow.header.fields [NUM_VERTEXES], 109);
assert_eq! (arrow.header.fields [NUM_TRIANGLES], 117);
assert_eq! (arrow.meshes.len (), 1);
assert_eq! (arrow.vertexarrays.len (), 4);
assert_eq! (truk.header.fields [NUM_VERTEXES], 256);
assert_eq! (truk.header.fields [NUM_TRIANGLES], 180);
assert_eq! (truk.meshes.len (), 5);
assert_eq! (truk.vertexarrays.len (), 4);
}
#[test]
pub fn load_zero_copy () {
use crate::zero_copy::*;
let airplane_bytes = load_file ("airplane.iqm");
let arrow_bytes = load_file ("arrow.iqm");
let truk_bytes = load_file ("truk.iqm");
let airplane = Model::get_root (&airplane_bytes);
let arrow = Model::get_root (&arrow_bytes);
let truk = Model::get_root (&truk_bytes);
}
}