You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

457 lines
12 KiB

use crate::{
data::{read, vertex::Vertex},
ffi,
math::*,
types::{Cast, StkStr},
};
use std::{
collections::HashMap,
io::{self, Read, Seek, SeekFrom},
ops::Range,
str::Utf8Error,
};
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Err {
#[error(transparent)]
Io(#[from] io::Error),
#[error("Bad magic number")]
Magic,
#[error("Incorrect number of poses")]
NumPoses,
#[error("Bad text offset")]
TextOffset,
#[error(transparent)]
Utf8(#[from] Utf8Error),
#[error("Bad parent joint")]
Parent,
#[error("Bad frame count")]
FrameCount,
#[error("Unknown vertex array type")]
VaType,
#[error("Unsupported vertex array format")]
VaFormat,
}
struct Pose {
par: usize,
msk: u32,
ofs: [f32; 10],
scl: [f32; 10],
}
struct Joint {
#[allow(dead_code)]
nam: StkStr,
mat: Mat4,
inv: Mat4,
}
pub struct Mesh {
mat: StkStr,
vtx: Range<usize>,
idx: Range<usize>,
}
/// Data to be uploaded to the GPU. Separate from Model so that it may
/// be dropped after upload.
pub struct ModelData {
pub vtx: Vec<Vertex>,
pub idx: Vec<u32>,
}
/// Run-time model data. Anything necessary for passing commands to
/// the GPU.
pub struct Model {
meshes: HashMap<StkStr, Mesh>,
frames: Vec<Vec<Mat4>>,
num_frms: usize,
}
const FMT_I8: u32 = 0;
const FMT_U8: u32 = 1;
const FMT_I16: u32 = 2;
const FMT_U16: u32 = 3;
const FMT_I32: u32 = 4;
const FMT_U32: u32 = 5;
const FMT_F16: u32 = 6;
const FMT_F32: u32 = 7;
const FMT_F64: u32 = 8;
const fn fmt_check(fm: u32) -> Result<(), Err> {
match fm {
| FMT_I8 | FMT_U8 | FMT_I16 | FMT_U16 | FMT_I32 | FMT_U32 | FMT_F16
| FMT_F32 | FMT_F64 => Ok(()),
| _ => Err(Err::VaFormat),
}
}
fn fmt_size(fm: u32) -> usize {
match fm {
| FMT_I8 | FMT_U8 => 1,
| FMT_I16 | FMT_U16 | FMT_F16 => 2,
| FMT_I32 | FMT_U32 | FMT_F32 => 4,
| FMT_F64 => 8,
| _ => unsafe { std::hint::unreachable_unchecked() },
}
}
fn fmt_read_abs(fm: u32, vec: &[u8], pos: usize) -> f32 {
match fm {
| FMT_I8 => read::i8x(vec, pos).into(),
| FMT_U8 => read::u8x(vec, pos).into(),
| FMT_I16 => read::i16le(vec, pos).into(),
| FMT_U16 => read::u16le(vec, pos).into(),
| FMT_I32 => read::i32le(vec, pos).cast(),
| FMT_U32 => read::u32le(vec, pos).cast(),
| FMT_F16 => read::f16le(vec, pos).to_f32(),
| FMT_F32 => read::f32le(vec, pos),
| FMT_F64 => read::f64le(vec, pos) as f32,
| _ => unsafe { std::hint::unreachable_unchecked() },
}
}
fn fmt_read_nrm(fm: u32, vec: &[u8], pos: usize) -> f32 {
match fm {
| FMT_I8 => f32::from(read::i8x(vec, pos)).abs() / 128.0,
| FMT_U8 => f32::from(read::u8x(vec, pos)) / 255.0,
| FMT_I16 => f32::from(read::i16le(vec, pos)).abs() / 32768.0,
| FMT_U16 => f32::from(read::u16le(vec, pos)) / 65535.0,
| FMT_I32 => (f64::from(read::i32le(vec, pos)) / 2147483648.0) as f32,
| FMT_U32 => (f64::from(read::u32le(vec, pos)) / 4294967295.0) as f32,
| FMT_F16 => f32::clamp(read::f16le(vec, pos).to_f32(), 0.0, 1.0),
| FMT_F32 => f32::clamp(read::f32le(vec, pos), 0.0, 1.0),
| FMT_F64 => f32::clamp(read::f64le(vec, pos) as f32, 0.0, 1.0),
| _ => unsafe { std::hint::unreachable_unchecked() },
}
}
impl Mesh {
pub fn get_mat(&self) -> &str {
&self.mat
}
pub const fn vtx_beg(&self) -> usize {
self.vtx.start
}
pub const fn vtx_end(&self) -> usize {
self.vtx.end
}
pub const fn vtx_len(&self) -> usize {
self.vtx.end - self.vtx.start
}
pub const fn idx_beg(&self) -> usize {
self.idx.start
}
pub const fn idx_end(&self) -> usize {
self.idx.end
}
pub const fn idx_len(&self) -> usize {
self.idx.end - self.idx.start
}
}
impl Model {
pub fn read<R>(rd: &mut R) -> Result<(Self, ModelData), Err>
where
R: Read + Seek,
{
fn from_stab(stab: &[u8], strn: usize) -> Result<&str, Err> {
let strn = stab.get(strn..).ok_or(Err::TextOffset)?;
ffi::cstr_from_slice(strn).ok_or(Err::TextOffset)
}
let mut head = [0; 124];
rd.read_exact(&mut head)?;
if &head[0..20] != b"INTERQUAKEMODEL\0\x02\0\0\0" {
return Err(Err::Magic);
}
let num_text = read::u32le(&head, 28) as usize;
let ofs_text = read::u32le(&head, 32).into();
let num_mesh = read::u32le(&head, 36) as usize;
let ofs_mesh = read::u32le(&head, 40).into();
let num_vinf = read::u32le(&head, 44) as usize;
let num_vert = read::u32le(&head, 48) as usize;
let ofs_vinf = read::u32le(&head, 52).into();
let num_tris = read::u32le(&head, 56) as usize;
let ofs_tris = read::u32le(&head, 60).into();
let num_join = read::u32le(&head, 68) as usize;
let ofs_join = read::u32le(&head, 72).into();
let num_pose = read::u32le(&head, 76) as usize;
let ofs_pose = read::u32le(&head, 80).into();
//let num_anim = read::u32le(&head, 84) as usize;
//let ofs_anim = read::u32le(&head, 88).into();
let num_frms = read::u32le(&head, 92) as usize;
let num_frmc = read::u32le(&head, 96) as usize;
let ofs_frms = read::u32le(&head, 100).into();
//let ofs_bnds = read::u32le(&head, 104).into();
if num_join != 0 && num_pose != 0 && num_pose != num_join {
return Err(Err::NumPoses);
}
// collect vertex info
let mut pos_v = Vec::with_capacity(num_vert);
let mut tex_v = Vec::with_capacity(num_vert);
let mut nrm_v = Vec::with_capacity(num_vert);
let mut tan_v = Vec::with_capacity(num_vert);
let mut idx_v = Vec::with_capacity(num_vert);
let mut wgt_v = Vec::with_capacity(num_vert);
let mut clr_v = Vec::with_capacity(num_vert);
rd.seek(SeekFrom::Start(ofs_vinf))?;
for array_def in read::hunk(rd, num_vinf * 20)?.chunks(20) {
let ty = read::u32le(array_def, 0);
//let fl = read::u32le(array_def, 4);
let fm = read::u32le(array_def, 8);
let sz = read::u32le(array_def, 12) as usize;
let of = read::u32le(array_def, 16).into();
fmt_check(fm)?;
rd.seek(SeekFrom::Start(of))?;
let obj_size = fmt_size(fm);
let obj_blks = obj_size * sz;
let hunk = read::hunk(rd, obj_blks * num_vert)?;
let v2 = |v: &mut Vec<Vec3>, rd: fn(u32, &[u8], usize) -> f32| {
if sz != 2 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
v.push(Vec3::new(x, y, 0.0));
}
Ok(())
}
};
let v3 = |v: &mut Vec<Vec3>, rd: fn(u32, &[u8], usize) -> f32| {
if sz != 3 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
let z = rd(fm, vec, obj_size * 2);
v.push(Vec3::new(x, y, z));
}
Ok(())
}
};
let v4 = |v: &mut Vec<Vec4>, rd: fn(u32, &[u8], usize) -> f32| {
if sz != 4 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
let z = rd(fm, vec, obj_size * 2);
let w = rd(fm, vec, obj_size * 3);
v.push(Vec4::new(x, y, z, w));
}
Ok(())
}
};
match ty {
| 0 => v3(&mut pos_v, fmt_read_abs)?,
| 1 => v2(&mut tex_v, fmt_read_abs)?,
| 2 => v3(&mut nrm_v, fmt_read_abs)?,
| 3 => v4(&mut tan_v, fmt_read_abs)?,
| 4 => v4(&mut idx_v, fmt_read_abs)?,
| 5 => v4(&mut wgt_v, fmt_read_nrm)?,
| 6 => v4(&mut clr_v, fmt_read_nrm)?,
| _ => return Err(Err::VaType),
}
}
// zip the vertex info
let mut vert_dat = Vec::with_capacity(num_vert);
for (pos, tex, nrm, tan, idx, wgt, clr) in crate::iter::OpenZip((
pos_v.into_iter(),
tex_v.into_iter(),
nrm_v.into_iter(),
tan_v.into_iter(),
idx_v.into_iter(),
wgt_v.into_iter(),
clr_v.into_iter(),
)) {
vert_dat.push(Vertex {
pos: pos.unwrap_or(Vec3::ZERO),
tex: tex.unwrap_or(Vec3::ZERO),
nrm: nrm.unwrap_or(Vec3::ZERO),
tan: tan.unwrap_or(Vec4::ZERO),
idx: idx.unwrap_or(Vec4::ZERO),
wgt: wgt.unwrap_or(Vec4::ZERO),
clr: clr.unwrap_or(Vec4::ONE),
});
}
// collect index info
let mut indx_dat = Vec::with_capacity(num_tris * 3);
rd.seek(SeekFrom::Start(ofs_tris))?;
for tri in read::hunk(rd, num_tris * 4 * 3)?.chunks(4 * 3) {
indx_dat.push(read::u32le(tri, 0));
indx_dat.push(read::u32le(tri, 4));
indx_dat.push(read::u32le(tri, 8));
}
// collect text
rd.seek(SeekFrom::Start(ofs_text))?;
let stab = read::hunk(rd, num_text)?;
// collect mesh info
let mut meshes = HashMap::with_capacity(num_mesh);
rd.seek(SeekFrom::Start(ofs_mesh))?;
for mesh in read::hunk(rd, num_mesh * 4 * 6)?.chunks(4 * 6) {
let nam = read::u32le(mesh, 0) as usize;
let mat = read::u32le(mesh, 4) as usize;
let vtx = read::u32le(mesh, 8) as usize;
let vnu = read::u32le(mesh, 12) as usize;
let idx = read::u32le(mesh, 16) as usize * 3;
let inu = read::u32le(mesh, 20) as usize * 3;
let nam = StkStr::new(from_stab(&stab, nam)?);
let mat = StkStr::new(from_stab(&stab, mat)?);
let vtx = vtx..vtx + vnu;
let idx = idx..idx + inu;
meshes.insert(nam, Mesh { mat, vtx, idx });
}
// collect joint info
let mut joints: Vec<Joint> = Vec::with_capacity(num_join);
rd.seek(SeekFrom::Start(ofs_join))?;
for join in read::hunk(rd, num_join * 4 * 12)?.chunks(4 * 12) {
let nam = read::u32le(join, 0) as usize;
let par = read::u32le(join, 4) as usize;
let trs = read::array(read::f32le, join, [0.0; 10], 4, 8);
let nam = StkStr::new(from_stab(&stab, nam)?);
let xlt = Vec3::new(trs[0], trs[1], trs[2]);
let rot = Quat::from_xyzw(trs[3], trs[4], trs[5], trs[6]).normalize();
let scl = Vec3::new(trs[7], trs[8], trs[9]);
let mut mat = Mat4::from_scale_rotation_translation(scl, rot, xlt);
let mut inv = mat.inverse();
if par & 0x8000_0000 == 0 {
let parent = joints.get(par).ok_or(Err::Parent)?;
mat = parent.mat * mat;
inv = inv * parent.inv;
}
joints.push(Joint { nam, mat, inv });
}
// collect frames
let mut frames = Vec::with_capacity(num_frms * num_frmc);
rd.seek(SeekFrom::Start(ofs_frms))?;
for samp in read::hunk(rd, num_frms * num_frmc * 2)?.chunks(2) {
frames.push(read::u16le(samp, 0));
}
// collect pose info
let mut poses = Vec::with_capacity(num_pose);
rd.seek(SeekFrom::Start(ofs_pose))?;
for pose in read::hunk(rd, num_pose * 4 * 22)?.chunks(4 * 22) {
let par = read::u32le(pose, 0) as usize;
let msk = read::u32le(pose, 4);
let ofs = read::array(read::f32le, pose, [0.0; 10], 4, 8);
let scl = read::array(read::f32le, pose, [0.0; 10], 4, 48);
poses.push(Pose { par, msk, ofs, scl });
}
// calculate frame matrices
let mut frame = frames.iter();
let mut frames = Vec::with_capacity(num_frms);
for _ in 0..num_frms {
let mut frame_mats = Vec::with_capacity(num_pose);
for (pose, joint) in poses.iter().zip(joints.iter()) {
let mut ofs = pose.ofs;
for (i, off) in ofs.iter_mut().enumerate() {
if pose.msk & 1 << i != 0 {
let frm = f32::from(*frame.next().ok_or(Err::FrameCount)?);
*off += frm * pose.scl[i];
}
}
let xlt = Vec3::new(ofs[0], ofs[1], ofs[2]);
let rot =
Quat::from_xyzw(ofs[3], ofs[4], ofs[5], ofs[6]).normalize();
let scl = Vec3::new(ofs[7], ofs[8], ofs[9]);
let mut mat = Mat4::from_scale_rotation_translation(scl, rot, xlt);
if pose.par & 0x8000_0000 == 0 {
let parent = joints.get(pose.par).ok_or(Err::Parent)?;
mat = parent.mat * mat * joint.inv;
let parent = frame_mats.get(pose.par).ok_or(Err::Parent)?;
mat = *parent * mat;
} else {
mat = mat * joint.inv;
}
frame_mats.push(mat);
}
frames.push(frame_mats);
}
Ok((
Self { meshes, frames, num_frms },
ModelData { vtx: vert_dat, idx: indx_dat },
))
}
pub fn frame(&self, joint: usize, frame: usize) -> Option<Mat4> {
Some(*self.frames.get(frame)?.get(joint)?)
}
pub const fn num_frames(&self) -> usize {
self.num_frms
}
}
impl std::ops::Deref for Model {
type Target = HashMap<StkStr, Mesh>;
fn deref(&self) -> &Self::Target {
&self.meshes
}
}
// EOF