Skip to main content

pfx_core/anim/
skeleton.rs

1use super::AnimError;
2use super::clip::Clip;
3use super::math::{IDENTITY, Mat4, Trs};
4
5pub const MAX_JOINTS: usize = 1024;
6
7#[derive(Clone, Debug, PartialEq)]
8pub struct Joint {
9    pub name: String,
10    pub parent: Option<usize>,
11    pub rest: Trs,
12}
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct Skeleton {
16    joints: Vec<Joint>,
17    inverse_binds: Vec<Mat4>,
18    root: Mat4,
19}
20
21impl Skeleton {
22    pub fn new(joints: Vec<Joint>, inverse_binds: Vec<Mat4>) -> Result<Self, AnimError> {
23        Self::rooted(joints, inverse_binds, IDENTITY)
24    }
25
26    pub fn rooted(
27        joints: Vec<Joint>,
28        inverse_binds: Vec<Mat4>,
29        root: Mat4,
30    ) -> Result<Self, AnimError> {
31        if joints.is_empty() {
32            return Err(AnimError::NoJoints);
33        }
34        if joints.len() > MAX_JOINTS {
35            return Err(AnimError::TooManyJoints(joints.len()));
36        }
37        if inverse_binds.len() != joints.len() {
38            return Err(AnimError::InverseBinds {
39                joints: joints.len(),
40                binds: inverse_binds.len(),
41            });
42        }
43        for (index, joint) in joints.iter().enumerate() {
44            if joint.parent.is_some_and(|parent| parent >= index) {
45                return Err(AnimError::ParentOrder(joint.name.clone()));
46            }
47            if !joint.rest.finite() {
48                return Err(AnimError::NotFinite(joint.name.clone()));
49            }
50        }
51        if !inverse_binds
52            .iter()
53            .chain(std::iter::once(&root))
54            .flatten()
55            .flatten()
56            .all(|value| value.is_finite())
57        {
58            return Err(AnimError::NotFinite("inverse bind matrices".into()));
59        }
60        Ok(Self {
61            joints,
62            inverse_binds,
63            root,
64        })
65    }
66
67    pub fn joints(&self) -> &[Joint] {
68        &self.joints
69    }
70
71    pub fn len(&self) -> usize {
72        self.joints.len()
73    }
74
75    pub fn is_empty(&self) -> bool {
76        self.joints.is_empty()
77    }
78
79    pub fn joint(&self, name: &str) -> Option<usize> {
80        self.joints.iter().position(|joint| joint.name == name)
81    }
82
83    pub fn inverse_binds(&self) -> &[Mat4] {
84        &self.inverse_binds
85    }
86
87    pub fn root(&self) -> Mat4 {
88        self.root
89    }
90}
91
92#[derive(Clone, Debug, PartialEq)]
93pub struct Rig {
94    skeleton: Skeleton,
95    clips: Vec<Clip>,
96}
97
98impl Rig {
99    pub fn new(skeleton: Skeleton, clips: Vec<Clip>) -> Result<Self, AnimError> {
100        for (index, clip) in clips.iter().enumerate() {
101            if clips[..index]
102                .iter()
103                .any(|other| other.name() == clip.name())
104            {
105                return Err(AnimError::DuplicateClip(clip.name().to_string()));
106            }
107            if let Some(channel) = clip
108                .channels()
109                .iter()
110                .find(|channel| channel.joint >= skeleton.len())
111            {
112                return Err(AnimError::Channel {
113                    clip: clip.name().to_string(),
114                    reason: format!(
115                        "channel names joint {} of {}",
116                        channel.joint,
117                        skeleton.len()
118                    ),
119                });
120            }
121        }
122        Ok(Self { skeleton, clips })
123    }
124
125    pub fn skeleton(&self) -> &Skeleton {
126        &self.skeleton
127    }
128
129    pub fn clips(&self) -> &[Clip] {
130        &self.clips
131    }
132
133    pub fn clip(&self, name: &str) -> Option<usize> {
134        self.clips.iter().position(|clip| clip.name() == name)
135    }
136
137    pub fn with_events(
138        mut self,
139        clip: &str,
140        events: Vec<super::ClipEvent>,
141    ) -> Result<Self, AnimError> {
142        let index = self
143            .clip(clip)
144            .ok_or_else(|| AnimError::UnknownClip(clip.to_string()))?;
145        let taken = std::mem::replace(&mut self.clips[index], Clip::empty(clip));
146        self.clips[index] = taken.with_events(events)?;
147        Ok(self)
148    }
149}