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
//a Imports
use indent_display::{IndentedDisplay, IndentedOptions, Indenter};
use crate::hierarchy;
use crate::{BonePose, Mat4, Skeleton};
//a SkeletonPose
//tp SkeletonPose
/// A pose structure for a complete [Skeleton]
///
/// This includes a set of [Mat4] matrix transformations for
/// mesh-space to animated-model-space
pub struct SkeletonPose<'a> {
/// The Skeleton the pose corresponds to
skeleton: &'a Skeleton,
/// A pose for every [crate::Bone] in the [Skeleton]
poses: Vec<BonePose<'a>>,
/// A mesh-to-animated-model-space matrix transformation for each
/// bone
data: Vec<Mat4>,
/// A monotonic counter to allow updating of the matrices once per
/// animation tick
last_updated: usize,
}
//ip SkeletonPose
impl<'a> SkeletonPose<'a> {
//fp new
/// Create a new [SkeletonPose] for a [Skeleton]
///
/// The [Skeleton] must have been resolved
pub fn new(skeleton: &'a Skeleton) -> Self {
let mut poses = Vec::new();
for b in skeleton.skeleton.borrow_elements().iter() {
poses.push(BonePose::new(&b.data));
}
let mut data = Vec::new();
for _ in 0..skeleton.max_index {
data.push([0.; 16]);
}
let last_updated = 0;
Self {
skeleton,
poses,
data,
last_updated,
}
}
//fp derive_animation
/// Derive the animation for the current poses of the [SkeletonPose]
///
/// This traverses the hierarchy as required
pub fn derive_animation(&mut self) {
let mut mat_depth = 0;
for (_, recipe) in &self.skeleton.roots {
for op in recipe.borrow_ops() {
match op {
hierarchy::NodeEnumOp::Push(n, _) => {
if mat_depth == 0 {
self.data[mat_depth] =
*self.poses[*n].derive_animation(true, &self.data[mat_depth]);
} else {
self.data[mat_depth + 1] =
*self.poses[*n].derive_animation(false, &self.data[mat_depth]);
}
mat_depth += 1;
}
_ => {
mat_depth -= 1;
}
}
}
}
}
//fp update
/// Update the animation matrices if required - depending on the
/// last updated tick
pub fn update(&mut self, tick: usize) {
if tick != self.last_updated {
self.last_updated = tick;
self.derive_animation();
let bones = self.skeleton.skeleton.borrow_elements();
for (i, bone) in bones.iter().enumerate().take(self.poses.len()) {
let matrix_index = bone.data.matrix_index;
self.data[matrix_index] = *self.poses[i].borrow_animated_mtm();
}
}
}
}
//ip IndentedDisplay for SkeletonPose
impl<'a, 'b, Opt: IndentedOptions<'a>> IndentedDisplay<'a, Opt> for SkeletonPose<'b> {
//mp fmt
/// Display for humans with indent
fn indent(&self, f: &mut Indenter<'a, Opt>) -> std::fmt::Result {
for (_, recipe) in &self.skeleton.roots {
let mut sub = f.sub();
for op in recipe.borrow_ops() {
match op {
hierarchy::NodeEnumOp::Push(_n, _) => {
sub = sub.sub();
}
_ => {
sub = sub.pop();
}
}
}
}
Ok(())
}
}
/*
pass
#f hier_debug
def hier_debug(self, hier:Hierarchy) -> Hierarchy:
hier.add(f"SkeletonPose {self.skeleton.roots} {self.max_index} {self.last_updated} {self.data}")
hier.push()
self.skeleton.hier_debug(hier)
for pose in self.poses:
pose.hier_debug(hier)
pass
hier.pop()
return hier
#f All done
pass
*/
/*
#c AnimatedBonePose
class AnimatedBonePose:
def __init__(self, poses:List[BonePose]) -> None:
self.poses = poses
self.animatable = Bezier2(Transformation())
self.animatable.set_target( t1=1.,
c0=Transformation( quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
c1=Transformation( quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
tgt=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
callback=self.animation_callback )
pass
def interpolate_to_time(self, t:float) -> None:
z = self.animatable.interpolate_to_time(t)
# print(t, z)
self.poses[1].transformation_reset()
self.poses[1].transform(z)
pass
def animation_callback(self, t:float) -> None:
t_sec = math.floor(t)
t_int = int(t_sec)
tgt = 1.0
if (t_int&1): tgt=-1.
self.animatable.set_target( t1=t_sec+1.,
c0=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), 0.3)),
c1=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(0.,1.,0.), 0.5)),
tgt=Transformation(quaternion=Glm.quat.setAxisAngle(Glm.quat.create(), Glm.vec3.fromValues(1.,0.,0.), tgt*0.3)),
callback=self.animation_callback )
pass
pass
*/