use std::io::Error;
use crate::avatar::Avatar;
use glam::{Mat4, Vec4};
use indexmap::IndexMap;
use metaverse_messages::{
http::mesh::Skin,
utils::skeleton::{Joint, JointName, Skeleton, Transform},
};
use uuid::Uuid;
pub fn update_global_avatar_skeleton(avatar: &mut Avatar, skeleton: &Skeleton) {
for joint in skeleton.joints.values() {
if let Some(global_joint) = avatar.skeleton.joints.get_mut(&joint.name) {
for transform in &joint.transforms {
set_rank(global_joint, transform, |j| &mut j.transforms);
}
for local_transform in &joint.local_transforms {
set_rank(global_joint, local_transform, |j| &mut j.local_transforms);
}
}
}
}
pub fn create_skeleton(object_name: String, id: Uuid, skin: &Skin) -> Result<Skeleton, Error> {
let default_skeleton = include!(concat!(env!("OUT_DIR"), "/default_skeleton.rs"));
let mut joints = IndexMap::new();
for (i, name) in skin.joint_names.iter().enumerate() {
let default_joints = default_skeleton.joints.get(name).unwrap().clone();
let mut default_transform = default_joints.transforms[0].transform;
default_transform.w_axis = Vec4::new(0.0, 0.0, 0.0, 1.0);
let transform_matrix = default_transform * skin.inverse_bind_matrices[i];
let transform = Transform {
name: object_name.clone(),
id,
transform: transform_matrix,
rank: 1,
};
let joint = Joint {
name: *name,
parent: default_joints.parent,
children: default_joints.children.into_iter().collect(),
transforms: vec![transform.clone()],
local_transforms: vec![],
};
joints.insert(*name, joint.clone());
}
let root_joints: Vec<JointName> = joints
.values()
.filter(|joint| joint.parent.is_none())
.map(|joint| joint.name)
.collect();
let ibm_map: IndexMap<JointName, Mat4> = skin
.joint_names
.iter()
.cloned()
.enumerate()
.map(|(i, name)| (name, skin.inverse_bind_matrices[i]))
.collect();
let last_transforms: IndexMap<JointName, Mat4> = joints
.iter()
.map(|(name, joint)| (*name, joint.transforms.last().unwrap().transform))
.collect();
for (_, joint) in joints.iter_mut() {
if let Some(parent_name) = &joint.parent {
let parent = last_transforms
.get(parent_name)
.or_else(|| {
Some(&default_skeleton.joints.get(parent_name).unwrap().transforms[0].transform)
})
.unwrap();
let child = last_transforms.get(&joint.name).unwrap();
let local_matrix = *parent * child.inverse();
let local_transform = Transform {
name: object_name.clone(),
id,
transform: local_matrix,
rank: 1,
};
joint.local_transforms.push(local_transform);
} else {
if let Some(child_ibm) = ibm_map.get(&joint.name) {
let local_transform = Transform {
name: object_name.clone(),
id,
transform: *child_ibm,
rank: 1,
};
joint.local_transforms.push(local_transform);
}
}
}
Ok(Skeleton {
root: root_joints,
joints,
})
}
fn set_rank<F>(joint: &mut Joint, transform: &Transform, mut get_vec: F)
where
F: FnMut(&mut Joint) -> &mut Vec<Transform>,
{
let transforms = get_vec(joint);
let mut matched_rank0 = false;
for t in transforms.iter_mut().skip(1) {
if t.transform.abs_diff_eq(transform.transform, 1e-4) {
t.rank += 1;
transforms.sort_by(|a, b| a.rank.cmp(&b.rank));
return;
}
}
if let Some(first) = transforms.first()
&& first.transform.abs_diff_eq(transform.transform, 1e-4)
{
matched_rank0 = true;
}
if matched_rank0
|| !transforms
.iter()
.any(|t| t.transform.abs_diff_eq(transform.transform, 1e-4))
{
let mut new_t = transform.clone();
if matched_rank0 {
new_t.rank = 1;
}
transforms.push(new_t);
transforms.sort_by(|a, b| a.rank.cmp(&b.rank));
}
}