#[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 , pub meshes: Vec , vertexarrays: Vec , } 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 { 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:: ().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 , 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 > (filename: P) -> Vec { 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); } }