540 lines
13 KiB
Rust
540 lines
13 KiB
Rust
use std::{
|
|
fs::File,
|
|
io::Write,
|
|
str::FromStr,
|
|
};
|
|
|
|
use glam::{
|
|
Mat4,
|
|
Vec3,
|
|
};
|
|
|
|
fn main () {
|
|
// The `clap` crate is very popular but also very slow to compile,
|
|
// so I'll do my own CLI parsing
|
|
let mut args = std::env::args ();
|
|
let _exe_name = args.next ().unwrap ();
|
|
|
|
let mut input_path = None;
|
|
let mut output_path = None;
|
|
let mut scale = 1.0;
|
|
|
|
while let Some (arg) = args.next () {
|
|
if arg == "--input" || arg == "-i" {
|
|
input_path = args.next ();
|
|
}
|
|
else if arg == "--output" || arg == "-o" {
|
|
output_path = args.next ();
|
|
}
|
|
else if arg == "--scale" {
|
|
scale = f32::from_str (&args.next ().unwrap ()).unwrap ();
|
|
}
|
|
}
|
|
|
|
let input_path = input_path.unwrap ();
|
|
let output_path = output_path.unwrap ();
|
|
|
|
let (document, buffers, _images) = gltf::import (input_path).unwrap ();
|
|
let buffer = buffers.get (0).unwrap ();
|
|
|
|
let mut vertexes = Vec::default ();
|
|
let mut triangles = Vec::default ();
|
|
let mut meshes = Vec::default ();
|
|
let mut texts = String::from ("\0");
|
|
|
|
for node in document.nodes () {
|
|
let mesh = match node.mesh () {
|
|
None => continue,
|
|
Some (x) => x,
|
|
};
|
|
|
|
let m = gltf_node_get_mat4 (&node);
|
|
|
|
for (i, prim) in mesh.primitives ().enumerate () {
|
|
assert_eq! (prim.mode (), gltf::mesh::Mode::Triangles);
|
|
|
|
let indices = prim.indices ().unwrap ();
|
|
let indices_view = indices.view ().unwrap ();
|
|
let indices_offset = indices.offset () + indices_view.offset ();
|
|
let indices_slice = &buffer [indices_offset..(indices_offset + indices.count () * 2)];
|
|
|
|
let pos = prim.get (&gltf::Semantic::Positions).unwrap ();
|
|
let pos_view = pos.view ().unwrap ();
|
|
let pos_offset = pos.offset () + pos_view.offset ();
|
|
let pos_slice = &buffer [pos_offset..(pos_offset + pos.count () * 4 * 3)];
|
|
|
|
let norm = prim.get (&gltf::Semantic::Normals).unwrap ();
|
|
let norm_view = norm.view ().unwrap ();
|
|
let norm_offset = norm.offset () + norm_view.offset ();
|
|
let norm_slice = &buffer [norm_offset..(norm_offset + norm.count () * 4 * 3)];
|
|
|
|
let first_vertex = vertexes.len ();
|
|
|
|
let mesh = iqm::Mesh {
|
|
name: u32::try_from (texts.len ()).unwrap (),
|
|
material: 0,
|
|
first_vertex: u32::try_from (first_vertex).unwrap (),
|
|
num_vertexes: u32::try_from (indices_slice.len () / 2).unwrap (),
|
|
// num_vertexes: u32::try_from (3).unwrap (),
|
|
first_triangle: u32::try_from (triangles.len ()).unwrap (),
|
|
num_triangles: u32::try_from (indices_slice.len () / 6).unwrap (),
|
|
//num_triangles: u32::try_from (1).unwrap (),
|
|
};
|
|
texts.push_str (&format! ("{}/{}\0", node.name ().unwrap (), i));
|
|
|
|
meshes.push (mesh);
|
|
|
|
for chunk in indices_slice.chunks_exact (2 * 3) {
|
|
let read_idx = |i: usize| u16::from_le_bytes ([chunk [i * 2 + 0], chunk [i * 2 + 1]]);
|
|
|
|
let idxs = [
|
|
read_idx (0),
|
|
read_idx (1),
|
|
read_idx (2),
|
|
];
|
|
|
|
let read_pos_coord = |i| f32::from_le_bytes ([
|
|
pos_slice [i * 4 + 0],
|
|
pos_slice [i * 4 + 1],
|
|
pos_slice [i * 4 + 2],
|
|
pos_slice [i * 4 + 3],
|
|
]);
|
|
|
|
let read_norm_coord = |i| f32::from_le_bytes ([
|
|
norm_slice [i * 4 + 0],
|
|
norm_slice [i * 4 + 1],
|
|
norm_slice [i * 4 + 2],
|
|
norm_slice [i * 4 + 3],
|
|
]);
|
|
|
|
let read_pos = move |i| {
|
|
let i = usize::try_from (i).unwrap ();
|
|
m.transform_point3 (Vec3::new (
|
|
read_pos_coord (i * 3 + 0),
|
|
read_pos_coord (i * 3 + 1),
|
|
read_pos_coord (i * 3 + 2),
|
|
))
|
|
};
|
|
|
|
let read_norm = move |i| {
|
|
let i = usize::try_from (i).unwrap ();
|
|
m.transform_vector3 (Vec3::new (
|
|
read_norm_coord (i * 3 + 0),
|
|
read_norm_coord (i * 3 + 1),
|
|
read_norm_coord (i * 3 + 2),
|
|
))
|
|
};
|
|
|
|
let verts = [
|
|
read_pos (idxs [0]),
|
|
read_pos (idxs [2]),
|
|
read_pos (idxs [1]),
|
|
];
|
|
|
|
let tri_start = u32::try_from (vertexes.len ()).unwrap ();
|
|
triangles.push ([
|
|
tri_start + 0,
|
|
tri_start + 1,
|
|
tri_start + 2,
|
|
]);
|
|
|
|
for i in [idxs [0], idxs [2], idxs [1]] {
|
|
let pos = read_pos (i);
|
|
let norm = read_norm (i);
|
|
|
|
vertexes.push (iqm::Vertex {
|
|
position: pos.to_array (),
|
|
normal: norm.to_array (),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let vertexarrays_unplaced = [
|
|
iqm::VertexArrayUnplaced {
|
|
r#type: iqm::POSITION,
|
|
flags: 0,
|
|
format: iqm::FLOAT,
|
|
size: 3,
|
|
},
|
|
iqm::VertexArrayUnplaced {
|
|
r#type: iqm::NORMAL,
|
|
flags: 0,
|
|
format: iqm::FLOAT,
|
|
size: 3,
|
|
},
|
|
iqm::VertexArrayUnplaced {
|
|
r#type: iqm::TANGENT,
|
|
flags: 0,
|
|
format: iqm::FLOAT,
|
|
size: 4,
|
|
},
|
|
iqm::VertexArrayUnplaced {
|
|
r#type: iqm::BLENDINDEXES,
|
|
flags: 0,
|
|
format: iqm::UBYTE,
|
|
size: 4,
|
|
},
|
|
iqm::VertexArrayUnplaced {
|
|
r#type: iqm::BLENDWEIGHTS,
|
|
flags: 0,
|
|
format: iqm::UBYTE,
|
|
size: 4,
|
|
},
|
|
];
|
|
|
|
let joints = [
|
|
iqm::Joint {
|
|
name: u32::try_from (texts.len ()).unwrap (),
|
|
parent: -1,
|
|
translate: [0.0, 0.0, 0.0],
|
|
rotate: [0.0, 0.0, 0.0, 1.0],
|
|
scale: [1.0, 1.0, 1.0],
|
|
},
|
|
];
|
|
texts.push_str ("bogus_joint\0");
|
|
|
|
let poses = [
|
|
iqm::Pose {
|
|
parent: -1,
|
|
channelmask: 1, // Should be X translation
|
|
channeloffset: [
|
|
0.0, 0.0, 0.0,
|
|
0.0, 0.0, 0.0, 1.0,
|
|
1.0, 1.0, 1.0,
|
|
],
|
|
channelscale: [16.0 / 32768.0; 10],
|
|
},
|
|
];
|
|
|
|
let anims = [
|
|
iqm::Anim {
|
|
name: u32::try_from (texts.len ()).unwrap (),
|
|
first_frame: 0,
|
|
num_frames: 2,
|
|
framerate: 0.5,
|
|
flags: 0,
|
|
},
|
|
];
|
|
texts.push_str ("bogus_anim\0");
|
|
|
|
let num_framechannels = 1;
|
|
|
|
let frames = [
|
|
0,
|
|
32768,
|
|
];
|
|
|
|
let vertexes = vertexes;
|
|
let triangles = triangles;
|
|
let meshes = meshes;
|
|
let texts = texts;
|
|
|
|
let num_vertexes = u32::try_from (vertexes.len ()).unwrap ();
|
|
let num_triangles = u32::try_from (triangles.len ()).unwrap ();
|
|
|
|
let num_joints = u32::try_from (joints.len ()).unwrap ();
|
|
let num_poses = u32::try_from (poses.len ()).unwrap ();
|
|
let num_anims = u32::try_from (anims.len ()).unwrap ();
|
|
let num_frames = u32::try_from (frames.len ()).unwrap () / num_framechannels;
|
|
|
|
let num_text = texts.len ();
|
|
let num_text = u32::try_from (num_text).unwrap ();
|
|
|
|
let num_meshes = u32::try_from (meshes.len ()).unwrap ();
|
|
let num_vertexarrays = u32::try_from (vertexarrays_unplaced.len ()).unwrap ();
|
|
|
|
let mut offset = 0;
|
|
|
|
let header_len = 124u32;
|
|
|
|
offset += header_len;
|
|
let ofs_meshes = offset;
|
|
offset += num_meshes * 6 * 4;
|
|
let ofs_vertexarrays = offset;
|
|
offset += num_vertexarrays * 5 * 4;
|
|
|
|
let mut vertexarrays = Vec::default ();
|
|
|
|
for va_in in vertexarrays_unplaced {
|
|
let stride = va_in.stride ();
|
|
|
|
vertexarrays.push (iqm::VertexArray {
|
|
r#type: va_in.r#type,
|
|
flags: va_in.flags,
|
|
format: va_in.format,
|
|
size: va_in.size,
|
|
offset,
|
|
});
|
|
|
|
offset += num_vertexes * stride;
|
|
}
|
|
|
|
let vertexarrays = vertexarrays;
|
|
|
|
let ofs_triangles = offset;
|
|
offset += num_triangles * 3 * 4;
|
|
|
|
let ofs_joints = offset;
|
|
offset += num_joints * 48;
|
|
|
|
let ofs_poses = offset;
|
|
offset += num_poses * 88;
|
|
|
|
let ofs_anims = offset;
|
|
offset += num_anims * 5 * 4;
|
|
|
|
let ofs_frames = offset;
|
|
offset += num_frames * num_framechannels * 2;
|
|
|
|
let ofs_text = offset;
|
|
offset += num_text;
|
|
|
|
let filesize = offset;
|
|
let _ = offset;
|
|
|
|
let mut f = File::create (output_path).unwrap ();
|
|
|
|
f.write_all (b"INTERQUAKEMODEL\0").unwrap ();
|
|
|
|
f.write_all (&(2u32.to_le_bytes ())).unwrap ();
|
|
|
|
// filesize
|
|
f.write_all (&(filesize.to_le_bytes ())).unwrap ();
|
|
|
|
// flags
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
|
|
// num_text, ofs_text
|
|
f.write_all (&(num_text.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_text.to_le_bytes ())).unwrap ();
|
|
|
|
// num_meshes, ofs_meshes
|
|
f.write_all (&(num_meshes.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_meshes.to_le_bytes ())).unwrap ();
|
|
|
|
// num_vertexarrays, num_vertexes, ofs_vertexarrays
|
|
f.write_all (&(num_vertexarrays.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(num_vertexes.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_vertexarrays.to_le_bytes ())).unwrap ();
|
|
|
|
// num_triangles, ofs_triangles, ofs_adjacency
|
|
f.write_all (&(num_triangles.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_triangles.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
|
|
// num_joints, ofs_joints
|
|
f.write_all (&(num_joints.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_joints.to_le_bytes ())).unwrap ();
|
|
|
|
// num_poses, ofs_poses
|
|
f.write_all (&(num_poses.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_poses.to_le_bytes ())).unwrap ();
|
|
|
|
// num_anims, ofs_anims
|
|
f.write_all (&(num_anims.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_anims.to_le_bytes ())).unwrap ();
|
|
|
|
// num_frames, num_framechannels, ofs_frames, ofs_bounds
|
|
f.write_all (&(num_frames.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(num_framechannels.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(ofs_frames.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
|
|
// num_comment, ofs_comment
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
|
|
// num_extensions, ofs_extensions
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(0u32.to_le_bytes ())).unwrap ();
|
|
|
|
// Meshes
|
|
|
|
for mesh in meshes {
|
|
f.write_all (&(mesh.name.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(mesh.material.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(mesh.first_vertex.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(mesh.num_vertexes.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(mesh.first_triangle.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(mesh.num_triangles.to_le_bytes ())).unwrap ();
|
|
}
|
|
|
|
// Vertex array meta-data
|
|
|
|
for va in &vertexarrays {
|
|
f.write_all (&(va.r#type.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(va.flags.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(va.format.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(va.size.to_le_bytes ())).unwrap ();
|
|
f.write_all (&(va.offset.to_le_bytes ())).unwrap ();
|
|
}
|
|
|
|
// Vertex data
|
|
|
|
for v in &vertexes {
|
|
for x in v.position {
|
|
f.write_all (&(f32::to_le_bytes (x * scale))).unwrap ();
|
|
}
|
|
}
|
|
|
|
for v in &vertexes {
|
|
for x in v.normal {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
}
|
|
|
|
for v in &vertexes {
|
|
for x in [0.0, 0.0, 0.0, 1.0] {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
}
|
|
|
|
for v in &vertexes {
|
|
f.write_all (&[0, 0, 0, 0]).unwrap ();
|
|
}
|
|
|
|
for v in &vertexes {
|
|
f.write_all (&[255, 0, 0, 0]).unwrap ();
|
|
}
|
|
|
|
// Triangles
|
|
|
|
for t in triangles {
|
|
for x in t {
|
|
f.write_all (&(u32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
}
|
|
|
|
// Joints
|
|
|
|
for j in joints {
|
|
f.write_all (&(u32::to_le_bytes (j.name))).unwrap ();
|
|
f.write_all (&(i32::to_le_bytes (j.parent))).unwrap ();
|
|
|
|
for x in j.translate {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
for x in j.rotate {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
for x in j.scale {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
}
|
|
|
|
for p in poses {
|
|
f.write_all (&(i32::to_le_bytes (p.parent))).unwrap ();
|
|
f.write_all (&(u32::to_le_bytes (p.channelmask))).unwrap ();
|
|
|
|
for x in p.channeloffset {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
for x in p.channelscale {
|
|
f.write_all (&(f32::to_le_bytes (x))).unwrap ();
|
|
}
|
|
}
|
|
|
|
for a in anims {
|
|
f.write_all (&(u32::to_le_bytes (a.name))).unwrap ();
|
|
f.write_all (&(u32::to_le_bytes (a.first_frame))).unwrap ();
|
|
f.write_all (&(u32::to_le_bytes (a.num_frames))).unwrap ();
|
|
f.write_all (&(f32::to_le_bytes (a.framerate))).unwrap ();
|
|
f.write_all (&(u32::to_le_bytes (a.flags))).unwrap ();
|
|
}
|
|
|
|
for x in frames {
|
|
f.write_all (&(u16::to_le_bytes (x))).unwrap ();
|
|
}
|
|
|
|
// Text
|
|
|
|
f.write_all (texts.as_bytes ()).unwrap ();
|
|
}
|
|
|
|
fn gltf_node_get_mat4 (node: &gltf::Node) -> Mat4 {
|
|
Mat4::from_cols_array_2d (&node.transform ().matrix ())
|
|
}
|
|
|
|
/// IQM structures as written to disk
|
|
|
|
mod iqm {
|
|
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,
|
|
}
|
|
|
|
pub const POSITION: u32 = 0;
|
|
pub const NORMAL: u32 = 2;
|
|
pub const TANGENT: u32 = 3;
|
|
pub const BLENDINDEXES: u32 = 4;
|
|
pub const BLENDWEIGHTS: u32 = 5;
|
|
|
|
pub const UBYTE: u32 = 1;
|
|
pub const FLOAT: u32 = 7;
|
|
|
|
pub struct VertexArrayUnplaced {
|
|
pub r#type: u32,
|
|
pub flags: u32,
|
|
pub format: u32,
|
|
pub size: u32,
|
|
}
|
|
|
|
impl VertexArrayUnplaced {
|
|
pub fn stride (&self) -> u32 {
|
|
self.size * match self.format {
|
|
UBYTE => 1,
|
|
FLOAT => 4,
|
|
_ => panic! ("Can't handle this vertexarray format yet"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct VertexArray {
|
|
pub r#type: u32,
|
|
pub flags: u32,
|
|
pub format: u32,
|
|
pub size: u32,
|
|
pub offset: u32,
|
|
}
|
|
|
|
struct Triangle {
|
|
vertex: [u32; 3],
|
|
}
|
|
|
|
pub struct Joint {
|
|
pub name: u32,
|
|
pub parent: i32,
|
|
pub translate: [f32; 3],
|
|
pub rotate: [f32; 4],
|
|
pub scale: [f32; 3],
|
|
}
|
|
|
|
// Basically extra data about the joint
|
|
|
|
pub struct Pose {
|
|
pub parent: i32,
|
|
pub channelmask: u32,
|
|
pub channeloffset: [f32; 10],
|
|
pub channelscale: [f32; 10],
|
|
}
|
|
|
|
pub struct Anim {
|
|
pub name: u32,
|
|
pub first_frame: u32,
|
|
pub num_frames: u32,
|
|
pub framerate: f32,
|
|
pub flags: u32,
|
|
}
|
|
|
|
pub struct Vertex {
|
|
pub position: [f32; 3],
|
|
pub normal: [f32; 3],
|
|
}
|
|
}
|