A key frame in an animation sequence defined by an AnimationTrack.
This class can be used as a basis for all kinds of key frames. The unifying principle is that multiple KeyFrames define an animation sequence, with the exact state of the animation being an interpolation between these key frames.
Example #1
0
        /// <summary>
        ///		Gets the 2 KeyFrame objects which are active at the time given, and the blend value between them.
        /// </summary>
        /// <remarks>
        ///		At any point in time  in an animation, there are either 1 or 2 keyframes which are 'active',
        ///		1 if the time index is exactly on a keyframe, 2 at all other times i.e. the keyframe before
        ///		and the keyframe after.
        /// </remarks>
        /// <param name="time">The time index in seconds.</param>
        /// <param name="keyFrame1">Receive the keyframe just before or at this time index.</param>
        /// <param name="keyFrame2">Receive the keyframe just after this time index.</param>
        /// <param name="firstKeyIndex">If supplied, will receive the index of the 'from' keyframe incase the caller needs it.</param>
        /// <returns>
        ///		Parametric value indicating how far along the gap between the 2 keyframes the time
        ///    value is, e.g. 0.0 for exactly at 1, 0.25 for a quarter etc. By definition the range of this
        ///    value is:  0.0 &lt;= returnValue &lt; 1.0 .
        ///</returns>
        public float GetKeyFramesAtTime(float time, out KeyFrame keyFrame1, out KeyFrame keyFrame2, out short firstKeyIndex)
        {
            short firstIndex  = -1;
            var   totalLength = this.parent.Length;

            // wrap time
            while (time > totalLength)
            {
                time -= totalLength;
            }

            var i = 0;

            // makes compiler happy so it wont complain about this var being unassigned
            keyFrame1 = null;

            // find the last keyframe before or on current time
            for (i = 0; i < this.keyFrameList.Count; i++)
            {
                var keyFrame = this.keyFrameList[i];

                // kick out now if the current frames time is greater than the current time
                if (keyFrame.Time > time)
                {
                    break;
                }

                keyFrame1 = keyFrame;
                ++firstIndex;
            }

            // trap case where there is no key before this time
            // use the first key anyway and pretend it's time index 0
            if (firstIndex == -1)
            {
                keyFrame1 = this.keyFrameList[0];
                ++firstIndex;
            }

            // fill index of the first key
            firstKeyIndex = firstIndex;

            // parametric time
            // t1 = time of previous keyframe
            // t2 = time of next keyframe
            float t1, t2;

            // find first keyframe after the time
            // if no next keyframe, wrap back to first
            // TODO: Verify logic
            if (firstIndex == (this.keyFrameList.Count - 1))
            {
                keyFrame2 = this.keyFrameList[0];
                t2        = totalLength;
            }
            else
            {
                keyFrame2 = this.keyFrameList[firstIndex + 1];
                t2        = keyFrame2.Time;
            }

            t1 = keyFrame1.Time;

            if (t1 == t2)
            {
                // same keyframe
                return(0.0f);
            }
            else
            {
                return((time - t1) / (t2 - t1));
            }
        }
Example #2
0
        /// <summary>
        ///		Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
        /// </summary>
        /// <remarks>
        ///		The KeyFrame objects held by this class are transformation snapshots at
        ///		discrete points in time. Normally however, you want to interpolate between these
        ///		keyframes to produce smooth movement, and this method allows you to do this easily.
        ///		In animation terminology this is called 'tweening'.
        /// </remarks>
        /// <param name="time">The time (in relation to the whole animation sequence).</param>
        ///<param name="kf"></param>
        ///<returns>
        ///		A new keyframe object containing the interpolated transforms. Note that the
        ///		position and scaling transforms are linearly interpolated (lerp), whilst the rotation is
        ///		spherically linearly interpolated (slerp) for the most natural result.
        /// </returns>
        public override KeyFrame GetInterpolatedKeyFrame(float time, KeyFrame kf)
        {
            // note: this is an un-attached keyframe
            var result = (TransformKeyFrame)kf;

            result.Time = time;

            // Keyframe pointers
            KeyFrame          kBase1, kBase2;
            TransformKeyFrame k1, k2;
            short             firstKeyIndex;

            var t = GetKeyFramesAtTime(time, out kBase1, out kBase2, out firstKeyIndex);

            k1 = (TransformKeyFrame)kBase1;
            k2 = (TransformKeyFrame)kBase2;

            if (t == 0.0f)
            {
                // just use k1
                result.Rotation  = k1.Rotation;
                result.Translate = k1.Translate;
                result.Scale     = k1.Scale;
            }
            else
            {
                // interpolate by t
                var mode = parent.InterpolationMode;
                var rim  = parent.RotationInterpolationMode;

                switch (mode)
                {
                case InterpolationMode.Linear:
                {
                    // linear interoplation
                    // Rotation
                    // Interpolate to nearest rotation if mUseShortestPath set
                    if (rim == RotationInterpolationMode.Linear)
                    {
                        result.Rotation = Quaternion.Nlerp(t, k1.Rotation, k2.Rotation, this.useShortestPath);
                    }
                    else         // RotationInterpolationMode.Spherical
                    {
                        result.Rotation = Quaternion.Slerp(t, k1.Rotation, k2.Rotation, this.useShortestPath);
                    }
                    result.Translate = k1.Translate + ((k2.Translate - k1.Translate) * t);
                    result.Scale     = k1.Scale + ((k2.Scale - k1.Scale) * t);
                }
                break;

                case InterpolationMode.Spline:
                {
                    // spline interpolation
                    if (this.isSplineRebuildNeeded)
                    {
                        BuildInterpolationSplines();
                    }

                    result.Rotation  = this.rotationSpline.Interpolate(firstKeyIndex, t, this.useShortestPath);
                    result.Translate = this.positionSpline.Interpolate(firstKeyIndex, t);
                    result.Scale     = this.scaleSpline.Interpolate(firstKeyIndex, t);
                }
                break;
                }
            }

            // return the resulting keyframe
            return(result);
        }
Example #3
0
 /// <summary>
 ///     This method in fact does nothing, since interpolation is not performed
 ///     inside the keyframes for this type of track.
 /// </summary>
 public override KeyFrame GetInterpolatedKeyFrame(float time, KeyFrame kf)
 {
     return(kf);
 }
Example #4
0
 /// <summary>
 ///     Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
 /// </summary>
 /// <remarks>
 ///    The KeyFrame objects held by this class are transformation snapshots at
 ///    discrete points in time. Normally however, you want to interpolate between these
 ///    keyframes to produce smooth movement, and this method allows you to do this easily.
 ///    In animation terminology this is called 'tweening'.
 /// </remarks>
 /// <param name="time">The time (in relation to the whole animation sequence)</param>
 ///	<param name="kf">Keyframe object to store results </param>
 public abstract KeyFrame GetInterpolatedKeyFrame(float time, KeyFrame kf);
Example #5
0
		public override KeyFrame GetInterpolatedKeyFrame( float timeIndex, KeyFrame kf )
		{
			NumericKeyFrame kret = (NumericKeyFrame)kf;

			// Keyframe pointers
			KeyFrame kBase1, kBase2;
			NumericKeyFrame k1, k2;
			short firstKeyIndex;

			float t = GetKeyFramesAtTime( timeIndex, out kBase1, out kBase2, out firstKeyIndex );
			k1 = (NumericKeyFrame)kBase1;
			k2 = (NumericKeyFrame)kBase2;

			if ( t == 0.0f )
			{
				// Just use k1
				kret.NumericValue = k1.NumericValue;
			}
			else
			{
				// Interpolate by t
				kret.NumericValue = AnimableValue.InterpolateValues( t, targetAnimable.Type,
																	k1.NumericValue, k2.NumericValue );
			}
			return kf;
		}
Example #6
0
	    /// <summary>
	    ///		Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
	    /// </summary>
	    /// <remarks>
	    ///		The KeyFrame objects held by this class are transformation snapshots at 
	    ///		discrete points in time. Normally however, you want to interpolate between these
	    ///		keyframes to produce smooth movement, and this method allows you to do this easily.
	    ///		In animation terminology this is called 'tweening'. 
	    /// </remarks>
	    /// <param name="time">The time (in relation to the whole animation sequence).</param>
	    ///<param name="kf"></param>
	    ///<returns>
	    ///		A new keyframe object containing the interpolated transforms. Note that the
	    ///		position and scaling transforms are linearly interpolated (lerp), whilst the rotation is
	    ///		spherically linearly interpolated (slerp) for the most natural result.
	    /// </returns>
	    public override KeyFrame GetInterpolatedKeyFrame( float time, KeyFrame kf )
		{
			// note: this is an un-attached keyframe
			TransformKeyFrame result = (TransformKeyFrame)kf;
			result.Time = time;

			// Keyframe pointers
			KeyFrame kBase1, kBase2;
			TransformKeyFrame k1, k2;
			short firstKeyIndex;

			float t = GetKeyFramesAtTime( time, out kBase1, out kBase2, out firstKeyIndex );
			k1 = (TransformKeyFrame)kBase1;
			k2 = (TransformKeyFrame)kBase2;

			if ( t == 0.0f )
			{
				// just use k1
				result.Rotation = k1.Rotation;
				result.Translate = k1.Translate;
				result.Scale = k1.Scale;
			}
			else
			{
				// interpolate by t
				InterpolationMode mode = parent.InterpolationMode;
				RotationInterpolationMode rim = parent.RotationInterpolationMode;

				switch ( mode )
				{
					case InterpolationMode.Linear:
						{
							// linear interoplation
							// Rotation
							// Interpolate to nearest rotation if mUseShortestPath set
							if ( rim == RotationInterpolationMode.Linear )
								result.Rotation = Quaternion.Nlerp( t, k1.Rotation, k2.Rotation, useShortestPath );
							else // RotationInterpolationMode.Spherical
								result.Rotation = Quaternion.Slerp( t, k1.Rotation, k2.Rotation, useShortestPath );
							result.Translate = k1.Translate + ( ( k2.Translate - k1.Translate ) * t );
							result.Scale = k1.Scale + ( ( k2.Scale - k1.Scale ) * t );

						}
						break;
					case InterpolationMode.Spline:
						{
							// spline interpolation
							if ( isSplineRebuildNeeded )
							{
								BuildInterpolationSplines();
							}

							result.Rotation = rotationSpline.Interpolate( firstKeyIndex, t, useShortestPath );
							result.Translate = positionSpline.Interpolate( firstKeyIndex, t );
							result.Scale = scaleSpline.Interpolate( firstKeyIndex, t );
						}
						break;

				}
			}

			// return the resulting keyframe
			return result;
		}
Example #7
0
		/// <summary>
		///		Gets the 2 KeyFrame objects which are active at the time given, and the blend value between them.
		/// </summary>
		/// <remarks>
		///		At any point in time  in an animation, there are either 1 or 2 keyframes which are 'active',
		///		1 if the time index is exactly on a keyframe, 2 at all other times i.e. the keyframe before
		///		and the keyframe after.
		/// </remarks>
		/// <param name="time">The time index in seconds.</param>
		/// <param name="keyFrame1">Receive the keyframe just before or at this time index.</param>
		/// <param name="keyFrame2">Receive the keyframe just after this time index.</param>
		/// <param name="firstKeyIndex">If supplied, will receive the index of the 'from' keyframe incase the caller needs it.</param>
		/// <returns>
		///		Parametric value indicating how far along the gap between the 2 keyframes the time
		///    value is, e.g. 0.0 for exactly at 1, 0.25 for a quarter etc. By definition the range of this 
		///    value is:  0.0 &lt;= returnValue &lt; 1.0 .
		///</returns>
		public float GetKeyFramesAtTime( float time, out KeyFrame keyFrame1, out KeyFrame keyFrame2, out short firstKeyIndex )
		{
			short firstIndex = -1;
			float totalLength = parent.Length;

			// wrap time
			while ( time > totalLength )
				time -= totalLength;

			int i = 0;

			// makes compiler happy so it wont complain about this var being unassigned
			keyFrame1 = null;

			// find the last keyframe before or on current time
			for ( i = 0; i < keyFrameList.Count; i++ )
			{
				KeyFrame keyFrame = keyFrameList[ i ];

				// kick out now if the current frames time is greater than the current time
				if ( keyFrame.Time > time )
					break;

				keyFrame1 = keyFrame;
				++firstIndex;
			}

			// trap case where there is no key before this time
			// use the first key anyway and pretend it's time index 0
			if ( firstIndex == -1 )
			{
				keyFrame1 = keyFrameList[ 0 ];
				++firstIndex;
			}

			// fill index of the first key
			firstKeyIndex = firstIndex;

			// parametric time
			// t1 = time of previous keyframe
			// t2 = time of next keyframe
			float t1, t2;

			// find first keyframe after the time
			// if no next keyframe, wrap back to first
			// TODO: Verify logic
			if ( firstIndex == ( keyFrameList.Count - 1 ) )
			{
				keyFrame2 = keyFrameList[ 0 ];
				t2 = totalLength;
			}
			else
			{
				keyFrame2 = keyFrameList[ firstIndex + 1 ];
				t2 = keyFrame2.Time;
			}

			t1 = keyFrame1.Time;

			if ( t1 == t2 )
			{
				// same keyframe
				return 0.0f;
			}
			else
			{
				return ( time - t1 ) / ( t2 - t1 );
			}
		}
Example #8
0
		/// <summary>
		///     Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
		/// </summary>
		/// <remarks>
		///    The KeyFrame objects held by this class are transformation snapshots at 
		///    discrete points in time. Normally however, you want to interpolate between these
		///    keyframes to produce smooth movement, and this method allows you to do this easily.
		///    In animation terminology this is called 'tweening'. 
        /// </remarks>
		/// <param name="time">The time (in relation to the whole animation sequence)</param>
		///	<param name="kf">Keyframe object to store results </param>
		public abstract KeyFrame GetInterpolatedKeyFrame( float time, KeyFrame kf );
Example #9
0
		/// <summary>
		///     This method in fact does nothing, since interpolation is not performed
		///  	inside the keyframes for this type of track. 
		/// </summary>
		public override KeyFrame GetInterpolatedKeyFrame( float time, KeyFrame kf )
		{
			return kf;
		}
Example #10
0
        /// <summary>
        ///		Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
        /// </summary>
        /// <remarks>
        ///		The KeyFrame objects held by this class are transformation snapshots at
        ///		discrete points in time. Normally however, you want to interpolate between these
        ///		keyframes to produce smooth movement, and this method allows you to do this easily.
        ///		In animation terminology this is called 'tweening'.
        /// </remarks>
        /// <param name="time">The time (in relation to the whole animation sequence).</param>
        /// <returns>
        ///		A new keyframe object containing the interpolated transforms. Note that the
        ///		position and scaling transforms are linearly interpolated (lerp), whilst the rotation is
        ///		spherically linearly interpolated (slerp) for the most natural result.
        /// </returns>
        public override KeyFrame GetInterpolatedKeyFrame(float time, KeyFrame kf)
        {
            // note: this is an un-attached keyframe
            TransformKeyFrame result = (TransformKeyFrame)kf;

            // Keyframe pointers
            KeyFrame          kBase1, kBase2;
            TransformKeyFrame k1, k2;
            ushort            firstKeyIndex;

            float t = GetKeyFramesAtTime(time, out kBase1, out kBase2, out firstKeyIndex);

            k1 = (TransformKeyFrame)kBase1;
            k2 = (TransformKeyFrame)kBase2;

            if (t == 0.0f)
            {
                // just use k1
                result.Rotation  = k1.Rotation;
                result.Translate = k1.Translate;
                result.Scale     = k1.Scale;
            }
            else
            {
                // interpolate by t
                InterpolationMode         mode = parent.interpolationMode;
                RotationInterpolationMode rim  = parent.rotationInterpolationMode;

                switch (mode)
                {
                case InterpolationMode.Linear: {
                    // linear interoplation
                    // Rotation
                    // Interpolate to nearest rotation if mUseShortestPath set
                    if (rim == RotationInterpolationMode.Linear)
                    {
                        Quaternion.NlerpRef(ref result.rotation, t, ref k1.rotation, ref k2.rotation, useShortestPath);
                    }
                    else     // RotationInterpolationMode.Spherical
                    {
                        result.rotation = Quaternion.Slerp(t, k1.rotation, k2.rotation, useShortestPath);
                    }
                    result.translate.x = k1.translate.x + ((k2.translate.x - k1.translate.x) * t);
                    result.translate.y = k1.translate.y + ((k2.translate.y - k1.translate.y) * t);
                    result.translate.z = k1.translate.z + ((k2.translate.z - k1.translate.z) * t);
                    result.scale.x     = k1.scale.x + ((k2.scale.x - k1.scale.x) * t);
                    result.scale.y     = k1.scale.y + ((k2.scale.y - k1.scale.y) * t);
                    result.scale.z     = k1.scale.z + ((k2.scale.z - k1.scale.z) * t);
                    result.Changed();
                }   break;

                case InterpolationMode.Spline: {
                    // spline interpolation
                    if (isSplineRebuildNeeded)
                    {
                        BuildInterpolationSplines();
                    }

                    result.Rotation  = rotationSpline.Interpolate(firstKeyIndex, t, useShortestPath);
                    result.Translate = positionSpline.Interpolate(firstKeyIndex, t);
                    result.Scale     = scaleSpline.Interpolate(firstKeyIndex, t);
                }   break;
                }
            }

            // return the resulting keyframe
            return(result);
        }
Example #11
0
        /// <summary>
        ///     Gets the 2 KeyFrame objects which are active at the time given, and the blend value between them.
        /// </summary>
        /// <remarks>
        ///     At any point in time  in an animation, there are either 1 or 2 keyframes which are 'active',
        ///     1 if the time index is exactly on a keyframe, 2 at all other times i.e. the keyframe before
        ///     and the keyframe after.
        /// </remarks>
        /// <param name="time">The time index in seconds.</param>
        /// <param name="keyFrame1">Receive the keyframe just before or at this time index.</param>
        /// <param name="keyFrame2">Receive the keyframe just after this time index.</param>
        /// <param name="firstKeyIndex">If supplied, will receive the index of the 'from' keyframe incase the caller needs it.</param>
        /// <returns>
        ///     Parametric value indicating how far along the gap between the 2 keyframes the time
        ///    value is, e.g. 0.0 for exactly at 1, 0.25 for a quarter etc. By definition the range of this
        ///    value is:  0.0 &lt;= returnValue &lt; 1.0 .
        ///</returns>
        public float GetKeyFramesAtTime(float time, out KeyFrame keyFrame1, out KeyFrame keyFrame2, out ushort firstKeyIndex)
        {
            short firstIndex  = -1;
            float totalLength = parent.Length;

            // wrap time
            while (time > totalLength)
            {
                time -= totalLength;
            }

            int i = 0;

            // makes compiler happy so it wont complain about this var being unassigned
            keyFrame1 = null;

            if (useKeyFrameIndex)
            {
                if (keyFrameIndex == null)
                {
                    rebuildKeyFrameIndex();
                }

                int index = Array.BinarySearch <float>(keyFrameIndex, time);
                if (index < 0)
                {
                    index = ~index - 1;
                    if (index < 0)
                    {
                        index = 0;
                    }
                }
                firstIndex = (short)index;
                keyFrame1  = keyFrameList[index];
            }
            else
            {
                // The old linear search lookup
                // find the last keyframe before or on current time
                for (i = 0; i < keyFrameList.Count; i++)
                {
                    KeyFrame keyFrame = keyFrameList[i];

                    // kick out now if the current frames time is greater than the current time
                    if (keyFrame.time > time)
                    {
                        break;
                    }

                    keyFrame1 = keyFrame;
                    ++firstIndex;
                }
            }

            // trap case where there is no key before this time
            // use the first key anyway and pretend it's time index 0
            if (firstIndex == -1)
            {
                keyFrame1 = keyFrameList[0];
                ++firstIndex;
            }

            // fill index of the first key
            firstKeyIndex = (ushort)firstIndex;

            // parametric time
            // t1 = time of previous keyframe
            // t2 = time of next keyframe
            float t1, t2;

            // find first keyframe after the time
            // if no next keyframe, wrap back to first
            // TODO: Verify logic
            if (firstIndex == (keyFrameList.Count - 1))
            {
                keyFrame2 = keyFrameList[0];
                t2        = totalLength;
            }
            else
            {
                keyFrame2 = keyFrameList[firstIndex + 1];
                t2        = keyFrame2.Time;
            }

            t1 = keyFrame1.Time;

            if (t1 == t2)
            {
                // same keyframe
                return(0.0f);
            }
            else
            {
                return((time - t1) / (t2 - t1));
            }
        }