♻️ Extract zero-copy interface

master
_ 2020-05-25 22:03:30 +00:00
parent d68d835baf
commit 86612f4d8e
4 changed files with 278 additions and 71 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
/Cargo.lock

View File

@ -7,65 +7,11 @@ use std::mem::size_of;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
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 names;
pub mod zero_copy;
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
}
}
pub use crate::names::consts;
pub use crate::names::types;
#[derive (Debug, Default)]
pub struct Mesh {
@ -109,13 +55,14 @@ pub enum ModelLoadErr {
ParseVertexArrayFailed,
}
use crate::names::MAGIC;
impl Header {
pub fn from_slice (input: &[u8]) -> Result <Header, ModelLoadErr> {
let magic = b"INTERQUAKEMODEL\0";
if &input [0..magic.len ()] != magic {
if &input [0..MAGIC.len ()] != MAGIC {
return Err (ModelLoadErr::BadMagic);
}
let input = &input [magic.len ()..];
let input = &input [MAGIC.len ()..];
let mut header = Header::default ();
LittleEndian::read_u32_into (&input [0..header.fields.len () * size_of::<u32> ()], &mut header.fields);
@ -288,18 +235,14 @@ impl <'a> Model <'a> {
#[cfg (test)]
mod test {
use super::*;
use std::fs::File;
use std::io::Read;
use std::path::Path;
#[test]
pub fn nothing () {
}
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 ();
@ -308,6 +251,8 @@ mod test {
#[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");
@ -316,7 +261,7 @@ mod test {
let arrow = Model::from_slice (&arrow_bytes).unwrap ();
let truk = Model::from_slice (&truk_bytes).unwrap ();
use consts::*;
use crate::names::consts::*;
assert_eq! (airplane.header.fields [NUM_VERTEXES], 304);
assert_eq! (airplane.header.fields [NUM_TRIANGLES], 156);
@ -333,4 +278,17 @@ mod test {
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);
}
}

82
src/names.rs Normal file
View File

@ -0,0 +1,82 @@
pub const MAGIC: &[u8] = b"INTERQUAKEMODEL\0";
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
}
iota! {
pub const
VA_TYPE: usize = iota;
, VA_FLAGS
, VA_FORMAT
, VA_SIZE
, VA_OFFSET
, VA_FIELD_COUNT
}
iota! {
pub const
MESH_NAME: usize = iota;
, MESH_MATERIAL
, MESH_FIRST_VERTEX
, MESH_NUM_VERTEXES
, MESH_FIRST_TRIANGLE
, MESH_NUM_TRIANGLES
, MESH_FIELD_COUNT
}
}
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
}
}

166
src/zero_copy.rs Normal file
View File

@ -0,0 +1,166 @@
use std::convert::TryFrom;
use std::mem::size_of;
use byteorder::{ByteOrder, LittleEndian};
pub struct Model <'data> {
pub data: &'data [u8],
}
pub struct VertexArray <'data> {
pub model: &'data Model <'data>,
ofs: usize,
}
pub struct Mesh <'data> {
pub model: &'data Model <'data>,
ofs: usize,
}
pub struct VertexArrays <'data> {
model: &'data Model <'data>,
idx: usize,
}
pub struct Meshes <'data> {
model: &'data Model <'data>,
idx: usize,
}
#[derive (Debug)]
pub enum Error {
BadMagic,
UnsupportedVersion,
HeaderTooShort,
ParseMeshFailed,
ParseVertexArrayFailed,
}
use crate::names;
use names::consts;
use names::MAGIC;
pub const HEADER_FIELD_COUNT: usize = 27;
// If you are using IQM models on an 8-bit or 16-bit microcontroller
// you're smarter than me anyway
fn usize_from (x: u32) -> usize {
usize::try_from (x).unwrap ()
}
impl <'data> Iterator for VertexArrays <'data> {
type Item = VertexArray <'data>;
fn next (&mut self) -> Option <Self::Item> {
if self.idx >= usize_from (self.model.header_field (crate::names::consts::NUM_VERTEXARRAYS)) {
return None;
}
let result = self.model.vertexarray (self.idx);
self.idx += 1;
Some (result)
}
}
impl <'data> Iterator for Meshes <'data> {
type Item = Mesh <'data>;
fn next (&mut self) -> Option <Self::Item> {
if self.idx >= usize_from (self.model.header_field (crate::names::consts::NUM_MESHES)) {
return None;
}
let result = self.model.mesh (self.idx);
self.idx += 1;
Some (result)
}
}
impl <'data> Model <'data> {
pub fn get_root (data: &[u8]) -> Result <Model, Error> {
if &data [0..MAGIC.len ()] != MAGIC {
return Err (Error::BadMagic);
}
if data.len () < MAGIC.len () + size_of::<u32> () * HEADER_FIELD_COUNT {
return Err (Error::HeaderTooShort);
}
let m = Model {
data,
};
if m.header_field (consts::VERSION) != 2 {
return Err (Error::UnsupportedVersion);
}
Ok (m)
}
pub fn header_field (&self, idx: usize) -> u32 {
LittleEndian::read_u32 (&self.data [MAGIC.len () + idx * size_of::<u32> ()..])
}
pub fn vertexarray (&self, idx: usize) -> VertexArray {
use crate::names::consts::*;
assert! (idx < usize_from (self.header_field (NUM_VERTEXARRAYS)));
let vertexarray_stride = VA_FIELD_COUNT * size_of::<u32> ();
VertexArray {
model: self,
ofs: usize_from (self.header_field (OFS_VERTEXARRAYS)) + idx * vertexarray_stride,
}
}
pub fn mesh (&self, idx: usize) -> Mesh {
use crate::names::consts::*;
assert! (idx < usize_from (self.header_field (NUM_MESHES)));
let mesh_stride = MESH_FIELD_COUNT * size_of::<u32> ();
Mesh {
model: self,
ofs: usize_from (self.header_field (OFS_MESHES)) + idx * mesh_stride,
}
}
pub fn vertexarrays (&self) -> VertexArrays {
VertexArrays {
model: self,
idx: 0,
}
}
pub fn meshes (&self) -> Meshes {
Meshes {
model: self,
idx: 0,
}
}
}
impl <'data> VertexArray <'data> {
pub fn field (&self, idx: usize) -> u32 {
LittleEndian::read_u32 (&self.model.data [self.ofs + idx * size_of::<u32>()..])
}
pub fn get_vertex_slice (&self) -> &[u8] {
use names::consts::*;
let stride = size_of::<f32> () * usize_from (self.field (VA_SIZE));
let offset = usize_from (self.field (VA_OFFSET));
let num_bytes = stride * usize_from (self.model.header_field (NUM_VERTEXES));
&self.model.data [offset..offset + num_bytes]
}
}
impl <'data> Mesh <'data> {
pub fn field (&self, idx: usize) -> u32 {
LittleEndian::read_u32 (&self.model.data [self.ofs + idx * size_of::<u32>()..])
}
}