/// <summary>
 /// Comparison function for sorting keyframes into ascending time order.
 /// </summary>
 static int CompareKeyframeTimes(Keyframe a, Keyframe b)
 {
     return a.Time.CompareTo(b.Time);
 }
        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContentDictionary
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Dictionary<string, AnimationClip> ProcessAnimations(
            AnimationContentDictionary animations, IList<BoneContent> bones,
            ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // Build up a table mapping bone names to indices.
            Dictionary<string, int> boneMap = new Dictionary<string, int>();

            for (int i = 0; i < bones.Count; i++)
            {
                string boneName = bones[i].Name;

                if (!string.IsNullOrEmpty(boneName))
                    boneMap.Add(boneName, i);
            }

            // Convert each animation in turn.
            Dictionary<string, AnimationClip> animationClips;
            animationClips = new Dictionary<string, AnimationClip>();

            // We process the original animation first, so we can use their keyframes
            foreach (KeyValuePair<string, AnimationContent> animation in animations)
            {
                AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key);

                animationClips.Add(animation.Key, processed);
            }

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                // Add the filename as a dependency, so if it changes, the model is rebuilt
                context.AddDependency(AnimPath);

                // Load the animation definition from the XML file
                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                // Break up the original animation clips into our new clips
                // First, we check if the clips contains our clip to break up
                if (animationClips.ContainsKey(AnimDef.OriginalClipName))
                {
                    // Grab the main clip that we are using
                    AnimationClip MainClip = animationClips[AnimDef.OriginalClipName];

                    // Now remove the original clip from our animations
                    animationClips.Remove(AnimDef.OriginalClipName);

                    // Process each of our new animation clip parts
                    foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts)
                    {
                        // Calculate the frame times
                        TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);
                        TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);

                        // Get all the keyframes for the animation clip
                        // that fall within the start and end time
                        List<Keyframe> Keyframes = new List<Keyframe>();
                        foreach (Keyframe AnimFrame in MainClip.Keyframes)
                        {
                            if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime))
                            {
                                Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform);
                                Keyframes.Add(NewFrame);
                            }
                        }

                        // Process the events
                        List<AnimationEvent> Events = new List<AnimationEvent>();
                        if (Part.Events != null)
                        {
                            // Process each event
                            foreach (AnimationDefinition.ClipPart.Event Event in Part.Events)
                            {
                                // Get the event time within the animation
                                TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);

                                // Offset the event time so it is relative to the start of the animation
                                EventTime -= StartTime;

                                // Create the event
                                AnimationEvent NewEvent = new AnimationEvent();
                                NewEvent.EventTime = EventTime;
                                NewEvent.EventName = Event.Name;
                                Events.Add(NewEvent);
                            }
                        }

                        // Create the clip
                        AnimationClip NewClip = new AnimationClip(EndTime - StartTime, Keyframes, Events, Part.ClipName);
                        animationClips[Part.ClipName] = NewClip;
                    }
                }
            }

            if (animationClips.Count == 0)
            {
                throw new InvalidContentException(
                            "Input file does not contain any animations.");
            }

            return animationClips;
        }