#region File Description //----------------------------------------------------------------------------- // CustomAvatarAnimationPlayer.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.GamerServices; #endregion namespace CustomAvatarAnimation { /// /// This type implements an animation at runtime, including the /// current state and updating that state for time. /// public class CustomAvatarAnimationPlayer : CustomAvatarAnimationData, IAvatarAnimation { #region Current Animation State /// /// The current keyframe in the animation. /// private int currentKeyframe = 0; /// /// The current expression keyframe in the animation /// private int currentExpressionKeyframe = 0; /// /// The current temporal position in the animation. /// private TimeSpan currentPosition = TimeSpan.Zero; /// /// The current temporal position in the animation. /// public TimeSpan CurrentPosition { get { return currentPosition; } set { currentPosition = value; // Set the current keyframe to 0 since we don't know where we are // in the list of keyframes. The next update will set the correct // keyframe. currentKeyframe = 0; currentExpressionKeyframe = 0; // update the animation for the new position, // elapsing zero additional time Update(TimeSpan.Zero, false); } } /// /// The current position of the bones as the current time in the animation. /// Matrix[] avatarBoneTransforms = new Matrix[AvatarRenderer.BoneCount]; /// /// The current position of the bones as the current time in the animation. /// public ReadOnlyCollection BoneTransforms { get { return boneTransforms; } } private ReadOnlyCollection boneTransforms; /// /// Returns the avatars current expression /// public AvatarExpression Expression { get { return avatarExpression; } } AvatarExpression avatarExpression = new AvatarExpression(); #endregion #region Initialization /// /// Constructs a new CustomAvatarAnimationPlayer object. /// /// The name of the animation. /// The length of the animation. /// The keyframes in the animation. public CustomAvatarAnimationPlayer(string name, TimeSpan length, List keyframes, List expressionKeyframes) : base(name, length, keyframes, expressionKeyframes) { // Reset the current bone transforms for (int i = 0; i < AvatarRenderer.BoneCount; i++) { avatarBoneTransforms[i] = Matrix.Identity; } boneTransforms = new ReadOnlyCollection(avatarBoneTransforms); // Update the current bone transforms to the first position in the animation Update(TimeSpan.Zero, false); } #endregion #region Updating /// /// Updates the current position of the animation. /// /// The elapsed time since the last update. /// If true, the animation will loop. public void Update(TimeSpan timeSpan, bool loop) { // Add the elapsed time to the current time. currentPosition += timeSpan; // Check current time against the length if (currentPosition > Length) { if (loop) { // Find the right time in the new loop iteration while (currentPosition > Length) { currentPosition -= Length; } // Set the keyframe to 0. currentKeyframe = 0; currentExpressionKeyframe = 0; } else { // If the animation is not looping, // then set the time to the end of the animation. currentPosition = Length; } } // Check to see if we are less than zero else if (currentPosition < TimeSpan.Zero) { if (loop) { // If the animation is looping, // then find the right time in the new loop iteration while (currentPosition < TimeSpan.Zero) { currentPosition += Length; } // Set the keyframe to the last keyframe currentKeyframe = Keyframes.Count - 1; currentExpressionKeyframe = ExpressionKeyframes.Count - 1; } else { // If the animation is not looping, // then set the time to the beginning of the animation. currentPosition = TimeSpan.Zero; } } // Update the bone transforms based on the current time. UpdateBoneTransforms(timeSpan >= TimeSpan.Zero); // Update the expression UpdateAvatarExpression(timeSpan >= TimeSpan.Zero); } /// /// Updates the transforms with the correct keyframes based on the current time. /// /// /// If true, the animation is playing forward; otherwise, it is playing backwards /// private void UpdateBoneTransforms(bool playingForward) { if (playingForward) { while (currentKeyframe < Keyframes.Count) { // Get the current keyframe AvatarKeyFrame keyframe = Keyframes[currentKeyframe]; // Stop when we've read up to the current time. if (keyframe.Time >= currentPosition) break; // Apply the current keyframe's transform to the bone array. avatarBoneTransforms[keyframe.Bone] = keyframe.Transform; // Move the current keyframe forward. currentKeyframe++; } } else { while (currentKeyframe >= 0) { // Get the current keyframe AvatarKeyFrame keyframe = Keyframes[currentKeyframe]; // Stop when we've read back to the current time. if (keyframe.Time <= currentPosition) break; // Apply the current keyframe's transform to the bone array. avatarBoneTransforms[keyframe.Bone] = keyframe.Transform; // Move the current keyframe backwards. currentKeyframe--; } } } /// /// Updates the expression with the correct keyframes based on the current time. /// /// /// If true, the animation is playing forward; otherwise, it is playing backwards /// private void UpdateAvatarExpression(bool playingForward) { // Check to see if we have an expression animation if (ExpressionKeyframes == null || ExpressionKeyframes.Count == 0) return; if (playingForward) { while (currentExpressionKeyframe < ExpressionKeyframes.Count) { // Get the current keyframe AvatarExpressionKeyFrame keyframe = ExpressionKeyframes[currentExpressionKeyframe]; // Stop when we've read up to the current time. if (keyframe.Time >= currentPosition) break; // Set the current expression avatarExpression = keyframe.Expression; // Move the current keyframe forward. currentExpressionKeyframe++; } } else { while (currentExpressionKeyframe >= 0) { // Get the current keyframe AvatarExpressionKeyFrame keyframe = ExpressionKeyframes[currentExpressionKeyframe]; // Stop when we've read back to the current time. if (keyframe.Time <= currentPosition) break; // Set the current expression avatarExpression = keyframe.Expression; // Move the current keyframe backwards. currentExpressionKeyframe--; } } } #endregion } }