♻️ Extract IQM crate
parent
cdea0ffbea
commit
6297079ae9
|
@ -99,6 +99,14 @@ dependencies = [
|
||||||
"adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inter_quake_model"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"iota 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iota"
|
name = "iota"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -209,6 +217,7 @@ dependencies = [
|
||||||
"float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glam 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glam 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"inter_quake_model 0.1.0",
|
||||||
"iota 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"iota 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -23,3 +23,5 @@ partial-min-max = "0.4.0"
|
||||||
png = "0.15.3"
|
png = "0.15.3"
|
||||||
rand = "0.6.5"
|
rand = "0.6.5"
|
||||||
sdl2 = "0.32.2"
|
sdl2 = "0.32.2"
|
||||||
|
|
||||||
|
inter_quake_model = {path = "../inter_quake_model"}
|
||||||
|
|
314
src/iqm.rs
314
src/iqm.rs
|
@ -1,314 +0,0 @@
|
||||||
use nom::{
|
|
||||||
IResult,
|
|
||||||
bytes::complete::{tag},
|
|
||||||
number::complete::{le_u32},
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
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 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Debug)]
|
|
||||||
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)]
|
|
||||||
pub struct Header {
|
|
||||||
pub fields: [u32; 27],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Debug)]
|
|
||||||
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 <u8>,
|
|
||||||
pub meshes: Vec <Mesh>,
|
|
||||||
vertexarrays: Vec <VertexArray>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Header {
|
|
||||||
pub fn from_slice (input: &[u8]) -> IResult <&[u8], Header> {
|
|
||||||
let (input, _) = tag (b"INTERQUAKEMODEL\0")(input)?;
|
|
||||||
let (input, version) = le_u32 (input)?;
|
|
||||||
|
|
||||||
// I only know how to parse version 2
|
|
||||||
assert_eq! (version, 2);
|
|
||||||
|
|
||||||
let mut input = input;
|
|
||||||
let mut fields = [0; 27];
|
|
||||||
fields [0] = version;
|
|
||||||
|
|
||||||
for field in fields.iter_mut ().skip (1) {
|
|
||||||
let (i, h) = le_u32 (input)?;
|
|
||||||
input = i;
|
|
||||||
*field = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok ((input, Header {
|
|
||||||
fields,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mesh {
|
|
||||||
pub fn from_slice (input: &[u8]) -> IResult <&[u8], Mesh> {
|
|
||||||
let mut result = Mesh {
|
|
||||||
name: 0,
|
|
||||||
material: 0,
|
|
||||||
first_vertex: 0,
|
|
||||||
num_vertexes: 0,
|
|
||||||
first_triangle: 0,
|
|
||||||
num_triangles: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut input = input;
|
|
||||||
for field in [
|
|
||||||
&mut result.name,
|
|
||||||
&mut result.material,
|
|
||||||
&mut result.first_vertex,
|
|
||||||
&mut result.num_vertexes,
|
|
||||||
&mut result.first_triangle,
|
|
||||||
&mut result.num_triangles,
|
|
||||||
].iter_mut () {
|
|
||||||
let (i, f) = le_u32 (input)?;
|
|
||||||
input = i;
|
|
||||||
**field = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok ((input, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VertexArray {
|
|
||||||
pub fn from_slice (input: &[u8]) -> IResult <&[u8], VertexArray> {
|
|
||||||
let mut result = VertexArray {
|
|
||||||
va_type: 0,
|
|
||||||
va_flags: 0,
|
|
||||||
va_format: 0,
|
|
||||||
va_size: 0,
|
|
||||||
va_offset: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut input = input;
|
|
||||||
for field in [
|
|
||||||
&mut result.va_type,
|
|
||||||
&mut result.va_flags,
|
|
||||||
&mut result.va_format,
|
|
||||||
&mut result.va_size,
|
|
||||||
&mut result.va_offset,
|
|
||||||
].iter_mut () {
|
|
||||||
let (i, f) = le_u32 (input)?;
|
|
||||||
input = i;
|
|
||||||
**field = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok ((input, result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive (Debug)]
|
|
||||||
pub enum ModelLoadErr {
|
|
||||||
ParseHeaderFailed,
|
|
||||||
ParseMeshFailed (usize),
|
|
||||||
ParseVertexArrayFailed (usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <'a> Model <'a> {
|
|
||||||
pub fn from_slice (data: &'a [u8]) -> Result <Model <'a>, ModelLoadErr> {
|
|
||||||
let header = match Header::from_slice (data) {
|
|
||||||
Ok ((_, h)) => h,
|
|
||||||
Err (_) => return Err (ModelLoadErr::ParseHeaderFailed),
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = match Mesh::from_slice (mesh_slice) {
|
|
||||||
Ok ((_, m)) => m,
|
|
||||||
Err (_) => return Err (ModelLoadErr::ParseMeshFailed (i)),
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = match VertexArray::from_slice (vertexarray_slice) {
|
|
||||||
Ok ((_, va)) => va,
|
|
||||||
Err (_) => return Err (ModelLoadErr::ParseVertexArrayFailed (i)),
|
|
||||||
};
|
|
||||||
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ pub mod file;
|
||||||
pub mod glezz;
|
pub mod glezz;
|
||||||
pub mod gl_state;
|
pub mod gl_state;
|
||||||
pub mod gpu_buffers;
|
pub mod gpu_buffers;
|
||||||
pub mod iqm;
|
|
||||||
pub mod physics;
|
pub mod physics;
|
||||||
pub mod renderable_model;
|
pub mod renderable_model;
|
||||||
pub mod shader;
|
pub mod shader;
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::convert::TryInto;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
|
||||||
use crate::gpu_buffers::*;
|
use crate::gpu_buffers::*;
|
||||||
use crate::iqm;
|
|
||||||
|
|
||||||
// Takes ownership of mesh stuff in an opaque way that's abstract
|
// Takes ownership of mesh stuff in an opaque way that's abstract
|
||||||
// from the IQM model. IQM is zero-copy, but this is not.
|
// from the IQM model. IQM is zero-copy, but this is not.
|
||||||
|
@ -49,10 +48,10 @@ pub const
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderableModel {
|
impl RenderableModel {
|
||||||
pub fn from_iqm (model: &iqm::Model) -> RenderableModel {
|
pub fn from_iqm (model: &inter_quake_model::Model) -> RenderableModel {
|
||||||
let pos_bytes = model.get_vertex_slice (iqm::types::POSITION);
|
let pos_bytes = model.get_vertex_slice (inter_quake_model::types::POSITION);
|
||||||
let uv_bytes = model.get_vertex_slice (iqm::types::TEXCOORD);
|
let uv_bytes = model.get_vertex_slice (inter_quake_model::types::TEXCOORD);
|
||||||
let normal_bytes = model.get_vertex_slice (iqm::types::NORMAL);
|
let normal_bytes = model.get_vertex_slice (inter_quake_model::types::NORMAL);
|
||||||
|
|
||||||
let num_pos = pos_bytes.len () / 4;
|
let num_pos = pos_bytes.len () / 4;
|
||||||
let num_uv = uv_bytes.len () / 4;
|
let num_uv = uv_bytes.len () / 4;
|
||||||
|
@ -147,7 +146,7 @@ pub fn renderable_from_iqm_file <P> (filename: P) -> RenderableModel
|
||||||
where P: AsRef <std::path::Path>
|
where P: AsRef <std::path::Path>
|
||||||
{
|
{
|
||||||
let data = crate::file::load_small_file (filename, 1024 * 1024).unwrap ();
|
let data = crate::file::load_small_file (filename, 1024 * 1024).unwrap ();
|
||||||
let model = crate::iqm::Model::from_slice (&data).unwrap ();
|
let model = inter_quake_model::Model::from_slice (&data).unwrap ();
|
||||||
|
|
||||||
RenderableModel::from_iqm (&model)
|
RenderableModel::from_iqm (&model)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue