StartClip Frame/Time?

Aug 10, 2008 at 9:31 PM
Is there a way to start a clip at a specific time within the clip?  Say I have an animation clip that I would like transition to but if i'm playing a specific animation I would like to transition into the same clip but later in the clip time.  Is this possible? 

Also, I'm a technical animator, not a real programmer, and I've been trying to get familiar with XNA and your library has been a great help to get me going with everything I need.  Thanks!
Coordinator
Aug 12, 2008 at 5:47 AM
Hi, sorry for the late reply. I'm out of office and with limited internet connection.

There's a Time property in the animation controller that you can set to any time that you want,  or you can just me a first update call passing the start time. However, you can only transition (using cross fade blending) to the begin of an animation clip. Why do you want a transition in another part of the clip?

And thanks for the kindly words I hope you enjoy the library.
Aug 12, 2008 at 7:40 AM
Transitioning to another time in the clip is something we do to save on animation memory in some cases.  Its not always a necessity but I guess its something ive become familiar with.  I'll play around with the time property and see if I can manage what I am trying to accomplish 

Thanks again!.
Coordinator
Aug 14, 2008 at 9:01 PM
Hi. It's the first time someone asks me about this feature. There are many features that I would like to add to the library and can support transitions to the same clip too, but since it is the first time I hear about this there are other features that I would like to add first.
Oct 2, 2008 at 11:56 PM
Edited Oct 3, 2008 at 12:02 AM
I may have found a solution to your problem.

It took me a couple of days to get accustomed to the library's architecture but thanks to a lot of great, easy to read code from Mr. Evangelista it didn't take me nearly as long as I feared it might.  I'm not sure if this discussion board will allow me to post the entire AnimationController.cs file since my changes are kind of peppered throughout the file but I'd be more than happy to share the changes to the code with you guys.

// --------------------------------------------------------------------------------------------

// *** all these changes take place in the AnimationController.cs file
// *** add a new member field and accessor / mutator to the AnimationController class

private TimeSpan _startTime;
public TimeSpan StartTime
{
     get { return _startTime; }
     set { _startTime = value; }
}

// *** add an overloaded StartClip() function to the AnimationController class

/// <summary>
/// Overloaded method for starting an animation clip at any time.
/// </summary>
public void StartClip(AnimationClip aClip, TimeSpan aStartTime)
{
    this.animationClip = aClip;
    hasFinished = false;
    isPlaying = true;

    time = CheckClipDuration(aClip, aStartTime, false);
    skeleton[0].CopyBindPoseTo(localBonePoses);
}

// *** replace the CrossFade() functions in the AnimationController class with the following

/// <summary>
/// Base method for starting a tween between the current AnimationClip and the first frame
/// of another AnimationClip.
/// </summary>
public void CrossFade(AnimationClip aClip, TimeSpan fadeTime)
{
    StartTime = TimeSpan.Zero;
    CrossFade(aClip, fadeTime, InterpolationMode.Linear, InterpolationMode.Linear,
        InterpolationMode.Linear);
}

/// <summary>
/// Overloaded method for starting a tween between the current AnimationClip and a specific
/// time in another AnimationClip.
/// </summary>
public void CrossFade(AnimationClip aClip, TimeSpan fadeTime, TimeSpan aStartTime)
{
    StartTime = CheckClipDuration(aClip, aStartTime, false);
    CrossFade(aClip, fadeTime, InterpolationMode.Linear, InterpolationMode.Linear,
        InterpolationMode.Linear);
}

/// <summary>
/// Overloaded method for tweening between one AnimationClip and another with interpolation data.
/// </summary>
public void CrossFade(AnimationClip aClip,
                      TimeSpan fadeTime,
                      InterpolationMode translationInterpolation,
                      InterpolationMode orientationInterpolation,
                      InterpolationMode scaleInterpolation)
{
    if (crossFadeEnabled)
    {
        crossFadeInterpolationAmount = 0;
        crossFadeTime = TimeSpan.Zero;
        crossFadeElapsedTime = TimeSpan.Zero;

        StartClip(crossFadeAnimationClip, StartTime);
    }

    crossFadeAnimationClip = aClip;
    crossFadeTime = fadeTime;
    crossFadeElapsedTime = TimeSpan.Zero;

    crossFadeTranslationInterpolation = translationInterpolation;
    crossFadeOrientationInterpolation = orientationInterpolation;
    crossFadeScaleInterpolation = scaleInterpolation;

    crossFadeEnabled = true;
}

// *** add the following function to the AnimationController class

// check a time against a AnimationClip's duration
// return a TimeSpan of 0 or the AnimationClip's duration if the targetStartTime is greater than the clip's duration
public TimeSpan CheckClipDuration(AnimationClip aClip, TimeSpan targetStartTime, Boolean returnClosestTime)
{
    return targetStartTime < aClip.Duration ? targetStartTime : returnClosestTime ? aClip.Duration : TimeSpan.Zero;
}

// *** replace the UpdateCrossFadeTime() function in the AnimationController class with:

/// <summary>
/// Updates the CrossFade time
/// </summary>
/// <param name="elapsedTime">Time elapsed since the last update.</param>
private void UpdateCrossFadeTime(TimeSpan elapsedTime)
{
    crossFadeElapsedTime += elapsedTime;

    if (crossFadeElapsedTime > crossFadeTime)
    {
        crossFadeEnabled = false;
        crossFadeInterpolationAmount = 0;
        crossFadeTime = TimeSpan.Zero;
        crossFadeElapsedTime = TimeSpan.Zero;

        StartClip(crossFadeAnimationClip, StartTime);
    }
    else
        crossFadeInterpolationAmount = crossFadeElapsedTime.Ticks / (float) crossFadeTime.Ticks;
}


// *** replace the UpdateChannelPoses() function in the AnimationController class with:

/// <summary>
/// Updates the pose of all skeleton's bones.
/// </summary>
private void UpdateChannelPoses()
{
    AnimationChannel animationChannel;
    Pose channelPose;

    for (int i = 0; i < localBonePoses.Length; i++)
    {
        // Search for the current channel in the current animation clip
        string animationChannelName = skeleton[i].Name;
        if (animationClip.Channels.TryGetValue(animationChannelName, out animationChannel))
        {
            InterpolateChannelPose(animationChannel, time, out channelPose);
            localBonePoses[i] = channelPose;
        }

        // If CrossFade is enabled blend this channel in two animation clips
        if (crossFadeEnabled)
        {
            // Search for the current channel in the cross fade clip
            if (crossFadeAnimationClip.Channels.TryGetValue(animationChannelName,
                    out animationChannel))
            {
                InterpolateChannelPose(animationChannel, StartTime, out channelPose);
            }
            else
                channelPose = skeleton[i].BindPose;

            // Interpolate each channel with the cross fade animation
            localBonePoses[i] =
                Pose.Interpolate(localBonePoses[i], channelPose, crossFadeInterpolationAmount,
                    crossFadeTranslationInterpolation, crossFadeOrientationInterpolation,
                    crossFadeScaleInterpolation);
        }
    }
}
// --------------------------------------------------------------------------------------------

Just a small handfull of changes to the class and the tweening tothe middle of a new AnimationClip seems to be working pretty well.  There was a little wierdness when the CheckClipDuration() function was trying to approximate the closest possible time if the target start time was greater than the length of the target AnimationClip but as long as you feed it good numbers or don't tell it to approximate the closest possible time it runs pretty well.

I'll be testing it pretty hard over the next couple days so I'll keep you posted if I need to change any code.

- Neal