1use pfx_core::anim::{Channel, Clip, Joint, MAX_JOINTS, Rig, Skeleton, Trs};
2
3use crate::mesh::Mesh;
4
5#[derive(Clone, Debug, PartialEq)]
6pub struct SkinRig {
7 pub rig: Rig,
8 pub remap: Vec<u16>,
9}
10
11impl SkinRig {
12 pub fn joints(&self, raw: &[[u16; 4]], weights: &[[f32; 4]]) -> Result<Vec<[u16; 4]>, String> {
13 raw.iter()
14 .zip(weights)
15 .map(|(joints, weights)| {
16 let mut out = [0u16; 4];
17 for slot in 0..4 {
18 if weights[slot] == 0.0 {
19 continue;
20 }
21 out[slot] = *self.remap.get(joints[slot] as usize).ok_or_else(|| {
22 format!(
23 "a vertex names joint {} of a skin of {}",
24 joints[slot],
25 self.remap.len()
26 )
27 })?;
28 }
29 Ok(out)
30 })
31 .collect()
32 }
33}
34
35impl Mesh {
36 pub fn rig(&self, skin: u32) -> Result<SkinRig, String> {
37 let found = self
38 .skins
39 .get(skin as usize)
40 .ok_or_else(|| format!("mesh: no skin {skin}"))?;
41 if found.joints.is_empty() {
42 return Err(format!("mesh: skin {skin} has no joints"));
43 }
44 if found.joints.len() > MAX_JOINTS {
45 return Err(format!(
46 "mesh: skin {skin} has {} joints, over {MAX_JOINTS}",
47 found.joints.len()
48 ));
49 }
50 let depth = |node: u32| {
51 let mut depth = 0usize;
52 let mut at = self.nodes[node as usize].parent;
53 while let Some(parent) = at {
54 depth += 1;
55 at = self.nodes[parent as usize].parent;
56 if depth > self.nodes.len() {
57 break;
58 }
59 }
60 depth
61 };
62 let mut order: Vec<usize> = (0..found.joints.len()).collect();
63 order.sort_by_key(|&slot| (depth(found.joints[slot]), slot));
64 let mut remap = vec![0u16; found.joints.len()];
65 for (new, &old) in order.iter().enumerate() {
66 remap[old] = new as u16;
67 }
68 let index_of = |node: u32| found.joints.iter().position(|&joint| joint == node);
69 let mut root_parent: Option<Option<u32>> = None;
70 let mut joints = Vec::with_capacity(order.len());
71 let mut binds = Vec::with_capacity(order.len());
72 for &old in &order {
73 let node_index = found.joints[old];
74 let node = &self.nodes[node_index as usize];
75 let mut parent = None;
76 let mut at = node.parent;
77 while let Some(up) = at {
78 if let Some(slot) = index_of(up) {
79 parent = Some(remap[slot] as usize);
80 break;
81 }
82 at = self.nodes[up as usize].parent;
83 }
84 if parent.is_none() {
85 match root_parent {
86 None => root_parent = Some(node.parent),
87 Some(other) if other != node.parent => {
88 return Err(format!(
89 "mesh: skin {skin} has root joints under different nodes"
90 ));
91 }
92 Some(_) => {}
93 }
94 }
95 joints.push(Joint {
96 name: if node.name.is_empty() {
97 format!("joint{node_index}")
98 } else {
99 node.name.clone()
100 },
101 parent,
102 rest: node.trs,
103 });
104 binds.push(found.inverse_binds[old]);
105 }
106 let root = root_parent
107 .flatten()
108 .and_then(|parent| self.world(parent))
109 .unwrap_or(pfx_core::anim::math::IDENTITY);
110 let skeleton = Skeleton::rooted(joints, binds, root)
111 .map_err(|error| format!("mesh: skin {skin}: {error}"))?;
112 let clips = self.clips(|node| index_of(node).map(|slot| remap[slot] as usize))?;
113 let rig = Rig::new(skeleton, clips).map_err(|error| format!("mesh: {error}"))?;
114 Ok(SkinRig { rig, remap })
115 }
116}
117
118impl Mesh {
119 fn clips(&self, joint_of: impl Fn(u32) -> Option<usize>) -> Result<Vec<Clip>, String> {
120 let mut clips = Vec::new();
121 for animation in &self.animations {
122 let channels: Vec<Channel> = animation
123 .channels
124 .iter()
125 .filter_map(|channel| {
126 Some(Channel {
127 joint: joint_of(channel.node)?,
128 property: channel.property,
129 interpolation: channel.interpolation,
130 times: channel.times.clone(),
131 values: channel.values.clone(),
132 })
133 })
134 .collect();
135 if channels.is_empty() {
136 continue;
137 }
138 clips.push(
139 Clip::new(animation.name.clone(), channels)
140 .map_err(|error| format!("mesh: {error}"))?,
141 );
142 }
143 Ok(clips)
144 }
145}
146
147impl Mesh {
148 pub fn node_rig(&self, walked: &[(u32, bool)]) -> Result<Option<Rig>, String> {
149 if self.animations.is_empty() || walked.is_empty() {
150 return Ok(None);
151 }
152 if walked.len() > MAX_JOINTS {
153 return Err(format!(
154 "mesh: {} animated nodes, over {MAX_JOINTS}",
155 walked.len()
156 ));
157 }
158 let joint_of = |node: u32| {
159 walked
160 .iter()
161 .position(|(walked, own)| *walked == node && *own)
162 };
163 let joints: Vec<Joint> = walked
164 .iter()
165 .enumerate()
166 .map(|(index, &(node, own))| {
167 let found = &self.nodes[node as usize];
168 let parent = if own {
169 found
170 .parent
171 .and_then(|parent| walked[..index].iter().position(|(n, _)| *n == parent))
172 } else {
173 None
174 };
175 Joint {
176 name: if found.name.is_empty() {
177 format!("node{node}")
178 } else {
179 found.name.clone()
180 },
181 parent,
182 rest: if own { found.trs } else { Trs::IDENTITY },
183 }
184 })
185 .collect();
186 let binds = vec![pfx_core::anim::math::IDENTITY; joints.len()];
187 let skeleton = Skeleton::new(joints, binds).map_err(|error| format!("mesh: {error}"))?;
188 let clips = self.clips(joint_of)?;
189 if clips.is_empty() {
190 return Ok(None);
191 }
192 Rig::new(skeleton, clips)
193 .map(Some)
194 .map_err(|error| format!("mesh: {error}"))
195 }
196}