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.
 
 
 

480 lines
12 KiB

use crate::{
data::{read, vertex::Vertex},
ffi,
};
use glam::{Mat4, Quat, Vec3, Vec3A, Vec4};
use smol_str::SmolStr;
use std::{
collections::HashMap,
io::{self, Read, Seek, SeekFrom},
ops::Range,
str::Utf8Error,
};
#[derive(Error, Debug)]
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 VertexZipper {
// kono jippa......
pos_v: std::vec::IntoIter<Vec3A>,
tex_v: std::vec::IntoIter<Vec3A>,
nrm_v: std::vec::IntoIter<Vec3A>,
tan_v: std::vec::IntoIter<Vec4>,
idx_v: std::vec::IntoIter<Vec4>,
wgt_v: std::vec::IntoIter<Vec4>,
clr_v: std::vec::IntoIter<Vec4>,
}
pub struct Mesh {
mat: SmolStr,
vtx: Range<usize>,
idx: Range<usize>,
}
pub struct Joint {
nam: SmolStr,
mat: Mat4,
inv: Mat4,
}
/// 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<SmolStr, Mesh>,
frames: Vec<Vec<Mat4>>,
num_frms: usize,
}
impl Iterator for VertexZipper {
type Item = (
Option<Vec3A>,
Option<Vec3A>,
Option<Vec3A>,
Option<Vec4>,
Option<Vec4>,
Option<Vec4>,
Option<Vec4>,
);
fn next(&mut self) -> Option<Self::Item> {
match (
self.pos_v.next(),
self.tex_v.next(),
self.nrm_v.next(),
self.tan_v.next(),
self.idx_v.next(),
self.wgt_v.next(),
self.clr_v.next(),
) {
| (None, None, None, None, None, None, None) => None,
| tuple => Some(tuple),
}
}
}
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_F32: u32 = 7;
const FMT_F64: u32 = 8;
fn fmt_check(fm: u32) -> Result<(), Err> {
match fm {
FMT_I8 | FMT_U8 |
FMT_I16 | FMT_U16 |
FMT_I32 | FMT_U32 | 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 => 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 => f32::from(vec[pos] as i8),
FMT_U8 => f32::from(vec[pos]),
FMT_I16 => f32::from(read::i16le_16(vec, pos)),
FMT_U16 => f32::from(read::u16le_16(vec, pos)),
FMT_I32 => read::i32le_32(vec, pos) as f32,
FMT_U32 => read::u32le_32(vec, pos) as f32,
FMT_F32 => read::f32le_32(vec, pos),
FMT_F64 => read::f64le_64(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::abs(f32::from(vec[pos] as i8) / 128.0),
FMT_U8 => f32::from(vec[pos]) / 255.0,
FMT_I16 => f32::abs(f32::from(read::i16le_16(vec, pos)) / 32768.0),
FMT_U16 => f32::from(read::u16le_16(vec, pos)) / 65535.0,
FMT_I32 => (read::i32le_32(vec, pos) as f64 / 2147483648.0) as f32,
FMT_U32 => (read::u32le_32(vec, pos) as f64 / 4294967295.0) as f32,
FMT_F32 => f32::clamp(read::f32le_32(vec, pos), 0.0, 1.0),
FMT_F64 => f32::clamp(read::f64le_64(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_sz(&head, 28);
let ofs_text = read::u32le_64(&head, 32);
let num_mesh = read::u32le_sz(&head, 36);
let ofs_mesh = read::u32le_64(&head, 40);
let num_vinf = read::u32le_sz(&head, 44);
let num_vert = read::u32le_sz(&head, 48);
let ofs_vinf = read::u32le_64(&head, 52);
let num_tris = read::u32le_sz(&head, 56);
let ofs_tris = read::u32le_64(&head, 60);
let num_join = read::u32le_sz(&head, 68);
let ofs_join = read::u32le_64(&head, 72);
let num_pose = read::u32le_sz(&head, 76);
let ofs_pose = read::u32le_64(&head, 80);
let num_anim = read::u32le_sz(&head, 84);
let ofs_anim = read::u32le_64(&head, 88);
let num_frms = read::u32le_sz(&head, 92);
let num_frmc = read::u32le_sz(&head, 96);
let ofs_frms = read::u32le_64(&head, 100);
let ofs_bnds = read::u32le_64(&head, 104);
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_32(array_def, 0);
let fl = read::u32le_32(array_def, 4);
let fm = read::u32le_32(array_def, 8);
let sz = read::u32le_32(array_def, 12);
let of = read::u32le_64(array_def, 16);
fmt_check(fm)?;
rd.seek(SeekFrom::Start(of))?;
let obj_size = fmt_size(fm);
let obj_amnt = sz as usize;
let obj_blks = obj_size * obj_amnt;
let hunk = read::hunk(rd, obj_blks * num_vert)?;
let v2 = |v: &mut Vec<Vec3A>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 2 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
v.push(Vec3A::new(x, y, 0.0));
}
Ok(())
}
};
let v3 = |v: &mut Vec<Vec3A>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 3 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
let z = rd(fm, vec, obj_size * 2);
v.push(Vec3A::new(x, y, z));
}
Ok(())
}
};
let v4 = |v: &mut Vec<Vec4>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 4 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
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 zipper = VertexZipper {
pos_v: pos_v.into_iter(),
tex_v: tex_v.into_iter(),
nrm_v: nrm_v.into_iter(),
tan_v: tan_v.into_iter(),
idx_v: idx_v.into_iter(),
wgt_v: wgt_v.into_iter(),
clr_v: clr_v.into_iter(),
};
let mut vert_dat = Vec::with_capacity(num_vert);
for (pos, tex, nrm, tan, idx, wgt, clr) in zipper {
let pos = pos.unwrap_or_else(|| Vec3A::ZERO);
let tex = tex.unwrap_or_else(|| Vec3A::ZERO);
let nrm = nrm.unwrap_or_else(|| Vec3A::ZERO);
let tan = tan.unwrap_or_else(|| Vec4::ZERO);
let idx = idx.unwrap_or_else(|| Vec4::ZERO);
let wgt = wgt.unwrap_or_else(|| Vec4::ZERO);
let clr = clr.unwrap_or_else(|| Vec4::ONE);
vert_dat.push(Vertex { pos, tex, nrm, tan, idx, wgt, clr });
}
// 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_32(tri, 0));
indx_dat.push(read::u32le_32(tri, 4));
indx_dat.push(read::u32le_32(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_sz(mesh, 0);
let mat = read::u32le_sz(mesh, 4);
let vtx = read::u32le_sz(mesh, 8);
let vnu = read::u32le_sz(mesh, 12);
let idx = read::u32le_sz(mesh, 16) * 3;
let inu = read::u32le_sz(mesh, 20) * 3;
let nam = SmolStr::new(from_stab(&stab, nam)?);
let mat = SmolStr::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 mut trs = [0.0; 10];
let nam = read::u32le_sz(join, 0);
let par = read::u32le_sz(join, 4);
read::array(read::f32le_32, join, &mut trs, 4, 8);
let nam = SmolStr::new(from_stab(&stab, nam)?);
let mut mat = Mat4::from_scale_rotation_translation(
Vec3::new(trs[7], trs[8], trs[9]),
Quat::from_xyzw(trs[3], trs[4], trs[5], trs[6]).normalize(),
Vec3::new(trs[0], trs[1], trs[2]),
);
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_16(samp, 0));
}
// collect pose info
let mut frame = frames.iter();
let mut frames = Vec::with_capacity(num_pose);
rd.seek(SeekFrom::Start(ofs_pose))?;
for (pose, pose_n) in
read::hunk(rd, num_pose * 4 * 22)?.chunks(4 * 22).zip(0..num_pose)
{
let par = read::u32le_sz(pose, 0);
let msk = read::u32le_32(pose, 4);
let mut ofs = [0.0; 10];
let mut scl = [0.0; 10];
read::array(read::f32le_32, pose, &mut ofs, 4, 8);
read::array(read::f32le_32, pose, &mut scl, 4, 48);
let mut pose_mats = Vec::with_capacity(num_frms);
for _frame_num in 0..num_frms {
let mut ofs = ofs;
for i in 0..10 {
if msk & 1 << i != 0 {
ofs[i] +=
f32::from(*frame.next().ok_or(Err::FrameCount)?) * scl[i];
}
}
let mut mat = Mat4::from_scale_rotation_translation(
Vec3::new(ofs[7], ofs[8], ofs[9]),
Quat::from_xyzw(ofs[3], ofs[4], ofs[5], ofs[6]).normalize(),
Vec3::new(ofs[0], ofs[1], ofs[2]),
);
let joint = &joints[pose_n];
if par & 0x8000_0000 == 0 {
let parent = joints.get(par).ok_or(Err::Parent)?;
mat = parent.mat * mat * joint.inv;
} else {
mat = mat * joint.inv;
}
pose_mats.push(mat);
}
frames.push(pose_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(joint)?.get(frame)?)
}
pub const fn num_frames(&self) -> usize {
self.num_frms
}
}
impl std::ops::Deref for Model {
type Target = HashMap<SmolStr, Mesh>;
fn deref(&self) -> &Self::Target {
&self.meshes
}
}
// EOF