/* * 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]; } } } }