AnimationClipCurveCache.cs 14 KB

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