public static void GetMaxError(KeyAnalyzer line, KeyAnalyzer newKeys, out float error, out int index) { var fcount = Math.Min(newKeys.FrameCount, line.FrameCount); error = 0; index = -1; for (int i = 0; i < fcount; i++) { var orgValue = line.GetValue(i); var newValue = newKeys.GetValue(i); var er = Math.Abs(newValue - orgValue); if (er > error) { error = er; index = i; } } }
/// <summary> /// /// </summary> /// <param name="values"></param> public static List <FOBJKey> Simplify(List <FOBJKey> keys, float maxError) { // check constant key track{ { bool keyTrack = true; for (int i = 1; i < keys.Count; i++) { if (Math.Abs(keys[i].Value - keys[0].Value) > 0.001f) { keyTrack = false; break; } } if (keyTrack) { var key = keys[0]; key.InterpolationType = HSDRaw.Common.Animation.GXInterpolationType.HSD_A_OP_KEY; keys.Clear(); keys.Add(key); return(keys); } } CalculateSlopes(keys); var newKeys = new List <FOBJKey>(); // find constant and linear keys { newKeys.Add(CloneKey(keys[0])); for (int i = 1; i < keys.Count - 2; i++) { // this key is constant, so skip if (Math.Abs(keys[i].Value - keys[i - 1].Value) < 0.00001f) { continue; } // step check if (Math.Abs(keys[i].Value - keys[i + 1].Value) < 0.00001f) { /*var nkey = CloneKey(keys[i]); * nkey.InterpolationType = HSDRaw.Common.Animation.GXInterpolationType.HSD_A_OP_CON; * nkey.Tan = 0; * newKeys.Add(nkey);*/ continue; } //TODO: lerp detection // linear check /*float FrameDiff = (i + 1) - keys[i].Frame; * float Weight = FrameDiff / (keys[i + 1].Frame - keys[i].Frame); * * var lerpIndex = i + 1; * while (lerpIndex < keys.Count - 1 && AnimationInterpolationHelper.Lerp(keys[i].Value, keys[lerpIndex].Value, Weight) == keys[lerpIndex].Value) * { * lerpIndex++; * FrameDiff = lerpIndex - keys[i].Frame; * Weight = FrameDiff / (keys[lerpIndex].Frame - keys[i].Frame); * } * * // if this key is linear then skip linear keys and change interpolation type * if (lerpIndex > i + 2) * { * System.Diagnostics.Debug.WriteLine("Linear"); * var nkey = CloneKey(keys[i]); * nkey.InterpolationType = HSDRaw.Common.Animation.GXInterpolationType.HSD_A_OP_LIN; * nkey.Tan = 0; * i = lerpIndex - 1; * newKeys.Add(nkey); * }*/ } newKeys.Add(CloneKey(keys[keys.Count - 1])); } // prepare analyzing var analyzer1 = new KeyAnalyzer(keys); var analyzer2 = new KeyAnalyzer(newKeys); // insert keys until error is reduced within threshold float error = float.MaxValue; var index = 0; int prevIndex = 0; while (error > maxError) { KeyAnalyzer.GetMaxError(analyzer1, analyzer2, out error, out index); if (index == prevIndex || index >= keys.Count || index < 0) { break; } var loc = newKeys.FindIndex(e => keys[index].Frame <= e.Frame); if (loc != -1) { newKeys.Insert(loc, CloneKey(keys[index])); } else { break; } prevIndex = index; } int removedLinear = 0; // use linear where possible { for (int j = 0; j < newKeys.Count - 1; j++) { var k = newKeys[j]; if ((newKeys[j + 1].Frame - k.Frame) <= 2) { k.InterpolationType = HSDRaw.Common.Animation.GXInterpolationType.HSD_A_OP_LIN; removedLinear++; } } //Console.WriteLine($"Removed {removed} keys"); } int removed = 0; // cleanup pass, make sure all frames are necessary List <FOBJKey> finalKeys = new List <FOBJKey>(); finalKeys.Add(keys[0]); for (int j = 1; j < newKeys.Count - 1; j++) { var k = newKeys[j]; analyzer1 = new KeyAnalyzer(newKeys.Where(e => e != k).ToList()); KeyAnalyzer.GetMaxError(analyzer1, analyzer2, out error, out index); if (error < maxError) { removed++; } else { finalKeys.Add(k); } } finalKeys.Add(keys[keys.Count - 1]); newKeys = finalKeys; Debug.WriteLine($"Removed {removed} keys converted {removedLinear} to linear"); // make sure to always have final key //if (!newKeys.Contains(keys[keys.Count - 1])) // newKeys.Add(keys[keys.Count - 1]); return(newKeys); }