18 public static class DefaultCurve {
20 public static AnimationCurve Zero {
22 AnimationCurve curve =
new AnimationCurve();
29 public static AnimationCurve One {
31 AnimationCurve curve =
new AnimationCurve();
38 public static AnimationCurve LinearUp {
40 AnimationCurve curve =
new AnimationCurve();
41 curve.AddKey(
new Keyframe(0, 0, 1, 1));
42 curve.AddKey(
new Keyframe(1, 1, 1, 1));
47 public static AnimationCurve LinearDown {
49 AnimationCurve curve =
new AnimationCurve();
50 curve.AddKey(
new Keyframe(0, 1, -1, -1));
51 curve.AddKey(
new Keyframe(1, 0, -1, -1));
56 public static AnimationCurve SigmoidUp {
58 AnimationCurve curve =
new AnimationCurve();
59 curve.AddKey(
new Keyframe(0, 0, 0, 0));
60 curve.AddKey(
new Keyframe(1, 1, 0, 0));
65 public static AnimationCurve SigmoidDown {
67 AnimationCurve curve =
new AnimationCurve();
68 curve.AddKey(
new Keyframe(0, 1, 0, 0));
69 curve.AddKey(
new Keyframe(1, 0, 0, 0));
74 public static AnimationCurve SigmoidUpDown {
76 AnimationCurve curve =
new AnimationCurve();
77 curve.AddKey(
new Keyframe(0, 0, 0, 0));
78 curve.AddKey(
new Keyframe(0.5f, 1, 0, 0));
79 curve.AddKey(
new Keyframe(1, 0, 0, 0));
85 public static class AnimationCurveUtil {
87 public static bool IsConstant(
this AnimationCurve curve) {
88 var keys = curve.keys;
90 for (
int i = 0; i < keys.Length; i++) {
93 if (!Mathf.Approximately(first.value, key.value)) {
97 if (!Mathf.Approximately(key.inTangent, 0) && !
float.IsInfinity(key.inTangent)) {
101 if (!Mathf.Approximately(key.outTangent, 0) && !
float.IsInfinity(key.outTangent)) {
108 public static bool ContainsKeyAtTime(
this AnimationCurve curve,
float time,
float tolerance = 0.0000001f) {
109 return curve.keys.Query().Any(k => Mathf.Abs(k.time - time) < tolerance);
112 public static AnimationCurve GetCropped(
this AnimationCurve curve,
float start,
float end,
bool slideToStart =
true) {
113 AnimationCurve newCurve =
new AnimationCurve();
116 Keyframe? latestBeforeStart =
null;
117 var keys = curve.keys;
118 for (
int i = 0; i < keys.Length; i++) {
120 if (key.time >= start)
break;
122 latestBeforeStart = key;
126 for (
int i = keys.Length; i-- != 0;) {
127 if (keys[i].time < start || keys[i].time > end) {
132 bool alreadyHasZero =
false;
133 for (
int i = 0; i < keys.Length; i++) {
135 if (key.time >= start && key.time <= end) {
140 if (Mathf.Approximately(key.time, 0)) {
141 alreadyHasZero =
true;
144 newCurve.AddKey(key);
148 if (latestBeforeStart.HasValue && !alreadyHasZero) {
149 var toInsert = latestBeforeStart.Value;
151 newCurve.AddKey(toInsert);
158 public static AnimationCurve Compress(AnimationCurve curve,
float maxDelta = 0.005f,
int checkSteps = 8) {
159 var curveArray =
new AnimationCurve[] { curve };
161 var result = CompressCurves(curveArray,
163 float originalValue = src[0].Evaluate(t);
164 float compressedValue = dst[0].Evaluate(t);
165 return Mathf.Abs(originalValue - compressedValue) < maxDelta;
172 public static void CompressRotations(AnimationCurve xCurve,
173 AnimationCurve yCurve,
174 AnimationCurve zCurve,
175 AnimationCurve wCurve,
176 out AnimationCurve compressedXCurve,
177 out AnimationCurve compressedYCurve,
178 out AnimationCurve compressedZCurve,
179 out AnimationCurve compressedWCurve,
180 float maxAngleError = 1,
181 int checkSteps = 8) {
182 var curveArray =
new AnimationCurve[] {
189 var result = CompressCurves(curveArray,
192 srcRot.x = src[0].Evaluate(t);
193 srcRot.y = src[1].Evaluate(t);
194 srcRot.z = src[2].Evaluate(t);
195 srcRot.w = src[3].Evaluate(t);
198 dstRot.x = dst[0].Evaluate(t);
199 dstRot.y = dst[1].Evaluate(t);
200 dstRot.z = dst[2].Evaluate(t);
201 dstRot.w = dst[3].Evaluate(t);
205 (
Quaternion.Inverse(dstRot) * srcRot).ToAngleAxis(out angle, out axis);
207 return angle < maxAngleError;
211 compressedXCurve = result[0];
212 compressedYCurve = result[1];
213 compressedZCurve = result[2];
214 compressedWCurve = result[3];
217 public static void CompressPositions(AnimationCurve xCurve,
218 AnimationCurve yCurve,
219 AnimationCurve zCurve,
220 out AnimationCurve compressedXCurve,
221 out AnimationCurve compressedYCurve,
222 out AnimationCurve compressedZCurve,
223 float maxDistanceError = 0.005f,
224 int checkSteps = 8) {
225 var curveArray =
new AnimationCurve[] {
231 var results = CompressCurves(curveArray,
234 srcPos.x = src[0].Evaluate(t);
235 srcPos.y = src[1].Evaluate(t);
236 srcPos.z = src[2].Evaluate(t);
239 dstPos.x = dst[0].Evaluate(t);
240 dstPos.y = dst[1].Evaluate(t);
241 dstPos.z = dst[2].Evaluate(t);
243 return Vector3.Distance(srcPos, dstPos) < maxDistanceError;
247 compressedXCurve = results[0];
248 compressedYCurve = results[1];
249 compressedZCurve = results[2];
252 public static AnimationCurve CompressScale(AnimationCurve curve,
253 float maxScaleFactor,
254 int checkSteps = 8) {
255 var curveArray =
new AnimationCurve[] {
259 var results = CompressCurves(curveArray,
261 float srcValue = src[0].Evaluate(t);
262 float dstValue = dst[0].Evaluate(t);
264 if (Mathf.Sign(srcValue) == Mathf.Sign(dstValue)) {
265 return srcValue / dstValue < maxScaleFactor
266 && dstValue / srcValue < maxScaleFactor;
276 public static void CompressColorsHSV(AnimationCurve rCurve,
277 AnimationCurve gCurve,
278 AnimationCurve bCurve,
279 out AnimationCurve compressedRCurve,
280 out AnimationCurve compressedGCurve,
281 out AnimationCurve compressedBCurve,
283 float maxSaturationError,
285 int checkSteps = 8) {
286 var curveArray =
new AnimationCurve[] {
292 var results = CompressCurves(curveArray,
295 srcColor.r = src[0].Evaluate(t);
296 srcColor.g = src[1].Evaluate(t);
297 srcColor.b = src[2].Evaluate(t);
301 dstColor.r = dst[0].Evaluate(t);
302 dstColor.g = dst[1].Evaluate(t);
303 dstColor.b = dst[2].Evaluate(t);
308 Color.RGBToHSV(srcColor, out sH, out sS, out sV);
309 Color.RGBToHSV(dstColor, out dH, out dS, out dV);
311 return Mathf.Abs(sH - dH) < maxHueError &&
312 Mathf.Abs(sS - dS) < maxSaturationError &&
313 Mathf.Abs(sV - dV) < maxValueError;
317 compressedRCurve = results[0];
318 compressedGCurve = results[1];
319 compressedBCurve = results[2];
322 public static AnimationCurve[] CompressCurves(AnimationCurve[] curves,
323 Func<AnimationCurve[], AnimationCurve[],
float,
bool> isGood,
324 int checkSteps = 8) {
325 var keyframes =
new Keyframe[curves.Length][];
326 var position =
new int[curves.Length];
327 var nextFrame =
new int[curves.Length];
328 var compressedCurves =
new AnimationCurve[curves.Length];
330 for (
int i = 0; i < curves.Length; i++) {
331 var keys = curves[i].keys;
333 compressedCurves[i] =
new AnimationCurve(keys);
335 for (
int j = 0; j < keys.Length; j++) {
336 var leftT = AnimationUtility.GetKeyLeftTangentMode(curves[i], j);
337 var rightT = AnimationUtility.GetKeyRightTangentMode(curves[i], j);
339 AnimationUtility.SetKeyLeftTangentMode(compressedCurves[i], j, leftT);
340 AnimationUtility.SetKeyRightTangentMode(compressedCurves[i], j, rightT);
344 position[i] = keys.Length - 2;
345 nextFrame[i] = keys.Length - 1;
349 for (
int i = 0; i < curves.Length; i++) {
351 if (position[i] > 0) {
352 Keyframe nextKeyframe = keyframes[i][nextFrame[i]];
353 Keyframe currKeyframe = keyframes[i][position[i]];
354 Keyframe prevKeyframe = keyframes[i][position[i] - 1];
356 var leftT = AnimationUtility.GetKeyLeftTangentMode(compressedCurves[i], position[i]);
357 var rightT = AnimationUtility.GetKeyRightTangentMode(compressedCurves[i], position[i]);
358 compressedCurves[i].RemoveKey(position[i]);
360 for (
int k = 0; k < checkSteps; k++) {
361 float percent = k / (checkSteps - 1.0f);
363 float prevTime = Mathf.Lerp(currKeyframe.time, prevKeyframe.time, percent);
364 float nextTime = Mathf.Lerp(currKeyframe.time, nextKeyframe.time, percent);
366 bool isPrevGood = isGood(curves, compressedCurves, prevTime);
367 bool isNextgood = isGood(curves, compressedCurves, nextTime);
369 if (!isPrevGood || !isNextgood) {
370 int index = compressedCurves[i].AddKey(currKeyframe);
371 AnimationUtility.SetKeyLeftTangentMode(compressedCurves[i], index, leftT);
372 AnimationUtility.SetKeyRightTangentMode(compressedCurves[i], index, rightT);
374 nextFrame[i] = position[i];
382 }
while (position.Query().Any(p => p > 0));
384 return compressedCurves;
388 public static void AddBooleanKey(
this AnimationCurve curve,
float time,
bool value) {
389 var keyframe =
new Keyframe() { time = time, value = value ? 1 : 0 };
392 int keyframeIdx = curve.AddKey(keyframe);
393 AnimationUtility.SetKeyBroken(curve, keyframeIdx,
true);
394 AnimationUtility.SetKeyLeftTangentMode(curve, keyframeIdx,
395 AnimationUtility.TangentMode.Constant);
396 AnimationUtility.SetKeyRightTangentMode(curve, keyframeIdx,
397 AnimationUtility.TangentMode.Constant);
399 curve.AddKey(keyframe);