1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
//! Data structures and methods for dealing with armatures. //! //! @see https://docs.blender.org/manual/en/dev/modeling/armature/introduction.html - Armature Introduction #[macro_use] extern crate serde_derive; use std::collections::HashMap; use crate::serde::serialize_hashmap_deterministic; pub use self::action::*; pub use self::bone::*; pub use self::coordinate_system::*; pub use self::export::*; pub use self::interpolate::*; use std::borrow::Borrow; use std::hash::Hash; mod action; mod bone; mod convert; mod coordinate_system; mod export; mod interpolate; mod serde; #[cfg(test)] mod test_util; /// Something went wrong in the Blender child process that was trying to parse your armature data. #[derive(Debug, thiserror::Error)] pub enum BlenderError { /// Errors in Blender are written to stderr. We capture the stderr from the `blender` child /// process that we spawned when attempting to export armature from a `.blend` file. #[error( "There was an issue while exporting armature: Blender stderr output: {}", _0 )] Stderr(String), } /// All of the data about a Blender armature that we've exported from Blender. /// A BlenderArmature should have all of the data that you need to implement skeletal /// animation. /// /// If you have other needs, such as a way to know the model space position of any bone at any /// time so that you can, say, render a baseball in on top of your hand bone.. Open an issue. /// (I plan to support this specific example in the future) #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] // TODO: BlenderArmature<T: Bone> for DQ and matrix pub struct BlenderArmature { name: String, #[serde(serialize_with = "serialize_hashmap_deterministic")] joint_indices: HashMap<String, u8>, bone_child_to_parent: HashMap<u8, u8>, inverse_bind_poses: Vec<Bone>, #[serde(serialize_with = "serialize_hashmap_deterministic")] bone_space_actions: HashMap<String, Action>, #[serde(serialize_with = "serialize_hashmap_deterministic")] bone_groups: HashMap<String, Vec<u8>>, #[serde(default)] coordinate_system: CoordinateSystem, } impl BlenderArmature { /// The name of the armature pub fn name(&self) -> &String { &self.name } /// Set the name of the armature. /// /// # Example /// /// ``` /// # use blender_armature::BlenderArmature; /// let mut armature = BlenderArmature::default(); /// armature.set_name("Some Name".to_string()); /// /// assert_eq!(armature.name(), "Some Name"); /// ``` pub fn set_name(&mut self, name: String) { self.name = name; } /// Blender [bone groups] /// /// Maps bone group name to a vector of the bones indices that are in that bone group. /// /// ```rust /// # use blender_armature::{Action, BlenderArmature, FrameOffset, SampleDesc, JointIndicesRef}; /// # use std::time::Duration; /// /// let armature = create_blender_armature(); /// /// let joint_indices = armature.bone_groups().get("My bone group").unwrap(); /// /// let sample_desc = SampleDesc { /// frame_offset: FrameOffset::new_with_elapsed_time_and_frames_per_second( /// Duration::from_secs(2), /// 24, /// ), /// should_loop: false /// }; /// /// let _bones = armature.interpolate_bones( /// "SomeAction", /// JointIndicesRef::Some(joint_indices), /// sample_desc /// ); /// /// # fn create_blender_armature() -> BlenderArmature { /// # let mut b = BlenderArmature::default(); /// # b.insert_bone_space_action("SomeAction".to_string(), Action::new()); /// # b.create_bone_group("My bone group".to_string(), vec![]); /// # b /// # } /// ``` /// /// [bone groups]: https://docs.blender.org/manual/en/latest/animation/armatures/properties/bone_groups.html pub fn bone_groups(&self) -> &HashMap<String, Vec<u8>> { &self.bone_groups } /// Create a new bone group pub fn create_bone_group(&mut self, name: String, joint_indices: Vec<u8>) { self.bone_groups.insert(name, joint_indices); } /// Get a bone's index into the various Vec<Bone> data structures that hold bone data. /// /// # Example /// /// ``` /// use blender_armature::BlenderArmature; /// let mut armature = BlenderArmature::default(); /// /// armature.insert_joint_index("Spine".to_string(), 0); /// /// assert_eq!(armature.joint_indices().len(), 1); /// ``` pub fn joint_indices(&self) -> &HashMap<String, u8> { &self.joint_indices } /// Set a bone's index into the various Vec<Bone> data structures that hold bone data. /// /// # Example /// /// ``` /// use blender_armature::BlenderArmature; /// let mut armature = BlenderArmature::default(); /// /// armature.insert_joint_index("Spine".to_string(), 0); /// armature.insert_joint_index("UpperArm".to_string(), 2); /// /// assert_eq!(armature.joint_indices().len(), 2); /// ``` pub fn insert_joint_index(&mut self, joint_name: String, joint_idx: u8) { self.joint_indices.insert(joint_name, joint_idx); } /// Every bone's inverse bind pose. /// /// # From Blender /// When exporting from Blender these include the armature's world space matrix. /// /// So, effectively these are `(armature_world_space_matrix * bone_bind_pose).inverse()` pub fn inverse_bind_poses(&self) -> &Vec<Bone> { &self.inverse_bind_poses } /// Set the inverse bind poses. pub fn set_inverse_bind_poses(&mut self, poses: Vec<Bone>) { self.inverse_bind_poses = poses; } /// All of the actions defined on the armature, keyed by action name. /// /// FIXME: Rename to `bone_local_space_actions` pub fn bone_space_actions(&self) -> &HashMap<String, Action> { &self.bone_space_actions } /// Insert an action into the map of actions. pub fn insert_bone_space_action(&mut self, name: String, action: Action) { self.bone_space_actions.insert(name, action); } /// Remove an action from the map. pub fn remove_bone_space_action<Q>(&mut self, name: &Q) -> Option<Action> where String: Borrow<Q>, Q: Hash + Eq, { self.bone_space_actions.remove(name) } /// A map of a bone chil to its parent /// /// If a bone is not stored in this map then it does not have a parent. pub fn bone_child_to_parent(&self) -> &HashMap<u8, u8> { &self.bone_child_to_parent } /// # Example /// /// ``` /// # use blender_armature::BlenderArmature; /// let mut armature = BlenderArmature::default(); /// /// let child_idx = 4; /// let parent_idx = 2; /// /// armature.insert_joint_index("UpperArm".to_string(), parent_idx); /// armature.insert_joint_index("Lower Arm".to_string(), child_idx); /// /// armature.insert_child_to_parent(child_idx, parent_idx); /// ``` pub fn insert_child_to_parent(&mut self, child: u8, parent: u8) { self.bone_child_to_parent.insert(child, parent); } } /// The pose bones at an individual keyframe time #[derive(Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(test, derive(Default, Clone))] pub struct Keyframe { frame: u16, bones: Vec<Bone>, } impl Keyframe { #[allow(missing_docs)] pub fn new(frame: u16, bones: Vec<Bone>) -> Self { Keyframe { frame, bones } } /// All of the bones for this keyframe. pub fn bones(&self) -> &Vec<Bone> { &self.bones } /// All of the bones for this keyframe. pub fn bones_mut(&mut self) -> &mut Vec<Bone> { &mut self.bones } /// The frame number pub fn frame(&self) -> u16 { self.frame } } // TODO: These methods can be abstracted into calling a method that takes a callback impl BlenderArmature { /// Tranpose all of the bone matrices in our armature's action keyframes. /// Blender uses row major matrices, but OpenGL uses column major matrices so you'll /// usually want to transpose your matrices before using them. pub fn transpose_actions(&mut self) { for (_name, action) in self.bone_space_actions.iter_mut() { for (_bone_idx, keyframes) in action.keyframes_mut().iter_mut() { for bone in keyframes.iter_mut() { bone.bone_mut().transpose(); } } } for bone in self.inverse_bind_poses.iter_mut() { bone.transpose(); } } } impl BlenderArmature { /// Convert your action matrices into dual quaternions so that you can implement /// dual quaternion linear blending. pub fn matrices_to_dual_quats(&mut self) { for (_, keyframes) in self.bone_space_actions.iter_mut() { for (bone_idx, keyframes) in keyframes.keyframes_mut().iter_mut() { for bone_keyframe in keyframes.iter_mut() { bone_keyframe .set_bone(BlenderArmature::matrix_to_dual_quat(&bone_keyframe.bone())); } } } for bone in self.inverse_bind_poses.iter_mut() { *bone = BlenderArmature::matrix_to_dual_quat(bone); } } } impl Bone { fn transpose(&mut self) { match self { Bone::Matrix(ref mut matrix) => { matrix.transpose_mut(); } Bone::DualQuat(_) => unimplemented!(), }; } // DELETE ME fn multiply(&mut self, rhs: Bone) { match self { Bone::Matrix(lhs_matrix) => match rhs { Bone::Matrix(rhs_matrix) => { // *self = Bone::Matrix(rhs_matrix * *lhs_matrix) } Bone::DualQuat(_) => {} }, Bone::DualQuat(_) => {} }; } } // DELETE ME impl BlenderArmature { /// Iterate over all of the action bones and apply and multiply in the inverse bind pose. /// /// TODO: another function to apply bind shape matrix? Most armatures seem to export an identity /// bind shape matrix but that might not be the same for every armature. /// /// TODO: Do not mutate the matrices and instead just return the new values and let the caller /// handle caching them? Would mean less moving parts in our data structures and you always /// know exactly what you are getting. Right now you have no way actions of knowing whether or /// not actions have their bind poses pre-multiplied in. pub fn apply_inverse_bind_poses(&mut self) { for (_name, action) in self.bone_space_actions.iter_mut() { for (bone_idx, keyframe) in action.keyframes_mut().iter_mut() { for (index, bone) in keyframe.iter_mut().enumerate() { bone.bone_mut() .multiply(self.inverse_bind_poses[*bone_idx as usize]); } } } } } #[cfg(test)] mod tests { use super::*; use crate::interpolate::tests::dq_to_bone; use crate::test_util::action_with_keyframes; use nalgebra::Matrix4; #[test] fn convert_actions_to_dual_quats() { let mut keyframes = vec![]; keyframes.push(BoneKeyframe::new( 1, Bone::Matrix(Matrix4::from_column_slice(&[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ])), )); let mut start_armature = BlenderArmature { bone_space_actions: action_with_keyframes(keyframes), ..BlenderArmature::default() }; start_armature.matrices_to_dual_quats(); let mut new_keyframes = vec![]; new_keyframes.push(BoneKeyframe::new( 1, dq_to_bone([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), )); let expected_armature = BlenderArmature { bone_space_actions: action_with_keyframes(new_keyframes), ..start_armature.clone() }; assert_eq!(start_armature, expected_armature); } // TODO: Function to return these start_actions that we keep using #[test] fn transpose_actions() { let keyframes = vec![BoneKeyframe::new( 1, Bone::Matrix(Matrix4::from_column_slice(&[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 5.0, 1.0, ])), )]; let mut start_armature = BlenderArmature { bone_space_actions: action_with_keyframes(keyframes), ..BlenderArmature::default() }; start_armature.transpose_actions(); let new_keyframes = vec![BoneKeyframe::new( 1, Bone::Matrix(Matrix4::from_column_slice(&[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 5.0, 0.0, 0.0, 0.0, 1.0, ])), )]; let expected_armature = BlenderArmature { bone_space_actions: action_with_keyframes(new_keyframes), ..start_armature.clone() }; assert_eq!(start_armature, expected_armature); } }