#[macro_use] extern crate iota; use std::convert::TryInto; use std::io::Cursor; use std::mem::size_of; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt}; pub mod names; pub mod zero_copy; pub use crate::names::consts; pub use crate::names::types; #[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 <'a> { data: &'a [u8], pub header: Header, text: Vec , pub meshes: Vec , vertexarrays: Vec , } #[derive (Debug)] pub enum ModelLoadErr { BadMagic, UnsupportedVersion, ParseMeshFailed, ParseVertexArrayFailed, } use crate::names::MAGIC; impl Header { pub fn from_slice (input: &[u8]) -> Result { if &input [0..MAGIC.len ()] != MAGIC { return Err (ModelLoadErr::BadMagic); } let input = &input [MAGIC.len ()..]; let mut header = Header::default (); LittleEndian::read_u32_into (&input [0..header.fields.len () * size_of:: ()], &mut header.fields); if header.fields [consts::VERSION] != 2 { return Err (ModelLoadErr::UnsupportedVersion); } Ok (header) } } impl Mesh { pub fn from_slice (input: &[u8]) -> Result { let mut mesh = Mesh::default (); let mut rdr = Cursor::new (input); for field in [ &mut mesh.name, &mut mesh.material, &mut mesh.first_vertex, &mut mesh.num_vertexes, &mut mesh.first_triangle, &mut mesh.num_triangles, ].iter_mut () { **field = rdr.read_u32:: ().map_err (|_| ModelLoadErr::ParseMeshFailed)?; } Ok (mesh) } } 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 (|_| ModelLoadErr::ParseVertexArrayFailed)?; } Ok (va) } } impl <'a> Model <'a> { pub fn from_slice (data: &'a [u8]) -> Result , ModelLoadErr> { let header = Header::from_slice (data)?; 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 = Mesh::from_slice (mesh_slice)?; 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 = VertexArray::from_slice (vertexarray_slice)?; 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] } } #[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); } }