Skip to content

Commit

Permalink
partial blend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Nan committed Jan 30, 2024
1 parent ef2e111 commit 7001d88
Show file tree
Hide file tree
Showing 22 changed files with 169 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ rkyv = ["dep:rkyv", "dep:bytecheck", "glam/rkyv", "glam/bytecheck"]
[dependencies]
bytecheck = { version = "0.6", optional = true, default-features = false }
glam = { version = "0.25", features = [ "core-simd", "libm" ] }
rkyv = { version = "0.7", optional = true, features = [ "copy", "validation" ] }
rkyv = { version = "0.7.43", optional = true, features = [ "copy", "validation" ] }
static_assertions = "1.1"
thiserror = "1.0"
Binary file added expected/partial_blend/partial_blend+0.00.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.10.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.20.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.30.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.40.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.50.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.60.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.70.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.80.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+0.90.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+1.00.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend+1.10.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend-0.10.rkyv
Binary file not shown.
Binary file added expected/partial_blend/partial_blend_cpp.bin
Binary file not shown.
Binary file added resource/partial_blend/animation_base.ozz
Binary file not shown.
Binary file added resource/partial_blend/animation_partial.ozz
Binary file not shown.
Binary file added resource/partial_blend/skeleton.ozz
Binary file not shown.
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nightly
nightly-2024-01-27
28 changes: 28 additions & 0 deletions src/skeleton.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,34 @@ impl Skeleton {
pub fn index_joint(&self, idx: i16) -> Option<&SoaTransform> {
return self.joint_rest_poses.get(idx as usize);
}

pub fn is_leaf(&self, joint: i16) -> bool {
let next = (joint + 1) as usize;
return next == self.num_joints() || self.joint_parents()[next] != joint;
}

pub fn iter_depth_first<F>(&self, from: i16, mut f: F)
where
F: FnMut(i16, i16),
{
let mut i = if from < 0 { 0 } else { from } as usize;
let mut process = i < self.num_joints();
while process {
f(i as i16, self.joint_parent(i));
i += 1;
process = i < self.num_joints() && self.joint_parent(i) >= from;
}
}

pub fn iter_depth_first_reverse<F>(&self, mut f: F)
where
F: FnMut(i16, i16),
{
for i in (0..self.num_joints()).rev() {
let parent = self.joint_parent(i);
f(i as i16, parent);
}
}
}

#[cfg(test)]
Expand Down
34 changes: 32 additions & 2 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use glam::Mat4;
use std::env::consts::{ARCH, OS};
use std::error::Error;
use std::fs::{self, File};
use std::io::Write;
use std::io::prelude::*;
use std::sync::OnceLock;
use std::{env, mem};
use std::{env, mem, slice};

// f16 -> f32
// ignore overflow, infinite, NaN
Expand All @@ -20,6 +21,35 @@ pub fn f16(f: f32) -> u16 {

static FOLDER: OnceLock<()> = OnceLock::new();

pub fn compare_with_cpp(folder: &str, name: &str, data: &[Mat4], diff: f32) -> Result<(), Box<dyn Error>> {
FOLDER.get_or_init(|| {
fs::create_dir_all(format!("./expected/{}", folder)).unwrap();
fs::create_dir_all(format!("./output/{}", folder)).unwrap();
});

let path = format!("./output/{0}/{1}_rust_{2}_{3}.bin", folder, name, OS, ARCH);
let mut file = File::create(path)?;
let data_size = data.len() * mem::size_of::<Mat4>();
file.write_all(unsafe { slice::from_raw_parts(data.as_ptr() as *mut _, data_size) })?;

let path = format!("./expected/{0}/{1}_cpp.bin", folder, name);
let mut file = File::open(&path)?;
if file.metadata()?.len() != data_size as u64 {
return Err(format!("compare_with_cpp() size:{}", data_size).into());
}

let mut expected: Vec<Mat4> = vec![Mat4::default(); data.len()];
file.read(unsafe { slice::from_raw_parts_mut(expected.as_mut_ptr() as *mut _, data_size) })?;
for i in 0..expected.len() {
if !Mat4::abs_diff_eq(&data[i], expected[i], diff) {
println!("actual: {:?}", data[i]);
println!("expected: {:?}", expected[i]);
return Err(format!("compare_with_cpp() idx:{}", i).into());
}
}
return Ok(());
}

#[cfg(feature = "rkyv")]
pub fn compare_with_rkyv<T>(folder: &str, name: &str, data: &T) -> Result<(), Box<dyn Error>>
where
Expand Down
107 changes: 107 additions & 0 deletions tests/partial_blend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use glam::{Mat4, Vec4};
use ozz_animation_rs::*;
use std::rc::Rc;

#[derive(Debug, PartialEq, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
struct TestData {
ratio: f32,
sample_out_lower: Vec<SoaTransform>,
sample_ctx_lower: SamplingContext,
sample_out_upper: Vec<SoaTransform>,
sample_ctx_upper: SamplingContext,
blending_out: Vec<SoaTransform>,
l2m_out: Vec<Mat4>,
}

#[test]
fn test_partial_blend() {
run_partial_blend(5..=5, |_, data| {
test_utils::compare_with_cpp("partial_blend", "partial_blend", &data.l2m_out, 1e-5).unwrap();
});
}

#[cfg(feature = "rkyv")]
#[test]
fn test_partial_blend_deterministic() {
run_partial_blend(-1..=11, |ratio, data| {
test_utils::compare_with_rkyv("partial_blend", &format!("partial_blend{:+.2}", ratio), data).unwrap();
});
}

fn run_partial_blend<I, T>(range: I, tester: T)
where
I: Iterator<Item = i32>,
T: Fn(f32, &TestData),
{
let skeleton = Rc::new(Skeleton::from_file("./resource/partial_blend/skeleton.ozz").unwrap());
let animation_lower = Rc::new(Animation::from_file("./resource/partial_blend/animation_base.ozz").unwrap());
let animation_upper = Rc::new(Animation::from_file("./resource/partial_blend/animation_partial.ozz").unwrap());

let mut sample_job_lower: SamplingJob = SamplingJob::default();
sample_job_lower.set_animation(animation_lower.clone());
sample_job_lower.set_context(SamplingContext::new(animation_lower.num_tracks()));
let sample_out_lower = ozz_buf(vec![SoaTransform::default(); skeleton.num_soa_joints()]);
sample_job_lower.set_output(sample_out_lower.clone());

let mut sample_job_upper: SamplingJob = SamplingJob::default();
sample_job_upper.set_animation(animation_upper.clone());
sample_job_upper.set_context(SamplingContext::new(animation_upper.num_tracks()));
let sample_out_upper = ozz_buf(vec![SoaTransform::default(); skeleton.num_soa_joints()]);
sample_job_upper.set_output(sample_out_upper.clone());

let mut blending_job = BlendingJob::default();
blending_job.set_skeleton(skeleton.clone());
let blending_out = ozz_buf(vec![SoaTransform::default(); skeleton.num_soa_joints()]);
blending_job.set_output(blending_out.clone());

let mut layer_lower = BlendingLayer::new(sample_out_lower.clone());
layer_lower.weight = 0.5;
layer_lower.joint_weights = vec![Vec4::splat(1.0); skeleton.num_soa_joints()];

let mut layer_upper = BlendingLayer::new(sample_out_upper.clone());
layer_upper.weight = 0.5;
layer_upper.joint_weights = vec![Vec4::splat(0.0); skeleton.num_soa_joints()];

let upper_root = skeleton.joint_by_name("Spine1").unwrap();
skeleton.iter_depth_first(upper_root, |joint, _| {
let joint = joint as usize;
layer_lower.joint_weights[joint / 4][joint % 4] = 0.5;
layer_upper.joint_weights[joint / 4][joint % 4] = 0.5;
});

blending_job.layers_mut().push(layer_lower);
blending_job.layers_mut().push(layer_upper);

let mut l2m_job: LocalToModelJob = LocalToModelJob::default();
l2m_job.set_skeleton(skeleton.clone());
l2m_job.set_input(blending_out.clone());
let l2m_out = ozz_buf(vec![Mat4::default(); skeleton.num_joints()]);
l2m_job.set_output(l2m_out.clone());

for i in range {
let ratio = i as f32 / 10.0;

sample_job_lower.set_ratio(ratio);
sample_job_lower.run().unwrap();

sample_job_upper.set_ratio(ratio);
sample_job_upper.run().unwrap();

blending_job.run().unwrap();

l2m_job.run().unwrap();

tester(
ratio,
&TestData {
ratio,
sample_out_lower: sample_out_lower.vec().unwrap().clone(),
sample_ctx_lower: sample_job_lower.context().unwrap().clone_without_animation_id(),
sample_out_upper: sample_out_upper.vec().unwrap().clone(),
sample_ctx_upper: sample_job_upper.context().unwrap().clone_without_animation_id(),
blending_out: blending_out.vec().unwrap().clone(),
l2m_out: l2m_out.vec().unwrap().clone(),
},
);
}
}

0 comments on commit 7001d88

Please sign in to comment.