Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spline animation decoder #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions src/compressedanimation/compressedanimation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use std::fmt::Debug;
use std::rc::Rc;
use crate::{
compressedanimation::splinecompressedanimation::{SplineCompressedAnimation},
error::{Error, Result},
NodeWalker,
};

/// Represent values that may change over time.
pub trait InterpolatableTimeToValueTrait<const COUNT: usize> {
/// Determine whether there are any values.
fn is_empty(&self) -> bool;

/// Determine whether there are more than one value.
fn is_static(&self) -> bool;

/// Get the duration stored.
fn duration(&self) -> f32;

/// Get the significant time points of frames, in seconds.
fn frame_times(&self) -> Vec<f32>;

/// Get the interpolated value over time in seconds.
fn interpolate(&self, t: f32) -> [f32; COUNT];
}

impl<const COUNT: usize> Debug for dyn InterpolatableTimeToValueTrait<COUNT> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "duration={}", self.duration())
}
}

/// Represent an animation consisting of TRS components.
pub trait AnimationTrait {
/// Get the duration of the animation.
fn duration(&self) -> f32;

/// Get the number of tracks(bones) stored in this animation.
fn num_tracks(&self) -> usize;

/// Get the significant time points of frames, in seconds.
fn frame_times(&self) -> Vec<f32>;

/// Get the translation component of this animation of specified track(bone).
fn translation(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<3>>;

/// Get the rotation component of this animation of specified track(bone).
fn rotation(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<4>>;

/// Get the scale component of this animation of specified track(bone).
fn scale(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<3>>;
}

impl Debug for dyn AnimationTrait {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "duration={}, num_tracks={}", self.duration(), self.num_tracks())
}
}

#[derive(Debug)]
pub struct BaseCompressedAnimation {
pub duration: f32,
pub num_tracks: usize,
}

impl BaseCompressedAnimation {
pub fn new(node: &NodeWalker) -> Result<Self> {
if !node.is_or_inherited_from("hkaAnimation") {
return Err(Error::Invalid(
"Given node is not a valid animation.".into()
));
}

let duration = node.field_f32("duration", None)?;
let num_transform_tracks = node.field_i32("numberOfTransformTracks", None)? as usize;

return Ok(Self {
duration,
num_tracks: num_transform_tracks,
});
}
}

/// Create a new animation from the given hkaAnimation node.
pub fn read_animation(animation_node: &NodeWalker) -> Result<Rc<dyn AnimationTrait>> {
let base = BaseCompressedAnimation::new(animation_node)?;

if animation_node.is_or_inherited_from("hkaSplineCompressedAnimation") {
Ok(Rc::new(SplineCompressedAnimation::new(animation_node, base)?))
} else {
Err(Error::Invalid(format!("Unsupported animation type.")))
}
}

/// Create a new vector of animations from the given root node of a tagfile.
pub fn new_from_root(root_node: &NodeWalker) -> Result<Vec<Rc<dyn AnimationTrait>>> {
root_node
.field_node_vec("namedVariants")?.first()
.ok_or(Error::Invalid("namedVariants node contains no children.".into()))?
.field_node("variant")?
.field_node_vec("animations")?
.iter()
.map(|animation_node| read_animation(animation_node))
.collect()
}
137 changes: 137 additions & 0 deletions src/compressedanimation/concatanimation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::rc::Rc;
use crate::{
compressedanimation::AnimationTrait,
compressedanimation::compressedanimation::InterpolatableTimeToValueTrait,
error::{Error, Result},
};

#[derive(Debug)]
pub struct ConcatAnimation {
parts: Vec<Rc<dyn AnimationTrait>>,
translations: Vec<Rc<ConcatInterpolatableTimeToValue<3>>>,
rotations: Vec<Rc<ConcatInterpolatableTimeToValue<4>>>,
scales: Vec<Rc<ConcatInterpolatableTimeToValue<3>>>,
}

impl ConcatAnimation {
pub fn new(parts: Vec<Rc<dyn AnimationTrait>>) -> Result<Self> {
let mut translations = Vec::<Rc<ConcatInterpolatableTimeToValue<3>>>::new();
let mut rotations = Vec::<Rc<ConcatInterpolatableTimeToValue<4>>>::new();
let mut scales = Vec::<Rc<ConcatInterpolatableTimeToValue<3>>>::new();

if !parts.is_empty() {
if parts.iter().skip(1).any(|x| x.duration() != parts[0].duration()) {
return Err(Error::Invalid("Durations of all parts must be equal.".into()))
}

let num_tracks = parts[0].num_tracks();
if parts.iter().skip(1).any(|x| x.num_tracks() != num_tracks) {
return Err(Error::Invalid("Number of tracks of all parts must be equal.".into()))
}

for track in 0..num_tracks {
translations.push(ConcatInterpolatableTimeToValue::new(parts.iter().map(|x| x.translation(track) as Rc<dyn InterpolatableTimeToValueTrait<3>>).collect())?.into());
rotations.push(ConcatInterpolatableTimeToValue::new(parts.iter().map(|x| x.rotation(track) as Rc<dyn InterpolatableTimeToValueTrait<4>>).collect())?.into());
scales.push(ConcatInterpolatableTimeToValue::new(parts.iter().map(|x| x.scale(track) as Rc<dyn InterpolatableTimeToValueTrait<3>>).collect())?.into());
}
}

Ok(Self {
parts,
translations,
rotations,
scales
})
}
}

impl AnimationTrait for ConcatAnimation {
fn duration(&self) -> f32 {
if self.parts.is_empty() { 0f32 } else { self.parts[0].duration() }
}

fn num_tracks(&self) -> usize {
if self.parts.is_empty() { 0 } else { self.parts[0].num_tracks() }
}

fn frame_times(&self) -> Vec<f32> {
let mut res = Vec::<f32>::new();
let mut t = 0f32;
for part in &self.parts {
res.extend(part.frame_times().iter().map(|x| x + t));
t += part.duration();
}
res
}

fn translation(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<3>> {
self.translations[track_index].clone()
}

fn rotation(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<4>> {
self.rotations[track_index].clone()
}

fn scale(&self, track_index: usize) -> Rc<dyn InterpolatableTimeToValueTrait<3>> {
self.scales[track_index].clone()
}
}

#[derive(Debug)]
struct ConcatInterpolatableTimeToValue<const COUNT: usize> {
parts: Vec<Rc<dyn InterpolatableTimeToValueTrait<COUNT>>>,
}

impl<const COUNT: usize> ConcatInterpolatableTimeToValue<COUNT> {
fn new(parts: Vec<Rc<dyn InterpolatableTimeToValueTrait<COUNT>>>) -> Result<Self> {
if parts.is_empty() {
return Err(Error::Invalid("There must be at least one part.".into()))
}

if parts.iter().any(|x| x.duration() != parts[0].duration()) {
return Err(Error::Invalid("All parts must have the same duration.".into()))
}

Ok(Self {
parts,
})
}
}

impl<const COUNT: usize> InterpolatableTimeToValueTrait<COUNT> for ConcatInterpolatableTimeToValue<COUNT> {
fn is_empty(&self) -> bool {
self.parts.iter().all(|x| x.is_empty())
}

fn is_static(&self) -> bool {
self.parts.iter().all(|x| x.is_static())
}

fn duration(&self) -> f32 {
self.parts.iter().map(|x| x.duration()).sum()
}

fn frame_times(&self) -> Vec<f32> {
let mut res = Vec::<f32>::new();

let mut t = 0f32;
for part in &self.parts {
res.extend(part.frame_times().iter().map(|x| x + t));
t += part.duration();
}

res
}

fn interpolate(&self, mut t: f32) -> [f32; COUNT] {
loop {
for part in &self.parts {
if t < part.duration() {
return part.interpolate(t)
}

t -= part.duration()
}
}
}
}
7 changes: 7 additions & 0 deletions src/compressedanimation/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Utilities for decompressing compressed TRS animation.

mod compressedanimation;
mod splinecompressedanimation;
mod concatanimation;

pub use compressedanimation::{AnimationTrait, InterpolatableTimeToValueTrait, read_animation, new_from_root};
Loading