mod3d_base/skeleton_pose.rs
1//a Imports
2use indent_display::{IndentedDisplay, IndentedOptions, Indenter};
3
4use crate::hierarchy;
5use crate::{BonePose, Mat4, Skeleton};
6
7//a SkeletonPose
8//tp SkeletonPose
9/// A pose structure for a complete [Skeleton]
10///
11/// This includes a set of [Mat4] matrix transformations for
12/// mesh-space to animated-model-space
13pub struct SkeletonPose<'a> {
14 /// The Skeleton the pose corresponds to
15 skeleton: &'a Skeleton,
16 /// A pose for every [crate::Bone] in the [Skeleton]
17 poses: Vec<BonePose<'a>>,
18 /// A mesh-to-animated-model-space matrix transformation for each
19 /// bone
20 data: Vec<Mat4>,
21 /// A monotonic counter to allow updating of the matrices once per
22 /// animation tick
23 last_updated: usize,
24}
25
26//ip SkeletonPose
27impl<'a> SkeletonPose<'a> {
28 //fp new
29 /// Create a new [SkeletonPose] for a [Skeleton]
30 ///
31 /// The [Skeleton] must have been resolved
32 pub fn new(skeleton: &'a Skeleton) -> Self {
33 let mut poses = Vec::new();
34 for b in skeleton.skeleton.borrow_elements().iter() {
35 poses.push(BonePose::new(&b.data));
36 }
37 let mut data = Vec::new();
38 for _ in 0..skeleton.max_index {
39 data.push([0.; 16]);
40 }
41 let last_updated = 0;
42 Self {
43 skeleton,
44 poses,
45 data,
46 last_updated,
47 }
48 }
49
50 //fp derive_animation
51 /// Derive the animation for the current poses of the [SkeletonPose]
52 ///
53 /// This traverses the hierarchy as required
54 pub fn derive_animation(&mut self) {
55 let mut mat_depth = 0;
56 for (_, recipe) in &self.skeleton.roots {
57 for op in recipe.borrow_ops() {
58 match op {
59 hierarchy::NodeEnumOp::Push(n, _) => {
60 if mat_depth == 0 {
61 self.data[mat_depth] =
62 *self.poses[*n].derive_animation(true, &self.data[mat_depth]);
63 } else {
64 self.data[mat_depth + 1] =
65 *self.poses[*n].derive_animation(false, &self.data[mat_depth]);
66 }
67 mat_depth += 1;
68 }
69 _ => {
70 mat_depth -= 1;
71 }
72 }
73 }
74 }
75 }
76
77 //fp update
78 /// Update the animation matrices if required - depending on the
79 /// last updated tick
80 pub fn update(&mut self, tick: usize) {
81 if tick != self.last_updated {
82 self.last_updated = tick;
83 self.derive_animation();
84 let bones = self.skeleton.skeleton.borrow_elements();
85 for (i, bone) in bones.iter().enumerate().take(self.poses.len()) {
86 let matrix_index = bone.data.matrix_index;
87 self.data[matrix_index] = *self.poses[i].borrow_animated_mtm();
88 }
89 }
90 }
91}
92
93//ip IndentedDisplay for SkeletonPose
94impl<'a, 'b, Opt: IndentedOptions<'a>> IndentedDisplay<'a, Opt> for SkeletonPose<'b> {
95 //mp fmt
96 /// Display for humans with indent
97 fn indent(&self, f: &mut Indenter<'a, Opt>) -> std::fmt::Result {
98 for (_, recipe) in &self.skeleton.roots {
99 let mut sub = f.sub();
100 for op in recipe.borrow_ops() {
101 match op {
102 hierarchy::NodeEnumOp::Push(_n, _) => {
103 sub = sub.sub();
104 }
105 _ => {
106 sub = sub.pop();
107 }
108 }
109 }
110 }
111 Ok(())
112 }
113}
114
115/*
116 pass
117 #f hier_debug
118 def hier_debug(self, hier:Hierarchy) -> Hierarchy:
119 hier.add(f"SkeletonPose {self.skeleton.roots} {self.max_index} {self.last_updated} {self.data}")
120 hier.push()
121 self.skeleton.hier_debug(hier)
122 for pose in self.poses:
123 pose.hier_debug(hier)
124 pass
125 hier.pop()
126 return hier
127 #f All done
128 pass
129*/
130
131/*
132#c AnimatedBonePose
133class AnimatedBonePose:
134 def __init__(self, poses:List[BonePose]) -> None:
135 self.poses = poses
136 self.animatable = Bezier2(Transformation())
137 self.animatable.set_target( t1=1.,
138 c0=Transformation( quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
139 c1=Transformation( quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
140 tgt=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
141 callback=self.animation_callback )
142 pass
143 def interpolate_to_time(self, t:float) -> None:
144 z = self.animatable.interpolate_to_time(t)
145 # print(t, z)
146 self.poses[1].transformation_reset()
147 self.poses[1].transform(z)
148 pass
149 def animation_callback(self, t:float) -> None:
150 t_sec = math.floor(t)
151 t_int = int(t_sec)
152 tgt = 1.0
153 if (t_int&1): tgt=-1.
154 self.animatable.set_target( t1=t_sec+1.,
155 c0=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
156 c1=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(0.,1.,0.), 0.5)),
157 tgt=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), tgt*0.3)),
158 callback=self.animation_callback )
159 pass
160 pass
161
162*/