ClipCurveEditor.cs 12 KB

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