use anyhow::Result;
use crate::gf;
use crate::usd::SchemaBase;
use super::anim_mapper::AnimMapper;
use super::schema::SkelBindingAPI;
use super::skinning::{rigid_skinning_transform, skin_normals_lbs, skin_points_lbs};
use super::{InfluenceInterpolation, SkinningMethod};
#[derive(Debug, Clone)]
pub struct SkinningResolver {
prim: String,
joint_indices: Vec<i32>,
joint_weights: Vec<f32>,
interpolation: InfluenceInterpolation,
skinning_method: SkinningMethod,
elements_per_element: i32,
mapper: AnimMapper,
geom_bind_transform: gf::Matrix4d,
}
impl SkinningResolver {
pub fn from_binding(binding: &SkelBindingAPI, skeleton_joint_order: &[String]) -> Result<Self> {
let joint_subset = binding.joint_subset()?;
let mapper = if joint_subset.is_empty() {
AnimMapper::new(skeleton_joint_order, skeleton_joint_order)
} else {
AnimMapper::new(skeleton_joint_order, &joint_subset)
};
Ok(Self {
prim: binding.path().as_str().to_string(),
joint_indices: binding.joint_indices()?,
joint_weights: binding.joint_weights()?,
interpolation: binding.interpolation()?,
skinning_method: binding.skinning_method()?,
elements_per_element: binding.elements_per_element()?,
mapper,
geom_bind_transform: binding.geom_bind_transform()?.unwrap_or(gf::Matrix4d::IDENTITY),
})
}
pub fn prim(&self) -> &str {
&self.prim
}
pub fn is_rigidly_deformed(&self) -> bool {
self.interpolation == InfluenceInterpolation::Constant
}
pub fn num_influences_per_component(&self) -> usize {
self.elements_per_element.max(1) as usize
}
pub fn has_joint_influences(&self) -> bool {
!self.joint_indices.is_empty() && !self.joint_weights.is_empty()
}
pub fn joint_order_len(&self) -> usize {
self.mapper.target_len()
}
pub fn geom_bind_transform(&self) -> gf::Matrix4d {
self.geom_bind_transform
}
pub fn skinning_method(&self) -> SkinningMethod {
self.skinning_method
}
pub fn remap_skinning_xforms(&self, skel_skinning_xforms: &[gf::Matrix4d]) -> Vec<gf::Matrix4d> {
if self.mapper.is_identity() {
return skel_skinning_xforms.to_vec();
}
(0..self.mapper.target_len())
.map(|t| match self.mapper.source_index(t) {
Some(i) => skel_skinning_xforms[i],
None => gf::Matrix4d::IDENTITY,
})
.collect()
}
pub fn compute_skinned_points(
&self,
points: &[gf::Vec3f],
skel_skinning_xforms: &[gf::Matrix4d],
) -> Vec<gf::Vec3f> {
assert!(
!self.is_rigidly_deformed(),
"compute_skinned_points called on rigidly-deformed binding"
);
let mesh_xforms = self.remap_skinning_xforms(skel_skinning_xforms);
skin_points_lbs(
points,
&self.joint_indices,
&self.joint_weights,
self.num_influences_per_component(),
self.geom_bind_transform,
&mesh_xforms,
)
}
pub fn compute_skinned_normals(
&self,
normals: &[gf::Vec3f],
skel_skinning_xforms: &[gf::Matrix4d],
) -> Vec<gf::Vec3f> {
assert!(
!self.is_rigidly_deformed(),
"compute_skinned_normals called on rigidly-deformed binding"
);
let mesh_xforms = self.remap_skinning_xforms(skel_skinning_xforms);
skin_normals_lbs(
normals,
&self.joint_indices,
&self.joint_weights,
self.num_influences_per_component(),
self.geom_bind_transform,
&mesh_xforms,
)
}
pub fn compute_rigid_transform(&self, skel_skinning_xforms: &[gf::Matrix4d]) -> gf::Matrix4d {
assert!(
self.is_rigidly_deformed(),
"compute_rigid_transform called on per-vertex binding"
);
let mesh_xforms = self.remap_skinning_xforms(skel_skinning_xforms);
rigid_skinning_transform(
&self.joint_indices,
&self.joint_weights,
self.num_influences_per_component(),
self.geom_bind_transform,
&mesh_xforms,
)
}
}