#region File Description
//-----------------------------------------------------------------------------
// AvatarBlendedAnimation.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
#endregion
namespace AvatarAnimationBlendingSample
{
public class AvatarBlendedAnimation : IAvatarAnimation
{
#region Properties
///
/// The current position of the bones
///
public ReadOnlyCollection BoneTransforms
{
get
{
return boneTransforms;
}
}
private Matrix[] avatarBones = new Matrix[AvatarRenderer.BoneCount];
private ReadOnlyCollection boneTransforms;
///
/// The current expression of the avatar in the animation
/// We do not blend the expression and only use the current animations
/// expression
///
public AvatarExpression Expression
{
get
{
return currentAnimation.Expression;
}
}
#endregion
#region Fields
// The current animation to play and blend from
private AvatarAnimation currentAnimation;
// The next animation to play and blend target
private AvatarAnimation targetAnimation;
// The amount of time to take to blend between animations
private TimeSpan blendTotalTime = new TimeSpan(0, 0, 0, 0, 250);
// The current time in the blend cycle
private TimeSpan blendCurrentTime;
#endregion
#region Initialization
///
/// Constructor
///
/// The first animation to play
public AvatarBlendedAnimation(AvatarAnimation startingAnimation)
{
// Set the first animation
currentAnimation = startingAnimation;
// Create collection we will use for blended transfroms
boneTransforms = new ReadOnlyCollection(avatarBones);
}
#endregion
#region Update and Play
///
/// Updates the animation and blends with the next animation if there is one.
///
/// Time since the last update
/// Should the animation loop
public void Update(TimeSpan elapsedAnimationTime, bool loop)
{
// Update the current animation
currentAnimation.Update(elapsedAnimationTime, loop);
// Check to see if we need to blend animations or not
if (targetAnimation == null)
{
// We are not blending so copy the current animations bone transforms
currentAnimation.BoneTransforms.CopyTo(avatarBones, 0);
}
else
{
// Update the target animation
targetAnimation.Update(elapsedAnimationTime, loop);
ReadOnlyCollection currentBoneTransforms =
currentAnimation.BoneTransforms;
ReadOnlyCollection targetBoneTransforms =
targetAnimation.BoneTransforms;
// Update the current blended time
blendCurrentTime += elapsedAnimationTime;
// Calculate blend factor
float blendFactor = (float)(blendCurrentTime.TotalSeconds /
blendTotalTime.TotalSeconds);
// Check to see if we are done blending
if (blendFactor >= 1.0f)
{
// Set the current animtion and remove the target animation
currentAnimation = targetAnimation;
targetAnimation = null;
blendFactor = 1.0f;
}
// Variables to hold the rotations and translations for the
// current, target, and blended transforms
Quaternion currentRotation, targetRotation, finalRotation;
Vector3 currentTranslation, targetTranslation, finalTranslation;
// Loop all of the bones in the avatar
for (int i = 0; i < avatarBones.Length; ++i)
{
// Find the rotation of the current and target bone transforms
currentRotation =
Quaternion.CreateFromRotationMatrix(currentBoneTransforms[i]);
targetRotation =
Quaternion.CreateFromRotationMatrix(targetBoneTransforms[i]);
// Calculate the blended rotation from the current to the target
Quaternion.Slerp(ref currentRotation, ref targetRotation,
blendFactor, out finalRotation);
// Find the translation of the current and target bone transforms
currentTranslation = currentBoneTransforms[i].Translation;
targetTranslation = targetBoneTransforms[i].Translation;
// Calculate the blended translation from the current to the target
Vector3.Lerp(ref currentTranslation, ref targetTranslation,
blendFactor, out finalTranslation);
// Build the final bone transform
avatarBones[i] = Matrix.CreateFromQuaternion(finalRotation) *
Matrix.CreateTranslation(finalTranslation);
}
}
}
///
/// Plays a new animation and blends from the current animation.
///
/// Next animation to play
public void Play(AvatarAnimation nextAnimation)
{
// Check to make sure we are not already playing the animation passed in
if (currentAnimation == nextAnimation)
return;
// Set the next animation
targetAnimation = nextAnimation;
// Reset the animation to the start
targetAnimation.CurrentPosition = TimeSpan.Zero;
// Reset the blend current time to zero
blendCurrentTime = TimeSpan.Zero;
}
#endregion
///
/// The current position in the animation
/// Uses the target animation while blending to it
///
public TimeSpan CurrentPosition
{
get
{
if (targetAnimation != null)
return targetAnimation.CurrentPosition;
else
return currentAnimation.CurrentPosition;
}
set
{
if (targetAnimation != null)
targetAnimation.CurrentPosition = value;
else
currentAnimation.CurrentPosition = value;
}
}
///
/// The length of the animation
/// Uses the target animation while blending to it
///
public TimeSpan Length
{
get
{
if (targetAnimation != null)
return targetAnimation.Length;
else
return currentAnimation.Length;
}
}
}
}