2022-08-06 18:15:33 -04:00
|
|
|
use crate::gamedata::MemoryBuffer;
|
|
|
|
use hard_xml::XmlRead;
|
2022-08-10 14:52:11 -04:00
|
|
|
use glam::Mat4;
|
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],
|
|
|
|
pub scale: [f32; 3]
|
2022-08-06 18:15:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Skeleton {
|
2022-08-10 14:52:11 -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.
|
|
|
|
pub fn from_packfile(buffer : &MemoryBuffer) -> Option<Skeleton> {
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkpackfile")]
|
|
|
|
struct HkPackfile {
|
|
|
|
#[xml(child = "hksection")]
|
|
|
|
sections: Vec<HkSection>,
|
|
|
|
#[xml(attr = "toplevelobject")]
|
|
|
|
top_level_object : String
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hksection")]
|
|
|
|
struct HkSection {
|
|
|
|
#[xml(attr = "name")]
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
#[xml(child = "hkobject")]
|
|
|
|
objects: Vec<HkObject>
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkobject")]
|
|
|
|
struct HkObject {
|
|
|
|
#[xml(attr = "name")]
|
|
|
|
name: Option<String>,
|
|
|
|
|
|
|
|
#[xml(attr = "class")]
|
|
|
|
class: Option<String>,
|
|
|
|
|
|
|
|
#[xml(child = "hkparam")]
|
|
|
|
params: Vec<HkParam>
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(XmlRead, Debug)]
|
|
|
|
#[xml(tag = "hkparam")]
|
|
|
|
struct HkParam {
|
|
|
|
#[xml(attr = "name")]
|
|
|
|
name : String,
|
|
|
|
|
|
|
|
#[xml(attr = "className")]
|
|
|
|
class_name : Option<String>,
|
|
|
|
|
|
|
|
#[xml(attr = "variant")]
|
|
|
|
variant : Option<String>,
|
|
|
|
|
|
|
|
#[xml(child = "hkobject")]
|
|
|
|
objects : Vec<HkObject>,
|
|
|
|
|
|
|
|
#[xml(text)]
|
|
|
|
content : String
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
let root_level_object = pak.sections[0].objects.iter()
|
|
|
|
.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.
|
|
|
|
pub fn from_skel(buffer : &MemoryBuffer) -> Option<Skeleton> {
|
|
|
|
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")]
|
|
|
|
struct BoneObject {
|
|
|
|
bone_name : String,
|
|
|
|
bone_number : i32,
|
|
|
|
bone_parent : i32,
|
|
|
|
pose_matrix : [f32; 16]
|
|
|
|
}
|
|
|
|
|
|
|
|
let json_bones : Vec<BoneObject> = serde_json::from_str(&string_repr).unwrap();
|
|
|
|
|
|
|
|
let mut skeleton = Skeleton {
|
|
|
|
bones: vec![]
|
|
|
|
};
|
|
|
|
|
|
|
|
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(),
|
|
|
|
scale: scale.to_array()
|
2022-08-06 18:15:33 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(skeleton)
|
|
|
|
}
|
|
|
|
}
|