2023-08-02 16:28:08 -04:00
|
|
|
use binrw::{binread, until_eof};
|
2022-08-06 18:15:33 -04:00
|
|
|
use crate::gamedata::MemoryBuffer;
|
2022-08-10 14:52:11 -04:00
|
|
|
use glam::Mat4;
|
2022-08-16 11:52:07 -04:00
|
|
|
use hard_xml::XmlRead;
|
2022-08-06 18:15:33 -04:00
|
|
|
|
2023-08-02 16:28:08 -04:00
|
|
|
#[binread]
|
|
|
|
struct SKLB_v1 {
|
|
|
|
unk_offset: i16,
|
|
|
|
havok_offset: i16
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
struct SKLB_v2 {
|
|
|
|
unk_offset: i32,
|
|
|
|
havok_offset: i32
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[br(magic = 0x736B6C62i32)]
|
|
|
|
struct SKLB {
|
|
|
|
version_one: i16,
|
|
|
|
version_two: i16,
|
|
|
|
havok_offset: i32,
|
|
|
|
|
|
|
|
#[br(count = havok_offset)]
|
|
|
|
raw_header: Vec<u8>,
|
|
|
|
#[br(parse_with = until_eof)]
|
|
|
|
raw_data: Vec<u8>
|
|
|
|
}
|
|
|
|
|
2022-08-06 18:15:33 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Bone {
|
2022-08-10 14:52:11 -04:00
|
|
|
pub name: String,
|
|
|
|
pub parent_index: i32,
|
2022-08-06 18:15:33 -04:00
|
|
|
|
2022-08-10 14:52:11 -04:00
|
|
|
pub position: [f32; 3],
|
|
|
|
pub rotation: [f32; 4],
|
2022-08-16 11:52:07 -04:00
|
|
|
pub scale: [f32; 3],
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Skeleton {
|
2022-08-16 11:52:07 -04:00
|
|
|
pub bones: Vec<Bone>,
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Skeleton {
|
|
|
|
/// Parses a Havok XML packfile generated by the Havok SDK.
|
2022-08-16 11:52:07 -04:00
|
|
|
pub fn from_packfile(buffer: &MemoryBuffer) -> Option<Skeleton> {
|
2022-08-06 18:15:33 -04:00
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkpackfile")]
|
|
|
|
struct HkPackfile {
|
|
|
|
#[xml(child = "hksection")]
|
|
|
|
sections: Vec<HkSection>,
|
|
|
|
#[xml(attr = "toplevelobject")]
|
2022-08-16 11:52:07 -04:00
|
|
|
top_level_object: String,
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hksection")]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-06 18:15:33 -04:00
|
|
|
struct HkSection {
|
|
|
|
#[xml(attr = "name")]
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
#[xml(child = "hkobject")]
|
2022-08-16 11:52:07 -04:00
|
|
|
objects: Vec<HkObject>,
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkobject")]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-06 18:15:33 -04:00
|
|
|
struct HkObject {
|
|
|
|
#[xml(attr = "name")]
|
|
|
|
name: Option<String>,
|
|
|
|
|
|
|
|
#[xml(attr = "class")]
|
|
|
|
class: Option<String>,
|
|
|
|
|
|
|
|
#[xml(child = "hkparam")]
|
2022-08-16 11:52:07 -04:00
|
|
|
params: Vec<HkParam>,
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkparam")]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-06 18:15:33 -04:00
|
|
|
struct HkParam {
|
|
|
|
#[xml(attr = "name")]
|
2022-08-16 11:52:07 -04:00
|
|
|
name: String,
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
#[xml(attr = "className")]
|
2022-08-16 11:52:07 -04:00
|
|
|
class_name: Option<String>,
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
#[xml(attr = "variant")]
|
2022-08-16 11:52:07 -04:00
|
|
|
variant: Option<String>,
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
#[xml(child = "hkobject")]
|
2022-08-16 11:52:07 -04:00
|
|
|
objects: Vec<HkObject>,
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
#[xml(text)]
|
2022-08-16 11:52:07 -04:00
|
|
|
content: String,
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
2022-08-16 11:50:18 -04:00
|
|
|
let pak = HkPackfile::from_str(std::str::from_utf8(buffer).unwrap())
|
2022-08-06 18:15:33 -04:00
|
|
|
.expect("Failed to parse sidecar file!");
|
|
|
|
|
|
|
|
// find the root level object
|
2022-08-16 11:52:07 -04:00
|
|
|
let root_level_object = pak.sections[0]
|
|
|
|
.objects
|
|
|
|
.iter()
|
2022-08-06 18:15:33 -04:00
|
|
|
.find(|s| s.name.as_ref() == Some(&pak.top_level_object))
|
|
|
|
.expect("Cannot locate root level object.");
|
|
|
|
|
|
|
|
println!("{:#?}", root_level_object);
|
|
|
|
|
|
|
|
println!("{:#?}", pak);
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parses the TexTools skeleton format, as a nice alternative to packfiles.
|
2022-08-16 11:52:07 -04:00
|
|
|
pub fn from_skel(buffer: &MemoryBuffer) -> Option<Skeleton> {
|
2022-08-06 18:15:33 -04:00
|
|
|
let mut string_repr = String::from_utf8(buffer.to_vec()).unwrap();
|
|
|
|
|
|
|
|
// for some reason, textools does NOT write valid JSON.
|
|
|
|
// let's begin by surrounding all of their json object blocks with an array, which is a valid
|
|
|
|
// JSON root.
|
|
|
|
string_repr.insert(0, '[');
|
|
|
|
string_repr.push(']');
|
|
|
|
|
|
|
|
// then we turn all of newlines into commas, except of course for the last one!
|
2022-08-16 11:50:18 -04:00
|
|
|
string_repr = string_repr.replacen('\n', ",", string_repr.matches('\n').count() - 1);
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(rename_all = "PascalCase")]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-06 18:15:33 -04:00
|
|
|
struct BoneObject {
|
2022-08-16 11:52:07 -04:00
|
|
|
bone_name: String,
|
|
|
|
bone_number: i32,
|
|
|
|
bone_parent: i32,
|
|
|
|
pose_matrix: [f32; 16],
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
2022-08-16 11:52:07 -04:00
|
|
|
let json_bones: Vec<BoneObject> = serde_json::from_str(&string_repr).unwrap();
|
2022-08-06 18:15:33 -04:00
|
|
|
|
2022-08-16 11:52:07 -04:00
|
|
|
let mut skeleton = Skeleton { bones: vec![] };
|
2022-08-06 18:15:33 -04:00
|
|
|
|
|
|
|
for bone in &json_bones {
|
2022-08-10 14:52:11 -04:00
|
|
|
let pose_matrix = Mat4::from_cols_array(&bone.pose_matrix);
|
|
|
|
|
|
|
|
let (scale, rotation, translation) = pose_matrix.to_scale_rotation_translation();
|
|
|
|
|
2022-08-06 18:15:33 -04:00
|
|
|
skeleton.bones.push(Bone {
|
|
|
|
name: bone.bone_name.clone(),
|
2022-08-10 14:52:11 -04:00
|
|
|
parent_index: bone.bone_parent,
|
|
|
|
position: translation.to_array(),
|
|
|
|
rotation: rotation.to_array(),
|
2022-08-16 11:52:07 -04:00
|
|
|
scale: scale.to_array(),
|
2022-08-06 18:15:33 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(skeleton)
|
|
|
|
}
|
2022-08-16 11:52:07 -04:00
|
|
|
}
|