/// <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 <= returnValue < 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)); } }
/// <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); }
/// <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); }
/// <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);
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; }
/// <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; }
/// <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 <= returnValue < 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 ); } }
/// <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 );
/// <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; }
/// <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); }
/// <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 <= returnValue < 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)); } }