private bool _CanReduce(_AnimCurve curve, _Keyframe key0, _Keyframe key1, float time, float allowedError) { float invT = Mathf.InverseLerp(key0.time, key1.time, time); switch (curve.KeyframeType) { case _AnimCurve.KFType.Position: case _AnimCurve.KFType.Scale: { Vector3 value = curve.Evaluate_Vector3(time); Vector3 v3Key0 = (Vector3)key0; Vector3 v3Key1 = (Vector3)key1; Vector3 interpolatedVal = Vector3.Lerp(v3Key0, v3Key1, invT); return(PositionDistanceError(value, interpolatedVal, allowedError)); } case _AnimCurve.KFType.Rotation: { Quaternion value = curve.Evaluate_Quaternion(time); Quaternion qKey0 = (Quaternion)key0; Quaternion qKey1 = (Quaternion)key1; Quaternion interpolatedVal = Quaternion.Lerp(qKey0, qKey1, invT); return(QuaternionDistanceError(value, interpolatedVal, allowedError)); } default: Dbg.LogErr("AnimCompress._CanReduce: unexpected KFtype: {0}", curve.KeyframeType); return(false); } }
public _Keyframe Clone() { int len = m_Kfs.Length; _Keyframe newKf = new _Keyframe(len); for (int i = 0; i < len; ++i) { newKf.m_Kfs[i] = m_Kfs[i]; } return(newKf); }
/// <summary> /// create an array of _Keyframe /// </summary> private void _InitKeyframes() { m_Keys = new _Keyframe[m_KeyCnt]; for (int i = 0; i < m_KeyCnt; ++i) //each key { var k = m_Keys[i] = new _Keyframe(m_Curves.Length); for (int cidx = 0; cidx < m_Curves.Length; ++cidx) //aggregate keys from each curve { k.Set(cidx, m_Kf4Curves[cidx][i]); } } }
private bool _CanReduce(_Keyframe fromKey, _Keyframe toKey, _AnimCurve curve, float allowedError, float delta, int firstKey, int lastKey, bool useKeyframeLimit) { float beginTime = fromKey.time; float endTime = toKey.time; bool canReduce = true; for (float t = beginTime + delta; t < endTime; t += delta) { if (!(canReduce = _CanReduce(curve, fromKey, toKey, t, allowedError))) { break; } } // we need to check that all keys can be reduced, because keys might be closer to each other than delta // this happens when we have steps in curve // TODO : we could skip the loop above if keyframes are close enough float lastTime = beginTime; for (int j = firstKey; canReduce && j < lastKey; ++j) { float time = curve.Get(j).time; // validates point at keyframe (j) and point between (j) and and (j-1) keyframes // TODO : For checking point at "time" it could just use keys[j].value instead - that would be faster than sampling the curve canReduce = _CanReduce(curve, fromKey, toKey, time, allowedError) && _CanReduce(curve, fromKey, toKey, (lastTime + time) / 2, allowedError); lastTime = time; } if (canReduce) { // validate point between last two keyframes float time = curve.Get(lastKey).time; canReduce = _CanReduce(curve, fromKey, toKey, (lastTime + time) / 2, allowedError); } // Don't reduce if we are about to reduce more than 50 samples at // once to prevent n^2 performance impact canReduce = canReduce && (!useKeyframeLimit || (endTime - beginTime < 50.0F * delta)); return(canReduce); }
// apply the data from _Keyframe[] to stored AnimationCurve[] public void ApplyToUnderlyingCurves() { for (int curveIdx = 0; curveIdx < m_Curves.Length; ++curveIdx) { AnimationCurve ac = m_Curves[curveIdx]; Keyframe[] tmpKeys = new Keyframe[m_KeyCnt]; for (int keyIdx = 0; keyIdx < m_KeyCnt; ++keyIdx) { _Keyframe compKey = m_Keys[keyIdx]; tmpKeys[keyIdx] = compKey.Get(curveIdx); } ac.keys = tmpKeys; //Dbg.Log("ac.length = {0}", ac.length); AnimationUtility.SetEditorCurve(m_Clip, m_Bindings[curveIdx], ac); //set back } }
private float _ReduceKeyFrames(_AnimCurve curve, float sampleRate, float allowedError) { int keycnt = curve.KeyCnt; if (keycnt <= 2) { return(100f); } float delta = 1f / sampleRate; List <_Keyframe> keyLst = new List <_Keyframe>(keycnt); _Keyframe[] keys = curve.keys; _Keyframe kfStart = keys[0].Clone(); _Keyframe kfEnd = keys[keycnt - 1].Clone(); // at first try reducing to const curve kfStart.inTangent = kfStart.outTangent = 0f; kfEnd.inTangent = kfEnd.outTangent = 0f; kfEnd.value = kfStart.value; bool bCanReduceToConstCurve = _CanReduce(kfStart, kfEnd, curve, allowedError, delta, 0, keycnt - 1, false); if (bCanReduceToConstCurve) { keyLst.Add(kfStart); keyLst.Add(kfEnd); } else { keyLst.Capacity = keycnt; keyLst.Add(keys[0]); int lastUsedKey = 0; for (int i = 1; i < keycnt - 1; i++) { _Keyframe fromKey = keys[lastUsedKey]; _Keyframe toKey = keys[i + 1]; //FitTangentsToCurve(curve, fromKey, toKey); bool canReduce = _CanReduce(fromKey, toKey, curve, allowedError, delta, lastUsedKey + 1, i + 1, true); if (!canReduce) { keyLst.Add(keys[i]); // fitting tangents between last two keys //FitTangentsToCurve(curve, *(output.end() - 2), output.back()); lastUsedKey = i; } } // We always add the last key keyLst.Add(keys[keycnt - 1]); // fitting tangents between last and the one before last keys //FitTangentsToCurve(curve, *(output.end() - 2), output.back()); } float compressRatio = (float)keyLst.Count / (float)keycnt * 100.0F; if (m_Verbose) { Dbg.Log("compressRatio = {0}%, {1}/{2}\t{3} : {4}", compressRatio, keyLst.Count, keycnt, curve.Binding0.path, curve.Binding0.propertyName); } curve.keys = keyLst.ToArray(); return(compressRatio); }