public void InvalidateTime() { reachedEnd = false; currentKeyFrame = keyFrames.GetEnumerator(); var animationInitialValues = new AnimationInitialValues <float>(); // Skip two elements (right before third) currentKeyFrame.MoveNext(); animationInitialValues.Value1 = currentKeyFrame.Current; currentKeyFrame.MoveNext(); animationInitialValues.Value2 = currentKeyFrame.Current; currentTime = animationInitialValues.Value1.Time; InitializeAnimation(ref data, ref animationInitialValues); }
protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location) { SetTime(ref channel, newTime); var currentTime = channel.CurrentTime; var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; // Extract data int timeStart = keyFrames[currentIndex + 0].Time.Ticks; int timeEnd = keyFrames[currentIndex + 1].Time.Ticks; // Compute interpolation factor float t = ((float)currentTime.Ticks - (float)timeStart) / ((float)timeEnd - (float)timeStart); if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { Interpolator.Quaternion.Cubic( ref keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value, ref keyFramesItems[currentIndex].Value, ref keyFramesItems[currentIndex + 1].Value, ref keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value, t, out *(Quaternion *)(location + channel.Offset)); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { Interpolator.Quaternion.SphericalLinear( ref keyFramesItems[currentIndex].Value, ref keyFramesItems[currentIndex + 1].Value, t, out *(Quaternion *)(location + channel.Offset)); } else { throw new NotImplementedException(); } }
protected static void SetTime(ref Channel channel, CompressedTimeSpan newTime) { var currentTime = channel.CurrentTime; if (newTime == currentTime) { return; } var currentIndex = channel.CurrentIndex; var keyFrames = channel.Curve.KeyFrames; var keyFramesItems = keyFrames.Items; var keyFramesCount = keyFrames.Count; if (newTime > currentTime) { while (currentIndex + 1 < keyFramesCount - 1 && newTime >= keyFramesItems[currentIndex + 1].Time) { ++currentIndex; } } else if (newTime <= keyFramesItems[0].Time) { // Special case: fast rewind to beginning of animation currentIndex = 0; } else // newTime < currentTime { while (currentIndex - 1 >= 0 && newTime < keyFramesItems[currentIndex].Time) { --currentIndex; } } channel.CurrentIndex = currentIndex; channel.CurrentTime = newTime; }
protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr location, float factor) { if (channel.InterpolationType == AnimationCurveInterpolationType.Cubic) { *(float *)(location + channel.Offset) = Interpolator.Cubic( channel.ValuePrev.Value, channel.ValueStart.Value, channel.ValueEnd.Value, channel.ValueNext.Value, factor); } else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear) { *(float *)(location + channel.Offset) = Interpolator.Linear( channel.ValueStart.Value, channel.ValueEnd.Value, factor); } else { throw new NotImplementedException(); } }
/// <summary> /// Evaluates the error within specified segment. /// </summary> /// <param name="originalCurve">The original curve.</param> /// <param name="evaluator">The evaluator.</param> /// <param name="stepSize">Size of the step.</param> /// <param name="keyFrame">The key frame.</param> /// <param name="nextKeyFrame">The next key frame.</param> /// <returns></returns> private KeyValuePair <CompressedTimeSpan, float> EvaluateError(Func <CompressedTimeSpan, float> originalCurve, Evaluator evaluator, CompressedTimeSpan stepSize, KeyFrameData <float> keyFrame, KeyFrameData <float> nextKeyFrame) { var startTime = keyFrame.Time; var endTime = nextKeyFrame.Time; var biggestDifference = 0.0f; var biggestDifferenceTime = startTime; // Rounds up start time (i.e. startTime is multiple of stepSize) startTime = new CompressedTimeSpan((startTime.Ticks / stepSize.Ticks + 1) * stepSize.Ticks); for (var time = startTime; time < endTime; time += stepSize) { var difference = Math.Abs(originalCurve(time) - evaluator.Evaluate(time)); if (difference > biggestDifference) { biggestDifference = difference; biggestDifferenceTime = time; } } return(new KeyValuePair <CompressedTimeSpan, float>(biggestDifferenceTime, biggestDifference)); }
protected void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr location) { var startTime = channel.ValueStart.Time; // Sampling before start (should not really happen because we add a keyframe at TimeSpan.Zero, but let's keep it in case it changes later. if (currentTime <= startTime) { Utilities.UnsafeWrite(location + channel.Offset, ref channel.ValueStart.Value); return; } var endTime = channel.ValueEnd.Time; // Sampling after end if (currentTime >= endTime) { Utilities.UnsafeWrite(location + channel.Offset, ref channel.ValueEnd.Value); return; } float factor = (float)(currentTime.Ticks - startTime.Ticks) / (float)(endTime.Ticks - startTime.Ticks); ProcessChannel(ref channel, currentTime, location, factor); }
/// <summary> /// Writes a new value at the end of the curve (used for building curves). /// It should be done in increasing order as it will simply add a new key at the end of <see cref="AnimationCurve{T}.KeyFrames"/>. /// </summary> /// <param name="newTime">The new time.</param> /// <param name="location">The location.</param> public abstract void AddValue(CompressedTimeSpan newTime, IntPtr location);
public abstract void Evaluate(CompressedTimeSpan newTime, object[] results);
public abstract void Evaluate(CompressedTimeSpan newTime, IntPtr location);
public override void Evaluate(CompressedTimeSpan newTime, object[] results) { throw new NotImplementedException(); }
protected abstract void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr location, float factor);
protected abstract void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location);
public void Fitting(Func <CompressedTimeSpan, float> originalCurve, CompressedTimeSpan stepSize, float maxErrorThreshold) { // Some info: http://wscg.zcu.cz/wscg2008/Papers_2008/full/A23-full.pdf // Compression of Temporal Video Data by Catmull-Rom Spline and Quadratic Bézier Curve Fitting // And: http://bitsquid.blogspot.jp/2009/11/bitsquid-low-level-animation-system.html // Only one or zero keys: no need to do anything. if (KeyFrames.Count <= 1) { return; } var keyFrames = new LinkedList <KeyFrameData <float> >(this.KeyFrames); var evaluator = new Evaluator(keyFrames); // Compute initial errors (using Least Square Equation) var errors = new LinkedList <ErrorNode>(); //var errors = new List<float>(); var errorQueue = new PriorityQueue <LinkedListNode <ErrorNode> >(new ErrorComparer()); foreach (var keyFrame in keyFrames.EnumerateNodes()) { if (keyFrame.Next == null) { break; } var error = EvaluateError(originalCurve, evaluator, stepSize, keyFrame.Value, keyFrame.Next.Value); var errorNode = errors.AddLast(new ErrorNode(keyFrame, error.Key, error.Value)); errorQueue.Enqueue(errorNode); } //for (int keyFrame = 0; keyFrame < KeyFrames.Count - 1; ++keyFrame) //{ // //errors.Add(EvaluateError(originalCurve, evaluator, stepSize, keyFrame)); // var errorNode = errors.AddLast(new ErrorNode(EvaluateError(originalCurve, evaluator, stepSize, keyFrame))); // errorQueue.Enqueue(errorNode); //} while (true) { // Already reached enough subdivisions var highestError = errorQueue.Dequeue(); if (highestError.Value.Error <= maxErrorThreshold) { break; } //// Find segment to optimize //var biggestErrorIndex = 0; //for (int keyFrame = 1; keyFrame < KeyFrames.Count - 1; ++keyFrame) //{ // if (errors[keyFrame] > errors[biggestErrorIndex]) // biggestErrorIndex = keyFrame; //} //// Already reached enough subdivisions //if (errors[biggestErrorIndex] <= maxErrorThreshold) // break; // Create new key (use middle point, but better heuristic might improve situation -- like point with biggest error) //var middleTime = (start.Value.Time + end.Value.Time) / 2; var middleTime = highestError.Value.BiggestDeltaTime; //KeyFrames.Insert(biggestErrorIndex + 1, new KeyFrameData<float> { Time = middleTime, Value = originalCurve(middleTime) }); var newKeyFrame = keyFrames.AddAfter(highestError.Value.KeyFrame, new KeyFrameData <float> { Time = middleTime, Value = originalCurve(middleTime) }); //errors.Insert(biggestErrorIndex + 1, 0.0f); var newError = errors.AddAfter(highestError, new ErrorNode(newKeyFrame, CompressedTimeSpan.Zero, 0.0f)); var highestErrorLastUpdate = newError; if (highestErrorLastUpdate.Next != null) { highestErrorLastUpdate = highestErrorLastUpdate.Next; } // Invalidate evaluator (data changed) evaluator.InvalidateTime(); // Update errors for (var error = highestError.Previous ?? highestError; error != null; error = error.Next) { if (error != highestError && error != newError) { errorQueue.Remove(error); } var errorInfo = EvaluateError(originalCurve, evaluator, stepSize, error.Value.KeyFrame.Value, error.Value.KeyFrame.Next.Value); error.Value.BiggestDeltaTime = errorInfo.Key; error.Value.Error = errorInfo.Value; errorQueue.Enqueue(error); if (error == highestErrorLastUpdate) { break; } } } KeyFrames = new List <KeyFrameData <float> >(keyFrames); }
public ErrorNode(LinkedListNode <KeyFrameData <float> > keyFrame, CompressedTimeSpan biggestDeltaTime, float error) { KeyFrame = keyFrame; BiggestDeltaTime = biggestDeltaTime; Error = error; }