/*
* AnimationController.cs
* Copyright (c) 2006 David Astle
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
namespace Xclna.Xna.Animation
{
///
/// Controls an animation by advancing it's time and affecting
/// bone transforms
///
public class AnimationController : IAnimationController
{
#region Member Variables
// Contains the interpolated transforms for all bones in an
// animation
private AnimationInfo animation;
// Multiplied by the time whenever the animation is advanced; determines
// the playback speed of the animation
private double speedFactor = 1.0;
// The elapsed time in the animation, can not be greater than the
// animation duration
private long elapsedTime = 0;
// Used as a buffer to store the total elapsed ticks every frame so that
private long elapsed;
///
/// Fired when the controller is not looping and the animation has ended.
///
public event EventHandler AnimationEnded;
// True fi the animation is looping
private bool isLooping = true;
#endregion
#region Constructors
///
/// Creates a new animation controller.
///
/// The game to which this controller will be attached.
/// The source animation that the controller will use.
/// This is stored in the ModelAnimator class.
public AnimationController(AnimationInfo sourceAnimation) : base()
{
animation = sourceAnimation;
// This is set so that the controller updates before the
// ModelAnimator by default
//base.UpdateOrder = 0;
//game.Components.Add(this);
}
#endregion
#region Properties
///
/// Gets or sets a value that determines if the animation is looping.
///
public bool IsLooping
{
get { return isLooping; }
set
{
isLooping = value;
}
}
///
/// Gets the total duration, in ticks, of the animation.
///
public long Duration
{
get { return animation.Duration; }
}
///
/// Gets the source animation that this controller is using.
///
public AnimationInfo AnimationSource
{
get { return animation; }
}
///
/// Gets or sets the elapsed time for the animation.
///
public long ElapsedTime
{
get { return elapsedTime; }
set
{
// Perform argument checking
if (value < 0 || value > animation.Duration)
throw new ArgumentOutOfRangeException("ElapsedTime",
"When setting the ElapsedTime for an animation, the value " +
" must be between 0 and the animation duration.");
elapsedTime = value;
}
}
///
/// Gets or sets the value that is multiplied by the time when it is
/// advanced to determine the playback speed of the animation.
///
public double SpeedFactor
{
get { return speedFactor; }
set { speedFactor = value; }
}
#endregion
#region Methods
///
/// Called when the current animation reaches the end.
///
/// The event args.
protected virtual void OnAnimationEnded(EventArgs args)
{
if (AnimationEnded != null)
AnimationEnded(this, args);
}
///
/// Advances the current time in the animation.
///
/// Contains the time by which the animation will be advanced
public void Update(GameTime gameTime)
{
// Speedfactor * elapsed time since last call to update
elapsed = (long)(speedFactor * gameTime.ElapsedGameTime.Ticks);
// If the animation is looping
if (isLooping)
{
// Don't do anything if the speedfactor=0
if (elapsed != 0)
{
elapsedTime = (elapsedTime + elapsed);
// If the elapsed time is greater than the duration,
// raise the animation ended event and restart the animation
if (elapsedTime > animation.Duration)
{
OnAnimationEnded(null);
elapsedTime %= (animation.Duration + 1);
}
}
}
// If we aren't looping, don't do anything if the animation is at the end
else if (elapsedTime != animation.Duration)
{
if (elapsed != 0)
{
// Set the elapsed time to the duration if the animation ends and
// raise the AnimationEnded event
elapsedTime = elapsedTime + elapsed;
if (elapsedTime >= animation.Duration || elapsedTime < 0)
{
elapsedTime = animation.Duration;
OnAnimationEnded(null);
}
}
}
}
///
/// Gets the current transform for the given BonePose object in the animation.
/// This is only called when a bone pose is affected by the current animation.
///
/// The BonePose object querying for the current transform in
/// the animation.
/// The current transform of the bone.
public virtual Matrix GetCurrentBoneTransform(BonePose pose)
{
AnimationChannelCollection channels = animation.AnimationChannels;
BoneKeyframeCollection channel = channels[pose.Name];
int boneIndex = channel.GetIndexByTime(elapsedTime);
return channel[boneIndex].Transform;
}
///
/// Returns true if the animation contains a track for the given BonePose.
///
/// The BonePose to test for track existence.
/// True if the animation contains a track for the given BonePose.
public bool ContainsAnimationTrack(BonePose pose)
{
return animation.AnimationChannels.AffectsBone(pose.Name);
}
///
/// Fired when the tracks change so that different bones can be affected by the controller.
///
/// The event args.
protected virtual void OnAnimationTracksChanged(EventArgs e)
{
if (AnimationTracksChanged != null)
AnimationTracksChanged(this, e);
}
///
/// Fired when the animation tracks change and different bones are affected.
///
public event EventHandler AnimationTracksChanged;
#endregion
#region IAnimationController Members
#endregion
}
}