use crate::{
components::transform::HaTransform,
math::{Mat4, Vec3},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SkeletonError {
InvalidParentIndex(usize, usize),
InvalidBonesOrder(usize, usize),
DuplicateBoneName(String),
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SkeletonHierarchy {
bone_name: String,
bone_target: Vec3,
transform: HaTransform,
children: Vec<SkeletonHierarchy>,
}
impl SkeletonHierarchy {
pub fn new(bone_name: impl ToString) -> Self {
Self {
bone_name: bone_name.to_string(),
bone_target: Default::default(),
transform: Default::default(),
children: vec![],
}
}
pub fn target(mut self, offset: Vec3) -> Self {
self.bone_target = offset;
self
}
pub fn transform(mut self, transform: HaTransform) -> Self {
self.transform = transform;
self
}
pub fn child(mut self, hierarchy: Self) -> Self {
self.children.push(hierarchy);
self
}
fn count(&self) -> usize {
self.children.iter().fold(1, |a, v| a + v.count())
}
fn visit<F>(&self, parent: Option<usize>, f: &mut F)
where
F: FnMut(&Self, Option<usize>) -> Option<usize>,
{
if let Some(parent) = f(self, parent) {
for child in &self.children {
child.visit(Some(parent), f);
}
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SkeletonBone {
parent: Option<usize>,
name: String,
target: Vec3,
transform: HaTransform,
}
impl SkeletonBone {
pub fn parent(&self) -> Option<usize> {
self.parent
}
pub fn name(&self) -> &str {
&self.name
}
pub fn target(&self) -> Vec3 {
self.target
}
pub fn transform(&self) -> &HaTransform {
&self.transform
}
pub fn local_matrix(&self) -> Mat4 {
self.transform.local_matrix()
}
pub fn bind_pose_matrix(&self) -> Mat4 {
self.transform.world_matrix()
}
pub fn bind_pose_inverse_matrix(&self) -> Mat4 {
self.transform.inverse_world_matrix()
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Skeleton {
bones: Vec<SkeletonBone>,
}
impl Skeleton {
pub fn new(bones: Vec<SkeletonBone>) -> Result<Self, SkeletonError> {
for (index, bone) in bones.iter().enumerate() {
if let Some(parent) = bone.parent() {
if parent >= index {
return Err(SkeletonError::InvalidBonesOrder(parent, index));
}
if parent >= bones.len() {
return Err(SkeletonError::InvalidParentIndex(parent, bones.len()));
}
}
if bones
.iter()
.enumerate()
.any(|(i, other)| i != index && other.name == bone.name)
{
return Err(SkeletonError::DuplicateBoneName(bone.name.to_owned()));
}
}
Ok(Self { bones })
}
pub fn root_bone(&self) -> Option<&SkeletonBone> {
self.bones.get(0)
}
pub fn has_bone_by_index(&self, index: usize) -> bool {
index < self.bones.len()
}
pub fn bone_by_index(&self, index: usize) -> Option<&SkeletonBone> {
self.bones.get(index)
}
pub fn has_bone_by_name(&self, name: &str) -> bool {
self.bones.iter().any(|bone| bone.name == name)
}
pub fn bone_by_name(&self, name: &str) -> Option<&SkeletonBone> {
self.bones.iter().find(|bone| bone.name == name)
}
pub fn bone_index(&self, name: &str) -> Option<usize> {
self.bones.iter().position(|bone| bone.name == name)
}
pub fn bone_with_index(&self, name: &str) -> Option<(&SkeletonBone, usize)> {
self.bones.iter().enumerate().find_map(|(index, bone)| {
if bone.name == name {
Some((bone, index))
} else {
None
}
})
}
pub fn bones(&self) -> &[SkeletonBone] {
&self.bones
}
pub fn children_by_index(&self, index: usize) -> impl Iterator<Item = &SkeletonBone> {
self.bones.iter().filter(move |bone| {
bone.parent
.map(|parent| parent == index)
.unwrap_or_default()
})
}
}
impl TryFrom<SkeletonHierarchy> for Skeleton {
type Error = SkeletonError;
fn try_from(hierarchy: SkeletonHierarchy) -> Result<Self, Self::Error> {
let count = hierarchy.count();
let mut bones = Vec::<SkeletonBone>::with_capacity(count);
hierarchy.visit(None, &mut |hierarchy, parent| {
if bones.iter().any(|bone| bone.name == hierarchy.bone_name) {
return None;
}
let mut transform = hierarchy.transform.to_owned();
transform.rebuild_world_matrix(parent.map(|index| bones[index].transform()));
bones.push(SkeletonBone {
parent,
name: hierarchy.bone_name.to_owned(),
target: hierarchy.bone_target,
transform,
});
Some(bones.len() - 1)
});
Self::new(bones)
}
}