mod3d_base/
skeleton.rs

1//a Imports
2use indent_display::{IndentedDisplay, IndentedOptions, Indenter, NullOptions};
3
4use crate::hierarchy;
5use crate::Bone;
6use crate::Mat4;
7use crate::Transformation;
8
9//a Skeleton
10//tp Skeleton
11/// A set of related bones, with one or more roots
12///
13/// This corresponds to a skeleton (or a number thereof), with each
14/// bone appearing once in each skeleton. The bones form a hierarchy.
15#[derive(Debug)]
16pub struct Skeleton {
17    /// The bones that make up the set, with the hierarchical relationships
18    pub skeleton: hierarchy::Hierarchy<Bone>,
19    /// The roots of the bones and hierarchical recipes for traversal
20    pub roots: Vec<(usize, hierarchy::Recipe)>,
21    /// An array of matrices long enough for the one per level of traversal
22    pub temp_mat4s: Vec<Mat4>,
23    /// Max bone index
24    pub max_index: usize,
25}
26
27//ip Default for Skeleton
28impl Default for Skeleton {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34//ip Skeleton
35impl Skeleton {
36    //fp new
37    /// Create a new set of bones
38    pub fn new() -> Self {
39        let skeleton = hierarchy::Hierarchy::new();
40        let roots = Vec::new();
41        let temp_mat4s = Vec::new();
42        Self {
43            skeleton,
44            roots,
45            temp_mat4s,
46            max_index: 0,
47        }
48    }
49
50    //mp add_bone
51    /// Add a bone with a given base [Transformation] relative to its
52    /// parent (if it has one), and an index to a Vec of Mat4 that the
53    /// bone pose will utilize
54    ///
55    /// It returns the bone reference index
56    pub fn add_bone(&mut self, transformation: Transformation, matrix_index: usize) -> usize {
57        self.roots.clear();
58        let bone = Bone::new(transformation, matrix_index);
59        self.skeleton.add_node(bone)
60    }
61
62    //mp relate
63    /// Relate a parent bone to a child bone (by bone reference indices)
64    pub fn relate(&mut self, parent: usize, child: usize) {
65        self.skeleton.relate(parent, child);
66    }
67
68    //mi find_max_matrix_index
69    /// Find the maximum matrix index of all the bones (plus 1)
70    fn find_max_matrix_index(&mut self) {
71        let mut max_index = 0;
72        for b in self.skeleton.borrow_elements() {
73            if b.data.matrix_index >= max_index {
74                max_index = b.data.matrix_index + 1
75            }
76        }
77        self.max_index = max_index;
78    }
79
80    //mp resolve
81    /// Resolve the [Skeleton] by finding the roots, generating
82    /// traversal [hierarchy::Recipe]s for each root, allocating the
83    /// required number of temporary [Mat4]s for the deepest of all
84    /// the recipes, and finding the number of bone matrices required
85    /// to be exported
86    pub fn resolve(&mut self) {
87        if self.roots.is_empty() {
88            self.skeleton.find_roots();
89            for r in self.skeleton.borrow_roots() {
90                self.roots
91                    .push((*r, hierarchy::Recipe::of_ops(self.skeleton.enum_from(*r))));
92            }
93            let mut max_depth = 0;
94            for (_, recipe) in &self.roots {
95                max_depth = if recipe.depth() > max_depth {
96                    recipe.depth()
97                } else {
98                    max_depth
99                };
100            }
101            self.temp_mat4s = Vec::new();
102            for _ in 0..max_depth {
103                self.temp_mat4s.push([0.; 16]);
104            }
105            self.find_max_matrix_index();
106        }
107    }
108
109    //mp rewrite_indices
110    /// Rewrite the bone matrix indices from 0 if required
111    ///
112    /// Each bone in the [Skeleton] is allocated the matrix index as it
113    /// is reached through traversal from the roots of the [Skeleton].
114    pub fn rewrite_indices(&mut self) {
115        self.resolve();
116        if self.max_index < self.skeleton.len() {
117            let mut bone_count = 0;
118            let (_, bones) = self.skeleton.borrow_mut();
119            for (_, recipe) in &self.roots {
120                for op in recipe.borrow_ops() {
121                    if let hierarchy::NodeEnumOp::Push(n, _) = op {
122                        bones[*n].data.matrix_index = bone_count;
123                        bone_count += 1;
124                    }
125                }
126            }
127            self.max_index = bone_count;
128        }
129    }
130
131    //mp derive_matrices
132    /// Derive the matrices (as specified by [Bone]) for every bone in
133    /// the [Skeleton] after the bones have been resolved.
134    ///
135    ///
136    pub fn derive_matrices(&mut self) {
137        assert!(
138            !self.roots.is_empty(),
139            "Resolve MUST have been invoked prior to derive_matrices"
140        );
141        let (_, bones) = self.skeleton.borrow_mut();
142        let mut mat_depth = 0;
143        for (_, recipe) in &self.roots {
144            for op in recipe.borrow_ops() {
145                match op {
146                    hierarchy::NodeEnumOp::Push(n, _) => {
147                        if mat_depth == 0 {
148                            self.temp_mat4s[mat_depth] = *bones[*n]
149                                .data
150                                .derive_matrices(true, &self.temp_mat4s[mat_depth]);
151                        } else {
152                            self.temp_mat4s[mat_depth] = *bones[*n]
153                                .data
154                                .derive_matrices(false, &self.temp_mat4s[mat_depth - 1]);
155                        }
156                        mat_depth += 1;
157                    }
158                    _ => {
159                        mat_depth -= 1;
160                    }
161                }
162            }
163        }
164    }
165
166    //fp iter_roots
167    /// Iterate through the root bone indices in the [Skeleton]
168    pub fn iter_roots(&self) -> impl Iterator<Item = usize> + '_ {
169        self.roots.iter().map(|(n, _)| *n)
170    }
171
172    //zz All done
173}
174
175//ip IndentedDisplay for Skeleton
176impl<'a, Opt: IndentedOptions<'a>> IndentedDisplay<'a, Opt> for Skeleton {
177    //mp fmt
178    /// Display for humans with indent
179    fn indent(&self, f: &mut Indenter<'a, Opt>) -> std::fmt::Result {
180        self.skeleton.indent(f)
181    }
182}
183
184//ip Display for Skeleton
185impl std::fmt::Display for Skeleton {
186    //mp fmt
187    /// Display for humans with indent
188    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
189        let mut v = Vec::<u8>::new();
190        let mut ind = Indenter::new(&mut v, " ", &NullOptions {});
191        self.indent(&mut ind)?;
192        drop(ind);
193        write!(f, "{}", &String::from_utf8(v).unwrap())
194    }
195}