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
//a Imports
use indent_display::{IndentedDisplay, IndentedOptions, Indenter, NullOptions};
use crate::hierarchy;
use crate::Bone;
use crate::Mat4;
use crate::Transformation;
//a Skeleton
//tp Skeleton
/// A set of related bones, with one or more roots
///
/// This corresponds to a skeleton (or a number thereof), with each
/// bone appearing once in each skeleton. The bones form a hierarchy.
#[derive(Debug)]
pub struct Skeleton {
/// The bones that make up the set, with the hierarchical relationships
pub skeleton: hierarchy::Hierarchy<Bone>,
/// The roots of the bones and hierarchical recipes for traversal
pub roots: Vec<(usize, hierarchy::Recipe)>,
/// An array of matrices long enough for the one per level of traversal
pub temp_mat4s: Vec<Mat4>,
/// Max bone index
pub max_index: usize,
}
//ip Default for Skeleton
impl Default for Skeleton {
fn default() -> Self {
Self::new()
}
}
//ip Skeleton
impl Skeleton {
//fp new
/// Create a new set of bones
pub fn new() -> Self {
let skeleton = hierarchy::Hierarchy::new();
let roots = Vec::new();
let temp_mat4s = Vec::new();
Self {
skeleton,
roots,
temp_mat4s,
max_index: 0,
}
}
//mp add_bone
/// Add a bone with a given base [Transformation] relative to its
/// parent (if it has one), and an index to a Vec of Mat4 that the
/// bone pose will utilize
///
/// It returns the bone reference index
pub fn add_bone(&mut self, transformation: Transformation, matrix_index: usize) -> usize {
self.roots.clear();
let bone = Bone::new(transformation, matrix_index);
self.skeleton.add_node(bone)
}
//mp relate
/// Relate a parent bone to a child bone (by bone reference indices)
pub fn relate(&mut self, parent: usize, child: usize) {
self.skeleton.relate(parent, child);
}
//mi find_max_matrix_index
/// Find the maximum matrix index of all the bones (plus 1)
fn find_max_matrix_index(&mut self) {
let mut max_index = 0;
for b in self.skeleton.borrow_elements() {
if b.data.matrix_index >= max_index {
max_index = b.data.matrix_index + 1
}
}
self.max_index = max_index;
}
//mp resolve
/// Resolve the [Skeleton] by finding the roots, generating
/// traversal [hierarchy::Recipe]s for each root, allocating the
/// required number of temporary [Mat4]s for the deepest of all
/// the recipes, and finding the number of bone matrices required
/// to be exported
pub fn resolve(&mut self) {
if self.roots.is_empty() {
self.skeleton.find_roots();
for r in self.skeleton.borrow_roots() {
self.roots
.push((*r, hierarchy::Recipe::of_ops(self.skeleton.enum_from(*r))));
}
let mut max_depth = 0;
for (_, recipe) in &self.roots {
max_depth = if recipe.depth() > max_depth {
recipe.depth()
} else {
max_depth
};
}
self.temp_mat4s = Vec::new();
for _ in 0..max_depth {
self.temp_mat4s.push([0.; 16]);
}
self.find_max_matrix_index();
}
}
//mp rewrite_indices
/// Rewrite the bone matrix indices from 0 if required
///
/// Each bone in the [Skeleton] is allocated the matrix index as it
/// is reached through traversal from the roots of the [Skeleton].
pub fn rewrite_indices(&mut self) {
self.resolve();
if self.max_index < self.skeleton.len() {
let mut bone_count = 0;
let (_, bones) = self.skeleton.borrow_mut();
for (_, recipe) in &self.roots {
for op in recipe.borrow_ops() {
if let hierarchy::NodeEnumOp::Push(n, _) = op {
bones[*n].data.matrix_index = bone_count;
bone_count += 1;
}
}
}
self.max_index = bone_count;
}
}
//mp derive_matrices
/// Derive the matrices (as specified by [Bone]) for every bone in
/// the [Skeleton] after the bones have been resolved.
///
///
pub fn derive_matrices(&mut self) {
assert!(
!self.roots.is_empty(),
"Resolve MUST have been invoked prior to derive_matrices"
);
let (_, bones) = self.skeleton.borrow_mut();
let mut mat_depth = 0;
for (_, recipe) in &self.roots {
for op in recipe.borrow_ops() {
match op {
hierarchy::NodeEnumOp::Push(n, _) => {
if mat_depth == 0 {
self.temp_mat4s[mat_depth] = *bones[*n]
.data
.derive_matrices(true, &self.temp_mat4s[mat_depth]);
} else {
self.temp_mat4s[mat_depth] = *bones[*n]
.data
.derive_matrices(false, &self.temp_mat4s[mat_depth - 1]);
}
mat_depth += 1;
}
_ => {
mat_depth -= 1;
}
}
}
}
}
//fp iter_roots
/// Iterate through the root bone indices in the [Skeleton]
pub fn iter_roots(&self) -> impl Iterator<Item = usize> + '_ {
self.roots.iter().map(|(n, _)| *n)
}
//zz All done
}
//ip IndentedDisplay for Skeleton
impl<'a, Opt: IndentedOptions<'a>> IndentedDisplay<'a, Opt> for Skeleton {
//mp fmt
/// Display for humans with indent
fn indent(&self, f: &mut Indenter<'a, Opt>) -> std::fmt::Result {
self.skeleton.indent(f)
}
}
//ip Display for Skeleton
impl std::fmt::Display for Skeleton {
//mp fmt
/// Display for humans with indent
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut v = Vec::<u8>::new();
let mut ind = Indenter::new(&mut v, " ", &NullOptions {});
self.indent(&mut ind)?;
drop(ind);
write!(f, "{}", &String::from_utf8(v).unwrap())
}
}