pfx_core/anim/
skeleton.rs1use 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}