ClipCurveEditor.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.Timeline;
  5. using UnityEngine;
  6. using UnityEngine.Timeline;
  7. namespace UnityEditor
  8. {
  9. class ClipCurveEditor
  10. {
  11. internal readonly CurveEditor m_CurveEditor;
  12. static readonly CurveEditorSettings s_CurveEditorSettings = new CurveEditorSettings
  13. {
  14. hSlider = false,
  15. vSlider = false,
  16. hRangeLocked = false,
  17. vRangeLocked = false,
  18. scaleWithWindow = true,
  19. hRangeMin = 0.0f,
  20. showAxisLabels = true,
  21. allowDeleteLastKeyInCurve = true,
  22. rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.MiniRectangleTool
  23. };
  24. static readonly float s_GridLabelWidth = 40.0f;
  25. readonly BindingSelector m_BindingHierarchy;
  26. public BindingSelector bindingHierarchy
  27. {
  28. get { return m_BindingHierarchy; }
  29. }
  30. public Rect shownAreaInsideMargins
  31. {
  32. get { return m_CurveEditor != null ? m_CurveEditor.shownAreaInsideMargins : new Rect(1, 1, 1, 1); }
  33. }
  34. Vector2 m_ScrollPosition = Vector2.zero;
  35. readonly CurveDataSource m_DataSource;
  36. float m_LastFrameRate = 30.0f;
  37. int m_LastClipVersion = -1;
  38. int m_LastCurveCount = -1;
  39. TrackViewModelData m_ViewModel;
  40. bool m_ShouldRestoreShownArea;
  41. bool isNewSelection
  42. {
  43. get
  44. {
  45. if (m_ViewModel == null || m_DataSource == null)
  46. return true;
  47. return m_ViewModel.lastInlineCurveDataID != m_DataSource.id;
  48. }
  49. }
  50. internal CurveEditor curveEditor
  51. {
  52. get { return m_CurveEditor; }
  53. }
  54. public ClipCurveEditor(CurveDataSource dataSource, TimelineWindow parentWindow, TrackAsset hostTrack)
  55. {
  56. m_DataSource = dataSource;
  57. m_CurveEditor = new CurveEditor(new Rect(0, 0, 1000, 100), new CurveWrapper[0], false);
  58. s_CurveEditorSettings.vTickStyle = new TickStyle
  59. {
  60. tickColor = { color = DirectorStyles.Instance.customSkin.colorInlineCurveVerticalLines },
  61. distLabel = 20,
  62. stubs = true
  63. };
  64. s_CurveEditorSettings.hTickStyle = new TickStyle
  65. {
  66. // hide horizontal lines by giving them a transparent color
  67. tickColor = { color = new Color(0.0f, 0.0f, 0.0f, 0.0f) },
  68. distLabel = 0
  69. };
  70. m_CurveEditor.settings = s_CurveEditorSettings;
  71. m_ViewModel = TimelineWindowViewPrefs.GetTrackViewModelData(hostTrack);
  72. m_ShouldRestoreShownArea = true;
  73. m_CurveEditor.ignoreScrollWheelUntilClicked = true;
  74. m_CurveEditor.curvesUpdated = OnCurvesUpdated;
  75. m_BindingHierarchy = new BindingSelector(parentWindow, m_CurveEditor, m_ViewModel.inlineCurvesState);
  76. }
  77. public void SelectAllKeys()
  78. {
  79. m_CurveEditor.SelectAll();
  80. }
  81. public void FrameClip()
  82. {
  83. m_CurveEditor.InvalidateBounds();
  84. m_CurveEditor.FrameClip(false, true);
  85. }
  86. public CurveDataSource dataSource
  87. {
  88. get { return m_DataSource; }
  89. }
  90. internal void OnCurvesUpdated()
  91. {
  92. if (m_DataSource == null)
  93. return;
  94. if (m_CurveEditor == null)
  95. return;
  96. if (m_CurveEditor.animationCurves.Length == 0)
  97. return;
  98. List<CurveWrapper> curvesToUpdate = m_CurveEditor.animationCurves.Where(c => c.changed).ToList();
  99. // nothing changed, return.
  100. if (curvesToUpdate.Count == 0)
  101. return;
  102. AnimationClip clip = m_DataSource.animationClip;
  103. // something changed, manage the undo properly.
  104. Undo.RegisterCompleteObjectUndo(clip, "Edit Clip Curve");
  105. foreach (CurveWrapper c in curvesToUpdate)
  106. {
  107. AnimationUtility.SetEditorCurve(clip, c.binding, c.curve);
  108. c.changed = false;
  109. }
  110. m_DataSource.UpdateCurves(curvesToUpdate);
  111. }
  112. public void DrawHeader(Rect headerRect)
  113. {
  114. m_BindingHierarchy.InitIfNeeded(headerRect, m_DataSource, isNewSelection);
  115. try
  116. {
  117. GUILayout.BeginArea(headerRect);
  118. m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar);
  119. m_BindingHierarchy.OnGUI(new Rect(0, 0, headerRect.width, headerRect.height));
  120. GUILayout.EndScrollView();
  121. GUILayout.EndArea();
  122. }
  123. catch (Exception e)
  124. {
  125. Debug.LogException(e);
  126. }
  127. }
  128. class FrameFormatCurveEditorState : ICurveEditorState
  129. {
  130. public TimeArea.TimeFormat timeFormat
  131. {
  132. get { return TimeArea.TimeFormat.Frame; }
  133. }
  134. public Vector2 timeRange { get { return new Vector2(0, 1); } }
  135. public bool rippleTime { get { return false; } }
  136. }
  137. class UnformattedCurveEditorState : ICurveEditorState
  138. {
  139. public TimeArea.TimeFormat timeFormat
  140. {
  141. get { return TimeArea.TimeFormat.None; }
  142. }
  143. public Vector2 timeRange { get { return new Vector2(0, 1); } }
  144. public bool rippleTime { get { return false; } }
  145. }
  146. void UpdateCurveEditorIfNeeded(WindowState state)
  147. {
  148. if ((Event.current.type != EventType.Layout) || (m_DataSource == null) || (m_BindingHierarchy == null) || (m_DataSource.animationClip == null))
  149. return;
  150. AnimationClipCurveInfo curveInfo = AnimationClipCurveCache.Instance.GetCurveInfo(m_DataSource.animationClip);
  151. int version = curveInfo.version;
  152. if (version != m_LastClipVersion)
  153. {
  154. // tree has changed
  155. if (m_LastCurveCount != curveInfo.curves.Length)
  156. {
  157. m_BindingHierarchy.RefreshTree();
  158. m_LastCurveCount = curveInfo.curves.Length;
  159. }
  160. else
  161. {
  162. // update just the curves
  163. m_BindingHierarchy.RefreshCurves();
  164. }
  165. m_LastClipVersion = version;
  166. }
  167. if (state.timeInFrames)
  168. m_CurveEditor.state = new FrameFormatCurveEditorState();
  169. else
  170. m_CurveEditor.state = new UnformattedCurveEditorState();
  171. m_CurveEditor.invSnap = state.referenceSequence.frameRate;
  172. }
  173. public void DrawCurveEditor(Rect rect, WindowState state, Vector2 clipRange, bool loop, bool selected)
  174. {
  175. SetupMarginsAndRect(rect, state);
  176. UpdateCurveEditorIfNeeded(state);
  177. if (m_ShouldRestoreShownArea)
  178. RestoreShownArea();
  179. var curveVisibleTimeRange = CalculateCurveVisibleTimeRange(state.timeAreaShownRange, m_DataSource);
  180. m_CurveEditor.SetShownHRangeInsideMargins(curveVisibleTimeRange.x, curveVisibleTimeRange.y); //align the curve with the clip.
  181. if (m_LastFrameRate != state.referenceSequence.frameRate)
  182. {
  183. m_CurveEditor.hTicks.SetTickModulosForFrameRate(state.referenceSequence.frameRate);
  184. m_LastFrameRate = state.referenceSequence.frameRate;
  185. }
  186. foreach (var cw in m_CurveEditor.animationCurves)
  187. cw.renderer.SetWrap(WrapMode.Default, loop ? WrapMode.Loop : WrapMode.Default);
  188. using (new GUIGroupScope(rect))
  189. {
  190. var localRect = new Rect(0.0f, 0.0f, rect.width, rect.height);
  191. var localClipRange = new Vector2(Mathf.Floor(clipRange.x - rect.xMin), Mathf.Ceil(clipRange.y - rect.xMin));
  192. var curveStartPosX = Mathf.Floor(state.TimeToPixel(m_DataSource.start) - rect.xMin);
  193. EditorGUI.DrawRect(new Rect(curveStartPosX, 0.0f, 1.0f, rect.height), new Color(1.0f, 1.0f, 1.0f, 0.5f));
  194. DrawCurveEditorBackground(localRect);
  195. if (selected)
  196. {
  197. var selectionRect = new Rect(localClipRange.x, 0.0f, localClipRange.y - localClipRange.x, localRect.height);
  198. DrawOutline(selectionRect);
  199. }
  200. EditorGUI.BeginChangeCheck();
  201. {
  202. var evt = Event.current;
  203. if (evt.type == EventType.Layout || evt.type == EventType.Repaint || selected)
  204. m_CurveEditor.CurveGUI();
  205. }
  206. if (EditorGUI.EndChangeCheck())
  207. OnCurvesUpdated();
  208. DrawOverlay(localRect, localClipRange, DirectorStyles.Instance.customSkin.colorInlineCurveOutOfRangeOverlay);
  209. DrawGrid(localRect, curveStartPosX);
  210. }
  211. }
  212. static Vector2 CalculateCurveVisibleTimeRange(Vector2 timeAreaShownRange, CurveDataSource curve)
  213. {
  214. var curveVisibleTimeRange = new Vector2
  215. {
  216. x = Math.Max(0.0f, timeAreaShownRange.x - curve.start),
  217. y = timeAreaShownRange.y - curve.start
  218. };
  219. return curveVisibleTimeRange * curve.timeScale;
  220. }
  221. void SetupMarginsAndRect(Rect rect, WindowState state)
  222. {
  223. var startX = state.TimeToPixel(m_DataSource.start) - rect.x;
  224. var timelineWidth = state.timeAreaRect.width;
  225. m_CurveEditor.rect = new Rect(0.0f, 0.0f, timelineWidth, rect.height);
  226. m_CurveEditor.leftmargin = Math.Max(startX, 0.0f);
  227. m_CurveEditor.rightmargin = 0.0f;
  228. m_CurveEditor.topmargin = m_CurveEditor.bottommargin = CalculateTopMargin(rect.height);
  229. }
  230. void RestoreShownArea()
  231. {
  232. if (isNewSelection)
  233. FrameClip();
  234. else
  235. m_CurveEditor.shownAreaInsideMargins = m_ViewModel.inlineCurvesShownAreaInsideMargins;
  236. m_ShouldRestoreShownArea = false;
  237. }
  238. static void DrawCurveEditorBackground(Rect rect)
  239. {
  240. if (EditorGUIUtility.isProSkin)
  241. return;
  242. var animEditorBackgroundRect = Rect.MinMaxRect(0.0f, rect.yMin, rect.xMax, rect.yMax);
  243. // Curves are not legible in Personal Skin so we need to darken the background a bit.
  244. EditorGUI.DrawRect(animEditorBackgroundRect, DirectorStyles.Instance.customSkin.colorInlineCurvesBackground);
  245. }
  246. static float CalculateTopMargin(float height)
  247. {
  248. return Mathf.Clamp(0.15f * height, 10.0f, 40.0f);
  249. }
  250. static void DrawOutline(Rect rect, float thickness = 2.0f)
  251. {
  252. // Draw top selected lines.
  253. EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), Color.white);
  254. // Draw bottom selected lines.
  255. EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), Color.white);
  256. // Draw Left Selected Lines
  257. EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), Color.white);
  258. // Draw Right Selected Lines
  259. EditorGUI.DrawRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), Color.white);
  260. }
  261. static void DrawOverlay(Rect rect, Vector2 clipRange, Color color)
  262. {
  263. var leftSide = new Rect(rect.xMin, rect.yMin, clipRange.x - rect.xMin, rect.height);
  264. EditorGUI.DrawRect(leftSide, color);
  265. var rightSide = new Rect(Mathf.Max(0.0f, clipRange.y), rect.yMin, rect.xMax, rect.height);
  266. EditorGUI.DrawRect(rightSide, color);
  267. }
  268. void DrawGrid(Rect rect, float curveXPosition)
  269. {
  270. var gridXPos = Mathf.Max(curveXPosition - s_GridLabelWidth, rect.xMin);
  271. var gridRect = new Rect(gridXPos, rect.y, s_GridLabelWidth, rect.height);
  272. var originalRect = m_CurveEditor.rect;
  273. m_CurveEditor.rect = new Rect(0.0f, 0.0f, rect.width, rect.height);
  274. using (new GUIGroupScope(gridRect))
  275. m_CurveEditor.GridGUI();
  276. m_CurveEditor.rect = originalRect;
  277. }
  278. }
  279. }