#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; } } } }