/Rendering/Models/Animation.cs
C# | 498 lines | 265 code | 65 blank | 168 comment | 21 complexity | d109824764e68510e39519425d38d1ed MD5 | raw file
Possible License(s): Apache-2.0
- using Delta.ContentSystem.Rendering;
- using Delta.Engine.Game;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.Rendering.Models
- {
- /// <summary>
- /// Simple animation class for mesh skinning.
- /// </summary>
- public class Animation
- {
- #region AnimationInfo Struct
- /// <summary>
- /// Animation info
- /// </summary>
- private struct AnimationInfo
- {
- #region Internal
-
- #region Name (Internal)
- /// <summary>
- /// Name of the current animation.
- /// </summary>
- internal string Name;
- #endregion
-
- #region StartIndex (Internal)
- /// <summary>
- /// The start frame index of the active animation.
- /// </summary>
- internal int StartIndex;
- #endregion
-
- #region EndIndex (Internal)
- /// <summary>
- /// The end frame index of the active animation.
- /// </summary>
- internal int EndIndex;
- #endregion
-
- #region FrameRate (Internal)
- /// <summary>
- /// The speed that the animation will run in. (Frames per second)
- /// </summary>
- internal float FrameRate;
- #endregion
-
- #region DefaultFrameRate (Internal)
- /// <summary>
- /// Default framerate of current animation,
- /// which was set at SetCurrentAnimation.
- /// </summary>
- internal float DefaultFrameRate;
- #endregion
-
- #region Loop (Internal)
- /// <summary>
- /// True if the animation should only run once then stop.
- /// </summary>
- internal bool Loop;
- #endregion
-
- #region FramesCount (Internal)
- /// <summary>
- /// Number of animation frames for currently played animation
- /// </summary>
- internal int FramesCount;
- #endregion
-
- #region FrameIndex (Internal)
- /// <summary>
- /// The current frame index of the active animation.
- /// </summary>
- internal int FrameIndex;
- #endregion
-
- #region FrameTimer (Internal)
- /// <summary>
- /// The time in seconds since the last frame index increase.
- /// </summary>
- internal float FrameTimer;
- #endregion
-
- #region BoneMatrices (Internal)
- /// <summary>
- /// Current bone matrices of the animation.
- /// </summary>
- internal Matrix[] BoneMatrices;
- #endregion
-
- #endregion
- }
- #endregion
-
- #region Constants
- /// <summary>
- /// The frame rate as a constant for now.
- /// </summary>
- private const int FrameRate = 30;
- #endregion
-
- #region Duration (Public)
- /// <summary>
- /// Length of the Animation in seconds.
- /// </summary>
- public float Duration
- {
- get;
- set;
- }
- #endregion
-
- #region FrameCount (Public)
- /// <summary>
- /// Number of frames.
- /// </summary>
- public int FrameCount
- {
- get
- {
- return Frames.Length;
- }
- }
- #endregion
-
- #region Frames (Public)
- /// <summary>
- /// Array of animation frames (as array of bone matrices)
- /// e.g.
- /// Frames[0] -> Frame 1
- /// Frames[0][0] -> Bone Matrix 1
- /// Frames[0][1] -> Bone Matrix 2
- /// Frames[1] -> Frame 2
- /// Frames[1][0] -> Bone Matrix 1
- /// Frames[1][1] -> Bone Matrix 2
- /// </summary>
- public Matrix[][] Frames;
- #endregion
-
- #region NextFrame (Public)
- /// <summary>
- /// Next frame
- /// </summary>
- public int NextFrame
- {
- get
- {
- return nextFrame;
- }
- }
- #endregion
-
- #region Private
-
- #region parent (Private)
- /// <summary>
- /// The parent mesh of this animation.
- /// </summary>
- private readonly Mesh parent;
- #endregion
-
- #region animation (Private)
- /// <summary>
- /// Animation data / info.
- /// </summary>
- private AnimationInfo animation;
- #endregion
-
- #region blendAnimation (Private)
- /// <summary>
- /// Blend animation data / info.
- /// </summary>
- private AnimationInfo blendAnimation;
- #endregion
-
- #region onBlending (Private)
- /// <summary>
- /// Currently on blending an animation.
- /// </summary>
- private bool onBlending;
- #endregion
-
- #region blendingTime (Private)
- /// <summary>
- /// Animation blending time.
- /// </summary>
- private float blendingTime;
- #endregion
-
- #region currentBlendingTime (Private)
- /// <summary>
- /// Current time of the animation blending.
- /// </summary>
- private float currentBlendingTime;
- #endregion
-
- #region nextFrame (Private)
- /// <summary>
- /// The next frame we want to interpolate to.
- /// </summary>
- private int nextFrame;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create a new animation.
- /// </summary>
- /// <param name="setParent">The parent mesh of this animation.</param>
- /// <param name="setAnimationData">The data of this animation.</param>
- public Animation(Mesh setParent, MeshAnimationData setAnimationData)
- {
- parent = setParent;
- Frames = setAnimationData.Frames;
-
- animation = new AnimationInfo();
- blendAnimation = new AnimationInfo();
-
- // Set the animation values.
- animation.StartIndex = 0;
- animation.EndIndex = Frames.GetLength(0);
- animation.FrameRate = 30;
- animation.DefaultFrameRate = animation.FrameRate;
- animation.BoneMatrices = parent.Geometry.FinalBoneMatrices;
- animation.FrameIndex = 0;
- animation.FramesCount = 0;
- animation.FrameTimer = 0.0f;
- animation.Loop = false;
- animation.Name = "";
-
- // Set the blend animation values.
- blendAnimation = animation;
-
- // Set default. No animation blending.
- onBlending = false;
- }
- #endregion
-
- #region SetAnimationSpeed (Public)
- /// <summary>
- /// Set animation speed.
- /// </summary>
- /// <param name="factor">Scale factor to apply.</param>
- public void SetAnimationSpeed(float factor)
- {
- animation.FrameRate = (factor * animation.DefaultFrameRate);
- }
- #endregion
-
- #region SetCurrentAnimation (Public)
- /// <summary>
- /// Change the current animation.
- /// </summary>
- /// <param name="animaitonName">The animation name to switch to.</param>
- /// <param name="indexStart">Animation index start.</param>
- /// <param name="indexEnd">Animation index end.</param>
- /// <param name="frameRate">Frame rate during animation playback.</param>
- /// <param name="loop">True to loop animation, false otherwise.</param>
- public void SetCurrentAnimation(string animaitonName, int indexStart,
- int indexEnd, float frameRate, bool loop)
- {
- SetAnimation(ref animation, animaitonName, indexStart, indexEnd,
- frameRate, loop);
- onBlending = false;
- }
- #endregion
-
- #region BlendAnimation (Public)
- /// <summary>
- /// Blend animation with current animation playing.
- /// </summary>
- /// <param name="animationName">The animation name to blend with.</param>
- /// <param name="indexStart">Animation index start.</param>
- /// <param name="indexEnd">Animation index end.</param>
- /// <param name="frameRate">Frame rate during animation playback.</param>
- /// <param name="loop">True to loop animation, false otherwise.</param>
- /// <param name="blendingTime">Blending duration time.</param>
- public void BlendAnimation(string animationName, int indexStart,
- int indexEnd, float frameRate, bool loop, float blendingTime)
- {
- onBlending = true;
-
- if (animation.FramesCount == 0)
- {
- //Log.Info("There are no animation to blend. " +
- // "Normal play of the given animation.");
- // No blending.
- onBlending = false;
- }
-
- blendAnimation = animation;
- SetAnimation(ref animation, animationName, indexStart, indexEnd,
- frameRate, loop);
-
- this.blendingTime = blendingTime;
- currentBlendingTime = 0.0f;
- }
- #endregion
-
- #region Update (Public)
- /// <summary>
- /// Update the animation.
- /// </summary>
- public bool Update()
- {
- //long totalTimeMs = Time.Milliseconds;
- //int frameRate = 30;
-
- // Check if there are any keyframes, if not then return.
- int numOfAnimations = Frames.GetLength(0);
- if (numOfAnimations == 0)
- {
- Log.Info("There are no keyframes for animation.");
- return false;
- }
-
- // Update the current animation.
- bool bResult = UpdateAnimation(ref animation);
-
- if (onBlending)
- {
- // Update the blending animation.
- UpdateAnimation(ref blendAnimation);
-
- // Increase the current blending time.
- currentBlendingTime += GameTime.Delta;
-
- // The blending is over.
- if (currentBlendingTime > blendingTime)
- {
- onBlending = false;
- }
-
- float blendFactor = currentBlendingTime / blendingTime;
-
- Matrix[] finalBoneMatrices =
- new Matrix[blendAnimation.BoneMatrices.Length];
-
- // Calculate the final bone matrices.
- for (int i = 0; i < finalBoneMatrices.Length; i++)
- {
- finalBoneMatrices[i] =
- Matrix.Multiply(blendAnimation.BoneMatrices[i], (1.0f - blendFactor)) +
- Matrix.Multiply(animation.BoneMatrices[i], blendFactor);
- }
-
- // Set the final bone matrices.
- parent.Geometry.FinalBoneMatrices = finalBoneMatrices;
- }
- else
- {
- parent.Geometry.FinalBoneMatrices = animation.BoneMatrices;
- }
-
- return bResult;
- }
- #endregion
-
- #region Methods (Private)
-
- #region UpdateAnimation
- /// <summary>
- /// Update animation simulation.
- /// </summary>
- /// <param name="currentAnimation">Animation info to update with.</param>
- /// <returns>True if animation correctly updated, false otherwise.</returns>
- private bool UpdateAnimation(ref AnimationInfo currentAnimation)
- {
- currentAnimation.FrameTimer += GameTime.Delta *
- (currentAnimation.FrameRate /
- currentAnimation.DefaultFrameRate);
-
- float secondsPerFrame = 1 / currentAnimation.DefaultFrameRate;
- Duration += secondsPerFrame;
-
- if (currentAnimation.Loop == false && currentAnimation.FrameIndex >=
- currentAnimation.FramesCount - 1 &&
- currentAnimation.FrameIndex > 0)
- {
- currentAnimation.FrameIndex = currentAnimation.FramesCount - 1;
- currentAnimation.BoneMatrices = Frames[currentAnimation.FrameIndex +
- currentAnimation.StartIndex];
- return true;
- }
-
- while (currentAnimation.FrameTimer >= secondsPerFrame)
- {
- currentAnimation.FrameTimer -= secondsPerFrame;
- currentAnimation.FrameIndex++;
-
- // Returning true if the animation is complete and is not looping.
- // If the animation is looping then we just null currentFrameIndex.
- if (currentAnimation.Loop == false &&
- currentAnimation.FrameIndex >= currentAnimation.FramesCount)
- {
- return true;
- }
- else if (currentAnimation.Loop)
- {
- currentAnimation.FrameIndex %= currentAnimation.FramesCount;
- }
-
- // Increase next frame so it becomes the frame we want to interpolate to
- nextFrame = currentAnimation.FrameIndex + 1;
- if (nextFrame >= currentAnimation.FramesCount)
- {
- nextFrame %= currentAnimation.FramesCount;
- }
-
- if (Frames[currentAnimation.FrameIndex +
- currentAnimation.StartIndex].Length == 0)
- {
- Log.Info("There are no bones that can be animated.");
- return false;
- }
- } // while
-
- // Calculate lerp
- // NOTE: Disabled for the demo!!!!!!!!!!!!!!!!!!!!!
- // The Animation flash sometimes and for the moment we don't need
- // any frame interpolations, because we have 2 different animations.
- // Like walk and run.
- //if (onBlending == false)
- //if (false)
- //{
- // float lerpAmount = currentAnimation.FrameTimer / secondsPerFrame;
- // int frameIndex = currentAnimation.FrameIndex + currentAnimation.StartIndex;
-
- // Matrix[] boneMatricesCopy = new Matrix[currentAnimation.BoneMatrices.Length];
- // //Frames[frameIndex].CopyTo(boneMatricesCopy, 0);
-
- // for (int i = 0; i < boneMatricesCopy.Length; i++)
- // {
- // //boneMatricesCopy[i] = Frames[frameIndex][i];
- // boneMatricesCopy[i] =
- // Matrix.Multiply(Frames[currentAnimation.FrameIndex +
- // currentAnimation.StartIndex][i], (1.0f - lerpAmount)) +
- // Matrix.Multiply(Frames[nextFrame +
- // currentAnimation.StartIndex][i], lerpAmount);
- // } // for
-
- // currentAnimation.BoneMatrices = boneMatricesCopy;
- //} // if
- //else
- {
- currentAnimation.BoneMatrices =
- Frames[currentAnimation.FrameIndex + currentAnimation.StartIndex];
- }
-
- return false;
- }
- #endregion
-
- #region SetAnimation
- /// <summary>
- /// Set animation info data.
- /// </summary>
- /// <param name="data">AnimationInfo data to apply</param>
- /// <param name="animationName">The animation name to blend with.</param>
- /// <param name="indexStart">Animation index start.</param>
- /// <param name="indexEnd">Animation index end.</param>
- /// <param name="frameRate">Frame rate during animation playback.</param>
- /// <param name="loop">True to loop animation, false otherwise.</param>
- private void SetAnimation(ref AnimationInfo data, string animationName,
- int indexStart, int indexEnd, float frameRate, bool loop)
- {
- if (indexStart > Frames.GetLength(0))
- {
- Log.Info("Trying to start animation at value higher "
- + "then number of animation frames.");
- return;
- }
-
- // Make sure that indexEnd is greater than indexStart and less then
- // the max number of frames.
- if (indexEnd <= indexStart || indexEnd > Frames.GetLength(0))
- {
- indexEnd = Frames.GetLength(0);
- }
-
- data.Name = animationName;
- data.FrameIndex = 0;
- data.StartIndex = indexStart;
- data.EndIndex = indexEnd;
- // Add a small random value to make animations asynchronous
- data.FrameRate = frameRate + RandomHelper.RandomFloat(-2.0f, 2.0f);
- data.DefaultFrameRate = frameRate;
- data.Loop = loop;
- data.FramesCount = (indexEnd - indexStart);
- }
- #endregion
-
- #endregion
- }
- }