mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-27 19:54:40 +00:00
Interpolate decoded animation keys across compression boundaries
This commit is contained in:
1 parent
e4311d8ffe
commit
cd48c0c809
2 files changed
+56
-3
No files matched your search
@@ -4,7 +4,7 @@ use skate_core::animation::{
|
||||
output::{self, NativeMatrix, Sqt},
|
||||
playback_tree::PoseCommand,
|
||||
pose_add, pose_blend, pose_mirror,
|
||||
pose_sample::{sample_key_vbr, select_frames},
|
||||
pose_sample::{sample_key, select_frames},
|
||||
pose_trajectory::{self, LoopTransform},
|
||||
};
|
||||
use skate_data::{
|
||||
@@ -163,7 +163,10 @@ fn sample_clip(clip: &ClipFrames, time: f32) -> Result<Vec<Sqt>, String> {
|
||||
.zip(&clip.frames[selection.second])
|
||||
.enumerate()
|
||||
.map(|(bone, (&first, &second))| {
|
||||
let mut sample = sample_key_vbr(sqt(first), sqt(second), selection);
|
||||
// ClipFrames already contains independently decoded keys. The raw
|
||||
// VBR block-boundary compatibility path substitutes the next key,
|
||||
// which skips then holds a frame when applied to this flat cache.
|
||||
let mut sample = sample_key(sqt(first), sqt(second), selection);
|
||||
sample.translation[3] = f32::from_bits(clip.channel_weights[bone]);
|
||||
sample
|
||||
})
|
||||
@@ -178,7 +181,7 @@ fn sample_bone(clip: &ClipFrames, time: f32, bone: usize) -> Result<Sqt, String>
|
||||
true,
|
||||
0.0,
|
||||
)?;
|
||||
let mut sample = sample_key_vbr(
|
||||
let mut sample = sample_key(
|
||||
sqt(clip.frames[selection.first][bone]),
|
||||
sqt(clip.frames[selection.second][bone]),
|
||||
selection,
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decoded_clip_interpolates_across_compression_boundaries() {
|
||||
// Uniform motion makes a skipped key or a held pose unambiguous. Exercise
|
||||
// both full-pose sampling and the trajectory's previous-frame sampler.
|
||||
for fps in [30.0_f32, 60.0] {
|
||||
let clip = ClipFrames {
|
||||
name: "BOUNDARY_RAMP".into(),
|
||||
source_offset: 0,
|
||||
fps_bits: fps.to_bits(),
|
||||
loop_translation_bits: [0; 3],
|
||||
loop_rotation_bits: [0, 0, 0, 1.0_f32.to_bits()],
|
||||
channel_animation: true,
|
||||
channel_weights: vec![0.25_f32.to_bits()],
|
||||
frames: (0..=17)
|
||||
.map(|frame| {
|
||||
let half_angle = frame as f32 * 0.02;
|
||||
vec![
|
||||
[
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
half_angle.sin(),
|
||||
half_angle.cos(),
|
||||
frame as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
]
|
||||
.map(f32::to_bits),
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
for quarter in 0..=68 {
|
||||
let frame = quarter as f32 * 0.25;
|
||||
let time = frame / fps;
|
||||
let pose = sample_clip(&clip, time).unwrap()[0];
|
||||
assert_eq!(pose, sample_bone(&clip, time, 0).unwrap());
|
||||
assert!(
|
||||
(pose.translation[0] - frame).abs() < 0.0001,
|
||||
"{fps} fps, frame {frame}: skipped/held decoded key: {pose:?}"
|
||||
);
|
||||
let angle = 2.0 * pose.rotation[2].atan2(pose.rotation[3]);
|
||||
assert!((angle - frame * 0.04).abs() < 0.0001);
|
||||
assert_eq!(pose.translation[3], 0.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "requires the user's decoded stock animation assets"]
|
||||
fn stock_pose_commands_sample_complete_bones_and_preserve_tree_order() {
|
||||
|
||||
Reference in new issue
Block a user