Engines/FlatRedBallXNA/FlatRedBall.Content/ModelAnimation/AnimatedModelProcessor.cs
/// <summary>
/// Called when an XML document is read that specifies how animations
/// should be split.
/// </summary>
/// <param name="animDict">The dictionary of animation name/AnimationContent
/// pairs. </param>
/// <param name="doc">The Xml document that contains info on how to split
/// the animations.</param>
Called when an XML document is read that specifies how animations
should be split.
Called when an XML document is read that specifies how animations should be split .
protected virtual void SubdivideAnimations(
            AnimationContentDictionary animDict, XmlDocument doc)
        {
            string[] animNames = new string[animDict.Keys.Count];
            animDict.Keys.CopyTo(animNames, 0);
            if (animNames.Length == 0)
                return;
            foreach (XmlNode node in doc)
            {
                XmlElement child = node as XmlElement;
                if (child == null || child.Name != "animation")
                    continue;
                string animName = null;
                if (child["name"] != null)
                {
                    animName = child["name"].InnerText;
                }
                else if (child["index"] != null)
                {
                    animName = animNames[int.Parse(child["index"].InnerText)];
                }
                else
                {
                    animName = animNames[0];
                }
                double animTicksPerSecond = 1.0, secondsPerTick = 0;
                try
                {
                    if (child["tickspersecond"] != null)
                    {
                        animTicksPerSecond = double.Parse(child["tickspersecond"].InnerText);
                    }
                }
                catch
                {
                    throw new Exception("Error parsing tickspersecond in xml file.");
                }
                if (animTicksPerSecond <= 0)
                    throw new InvalidDataException("AnimTicksPerSecond in XML file must be " +
                        "a positive number.");
                secondsPerTick = 1.0 / animTicksPerSecond;
                AnimationContent anim = null;
                try
                {
                    anim = animDict[animName];
                }
                catch
                {
                    throw new Exception("Animation named " + animName + " specified in XML file does not exist in model.");
                }
                animDict.Remove(anim.Name);
                XmlNodeList subAnimations = child.GetElementsByTagName("animationsubset");
                foreach (XmlElement subAnim in subAnimations)
                {
                    AnimationContent newAnim = new AnimationContent();
                    XmlElement subAnimNameElement = subAnim["name"];
                    if (subAnimNameElement != null)
                        newAnim.Name = subAnimNameElement.InnerText;
                    long startTime, endTime;
                    if (subAnim["starttime"] != null)
                    {
                        try
                        {
                            startTime = TimeSpan.FromSeconds(double.Parse(subAnim["starttime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing starttime node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["startframe"] != null)
                    {
                        try
                        {
                            double seconds =
                                double.Parse(subAnim["startframe"].InnerText) * secondsPerTick;
                            startTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing startframe node in XML file.  Node inner text "
                              + "must be a non negative number.");
                        }
                    }
                    else
                        throw new Exception("Sub animation in XML file must have either a starttime or startframe node.");
                    if (subAnim["endtime"] != null)
                    {
                        try
                        {
                            endTime = TimeSpan.FromSeconds(double.Parse(subAnim["endtime"].InnerText)).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endtime node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else if (subAnim["endframe"] != null)
                    {
                        try
                        {
                            double seconds = double.Parse(subAnim["endframe"].InnerText)
                                * secondsPerTick;
                            endTime = TimeSpan.FromSeconds(
                                seconds).Ticks;
                        }
                        catch
                        {
                            throw new Exception("Error parsing endframe node in XML file.  Node inner text "
                                + "must be a non negative number.");
                        }
                    }
                    else
                        throw new Exception("Sub animation in XML file must have either an endtime or endframe node.");
                    if (endTime < startTime)
                        throw new Exception("Start time must be <= end time in XML file.");
                    foreach (KeyValuePair<string, AnimationChannel> k in anim.Channels)
                    {
                        long currentStartDiff;
                        long currentEndDiff;
                        long bestStartDiff=long.MaxValue;
                        long bestEndDiff=long.MaxValue;
                        int startIndex = -1;
                        int endIndex = -1;
                        AnimationChannel newChan = new AnimationChannel();
                        AnimationChannel oldChan = k.Value;
                        for (int i = 0; i < oldChan.Count; i++)
                        {
                            long ticks = oldChan[i].Time.Ticks;
                            currentStartDiff = System.Math.Abs(startTime - ticks);
                            currentEndDiff = System.Math.Abs(endTime - ticks);
                            if (startIndex == -1 || currentStartDiff<bestStartDiff)
                            {
                                startIndex = i;
                                bestStartDiff = currentStartDiff;
                            }
                            if (endIndex == -1 || currentEndDiff<bestEndDiff)
                            {
                                endIndex = i;
                                bestEndDiff = currentEndDiff;
                            }
                        }
                        for (int i = startIndex; i <= endIndex; i++)
                        {
                            AnimationKeyframe frame = oldChan[i];
                            long time;
                            if (frame.Time.Ticks < startTime)
                                time = 0;
                            else if (frame.Time.Ticks > endTime)
                                time = endTime - startTime;
                            else 
                                time = frame.Time.Ticks - startTime;
                            AnimationKeyframe keyframe = new AnimationKeyframe(
                                TimeSpan.FromTicks(time),
                                frame.Transform);
                            newChan.Add(keyframe);
                        }
                        newAnim.Channels.Add(k.Key, newChan);
                        if (newChan[newChan.Count - 1].Time > newAnim.Duration)
                            newAnim.Duration = newChan[newChan.Count - 1].Time;
                    }
                    try
                    {
                        animDict.Add(newAnim.Name, newAnim);
                    }
                    catch
                    {
                        throw new Exception("Attempt to add an animation when one by the same name already exists. " +
                            "Name: " + newAnim.Name);
                    }
                }
            }
        }
protected virtual void SubdivideAnimations ( AnimationContentDictionary animDict , XmlDocument doc ) { string [ ] animNames = new string [ animDict . Keys . Count ] ; animDict . Keys . CopyTo ( animNames , 0 ) ; if ( animNames . Length == 0 ) return ; foreach ( XmlNode node in doc ) { XmlElement child = node as XmlElement ; if ( child == null || child . Name != " " ) continue ; string animName = null ; if ( child [ " " ] != null ) { animName = child [ " " ] . InnerText ; } else if ( child [ " " ] != null ) { animName = animNames [ int . Parse ( child [ " " ] . InnerText ) ] ; } else { animName = animNames [ 0 ] ; } double animTicksPerSecond = 1.0 , secondsPerTick = 0 ; try { if ( child [ " " ] != null ) { animTicksPerSecond = double . Parse ( child [ " " ] . InnerText ) ; } } catch { throw new Exception ( " " ) ; } if ( animTicksPerSecond <= 0 ) throw new InvalidDataException ( " " + " " ) ; secondsPerTick = 1.0 / animTicksPerSecond ; AnimationContent anim = null ; try { anim = animDict [ animName ] ; } catch { throw new Exception ( " " + animName + " " ) ; } animDict . Remove ( anim . Name ) ; XmlNodeList subAnimations = child . GetElementsByTagName ( " " ) ; foreach ( XmlElement subAnim in subAnimations ) { AnimationContent newAnim = new AnimationContent ( ) ; XmlElement subAnimNameElement = subAnim [ " " ] ; if ( subAnimNameElement != null ) newAnim . Name = subAnimNameElement . InnerText ; long startTime , endTime ; if ( subAnim [ " " ] != null ) { try { startTime = TimeSpan . FromSeconds ( double . Parse ( subAnim [ " " ] . InnerText ) ) . Ticks ; } catch { throw new Exception ( " " + " " ) ; } } else if ( subAnim [ " " ] != null ) { try { double seconds = double . Parse ( subAnim [ " " ] . InnerText ) * secondsPerTick ; startTime = TimeSpan . FromSeconds ( seconds ) . Ticks ; } catch { throw new Exception ( " " + " " ) ; } } else throw new Exception ( " " ) ; if ( subAnim [ " " ] != null ) { try { endTime = TimeSpan . FromSeconds ( double . Parse ( subAnim [ " " ] . InnerText ) ) . Ticks ; } catch { throw new Exception ( " " + " " ) ; } } else if ( subAnim [ " " ] != null ) { try { double seconds = double . Parse ( subAnim [ " " ] . InnerText ) * secondsPerTick ; endTime = TimeSpan . FromSeconds ( seconds ) . Ticks ; } catch { throw new Exception ( " " + " " ) ; } } else throw new Exception ( " " ) ; if ( endTime < startTime ) throw new Exception ( " " ) ; foreach ( KeyValuePair < string , AnimationChannel > k in anim . Channels ) { long currentStartDiff ; long currentEndDiff ; long bestStartDiff = long . MaxValue ; long bestEndDiff = long . MaxValue ; int startIndex = - 1 ; int endIndex = - 1 ; AnimationChannel newChan = new AnimationChannel ( ) ; AnimationChannel oldChan = k . Value ; for ( int i = 0 ; i < oldChan . Count ; i ++ ) { long ticks = oldChan [ i ] . Time . Ticks ; currentStartDiff = System . Math . Abs ( startTime - ticks ) ; currentEndDiff = System . Math . Abs ( endTime - ticks ) ; if ( startIndex == - 1 || currentStartDiff < bestStartDiff ) { startIndex = i ; bestStartDiff = currentStartDiff ; } if ( endIndex == - 1 || currentEndDiff < bestEndDiff ) { endIndex = i ; bestEndDiff = currentEndDiff ; } } for ( int i = startIndex ; i <= endIndex ; i ++ ) { AnimationKeyframe frame = oldChan [ i ] ; long time ; if ( frame . Time . Ticks < startTime ) time = 0 ; else if ( frame . Time . Ticks > endTime ) time = endTime - startTime ; else time = frame . Time . Ticks - startTime ; AnimationKeyframe keyframe = new AnimationKeyframe ( TimeSpan . FromTicks ( time ) , frame . Transform ) ; newChan . Add ( keyframe ) ; } newAnim . Channels . Add ( k . Key , newChan ) ; if ( newChan [ newChan . Count - 1 ] . Time > newAnim . Duration ) newAnim . Duration = newChan [ newChan . Count - 1 ] . Time ; } try { animDict . Add ( newAnim . Name , newAnim ) ; } catch { throw new Exception ( " " + " " + newAnim . Name ) ; } } } }
Called when an XML document is read that specifies how animations
should be split.
Called when an XML document is read that specifies how animations should be split .
// Traverse each xml node that represents an animation to be subdivided // The name of the animation to be split // If the tickspersecond node is filled, use that to calculate seconds per tick // Get the animation and remove it from the dict // Check to see if the animation specified in the xml file exists // Get the list of new animations // Create the new sub animation // If a starttime node exists, use that to get the start time // else use the secondspertick combined with the startframe node value // Same with endtime/endframe // Now that we have the start and end times, we associate them with // start and end indices for each animation track/channel // The current difference between the start time and the // time at the current index // The current difference between the end time and the // time at the current index // The difference between the start time and the time // at the start index // The difference between the end time and the time at // the end index // The start and end indices // Create a new channel and reference the old channel // Iterate through the keyframes in the channel // Update the startIndex, endIndex, bestStartDiff, // and bestEndDiff // Now we have our start and end index for the channel // Clamp the time so that it can't be less than the // start time // Clamp the time so that it can't be greater than the // end time // Else get the time // Finally... create the new keyframe and add it to the new channel // Add the channel and update the animation duration based on the // length of the animation track. // Add the subdived animation to the dictionary.
animDict
AnimationContentDictionary
The dictionary of animation name/AnimationContent
pairs.
The dictionary of animation name / AnimationContent pairs .
doc
XmlDocument
The Xml document that contains info on how to split
the animations.
The Xml document that contains info on how to split the animations .