294 lines
6.7 KiB
Rust
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);
|
|
}
|
|
}
|