#region File Description
//-----------------------------------------------------------------------------
// AnimationPlayer.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
#endregion
namespace SkinnedModel
{
///
/// The animation player is in charge of decoding bone position
/// matrices from an animation clip.
///
public class AnimationPlayer
{
#region Fields
// Information about the currently playing animation clip.
AnimationClip currentClipValue;
TimeSpan currentTimeValue;
int currentKeyframe;
// Current animation transform matrices.
Matrix[] boneTransforms;
Matrix[] worldTransforms;
Matrix[] skinTransforms;
// Backlink to the bind pose and skeleton hierarchy data.
SkinningData skinningDataValue;
#endregion
///
/// Constructs a new animation player.
///
public AnimationPlayer(SkinningData skinningData)
{
if (skinningData == null)
throw new ArgumentNullException("skinningData");
skinningDataValue = skinningData;
boneTransforms = new Matrix[skinningData.BindPose.Count];
worldTransforms = new Matrix[skinningData.BindPose.Count];
skinTransforms = new Matrix[skinningData.BindPose.Count];
}
///
/// Starts decoding the specified animation clip.
///
public void StartClip(AnimationClip clip)
{
if (clip == null)
throw new ArgumentNullException("clip");
currentClipValue = clip;
currentTimeValue = TimeSpan.Zero;
currentKeyframe = 0;
// Initialize bone transforms to the bind pose.
skinningDataValue.BindPose.CopyTo(boneTransforms, 0);
}
///
/// Advances the current animation position.
///
public void Update(TimeSpan time, bool relativeToCurrentTime,
Matrix rootTransform)
{
UpdateBoneTransforms(time, relativeToCurrentTime);
UpdateWorldTransforms(rootTransform);
UpdateSkinTransforms();
}
///
/// Helper used by the Update method to refresh the BoneTransforms data.
///
public void UpdateBoneTransforms(TimeSpan time, bool relativeToCurrentTime)
{
if (currentClipValue == null)
throw new InvalidOperationException(
"AnimationPlayer.Update was called before StartClip");
// Update the animation position.
if (relativeToCurrentTime)
{
time += currentTimeValue;
// If we reached the end, loop back to the start.
while (time >= currentClipValue.Duration)
time -= currentClipValue.Duration;
}
if ((time < TimeSpan.Zero) || (time >= currentClipValue.Duration))
throw new ArgumentOutOfRangeException("time");
// If the position moved backwards, reset the keyframe index.
if (time < currentTimeValue)
{
currentKeyframe = 0;
skinningDataValue.BindPose.CopyTo(boneTransforms, 0);
}
currentTimeValue = time;
// Read keyframe matrices.
IList keyframes = currentClipValue.Keyframes;
while (currentKeyframe < keyframes.Count)
{
Keyframe keyframe = keyframes[currentKeyframe];
// Stop when we've read up to the current time position.
if (keyframe.Time > currentTimeValue)
break;
// Use this keyframe.
boneTransforms[keyframe.Bone] = keyframe.Transform;
currentKeyframe++;
}
}
///
/// Helper used by the Update method to refresh the WorldTransforms data.
///
public void UpdateWorldTransforms(Matrix rootTransform)
{
// Root bone.
worldTransforms[0] = boneTransforms[0] * rootTransform;
// Child bones.
for (int bone = 1; bone < worldTransforms.Length; bone++)
{
int parentBone = skinningDataValue.SkeletonHierarchy[bone];
worldTransforms[bone] = boneTransforms[bone] *
worldTransforms[parentBone];
}
}
///
/// Helper used by the Update method to refresh the SkinTransforms data.
///
public void UpdateSkinTransforms()
{
for (int bone = 0; bone < skinTransforms.Length; bone++)
{
skinTransforms[bone] = skinningDataValue.InverseBindPose[bone] *
worldTransforms[bone];
}
}
///
/// Gets the current bone transform matrices, relative to their parent bones.
///
public Matrix[] GetBoneTransforms()
{
return boneTransforms;
}
///
/// Gets the current bone transform matrices, in absolute format.
///
public Matrix[] GetWorldTransforms()
{
return worldTransforms;
}
///
/// Gets the current bone transform matrices,
/// relative to the skinning bind pose.
///
public Matrix[] GetSkinTransforms()
{
return skinTransforms;
}
///
/// Gets the clip currently being decoded.
///
public AnimationClip CurrentClip
{
get { return currentClipValue; }
}
///
/// Gets the current play position.
///
public TimeSpan CurrentTime
{
get { return currentTimeValue; }
}
}
}