#region File Description //----------------------------------------------------------------------------- // AnimationBlender.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.Text; using Microsoft.Xna.Framework; using RobotGameData.GameInterface; #endregion namespace RobotGameData.GameObject { /// /// this class blends two animation key frames. /// However, even when the animation blend is not carried out, /// there must be AnimationBinder with one or more key frame in order /// to play animation. /// Each bone of character corresponds to an AnimationBlender and /// may have one or two AnimationBinder. /// public class AnimationBlender : INamed { #region Fields /// /// animation binder 1 /// AnimationBinder firstBinder = null; /// /// animation binder 2 /// AnimationBinder secondBinder = null; string name = String.Empty; int bindCount = 0; float blendTime = 0.0f; float elapsedTime = 0.0f; #endregion #region Properties public string Name { get { return name; } set { name = value; } } public float BlendTime { get { return blendTime; } } public AnimationBinder AnimationBinder { get { return firstBinder; } } public int BindCount { get { return bindCount; } } public bool IsEmpty { get { return (BindCount == 0); } } #endregion /// /// Constructor. /// public AnimationBlender() { this.firstBinder = new AnimationBinder(); this.secondBinder = new AnimationBinder(); this.bindCount = 0; } /// /// inserts the new KeyFrameSequence, which is specified in the Binder. /// If there are two Binder’s, the first Binder will be gone and /// the second Binder will be moved as the first Binder, /// and the new KeyFrameSequence will be inserted into the second Binder. /// When the every Binder is empty, it will then insert to the first Binder /// /// Animation key frame sequence /// Start time of the animaton /// Blend time of two the animaton /// Time scale of the animaton /// Play mode of the animaton public void AddKeyFrameSequence(KeyFrameSequence keyFrame, float startTime, float blendTime, float timeScaleFactor, AnimPlayMode mode) { this.blendTime = blendTime; this.elapsedTime = startTime; if (this.BlendTime == 0.0f) { ClearAllBinder(); firstBinder.BindKeyFrameSequence(keyFrame, startTime, timeScaleFactor, mode); } else { // If binding above two binders, push out one binder if (this.bindCount == 2) ShiftBinder(); if (this.bindCount == 0) { firstBinder.BindKeyFrameSequence(keyFrame, startTime, timeScaleFactor, mode); } else if (this.bindCount == 1) { secondBinder.BindKeyFrameSequence(keyFrame, startTime, timeScaleFactor, mode); } } this.bindCount++; } /// /// calculates the key frame of the animation of the specified time. /// If there are two bound animations, it will calculate by blending. /// /// animation time /// Matrix of the animation key frame public Matrix GetKeyFrameMatrix(float time) { Matrix keyFrameMatrix = Matrix.Identity; if (firstBinder == null) return keyFrameMatrix; this.elapsedTime += time; // We have to blend animations, if it has multiple binders if (bindCount > 1) { float t = this.elapsedTime / this.blendTime; // Blending finished if (t > 1.0f) { keyFrameMatrix = secondBinder.GetKeyFrameMatrix(time); ShiftBinder(); } // Calculate blending matrix else { KeyFrame[] sourceKeyFrame = new KeyFrame[2]; KeyFrame targetKeyFrame = new KeyFrame(); // Calculate two keyFrame sourceKeyFrame[0] = firstBinder.GetKeyFrame(time); sourceKeyFrame[1] = secondBinder.GetKeyFrame(time); // Calculate blending key frame { // Interpolate translation using two key frame if (sourceKeyFrame[0].HasTranslation && sourceKeyFrame[1].HasTranslation) { targetKeyFrame.Translation = Vector3.Lerp( sourceKeyFrame[0].Translation, sourceKeyFrame[1].Translation, t); targetKeyFrame.HasTranslation = true; } else if (sourceKeyFrame[0].HasTranslation) { targetKeyFrame.Translation = sourceKeyFrame[0].Translation; targetKeyFrame.HasTranslation = true; } else if (sourceKeyFrame[1].HasTranslation) { targetKeyFrame.Translation = sourceKeyFrame[1].Translation; targetKeyFrame.HasTranslation = true; } // Interpolate scale using two key frame if (sourceKeyFrame[0].HasScale && sourceKeyFrame[1].HasScale) { targetKeyFrame.Scale = Vector3.Lerp( sourceKeyFrame[0].Scale, sourceKeyFrame[1].Scale, t); targetKeyFrame.HasScale = true; } else if (sourceKeyFrame[0].HasScale) { targetKeyFrame.Scale = sourceKeyFrame[0].Scale; targetKeyFrame.HasScale = true; } else if (sourceKeyFrame[1].HasScale) { targetKeyFrame.Scale = sourceKeyFrame[1].Scale; targetKeyFrame.HasScale = true; } // Interpolate rotation using two key frame if (sourceKeyFrame[0].HasRotation && sourceKeyFrame[1].HasRotation) { targetKeyFrame.Rotation = Quaternion.Slerp( sourceKeyFrame[0].Rotation, sourceKeyFrame[1].Rotation, t); targetKeyFrame.HasRotation = true; } else if (sourceKeyFrame[0].HasRotation) { targetKeyFrame.Rotation = sourceKeyFrame[0].Rotation; targetKeyFrame.HasRotation = true; } else if (sourceKeyFrame[1].HasRotation) { targetKeyFrame.Rotation = sourceKeyFrame[1].Rotation; targetKeyFrame.HasRotation = true; } } // Final, creates a frame matrix using blending key frame { // Has Rotation ? if (targetKeyFrame.HasRotation) { // Calculates rotation matrix using the quaternion keyFrameMatrix = Matrix.CreateFromQuaternion( targetKeyFrame.Rotation); } // Has translation ? if (targetKeyFrame.HasTranslation) { // Calculates position using keyframe keyFrameMatrix.Translation = targetKeyFrame.Translation; } // Has scale ? if (targetKeyFrame.HasScale) { // Calculates scale value using keyframe keyFrameMatrix = keyFrameMatrix * Matrix.CreateScale(targetKeyFrame.Scale); } } return keyFrameMatrix; } } else { // No blending, or finished blending keyFrameMatrix = firstBinder.GetKeyFrameMatrix(time); } return keyFrameMatrix; } /// /// moves the position of the Binder. /// If there are two binders already, /// the first Binder will be gone and the second Binder will be moved /// to the first Binder. /// private void ShiftBinder() { this.firstBinder.Initialize(); this.secondBinder.CopyTo(firstBinder); this.secondBinder.Initialize(); this.bindCount--; } private void ClearAllBinder() { this.firstBinder.Initialize(); this.secondBinder.Initialize(); this.bindCount = 0; } } }