#region File Description
//-----------------------------------------------------------------------------
// ModelAnimationPlayerBase.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace CustomModelAnimation
{
///
/// This class serves as a base class for various animation players. It contains
/// common functionality to deal with a clip, playing it back at a speed,
/// notifying clients of completion, etc.
///
public abstract class ModelAnimationPlayerBase
{
// Clip currently being played
ModelAnimationClip currentClipValue;
// Current timeindex and keyframe in the clip
TimeSpan currentTimeValue;
int currentKeyframe;
// Speed of playback
float playbackRate = 1.0f;
// The amount of time for which the animation will play.
// TimeSpan.MaxValue will loop forever. TimeSpan.Zero will play once.
TimeSpan duration = TimeSpan.MaxValue;
// Amount of time elapsed while playing
TimeSpan elapsedPlaybackTime = TimeSpan.Zero;
// Whether or not playback is paused
bool paused;
///
/// Gets the clip currently being decoded.
///
public ModelAnimationClip CurrentClip
{
get { return currentClipValue; }
}
///
/// Get/Set the current key frame index
///
public int CurrentKeyFrame
{
get { return currentKeyframe; }
set
{
IList keyframes = currentClipValue.Keyframes;
TimeSpan time = keyframes[value].Time;
CurrentTimeValue = time;
}
}
///
/// Gets/set the current play position.
///
public TimeSpan CurrentTimeValue
{
get { return currentTimeValue; }
set
{
TimeSpan time = value;
// If the position moved backwards, reset the keyframe index.
if (time < currentTimeValue)
{
currentKeyframe = 0;
InitClip();
}
currentTimeValue = time;
// Read keyframe matrices.
IList keyframes = currentClipValue.Keyframes;
while (currentKeyframe < keyframes.Count)
{
ModelKeyframe keyframe = keyframes[currentKeyframe];
// Stop when we've read up to the current time position.
if (keyframe.Time > currentTimeValue)
break;
// Use this keyframe
SetKeyframe(keyframe);
currentKeyframe++;
}
}
}
///
/// Invoked when playback has completed.
///
public event EventHandler Completed;
///
/// Starts decoding the specified animation clip.
///
public void StartClip(ModelAnimationClip clip)
{
StartClip(clip, 1.0f, TimeSpan.MaxValue);
}
///
/// Starts playing a clip
///
/// Animation clip to play
/// Speed to playback
/// Length of time to play (max is looping, 0 is once)
public void StartClip(ModelAnimationClip clip, float playbackRate, TimeSpan duration)
{
if (clip == null)
throw new ArgumentNullException("Clip required");
// Store the clip and reset playing data
currentClipValue = clip;
currentKeyframe = 0;
CurrentTimeValue = TimeSpan.Zero;
elapsedPlaybackTime = TimeSpan.Zero;
paused = false;
// Store the data about how we want to playback
this.playbackRate = playbackRate;
this.duration = duration;
// Call the virtual to allow initialization of the clip
InitClip();
}
///
/// Will pause the playback of the current clip
///
public void PauseClip()
{
paused = true;
}
///
/// Will resume playback of the current clip
///
public void ResumeClip()
{
paused = false;
}
///
/// Virtual method allowing subclasses to do any initialization of data when the clip is initialized.
///
protected virtual void InitClip()
{
}
///
/// Virtual method allowing subclasses to set any data associated with a particular keyframe.
///
/// Keyframe being set
protected virtual void SetKeyframe(ModelKeyframe keyframe)
{
}
///
/// Virtual method allowing subclasses to perform data needed after the animation
/// has been updated for a new time index.
///
protected virtual void OnUpdate()
{
}
///
/// Called during the update loop to move the animation forward
///
///
public virtual void Update(GameTime gameTime)
{
if (currentClipValue == null)
return;
if (paused)
return;
TimeSpan time = gameTime.ElapsedGameTime;
// Adjust for the rate
if (playbackRate != 1.0f)
time = TimeSpan.FromMilliseconds(time.TotalMilliseconds * playbackRate);
elapsedPlaybackTime += time;
// See if we should terminate
if (elapsedPlaybackTime > duration && duration != TimeSpan.Zero ||
elapsedPlaybackTime > currentClipValue.Duration && duration == TimeSpan.Zero)
{
if (Completed != null)
Completed(this, EventArgs.Empty);
currentClipValue = null;
return;
}
// Update the animation position.
time += currentTimeValue;
// If we reached the end, loop back to the start.
while (time >= currentClipValue.Duration)
time -= currentClipValue.Duration;
CurrentTimeValue = time;
OnUpdate();
}
}
}