openusd 0.4.0

Rust native USD library
Documentation
//! Static (time-independent) resolver bundle for a `Skeleton`.
//!
//! Mirrors the *static* surface of Pixar's `UsdSkelSkeletonQuery`:
//! topology, joint order, world-space bind transforms (per spec
//! `bindTransforms` is already world-space), pre-computed inverse-bind
//! matrices, and the helpers that compose joint-local transforms into
//! skel-space and combine them with current poses to produce skinning
//! matrices.
//!
//! Time-dependent methods (`ComputeJointLocalTransforms(time)`,
//! `ComputeSkinningTransforms(time)`, etc.) require evaluating a
//! bound `SkelAnimation` at a specific stage time. That belongs to
//! the anim layer and is intentionally NOT provided here — once
//! anim lands, callers will feed its pre-evaluated local transforms
//! into [`SkeletonResolver::compute_skinning_transforms_from_local`]
//! below.

use super::skinning::{
    compute_inverse_bind_transforms, compute_skinning_transforms as math_skin_xforms, joint_local_to_skel_space,
    joint_skel_to_world,
};
use super::topology::Topology;
use super::types::ReadSkeleton;

/// Resolved, time-independent view of one `Skeleton` prim.
///
/// Build with [`SkeletonResolver::new`]; the constructor pre-computes
/// the topology and the inverse-bind matrices that every skinning
/// pipeline reuses every frame.
#[derive(Debug, Clone)]
pub struct SkeletonResolver {
    skeleton: ReadSkeleton,
    topology: Topology,
    inverse_bind_transforms: Vec<[f64; 16]>,
}

impl SkeletonResolver {
    /// Pre-compute topology + inverse-bind matrices for `skeleton`.
    /// Joints with a singular bind transform fall back to the identity
    /// inverse — same as Pixar's reference implementation.
    pub fn new(skeleton: ReadSkeleton) -> Self {
        let topology = Topology::from_joint_paths(&skeleton.joints);
        let inverse_bind_transforms = compute_inverse_bind_transforms(&skeleton.bind_transforms);
        Self {
            skeleton,
            topology,
            inverse_bind_transforms,
        }
    }

    /// Borrow the decoded `Skeleton` data.
    pub fn skeleton(&self) -> &ReadSkeleton {
        &self.skeleton
    }

    /// Borrow the skeleton's path-encoded joint order.
    pub fn joint_order(&self) -> &[String] {
        &self.skeleton.joints
    }

    /// Borrow the topology — handy for callers that want to do their
    /// own walks (e.g. when computing a name→index mapping).
    pub fn topology(&self) -> &Topology {
        &self.topology
    }

    /// World-space joint bind transforms — verbatim from
    /// `Skeleton.bindTransforms`.
    pub fn joint_world_bind_transforms(&self) -> &[[f64; 16]] {
        &self.skeleton.bind_transforms
    }

    /// Pre-computed `inverse(bindTransform)` per joint. Used by
    /// [`compute_skinning_transforms_from_world`] /
    /// [`compute_skinning_transforms_from_local`].
    pub fn inverse_bind_transforms(&self) -> &[[f64; 16]] {
        &self.inverse_bind_transforms
    }

    /// Compose joint-local transforms into skel-space transforms.
    pub fn joint_local_to_skel_space(&self, local: &[[f64; 16]]) -> Vec<[f64; 16]> {
        joint_local_to_skel_space(local, &self.topology)
    }

    /// Lift skel-space joint transforms into world space using the
    /// supplied SkelRoot local-to-world matrix.
    pub fn joint_skel_to_world(&self, skel: &[[f64; 16]], skel_local_to_world: &[f64; 16]) -> Vec<[f64; 16]> {
        joint_skel_to_world(skel, skel_local_to_world)
    }

    /// Compute skinning transforms (`inverse(bind) · joint_world`)
    /// from world-space joint transforms.
    pub fn compute_skinning_transforms_from_world(&self, joint_world: &[[f64; 16]]) -> Vec<[f64; 16]> {
        math_skin_xforms(joint_world, &self.inverse_bind_transforms)
    }

    /// Compute skinning transforms from joint-local transforms in one
    /// step: compose to skel-space, lift to world, then multiply by
    /// the inverse-bind. Use when you have only local poses (typical
    /// case after evaluating a SkelAnimation).
    pub fn compute_skinning_transforms_from_local(
        &self,
        joint_local: &[[f64; 16]],
        skel_local_to_world: &[f64; 16],
    ) -> Vec<[f64; 16]> {
        let skel = self.joint_local_to_skel_space(joint_local);
        let world = self.joint_skel_to_world(&skel, skel_local_to_world);
        math_skin_xforms(&world, &self.inverse_bind_transforms)
    }

    /// Convenience: the rest-pose joint-local transforms a caller
    /// can hand to [`compute_skinning_transforms_from_local`] when
    /// no SkelAnimation is bound (or the consumer wants the rest
    /// pose for preview / fallback).
    pub fn rest_pose_local(&self) -> &[[f64; 16]] {
        &self.skeleton.rest_transforms
    }

    /// Rest pose composed into skel-space — equivalent to feeding
    /// `rest_pose_local()` into [`joint_local_to_skel_space`].
    pub fn rest_pose_skel_space(&self) -> Vec<[f64; 16]> {
        joint_local_to_skel_space(&self.skeleton.rest_transforms, &self.topology)
    }
}