A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using System.Linq;
3using UnityEngine;
4using UnityEditor;
5using UnityEditorInternal;
6
7namespace UnityEditor.Timeline
8{
9 struct CurveBindingPair
10 {
11 public EditorCurveBinding binding;
12 public AnimationCurve curve;
13 public ObjectReferenceKeyframe[] objectCurve;
14 }
15
16 class CurveBindingGroup
17 {
18 public CurveBindingPair[] curveBindingPairs { get; set; }
19 public Vector2 timeRange { get; set; }
20 public Vector2 valueRange { get; set; }
21
22 public bool isFloatCurve
23 {
24 get
25 {
26 return curveBindingPairs != null && curveBindingPairs.Length > 0 &&
27 curveBindingPairs[0].curve != null;
28 }
29 }
30
31 public bool isObjectCurve
32 {
33 get
34 {
35 return curveBindingPairs != null && curveBindingPairs.Length > 0 &&
36 curveBindingPairs[0].objectCurve != null;
37 }
38 }
39
40 public int count
41 {
42 get
43 {
44 if (curveBindingPairs == null)
45 return 0;
46 return curveBindingPairs.Length;
47 }
48 }
49 }
50
51 class AnimationClipCurveInfo
52 {
53 bool m_CurveDirty = true;
54 bool m_KeysDirty = true;
55
56 public bool dirty
57 {
58 get { return m_CurveDirty; }
59 set
60 {
61 m_CurveDirty = value;
62 if (m_CurveDirty)
63 {
64 m_KeysDirty = true;
65 if (m_groupings != null)
66 m_groupings.Clear();
67 }
68 }
69 }
70
71 public AnimationCurve[] curves;
72 public EditorCurveBinding[] bindings;
73
74 public EditorCurveBinding[] objectBindings;
75 public List<ObjectReferenceKeyframe[]> objectCurves;
76
77 Dictionary<string, CurveBindingGroup> m_groupings;
78
79 // to tell whether the cache has changed
80 public int version { get; private set; }
81
82 float[] m_KeyTimes;
83
84 Dictionary<EditorCurveBinding, float[]> m_individualBindinsKey;
85
86 public float[] keyTimes
87 {
88 get
89 {
90 if (m_KeysDirty || m_KeyTimes == null)
91 {
92 RebuildKeyCache();
93 }
94 return m_KeyTimes;
95 }
96 }
97
98 public float[] GetCurveTimes(EditorCurveBinding curve)
99 {
100 return GetCurveTimes(new[] { curve });
101 }
102
103 public float[] GetCurveTimes(EditorCurveBinding[] curves)
104 {
105 if (m_KeysDirty || m_KeyTimes == null)
106 {
107 RebuildKeyCache();
108 }
109
110 var keyTimes = new List<float>();
111 for (int i = 0; i < curves.Length; i++)
112 {
113 var c = curves[i];
114 if (m_individualBindinsKey.ContainsKey(c))
115 {
116 keyTimes.AddRange(m_individualBindinsKey[c]);
117 }
118 }
119 return keyTimes.ToArray();
120 }
121
122 void RebuildKeyCache()
123 {
124 m_individualBindinsKey = new Dictionary<EditorCurveBinding, float[]>();
125
126 List<float> keys = curves.SelectMany(y => y.keys).Select(z => z.time).ToList();
127 for (int i = 0; i < objectCurves.Count; i++)
128 {
129 var kf = objectCurves[i];
130 keys.AddRange(kf.Select(x => x.time));
131 }
132
133 for (int b = 0; b < bindings.Count(); b++)
134 {
135 m_individualBindinsKey.Add(bindings[b], curves[b].keys.Select(k => k.time).Distinct().ToArray());
136 }
137
138 m_KeyTimes = keys.OrderBy(x => x).Distinct().ToArray();
139 m_KeysDirty = false;
140 }
141
142 public void Update(AnimationClip clip)
143 {
144 List<EditorCurveBinding> postfilter = new List<EditorCurveBinding>();
145 var clipBindings = AnimationUtility.GetCurveBindings(clip);
146 for (int i = 0; i < clipBindings.Length; i++)
147 {
148 var bind = clipBindings[i];
149 if (!bind.propertyName.Contains("LocalRotation.w"))
150 postfilter.Add(RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(bind, clip));
151 }
152 bindings = postfilter.ToArray();
153
154 curves = new AnimationCurve[bindings.Length];
155 for (int i = 0; i < bindings.Length; i++)
156 {
157 curves[i] = AnimationUtility.GetEditorCurve(clip, bindings[i]);
158 }
159
160 objectBindings = AnimationUtility.GetObjectReferenceCurveBindings(clip);
161 objectCurves = new List<ObjectReferenceKeyframe[]>(objectBindings.Length);
162 for (int i = 0; i < objectBindings.Length; i++)
163 {
164 objectCurves.Add(AnimationUtility.GetObjectReferenceCurve(clip, objectBindings[i]));
165 }
166
167 m_CurveDirty = false;
168 m_KeysDirty = true;
169
170 version = version + 1;
171 }
172
173 public bool GetBindingForCurve(AnimationCurve curve, ref EditorCurveBinding binding)
174 {
175 for (int i = 0; i < curves.Length; i++)
176 {
177 if (curve == curves[i])
178 {
179 binding = bindings[i];
180 return true;
181 }
182 }
183 return false;
184 }
185
186 public AnimationCurve GetCurveForBinding(EditorCurveBinding binding)
187 {
188 for (int i = 0; i < curves.Length; i++)
189 {
190 if (binding.Equals(bindings[i]))
191 {
192 return curves[i];
193 }
194 }
195 return null;
196 }
197
198 public ObjectReferenceKeyframe[] GetObjectCurveForBinding(EditorCurveBinding binding)
199 {
200 if (objectCurves == null)
201 return null;
202
203 for (int i = 0; i < objectCurves.Count; i++)
204 {
205 if (binding.Equals(objectBindings[i]))
206 {
207 return objectCurves[i];
208 }
209 }
210 return null;
211 }
212
213 // given a groupID, get the list of curve bindings
214 public CurveBindingGroup GetGroupBinding(string groupID)
215 {
216 if (m_groupings == null)
217 m_groupings = new Dictionary<string, CurveBindingGroup>();
218
219 CurveBindingGroup result = null;
220 if (!m_groupings.TryGetValue(groupID, out result))
221 {
222 result = new CurveBindingGroup();
223 result.timeRange = new Vector2(float.MaxValue, float.MinValue);
224 result.valueRange = new Vector2(float.MaxValue, float.MinValue);
225 List<CurveBindingPair> found = new List<CurveBindingPair>();
226 for (int i = 0; i < bindings.Length; i++)
227 {
228 if (bindings[i].GetGroupID() == groupID)
229 {
230 CurveBindingPair pair = new CurveBindingPair();
231 pair.binding = bindings[i];
232 pair.curve = curves[i];
233 found.Add(pair);
234
235 for (int k = 0; k < curves[i].keys.Length; k++)
236 {
237 var key = curves[i].keys[k];
238 result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y));
239 result.valueRange = new Vector2(Mathf.Min(key.value, result.valueRange.x), Mathf.Max(key.value, result.valueRange.y));
240 }
241 }
242 }
243 for (int i = 0; i < objectBindings.Length; i++)
244 {
245 if (objectBindings[i].GetGroupID() == groupID)
246 {
247 CurveBindingPair pair = new CurveBindingPair();
248 pair.binding = objectBindings[i];
249 pair.objectCurve = objectCurves[i];
250 found.Add(pair);
251
252 for (int k = 0; k < objectCurves[i].Length; k++)
253 {
254 var key = objectCurves[i][k];
255 result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y));
256 }
257 }
258 }
259
260 result.curveBindingPairs = found.OrderBy(x => AnimationWindowUtility.GetComponentIndex(x.binding.propertyName)).ToArray();
261
262 m_groupings.Add(groupID, result);
263 }
264 return result;
265 }
266 }
267
268 // Cache for storing the animation clip data
269 class AnimationClipCurveCache
270 {
271 static AnimationClipCurveCache s_Instance;
272 Dictionary<AnimationClip, AnimationClipCurveInfo> m_ClipCache = new Dictionary<AnimationClip, AnimationClipCurveInfo>();
273 bool m_IsEnabled;
274
275
276 public static AnimationClipCurveCache Instance
277 {
278 get
279 {
280 if (s_Instance == null)
281 {
282 s_Instance = new AnimationClipCurveCache();
283 }
284
285 return s_Instance;
286 }
287 }
288
289 public void OnEnable()
290 {
291 if (!m_IsEnabled)
292 {
293 AnimationUtility.onCurveWasModified += OnCurveWasModified;
294 m_IsEnabled = true;
295 }
296 }
297
298 public void OnDisable()
299 {
300 if (m_IsEnabled)
301 {
302 AnimationUtility.onCurveWasModified -= OnCurveWasModified;
303 m_IsEnabled = false;
304 }
305 }
306
307 // callback when a curve is edited. Force the cache to update next time it's accessed
308 void OnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType modification)
309 {
310 AnimationClipCurveInfo data;
311 if (m_ClipCache.TryGetValue(clip, out data))
312 {
313 data.dirty = true;
314 }
315 }
316
317 public AnimationClipCurveInfo GetCurveInfo(AnimationClip clip)
318 {
319 AnimationClipCurveInfo data;
320 if (clip == null)
321 return null;
322 if (!m_ClipCache.TryGetValue(clip, out data))
323 {
324 data = new AnimationClipCurveInfo();
325 data.dirty = true;
326 m_ClipCache[clip] = data;
327 }
328 if (data.dirty)
329 {
330 data.Update(clip);
331 }
332 return data;
333 }
334
335 public void ClearCachedProxyClips()
336 {
337 var toRemove = new List<AnimationClip>();
338 foreach (var entry in m_ClipCache)
339 {
340 var clip = entry.Key;
341 if (clip != null && (clip.hideFlags & HideFlags.HideAndDontSave) == HideFlags.HideAndDontSave)
342 toRemove.Add(clip);
343 }
344
345 foreach (var clip in toRemove)
346 {
347 m_ClipCache.Remove(clip);
348 Object.DestroyImmediate(clip, true);
349 }
350 }
351
352 public void Clear()
353 {
354 ClearCachedProxyClips();
355 m_ClipCache.Clear();
356 }
357 }
358
359 static class EditorCurveBindingExtension
360 {
361 // identifier to generate an id thats the same for all curves in the same group
362 public static string GetGroupID(this EditorCurveBinding binding)
363 {
364 return binding.type + AnimationWindowUtility.GetPropertyGroupName(binding.propertyName);
365 }
366 }
367
368
369 static class CurveBindingGroupExtensions
370 {
371 // Extentions to determine curve types
372 public static bool IsEnableGroup(this CurveBindingGroup curves)
373 {
374 return curves.isFloatCurve && curves.count == 1 && curves.curveBindingPairs[0].binding.propertyName == "m_Enabled";
375 }
376
377 public static bool IsVectorGroup(this CurveBindingGroup curves)
378 {
379 if (!curves.isFloatCurve)
380 return false;
381 if (curves.count <= 1 || curves.count > 4)
382 return false;
383 char l = curves.curveBindingPairs[0].binding.propertyName.Last();
384 return l == 'x' || l == 'y' || l == 'z' || l == 'w';
385 }
386
387 public static bool IsColorGroup(this CurveBindingGroup curves)
388 {
389 if (!curves.isFloatCurve)
390 return false;
391 if (curves.count != 3 && curves.count != 4)
392 return false;
393 char l = curves.curveBindingPairs[0].binding.propertyName.Last();
394 return l == 'r' || l == 'g' || l == 'b' || l == 'a';
395 }
396
397 public static string GetDescription(this CurveBindingGroup group, float t)
398 {
399 string result = string.Empty;
400 if (group.isFloatCurve)
401 {
402 if (group.count > 1)
403 {
404 result += "(" + group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##");
405 for (int j = 1; j < group.curveBindingPairs.Length; j++)
406 {
407 result += "," + group.curveBindingPairs[j].curve.Evaluate(t).ToString("0.##");
408 }
409 result += ")";
410 }
411 else
412 {
413 result = group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##");
414 }
415 }
416 else if (group.isObjectCurve)
417 {
418 Object obj = null;
419 if (group.curveBindingPairs[0].objectCurve.Length > 0)
420 obj = CurveEditUtility.Evaluate(group.curveBindingPairs[0].objectCurve, t);
421 result = (obj == null ? "None" : obj.name);
422 }
423
424 return result;
425 }
426 }
427}