♻️ Extract zero-copy interface
parent
d68d835baf
commit
86612f4d8e
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
/Cargo.lock
|
||||||
|
|
100
src/lib.rs
100
src/lib.rs
|
@ -7,65 +7,11 @@ use std::mem::size_of;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
|
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
|
||||||
|
|
||||||
pub mod consts {
|
pub mod names;
|
||||||
use iota::iota;
|
pub mod zero_copy;
|
||||||
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 types {
|
pub use crate::names::consts;
|
||||||
iota! {
|
pub use crate::names::types;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Debug, Default)]
|
#[derive (Debug, Default)]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
|
@ -109,13 +55,14 @@ pub enum ModelLoadErr {
|
||||||
ParseVertexArrayFailed,
|
ParseVertexArrayFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::names::MAGIC;
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
pub fn from_slice (input: &[u8]) -> Result <Header, ModelLoadErr> {
|
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);
|
return Err (ModelLoadErr::BadMagic);
|
||||||
}
|
}
|
||||||
let input = &input [magic.len ()..];
|
let input = &input [MAGIC.len ()..];
|
||||||
|
|
||||||
let mut header = Header::default ();
|
let mut header = Header::default ();
|
||||||
LittleEndian::read_u32_into (&input [0..header.fields.len () * size_of::<u32> ()], &mut header.fields);
|
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)]
|
#[cfg (test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn nothing () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_file <P: AsRef <Path>> (filename: P) -> Vec <u8> {
|
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 f = File::open (filename).unwrap ();
|
||||||
let mut data = vec! [0u8; f.metadata ().unwrap ().len ().try_into ().unwrap ()];
|
let mut data = vec! [0u8; f.metadata ().unwrap ().len ().try_into ().unwrap ()];
|
||||||
f.read_exact (&mut data).unwrap ();
|
f.read_exact (&mut data).unwrap ();
|
||||||
|
@ -308,6 +251,8 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn load_models () {
|
pub fn load_models () {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
let airplane_bytes = load_file ("airplane.iqm");
|
let airplane_bytes = load_file ("airplane.iqm");
|
||||||
let arrow_bytes = load_file ("arrow.iqm");
|
let arrow_bytes = load_file ("arrow.iqm");
|
||||||
let truk_bytes = load_file ("truk.iqm");
|
let truk_bytes = load_file ("truk.iqm");
|
||||||
|
@ -316,7 +261,7 @@ mod test {
|
||||||
let arrow = Model::from_slice (&arrow_bytes).unwrap ();
|
let arrow = Model::from_slice (&arrow_bytes).unwrap ();
|
||||||
let truk = Model::from_slice (&truk_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_VERTEXES], 304);
|
||||||
assert_eq! (airplane.header.fields [NUM_TRIANGLES], 156);
|
assert_eq! (airplane.header.fields [NUM_TRIANGLES], 156);
|
||||||
|
@ -333,4 +278,17 @@ mod test {
|
||||||
assert_eq! (truk.meshes.len (), 5);
|
assert_eq! (truk.meshes.len (), 5);
|
||||||
assert_eq! (truk.vertexarrays.len (), 4);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>()..])
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue