use std::collections::HashMap;
use super::ag_anim::{AffineParts, AGAnimData, ATCAnimData, ChannelData};
use super::controller::{Controller, LeafController, TMController, CompoundController};
use super::keys::{KeyFrame, KeyType, decompress_quat32, decompress_quat64};
#[derive(Debug, Clone)]
pub enum BoneChannel {
Animated {
controller: Controller,
initial_pose: AffineParts,
},
Constant {
pose: AffineParts,
},
}
#[derive(Debug, Clone)]
pub struct AnimClip {
pub name: String,
pub start: f32,
pub end: f32,
pub do_loop: bool,
pub loop_start: f32,
pub loop_end: f32,
pub bone_channels: HashMap<String, BoneChannel>,
}
fn extract_bone_channels(applicators: &[super::ag_anim::AnimApplicator]) -> HashMap<String, BoneChannel> {
let mut bone_channels = HashMap::new();
for app in applicators {
if !app.enabled { continue; }
let bone_name = if !app.channel_name.is_empty() {
&app.channel_name
} else if !app.channel_name_from_channel.is_empty() {
&app.channel_name_from_channel
} else {
continue;
};
match &app.channel_data {
ChannelData::MatrixController { controller: Some(ctrl), initial_pose } => {
bone_channels.insert(bone_name.clone(), BoneChannel::Animated {
controller: ctrl.clone(),
initial_pose: initial_pose.clone(),
});
}
ChannelData::MatrixController { controller: None, initial_pose } => {
bone_channels.insert(bone_name.clone(), BoneChannel::Constant {
pose: initial_pose.clone(),
});
}
ChannelData::MatrixConstant { pose } => {
bone_channels.insert(bone_name.clone(), BoneChannel::Constant {
pose: pose.clone(),
});
}
_ => {}
}
}
bone_channels
}
impl AnimClip {
pub fn from_atc_anim(anim: &ATCAnimData) -> Self {
Self {
name: anim.base.name.clone(),
start: anim.base.start,
end: anim.base.end,
do_loop: anim.do_loop,
loop_start: anim.loop_start,
loop_end: anim.loop_end,
bone_channels: extract_bone_channels(&anim.base.applicators),
}
}
pub fn from_ag_anim(anim: &AGAnimData, do_loop: bool) -> Self {
Self {
name: anim.name.clone(),
start: anim.start,
end: anim.end,
do_loop,
loop_start: anim.start,
loop_end: anim.end,
bone_channels: extract_bone_channels(&anim.applicators),
}
}
pub fn duration(&self) -> f32 {
self.end - self.start
}
pub fn animated_bone_count(&self) -> usize {
self.bone_channels.values().filter(|ch| matches!(ch, BoneChannel::Animated { .. })).count()
}
}
pub fn evaluate_bone_channel(channel: &BoneChannel, time: f32) -> [f32; 16] {
match channel {
BoneChannel::Animated { controller, initial_pose } => {
evaluate_controller_with_initial(controller, time, initial_pose)
}
BoneChannel::Constant { pose } => {
compose_affine_parts(pose)
}
}
}
fn evaluate_controller_with_initial(ctrl: &Controller, time: f32, initial: &AffineParts) -> [f32; 16] {
match ctrl {
Controller::TM(tm) => evaluate_tm_controller(tm, time),
Controller::Compound(compound) => evaluate_compound_with_initial(compound, time, initial),
Controller::Leaf(leaf) => {
match leaf.key_type {
KeyType::Matrix44 => evaluate_leaf_matrix44(leaf, time),
KeyType::Quat | KeyType::CompressedQuat32 | KeyType::CompressedQuat64 => {
let quat = evaluate_leaf_quat(leaf, time);
quat_to_matrix(&quat)
}
_ => identity_matrix(),
}
}
}
}
pub fn evaluate_controller(ctrl: &Controller, time: f32) -> [f32; 16] {
let default_ap = AffineParts {
translation: [0.0, 0.0, 0.0],
rotation: [0.0, 0.0, 0.0, 1.0],
stretch_rotation: [0.0, 0.0, 0.0, 1.0],
scale: [1.0, 1.0, 1.0],
det_sign: 1.0,
};
evaluate_controller_with_initial(ctrl, time, &default_ap)
}
fn evaluate_compound_with_initial(compound: &CompoundController, time: f32, initial: &AffineParts) -> [f32; 16] {
let pos = compound.x_controller.as_ref()
.map(|c| evaluate_position(c, time))
.unwrap_or(initial.translation);
let quat = compound.y_controller.as_ref()
.map(|c| evaluate_rotation(c, time))
.unwrap_or(initial.rotation);
let (scale, stretch_rot) = compound.z_controller.as_ref()
.map(|c| evaluate_scale_with_stretch(c, time))
.unwrap_or((initial.scale, initial.stretch_rotation));
let det_sign = initial.det_sign;
compose_full_affine(&pos, &quat, &stretch_rot, &scale, det_sign)
}
pub fn compose_affine_parts(ap: &AffineParts) -> [f32; 16] {
compose_full_affine(&ap.translation, &ap.rotation, &ap.stretch_rotation, &ap.scale, ap.det_sign)
}
fn compose_full_affine(pos: &[f32; 3], rotation: &[f32; 4], stretch_rot: &[f32; 4], scale: &[f32; 3], det_sign: f32) -> [f32; 16] {
use crate::core::transform::mat44_multiply;
let u_is_identity = (stretch_rot[0].abs() < 1e-6)
&& (stretch_rot[1].abs() < 1e-6)
&& (stretch_rot[2].abs() < 1e-6)
&& ((stretch_rot[3] - 1.0).abs() < 1e-6 || (stretch_rot[3] + 1.0).abs() < 1e-6);
let f = if det_sign < 0.0 { -1.0f32 } else { 1.0f32 };
if u_is_identity {
let rot = quat_to_matrix(rotation);
[
rot[0] * scale[0] * f, rot[1] * scale[1] * f, rot[2] * scale[2] * f, pos[0],
rot[4] * scale[0] * f, rot[5] * scale[1] * f, rot[6] * scale[2] * f, pos[1],
rot[8] * scale[0] * f, rot[9] * scale[1] * f, rot[10] * scale[2] * f, pos[2],
0.0, 0.0, 0.0, 1.0,
]
} else {
let u_inv = [stretch_rot[0], stretch_rot[1], stretch_rot[2], -stretch_rot[3]]; let ut_mat = quat_to_matrix(&u_inv);
let k_ut = [
ut_mat[0] * scale[0], ut_mat[1] * scale[0], ut_mat[2] * scale[0], 0.0,
ut_mat[4] * scale[1], ut_mat[5] * scale[1], ut_mat[6] * scale[1], 0.0,
ut_mat[8] * scale[2], ut_mat[9] * scale[2], ut_mat[10] * scale[2], 0.0,
0.0, 0.0, 0.0, 1.0,
];
let u_mat = quat_to_matrix(stretch_rot);
let u_k_ut = mat44_multiply(&u_mat, &k_ut);
let r_mat = quat_to_matrix(rotation);
let r_u_k_ut = mat44_multiply(&r_mat, &u_k_ut);
[
r_u_k_ut[0] * f, r_u_k_ut[1] * f, r_u_k_ut[2] * f, pos[0],
r_u_k_ut[4] * f, r_u_k_ut[5] * f, r_u_k_ut[6] * f, pos[1],
r_u_k_ut[8] * f, r_u_k_ut[9] * f, r_u_k_ut[10] * f, pos[2],
0.0, 0.0, 0.0, 1.0,
]
}
}
fn evaluate_tm_controller(tm: &TMController, time: f32) -> [f32; 16] {
let pos = tm.pos_controller.as_ref()
.map(|c| evaluate_position(c, time))
.unwrap_or([0.0, 0.0, 0.0]);
let quat = tm.rot_controller.as_ref()
.map(|c| evaluate_rotation(c, time))
.unwrap_or([0.0, 0.0, 0.0, 1.0]);
let scale = tm.scale_controller.as_ref()
.map(|c| evaluate_scale(c, time))
.unwrap_or([1.0, 1.0, 1.0]);
compose_trs(&pos, &quat, &scale)
}
fn evaluate_position(ctrl: &Controller, time: f32) -> [f32; 3] {
match ctrl {
Controller::Leaf(leaf) => evaluate_leaf_point3(leaf, time),
Controller::Compound(compound) => {
let x = compound.x_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
let y = compound.y_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
let z = compound.z_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
[x, y, z]
}
Controller::TM(_) => [0.0, 0.0, 0.0],
}
}
fn evaluate_rotation(ctrl: &Controller, time: f32) -> [f32; 4] {
match ctrl {
Controller::Leaf(leaf) => evaluate_leaf_quat(leaf, time),
Controller::Compound(compound) => {
let x = compound.x_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
let y = compound.y_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
let z = compound.z_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(0.0);
euler_to_quat(x, y, z)
}
Controller::TM(_) => [0.0, 0.0, 0.0, 1.0],
}
}
fn evaluate_scale(ctrl: &Controller, time: f32) -> [f32; 3] {
evaluate_scale_with_stretch(ctrl, time).0
}
fn evaluate_scale_with_stretch(ctrl: &Controller, time: f32) -> ([f32; 3], [f32; 4]) {
match ctrl {
Controller::Leaf(leaf) => {
match leaf.key_type {
KeyType::Scale | KeyType::BezScale => evaluate_leaf_scale_with_stretch(leaf, time),
KeyType::Point3 | KeyType::BezPoint3 => {
(evaluate_leaf_point3(leaf, time), [0.0, 0.0, 0.0, 1.0])
}
KeyType::Scalar | KeyType::BezScalar => {
let s = evaluate_leaf_scalar(leaf, time);
([s, s, s], [0.0, 0.0, 0.0, 1.0])
}
_ => ([1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 1.0]),
}
}
Controller::Compound(compound) => {
let x = compound.x_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(1.0);
let y = compound.y_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(1.0);
let z = compound.z_controller.as_ref()
.map(|c| evaluate_scalar(c, time)).unwrap_or(1.0);
([x, y, z], [0.0, 0.0, 0.0, 1.0])
}
Controller::TM(_) => ([1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 1.0]),
}
}
fn evaluate_scalar(ctrl: &Controller, time: f32) -> f32 {
match ctrl {
Controller::Leaf(leaf) => evaluate_leaf_scalar(leaf, time),
_ => 0.0,
}
}
fn find_keyframe_pair(keys: &[KeyFrame], time: f32, fps: f32) -> (usize, usize, f32) {
if keys.is_empty() {
return (0, 0, 0.0);
}
if keys.len() == 1 {
return (0, 0, 0.0);
}
let frame_time = time * fps;
let mut before = 0;
let mut after = keys.len() - 1;
for (i, key) in keys.iter().enumerate() {
let kf = key.frame() as f32;
if kf <= frame_time {
before = i;
}
if kf >= frame_time && i > before {
after = i;
break;
}
}
if before == after {
return (before, after, 0.0);
}
let before_frame = keys[before].frame() as f32;
let after_frame = keys[after].frame() as f32;
let range = after_frame - before_frame;
let t = if range > 0.0 {
((frame_time - before_frame) / range).clamp(0.0, 1.0)
} else {
0.0
};
(before, after, t)
}
const DEFAULT_FPS: f32 = 30.0;
fn evaluate_leaf_scalar(leaf: &LeafController, time: f32) -> f32 {
if leaf.keys.is_empty() { return 0.0; }
let (i0, i1, t) = find_keyframe_pair(&leaf.keys, time, DEFAULT_FPS);
let v0 = key_scalar_value(&leaf.keys[i0]);
let v1 = key_scalar_value(&leaf.keys[i1]);
lerp(v0, v1, t)
}
fn evaluate_leaf_point3(leaf: &LeafController, time: f32) -> [f32; 3] {
if leaf.keys.is_empty() { return [0.0; 3]; }
let (i0, i1, t) = find_keyframe_pair(&leaf.keys, time, DEFAULT_FPS);
let v0 = key_point3_value(&leaf.keys[i0]);
let v1 = key_point3_value(&leaf.keys[i1]);
lerp3(&v0, &v1, t)
}
fn evaluate_leaf_quat(leaf: &LeafController, time: f32) -> [f32; 4] {
if leaf.keys.is_empty() { return [0.0, 0.0, 0.0, 1.0]; }
let (i0, i1, t) = find_keyframe_pair(&leaf.keys, time, DEFAULT_FPS);
let q0 = key_quat_value(&leaf.keys[i0]);
let q1 = key_quat_value(&leaf.keys[i1]);
slerp(&q0, &q1, t)
}
fn evaluate_leaf_scale(leaf: &LeafController, time: f32) -> [f32; 3] {
evaluate_leaf_scale_with_stretch(leaf, time).0
}
fn evaluate_leaf_scale_with_stretch(leaf: &LeafController, time: f32) -> ([f32; 3], [f32; 4]) {
if leaf.keys.is_empty() { return ([1.0; 3], [0.0, 0.0, 0.0, 1.0]); }
let (i0, i1, t) = find_keyframe_pair(&leaf.keys, time, DEFAULT_FPS);
let (s0, q0) = key_scale_and_stretch(&leaf.keys[i0]);
let (s1, q1) = key_scale_and_stretch(&leaf.keys[i1]);
(lerp3(&s0, &s1, t), slerp(&q0, &q1, t))
}
fn evaluate_leaf_matrix44(leaf: &LeafController, time: f32) -> [f32; 16] {
if leaf.keys.is_empty() { return identity_matrix(); }
let (i0, _i1, _t) = find_keyframe_pair(&leaf.keys, time, DEFAULT_FPS);
key_matrix44_value(&leaf.keys[i0])
}
fn key_scalar_value(key: &KeyFrame) -> f32 {
match key {
KeyFrame::Scalar { value, .. } => *value,
KeyFrame::BezScalar { value, .. } => *value,
_ => 0.0,
}
}
fn key_point3_value(key: &KeyFrame) -> [f32; 3] {
match key {
KeyFrame::Point3 { value, .. } => *value,
KeyFrame::BezPoint3 { value, .. } => *value,
_ => [0.0; 3],
}
}
fn key_quat_value(key: &KeyFrame) -> [f32; 4] {
match key {
KeyFrame::Quat { value, .. } => *value,
KeyFrame::CompressedQuat32 { data, .. } => decompress_quat32(*data),
KeyFrame::CompressedQuat64 { data, .. } => decompress_quat64(*data),
_ => [0.0, 0.0, 0.0, 1.0],
}
}
fn key_scale_value(key: &KeyFrame) -> [f32; 3] {
match key {
KeyFrame::Scale { scale, .. } => *scale,
KeyFrame::BezScale { scale, .. } => *scale,
_ => [1.0; 3],
}
}
fn key_scale_and_stretch(key: &KeyFrame) -> ([f32; 3], [f32; 4]) {
match key {
KeyFrame::Scale { scale, quat, .. } => (*scale, *quat),
KeyFrame::BezScale { scale, quat, .. } => (*scale, *quat),
_ => ([1.0; 3], [0.0, 0.0, 0.0, 1.0]),
}
}
fn key_matrix44_value(key: &KeyFrame) -> [f32; 16] {
match key {
KeyFrame::Matrix44 { value, .. } => *value,
_ => identity_matrix(),
}
}
fn identity_matrix() -> [f32; 16] {
[
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,
]
}
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
fn lerp3(a: &[f32; 3], b: &[f32; 3], t: f32) -> [f32; 3] {
[lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)]
}
fn slerp(q0: &[f32; 4], q1: &[f32; 4], t: f32) -> [f32; 4] {
let mut dot = q0[0]*q1[0] + q0[1]*q1[1] + q0[2]*q1[2] + q0[3]*q1[3];
let mut q1_adj = *q1;
if dot < 0.0 {
dot = -dot;
q1_adj = [-q1[0], -q1[1], -q1[2], -q1[3]];
}
if dot > 0.9995 {
let result = [
lerp(q0[0], q1_adj[0], t),
lerp(q0[1], q1_adj[1], t),
lerp(q0[2], q1_adj[2], t),
lerp(q0[3], q1_adj[3], t),
];
return normalize_quat(&result);
}
let theta = dot.acos();
let sin_theta = theta.sin();
let w0 = ((1.0 - t) * theta).sin() / sin_theta;
let w1 = (t * theta).sin() / sin_theta;
[
q0[0] * w0 + q1_adj[0] * w1,
q0[1] * w0 + q1_adj[1] * w1,
q0[2] * w0 + q1_adj[2] * w1,
q0[3] * w0 + q1_adj[3] * w1,
]
}
fn normalize_quat(q: &[f32; 4]) -> [f32; 4] {
let len = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]).sqrt();
if len > 1e-10 {
[q[0]/len, q[1]/len, q[2]/len, q[3]/len]
} else {
[0.0, 0.0, 0.0, 1.0]
}
}
fn euler_to_quat(x: f32, y: f32, z: f32) -> [f32; 4] {
let (sx, cx) = (x * 0.5).sin_cos();
let (sy, cy) = (y * 0.5).sin_cos();
let (sz, cz) = (z * 0.5).sin_cos();
[
sx * cy * cz - cx * sy * sz,
cx * sy * cz + sx * cy * sz,
cx * cy * sz - sx * sy * cz,
cx * cy * cz + sx * sy * sz,
]
}
fn quat_to_matrix(q: &[f32; 4]) -> [f32; 16] {
let (x, y, z, w) = (q[0], q[1], q[2], q[3]);
let xx = x * x; let yy = y * y; let zz = z * z;
let xy = x * y; let xz = x * z; let yz = y * z;
let wx = w * x; let wy = w * y; let wz = w * z;
[
1.0 - 2.0*(yy+zz), 2.0*(xy-wz), 2.0*(xz+wy), 0.0,
2.0*(xy+wz), 1.0 - 2.0*(xx+zz), 2.0*(yz-wx), 0.0,
2.0*(xz-wy), 2.0*(yz+wx), 1.0 - 2.0*(xx+yy), 0.0,
0.0, 0.0, 0.0, 1.0,
]
}
fn compose_trs(pos: &[f32; 3], quat: &[f32; 4], scale: &[f32; 3]) -> [f32; 16] {
let rot = quat_to_matrix(quat);
[
rot[0] * scale[0], rot[1] * scale[1], rot[2] * scale[2], pos[0],
rot[4] * scale[0], rot[5] * scale[1], rot[6] * scale[2], pos[1],
rot[8] * scale[0], rot[9] * scale[1], rot[10] * scale[2], pos[2],
0.0, 0.0, 0.0, 1.0,
]
}
pub fn blend_matrices(a: &[f32; 16], b: &[f32; 16], weight: f32) -> [f32; 16] {
let mut result = [0.0f32; 16];
for i in 0..16 {
result[i] = a[i] * (1.0 - weight) + b[i] * weight;
}
result
}