use anyhow::Result;
use crate::sdf::Path;
use crate::usd::{Prim, Stage};
use crate::schemas::skel::tokens::{A_BIND_TRANSFORMS, A_JOINTS, A_JOINT_NAMES, A_REST_TRANSFORMS, T_SKELETON};
use super::common::{author_uniform_matrix4d_array, author_uniform_token_array};
pub fn define_skeleton<'s>(stage: &'s Stage, path: impl Into<Path>) -> Result<SkeletonAuthor<'s>> {
let prim = stage.define_prim(path)?.set_type_name(T_SKELETON)?;
Ok(SkeletonAuthor { prim })
}
pub struct SkeletonAuthor<'s> {
prim: Prim<'s>,
}
impl<'s> SkeletonAuthor<'s> {
pub fn into_prim(self) -> Prim<'s> {
self.prim
}
pub fn set_joints<I, S>(self, joints: I) -> Result<Self>
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
let tokens: Vec<String> = joints.into_iter().map(Into::into).collect();
author_uniform_token_array(self.prim.stage(), self.prim.path(), A_JOINTS, tokens)?;
Ok(self)
}
pub fn set_joint_names<I, S>(self, names: I) -> Result<Self>
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
let tokens: Vec<String> = names.into_iter().map(Into::into).collect();
author_uniform_token_array(self.prim.stage(), self.prim.path(), A_JOINT_NAMES, tokens)?;
Ok(self)
}
pub fn set_bind_transforms(self, transforms: impl Into<Vec<[f64; 16]>>) -> Result<Self> {
author_uniform_matrix4d_array(
self.prim.stage(),
self.prim.path(),
A_BIND_TRANSFORMS,
transforms.into(),
)?;
Ok(self)
}
pub fn set_rest_transforms(self, transforms: impl Into<Vec<[f64; 16]>>) -> Result<Self> {
author_uniform_matrix4d_array(
self.prim.stage(),
self.prim.path(),
A_REST_TRANSFORMS,
transforms.into(),
)?;
Ok(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sdf;
fn identity() -> [f64; 16] {
let mut m = [0.0; 16];
m[0] = 1.0;
m[5] = 1.0;
m[10] = 1.0;
m[15] = 1.0;
m
}
fn translated(y: f64) -> [f64; 16] {
let mut m = identity();
m[13] = y;
m
}
#[test]
fn define_skeleton_writes_all_spec_arrays() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
define_skeleton(&stage, sdf::path("/Rig")?)?
.set_joints(["Root", "Root/Hip", "Root/Hip/Knee"])?
.set_joint_names(["root", "hip", "knee"])?
.set_bind_transforms(vec![identity(), translated(1.0), translated(2.0)])?
.set_rest_transforms(vec![identity(), translated(1.0), translated(1.0)])?;
let rig = sdf::path("/Rig")?;
assert_eq!(stage.type_name(&rig)?.as_deref(), Some(T_SKELETON));
let joints = stage.field::<sdf::Value>("/Rig.joints", sdf::FieldKey::Default)?;
assert_eq!(
joints,
Some(sdf::Value::TokenVec(vec![
"Root".into(),
"Root/Hip".into(),
"Root/Hip/Knee".into(),
])),
);
assert_eq!(
stage.field::<sdf::Value>("/Rig.joints", sdf::FieldKey::Variability)?,
Some(sdf::Value::Variability(sdf::Variability::Uniform)),
);
let names = stage.field::<sdf::Value>("/Rig.jointNames", sdf::FieldKey::Default)?;
assert_eq!(
names,
Some(sdf::Value::TokenVec(vec!["root".into(), "hip".into(), "knee".into()])),
);
let bind = stage.field::<sdf::Value>("/Rig.bindTransforms", sdf::FieldKey::Default)?;
let rest = stage.field::<sdf::Value>("/Rig.restTransforms", sdf::FieldKey::Default)?;
match bind {
Some(sdf::Value::Matrix4dVec(v)) => {
assert_eq!(v.len(), 3);
assert_eq!(v[1][13], 1.0);
}
other => panic!("expected matrix4d[] for bindTransforms, got {other:?}"),
}
match rest {
Some(sdf::Value::Matrix4dVec(v)) => assert_eq!(v.len(), 3),
other => panic!("expected matrix4d[] for restTransforms, got {other:?}"),
}
Ok(())
}
#[test]
fn jointnames_are_optional() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
define_skeleton(&stage, sdf::path("/Rig")?)?
.set_joints(["A", "B"])?
.set_bind_transforms(vec![identity(), identity()])?;
assert!(stage
.field::<sdf::Value>("/Rig.jointNames", sdf::FieldKey::Default)?
.is_none());
Ok(())
}
}