/*
* AnimationInfo.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 System.Collections.ObjectModel;
using Microsoft.Xna.Framework.Graphics;
namespace Xclna.Xna.Animation
{
///
/// A collection of BoneKeyFrames that represents an animation track.
///
public class BoneKeyframeCollection : ReadOnlyCollection
{
#region Member Variables
// The name of the bone represented by this animation track
private string boneName;
// Duration of the track
private long duration;
#endregion
#region Constructors
// Only allow creation from inside the library (only in AnimationReader)
internal BoneKeyframeCollection(string boneName,
IList list) : base(list)
{
this.boneName = boneName;
duration = list[list.Count - 1].Time;
}
#endregion
#region Properties
///
/// Gets the duration of the animation track.
///
public long Duration
{
get { return duration; }
}
///
/// Gets the name of the bone associated with the animation track.
///
public string BoneName
{ get { return boneName; } }
#endregion
#region Methods
///
/// Gets the index in the track at the given time.
///
/// The time for which the index is found.
/// The index in the track at the given time.
public int GetIndexByTime(long ticks)
{
// Since the animation is usually interpolated to 60 fps, this will
// almost always be the index to return
int firstFrameIndexToCheck = (int)(ticks / Util.TICKS_PER_60FPS);
// Do out of bounds checking
if (firstFrameIndexToCheck >= base.Count)
firstFrameIndexToCheck = base.Count - 1;
// Increment the index until the time at the next index is greater than the
// specified time
while (firstFrameIndexToCheck < base.Count - 1
&& base[firstFrameIndexToCheck+1].Time < ticks)
{
++firstFrameIndexToCheck;
}
// Decrement the index till the time at the index is not greater than the
// specified time
while (firstFrameIndexToCheck >= 0 && base[firstFrameIndexToCheck].Time >
ticks)
{
--firstFrameIndexToCheck;
}
return firstFrameIndexToCheck;
}
#endregion
}
///
/// Represents a keyframe in an animation track.
///
public struct BoneKeyframe
{
///
/// Creats a new BoneKeyframe.
///
/// The transform for the keyframe.
/// The time in ticks for the keyframe.
public BoneKeyframe(Matrix transform, long time)
{
this.Transform = transform;
this.Time = time;
}
///
/// The transform for the keyframe.
///
public readonly Matrix Transform;
///
/// The time for the keyframe.
///
public readonly long Time;
}
///
/// A collection of animation channels or tracks, which are sections of an
/// animation that run for one bone.
///
public class AnimationChannelCollection : ReadOnlyCollection
{
// Allow quick access to channels by BoneName
private Dictionary dict =
new Dictionary();
// The bones affected by the tracks contained in this collection
private ReadOnlyCollection affectedBones;
// This immutable data structure should not be created by the library user
internal AnimationChannelCollection(IList channels)
: base(channels)
{
// Find the affected bones
List affected = new List();
foreach (BoneKeyframeCollection frames in channels)
{
dict.Add(frames.BoneName, frames);
affected.Add(frames.BoneName);
}
affectedBones = new ReadOnlyCollection(affected);
}
///
/// Gets the BoneKeyframeCollection that is associated with the given bone.
///
/// The name of the bone that contains a track in this
/// AnimationChannelCollection.
/// The track associated with the given bone.
public BoneKeyframeCollection this[string boneName]
{
get { return dict[boneName]; }
}
// See AnimationInfo's equivalent method for documentation
internal bool AffectsBone(string boneName)
{ return dict.ContainsKey(boneName); }
// See AnimationInfo's equivalent method for documentation
internal ReadOnlyCollection AffectedBones
{
get { return affectedBones; }
}
}
///
/// Contains information about an animation.
///
public class AnimationInfo
{
private long duration = 0;
private string animationName;
// The bone animation tracks
private AnimationChannelCollection boneAnimations;
// Internal because it should only be created by the AnimationReader
internal AnimationInfo(string animationName, AnimationChannelCollection
anims)
{
this.animationName = animationName;
boneAnimations = anims;
foreach (BoneKeyframeCollection channel in anims)
{
if (channel.Duration > duration)
duration = channel.Duration;
}
}
///
/// Gets a collection of channels that represent the bone animation
/// tracks for this animation.
///
public AnimationChannelCollection AnimationChannels
{ get { return boneAnimations; } }
///
/// Gets a collection of bones that have tracks in this animation.
///
public ReadOnlyCollection AffectedBones
{ get { return boneAnimations.AffectedBones; } }
///
/// Gets the total duration of this animation in ticks.
///
public long Duration
{
get { return duration; }
}
///
/// Gets the name of the animation.
///
public string Name
{
get { return animationName; }
}
///
/// Returns true if the animation contains any tracks that affect the given
/// bone.
///
/// The bone to test for track information.
/// True if the animation contains any tracks that affect the given
/// bone.
public bool AffectsBone(string boneName)
{ return boneAnimations.AffectsBone(boneName); }
}
///
/// A collection of AnimationInfo objects.
///
public class AnimationInfoCollection : SortedList
{
// New instances should only be created by the AnimationReader
internal AnimationInfoCollection()
{
}
///
/// Gets a collection of animations stored in the model.
///
/// The model that contains the animations.
/// The animations stored in the model.
public static AnimationInfoCollection FromModel(Model model)
{
// Grab the tag that was set in the processor; this is a dictionary so that users can extend
// the processor and pass their own data into the program without messing up the animation data
Dictionary modelTagData = (Dictionary)model.Tag;
if (modelTagData == null || !modelTagData.ContainsKey("Animations"))
{
return new AnimationInfoCollection();
}
else
{
AnimationInfoCollection animations = (AnimationInfoCollection)modelTagData["Animations"];
return animations;
}
}
///
/// Gets the AnimationInfo object at the given index.
///
/// The index of the AnimationInfo object.
/// The AnimationInfo object at the given index.
public AnimationInfo this[int index]
{
get
{
return this.Values[index];
}
}
}
}