Example #1
0
        /// lhsIndex and rhsIndex are the indices in the keys array. The lhsIndex/rhsIndex may be -1, in which it creates a synthetic first key
        /// at startTime, or beyond the length of the array, in which case it creates a synthetic key at endTime.
        static private Keyframe EvalKeyAtTime([DisallowNull] NativeArray <Keyframe> keys, int lhsIndex, int rhsIndex, float startTime, float endTime, float currTime)
        {
            var lhsKey = KeyframeUtility.FetchKeyFromIndexClampEdge(keys, lhsIndex, startTime, endTime);
            var rhsKey = KeyframeUtility.FetchKeyFromIndexClampEdge(keys, rhsIndex, startTime, endTime);

            float currValue;
            float currDeriv;

            KeyframeUtility.EvalCurveSegmentAndDeriv(out currValue, out currDeriv, lhsKey, rhsKey, currTime);

            return(new Keyframe(currTime, currValue, currDeriv, currDeriv));
        }
Example #2
0
        /// <summary>
        /// Interpolates two AnimationCurves. Since both curves likely have control points at different places
        /// in the curve, this method will create a new curve from the union of times between both curves. However, to avoid creating
        /// garbage, this function will always replace the keys of lhsAndResultCurve with the final result, and return lhsAndResultCurve.
        /// </summary>
        /// <param name="lhsAndResultCurve">The start value. Additionaly, this instance will be reused and returned as the result.</param>
        /// <param name="rhsCurve">The end value.</param>
        /// <param name="t">The interpolation factor in range [0,1].</param>
        static public void InterpAnimationCurve(ref AnimationCurve lhsAndResultCurve, [DisallowNull] AnimationCurve rhsCurve, float t)
        {
            if (t <= 0.0f || rhsCurve.length == 0)
            {
                // no op. lhsAndResultCurve is already the result
            }
            else if (t >= 1.0f || lhsAndResultCurve.length == 0)
            {
                // In this case the obvous solution would be to return the rhsCurve. BUT (!) the lhsCurve and rhsCurve are different. This function is
                // called by:
                //      stateParam.Interp(stateParam, toParam, interpFactor);
                //
                // stateParam (lhsCurve) is a temporary in/out parameter, but toParam (rhsCurve) might point to the original component, so it's unsafe to
                // change that data. Thus, we need to copy the keys from the rhsCurve to the lhsCurve instead of returning rhsCurve.
                KeyframeUtility.ResetAnimationCurve(lhsAndResultCurve);

                for (int i = 0; i < rhsCurve.length; i++)
                {
                    lhsAndResultCurve.AddKey(rhsCurve[i]);
                }
                lhsAndResultCurve.postWrapMode = rhsCurve.postWrapMode;
                lhsAndResultCurve.preWrapMode  = rhsCurve.preWrapMode;
            }
            else
            {
                // Note: If we reached this code, we are guaranteed that both lhsCurve and rhsCurve are valid with at least 1 key

                // create a native array for the temp keys to avoid GC
                var lhsCurveKeys = new NativeArray <Keyframe>(lhsAndResultCurve.length, Allocator.Temp);
                var rhsCurveKeys = new NativeArray <Keyframe>(rhsCurve.length, Allocator.Temp);

                for (int i = 0; i < lhsAndResultCurve.length; i++)
                {
                    lhsCurveKeys[i] = lhsAndResultCurve[i];
                }

                for (int i = 0; i < rhsCurve.length; i++)
                {
                    rhsCurveKeys[i] = rhsCurve[i];
                }

                float startTime = Mathf.Min(lhsCurveKeys[0].time, rhsCurveKeys[0].time);
                float endTime   = Mathf.Max(lhsCurveKeys[lhsAndResultCurve.length - 1].time, rhsCurveKeys[rhsCurve.length - 1].time);

                // we don't know how many keys the resulting curve will have (because we will compact keys that are at the exact
                // same time), but in most cases we will need the worst case number of keys. So allocate the worst case.
                int maxNumKeys  = lhsAndResultCurve.length + rhsCurve.length;
                int currNumKeys = 0;
                var dstKeys     = new NativeArray <Keyframe>(maxNumKeys, Allocator.Temp);

                int lhsKeyCurr = 0;
                int rhsKeyCurr = 0;

                while (lhsKeyCurr < lhsCurveKeys.Length || rhsKeyCurr < rhsCurveKeys.Length)
                {
                    // the index is considered invalid once it goes off the end of the array
                    bool lhsValid = lhsKeyCurr < lhsCurveKeys.Length;
                    bool rhsValid = rhsKeyCurr < rhsCurveKeys.Length;

                    // it's actually impossible for lhsKey/rhsKey to be uninitialized, but have to
                    // add initialize here to prevent compiler erros
                    var lhsKey = new Keyframe();
                    var rhsKey = new Keyframe();
                    if (lhsValid && rhsValid)
                    {
                        lhsKey = GetKeyframeAndClampEdge(lhsCurveKeys, lhsKeyCurr);
                        rhsKey = GetKeyframeAndClampEdge(rhsCurveKeys, rhsKeyCurr);

                        if (lhsKey.time == rhsKey.time)
                        {
                            lhsKeyCurr++;
                            rhsKeyCurr++;
                        }
                        else if (lhsKey.time < rhsKey.time)
                        {
                            // in this case:
                            //     rhsKey[curr-1].time <= lhsKey.time <= rhsKey[curr].time
                            // so interpolate rhsKey at the lhsKey.time.
                            rhsKey = KeyframeUtility.EvalKeyAtTime(rhsCurveKeys, rhsKeyCurr - 1, rhsKeyCurr, startTime, endTime, lhsKey.time);
                            lhsKeyCurr++;
                        }
                        else
                        {
                            // only case left is (lhsKey.time > rhsKey.time)
                            Assert.IsTrue(lhsKey.time > rhsKey.time);

                            // this is the reverse of the lhs key case
                            //     lhsKey[curr-1].time <= rhsKey.time <= lhsKey[curr].time
                            // so interpolate lhsKey at the rhsKey.time.
                            lhsKey = KeyframeUtility.EvalKeyAtTime(lhsCurveKeys, lhsKeyCurr - 1, lhsKeyCurr, startTime, endTime, rhsKey.time);
                            rhsKeyCurr++;
                        }
                    }
                    else if (lhsValid)
                    {
                        // we are still processing lhsKeys, but we are out of rhsKeys, so increment lhs and evaluate rhs
                        lhsKey = GetKeyframeAndClampEdge(lhsCurveKeys, lhsKeyCurr);

                        // rhs will be evaluated between the last rhs key and the extrapolated rhs key at the end time
                        rhsKey = KeyframeUtility.EvalKeyAtTime(rhsCurveKeys, rhsKeyCurr - 1, rhsKeyCurr, startTime, endTime, lhsKey.time);

                        lhsKeyCurr++;
                    }
                    else
                    {
                        // either lhsValid is True, rhsValid is True, or they are both True. So to miss the first two cases,
                        // right here rhsValid must be true.
                        Assert.IsTrue(rhsValid);

                        // we still have rhsKeys to lerp, but we are out of lhsKeys, to increment rhs and evaluate lhs
                        rhsKey = GetKeyframeAndClampEdge(rhsCurveKeys, rhsKeyCurr);

                        // lhs will be evaluated between the last lhs key and the extrapolated lhs key at the end time
                        lhsKey = KeyframeUtility.EvalKeyAtTime(lhsCurveKeys, lhsKeyCurr - 1, lhsKeyCurr, startTime, endTime, rhsKey.time);

                        rhsKeyCurr++;
                    }

                    var dstKey = KeyframeUtility.LerpSingleKeyframe(lhsKey, rhsKey, t);
                    dstKeys[currNumKeys] = dstKey;
                    currNumKeys++;
                }

                // Replace the keys in lhsAndResultCurve with our interpolated curve.
                KeyframeUtility.ResetAnimationCurve(lhsAndResultCurve);
                for (int i = 0; i < currNumKeys; i++)
                {
                    lhsAndResultCurve.AddKey(dstKeys[i]);
                }

                dstKeys.Dispose();
            }
        }