Example #1
0
            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 void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, UpdateObjectData[] objects)
        {
            if (channel.Offset == -1)
            {
                return;
            }

            var startTime = channel.ValueStart.Time;

            // TODO: Should we really do that?
            // 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)
            {
                objects[channel.Offset].Value = channel.ValueStart.Value;
                return;
            }

            // (This including sampling after end)
            objects[channel.Offset].Value = channel.ValueStart.Value;
        }
Example #3
0
        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)
            {
                *(float *)(location + channel.Offset) = Interpolator.Cubic(
                    keyFramesItems[currentIndex > 0 ? currentIndex - 1 : 0].Value,
                    keyFramesItems[currentIndex].Value,
                    keyFramesItems[currentIndex + 1].Value,
                    keyFramesItems[currentIndex + 2 >= keyFramesCount ? currentIndex + 1 : currentIndex + 2].Value,
                    t);
            }
            else if (channel.InterpolationType == AnimationCurveInterpolationType.Linear)
            {
                *(float *)(location + channel.Offset) = MathUtil.Lerp(keyFramesItems[currentIndex].Value, keyFramesItems[currentIndex + 1].Value, t);
            }
            else if (channel.InterpolationType == AnimationCurveInterpolationType.Constant)
            {
                *(float *)(location + channel.Offset) = keyFrames[currentIndex].Value;
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Example #4
0
        /// <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));
        }
Example #5
0
        public float EvaluateCubic(CompressedTimeSpan time)
        {
            int keyIndex;

            for (keyIndex = 0; keyIndex < KeyFrames.Count - 1; ++keyIndex)
            {
                if (time < KeyFrames[keyIndex + 1].Time)
                {
                    break;
                }
            }

            // TODO: Check if before or after curve (and on those limits)

            // Exact value, just returns it.
            if (time == KeyFrames[keyIndex].Time)
            {
                return(KeyFrames[keyIndex].Value);
            }

            // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Interpolation_on_the_unit_interval_without_exact_derivatives
            long timeStart = KeyFrames[keyIndex + 0].Time.Ticks;
            long timeEnd   = KeyFrames[keyIndex + 1].Time.Ticks;

            // Compute interpolation factor and avoid NaN operations when timeStart >= timeEnd
            float t = (timeEnd <= timeStart) ? 0 : ((float)time.Ticks - (float)timeStart) / ((float)timeEnd - (float)timeStart);

            var evaluator = new EvaluatorData();

            evaluator.ValuePrev  = KeyFrames[keyIndex > 0 ? keyIndex - 1 : 0];
            evaluator.ValueStart = KeyFrames[keyIndex + 0];
            evaluator.ValueEnd   = KeyFrames[keyIndex + 1];
            evaluator.ValueNext  = KeyFrames[keyIndex + 2 < KeyFrames.Count ? keyIndex + 2 : KeyFrames.Count - 1];

            return(evaluator.Evaluate(t));
        }
 protected abstract void ProcessChannel(ref Channel channel, CompressedTimeSpan newTime, IntPtr location);
Example #7
0
        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);
        }
Example #8
0
 public ErrorNode(LinkedListNode <KeyFrameData <float> > keyFrame, CompressedTimeSpan biggestDeltaTime, float error)
 {
     KeyFrame         = keyFrame;
     BiggestDeltaTime = biggestDeltaTime;
     Error            = error;
 }
Example #9
0
 /// <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);
 protected abstract void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr data, float factor);
Example #11
0
 protected unsafe override void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr location, float factor)
 {
     *(int *)(location + channel.Offset) = channel.ValueStart.Value;
 }
Example #12
0
 public KeyFrameData(CompressedTimeSpan time, T value)
 {
     Time  = time;
     Value = value;
 }
 protected override unsafe void ProcessChannel(ref Channel channel, CompressedTimeSpan currentTime, IntPtr location, float factor)
 {
     Interop.CopyInline((void *)(location + channel.Offset), ref channel.ValueStart.Value);
 }
Example #14
0
 /// <summary>
 /// Shifts all animation keys by the specified time, adding it to all <see cref="KeyFrameData.Time"/>
 /// </summary>
 /// <param name="shiftTimeSpan">The time span by which the keys should be shifted</param>
 public virtual void ShiftKeys(CompressedTimeSpan shiftTimeSpan)
 {
 }
Example #15
0
 public abstract void Evaluate(CompressedTimeSpan newTime, IntPtr data, UpdateObjectData[] objects);