I have fixed the cross fade function in the skeletal XNAnimation Library for XNA 4.0 & MonoGame. Cross Fade interpolates between two animation clips, fading out the current animation clip and fading in a new one. This allows the character's animations to transition more smoothly when their actions change. Animations can be played forward and backward. The animation speed can also be controlled and looped. The XNAnimation library supports models up to 80 bones and a .xml file can be used to split animation into a new set of animations based on time or keyframes. I am currently working on another technique called Additve Blending. The sample source code for cross fade animation blending is below.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAnimation;
using XNAnimation.Controllers;
using XNAnimation.Effects;
namespace CrossFadeBlendSample
{
///
/// This sample illustrates how to use an animation controller
///
public class XNAnimationSample : Microsoft.Xna.Framework.Gameusing Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XNAnimation;
using XNAnimation.Controllers;
using XNAnimation.Effects;
namespace CrossFadeBlendSample
{
///
/// This sample illustrates how to use an animation controller
///
{
GraphicsDeviceManager graphics;
///
/// Graphics 2D stuff
///
SpriteBatch spriteBatch;
SpriteFont spriteFont;
///
/// View and Projection camera matrices
///
Matrix[] absoluteBoneTransforms;
Matrix cameraView;
Matrix cameraProjection;
///
/// Stores the last keyboard state
///
KeyboardState lastKeyboradState;
///
/// The SkinnedModel class handles skeletal animated models
///
SkinnedModel skinnedModel;
///
/// Index of the active skinned model being drawed
///
int activeSkinnedModelIndex = 0;
///
/// An array with all the skinned model files available
///
readonly String[] skinnedModelFiles = { "Models/PlayerMarine", "Models/EnemyBeast"};
///
/// Custom world transformation for each model
///
readonly Matrix[] worldTransformations = {Matrix.CreateTranslation(0, -12, 0),
Matrix.Identity, Matrix.Identity};
///
/// The AnimationController class handles the animation playback
///
AnimationController animationController;
///
/// Index of the active animation being played
///
int activeAnimationClipIndex = 0;
///
/// If true, enable the keyframe interpolation
///
bool enableInterpolation = false;
///
/// Copyright messages that appears when each model is draw
///
readonly String[] copyrightMessages = {
"Model courtesy of FloatBox Studios",
"Model courtesy of Psionic",
"DirectX SDK Sample Model"};
public XNAnimationSample()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferMultiSampling = true;
Content.RootDirectory = "Content";
}
private void LoadSkinnedModel()
{
// Loads an animated model
skinnedModel = Content.Load<SkinnedModel>
// Copy the absolute transformation of each node
absoluteBoneTransforms = new Matrix[skinnedModel.Model.Bones.Count];
skinnedModel.Model.CopyBoneTransformsTo(absoluteBoneTransforms);
// Creates an animation controller
animationController = new AnimationController(skinnedModel.SkeletonBones);
// Start the first animation stored in the AnimationClips dictionary
animationController.StartClip(
skinnedModel.AnimationClips.Values[activeAnimationClipIndex]);
}
protected override void LoadContent()
{
// Create a SpriteBatch and a SpritFont for 2D drawing
spriteBatch = new SpriteBatch(GraphicsDevice);
spriteFont = Content.Load<SpriteFont>
// Setup camera
cameraView = Matrix.CreateLookAt(new Vector3(10, 15, 30), new Vector3(0, 12, 0), Vector3.Up);
cameraProjection = Matrix.CreatePerspectiveFieldOfView(1, 1, 1, 1000);
// Load a skinned model
activeSkinnedModelIndex = 0;
activeAnimationClipIndex = 0;
LoadSkinnedModel();
}
protected override void Update(GameTime gameTime)
{
#region Keyboard Handling
KeyboardState keyboradState = Keyboard.GetState();
// Exit
if (keyboradState.IsKeyDown(Keys.Escape))
Exit();
// 'Up' and 'Down' changes the animation speed
if (keyboradState.IsKeyDown(Keys.Up))
{
animationController.Speed += 0.005f;
}
else if (keyboradState.IsKeyDown(Keys.Down))
{
animationController.Speed = (animationController.Speed < 0.1f) ?
0.1f : animationController.Speed - 0.005f;
}
// 'Left' and 'Right loop through the animations
if (keyboradState.IsKeyDown(Keys.Left) && lastKeyboradState.IsKeyUp(Keys.Left))
{
activeAnimationClipIndex = ((activeAnimationClipIndex - 1) < 0) ?
skinnedModel.AnimationClips.Count - 1 : activeAnimationClipIndex - 1;
animationController.CrossFade(skinnedModel.AnimationClips.Values[activeAnimationClipIndex],
TimeSpan.FromMilliseconds(300));
}
else if (keyboradState.IsKeyDown(Keys.Right) && lastKeyboradState.IsKeyUp(Keys.Right))
{
activeAnimationClipIndex = (activeAnimationClipIndex + 1) % skinnedModel.AnimationClips.Count;
animationController.CrossFade(skinnedModel.AnimationClips.Values[activeAnimationClipIndex],
TimeSpan.FromMilliseconds(300));
}
// Use CAPSLOCK to cross fade through the animations
if (keyboradState.IsKeyDown(Keys.CapsLock) && lastKeyboradState.IsKeyUp(Keys.CapsLock))
{
activeAnimationClipIndex = (activeAnimationClipIndex + 1) % skinnedModel.AnimationClips.Count;
animationController.CrossFade(skinnedModel.AnimationClips.Values[activeAnimationClipIndex],
System.TimeSpan.FromMilliseconds(300));
}
// 'Enter' enable/disable animation interpolation
if (keyboradState.IsKeyDown(Keys.Enter) && lastKeyboradState.IsKeyUp(Keys.Enter))
{
enableInterpolation = !enableInterpolation;
if (enableInterpolation)
{
animationController.TranslationInterpolation = InterpolationMode.Linear;
animationController.OrientationInterpolation = InterpolationMode.Spherical;
animationController.ScaleInterpolation = InterpolationMode.Linear;
}
else
{
animationController.TranslationInterpolation = InterpolationMode.None;
animationController.OrientationInterpolation = InterpolationMode.None;
animationController.ScaleInterpolation = InterpolationMode.None;
}
}
// 'Tab' changes the current animated model
if (keyboradState.IsKeyDown(Keys.Tab) && lastKeyboradState.IsKeyUp(Keys.Tab))
{
activeSkinnedModelIndex = (activeSkinnedModelIndex + 1) % skinnedModelFiles.Length;
activeAnimationClipIndex = 0;
LoadSkinnedModel();
}
lastKeyboradState = keyboradState;
#endregion
// Update the animation according to the elapsed time
animationController.Update(gameTime.ElapsedGameTime, Matrix.Identity);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Gray);
graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
graphics.GraphicsDevice.BlendState = BlendState.Opaque;
// In this first version of the library the animated model is draw
// through an internal Model object
foreach (ModelMesh modelMesh in skinnedModel.Model.Meshes)
{
foreach (SkinnedEffect effect in modelMesh.Effects)
{
// Set the animated bones to the model
effect.SetBoneTransforms(animationController.SkinnedBoneTransforms);
// OPTIONAL - Configure Material
effect.DiffuseColor = new Vector3(0.8f);
effect.SpecularColor = new Vector3(0.3f);
effect.SpecularPower = 8;
// OPTIONAL - Configure lights
effect.EnableDefaultLighting();
effect.AmbientLightColor = new Vector3(0.1f);
effect.PreferPerPixelLighting = true;
// Setup camera properties
effect.View = cameraView;
effect.Projection = cameraProjection;
}
// Draw a model mesh
modelMesh.Draw();
}
base.Draw(gameTime);
Draw2D();
}
#region Draw 2D Stuff
private void Draw2D()
{
spriteBatch.Begin();
spriteBatch.DrawString(spriteFont, "Use [LEFT/RIGHT] to Cross Fade Blend with a new animation clip.",
new Vector2(10, 5), Color.White);
spriteBatch.DrawString(spriteFont, "Use [UP/DOWN] to change the animation speed.",
new Vector2(10, 22), Color.White);
spriteBatch.DrawString(spriteFont, "Use [ENTER] to enable/disable animation interpolation.",
new Vector2(10, 39), Color.White);
spriteBatch.DrawString(spriteFont, "Use [TAB] to change the animated model.",
new Vector2(10, 56), Color.White);
spriteBatch.DrawString(spriteFont, copyrightMessages[activeSkinnedModelIndex],
new Vector2(560, 580), Color.White);
spriteBatch.End();
}
#endregion
#region Entry Point
static void Main(string[] args)
{
using (XNAnimationSample game = new XNAnimationSample())
{
game.Run();
}
}
#endregion
}
}
No comments:
Post a Comment