368 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| using UnityEditor.Timeline;
 | |
| using UnityEngine;
 | |
| using UnityEngine.Timeline;
 | |
| 
 | |
| namespace UnityEditor
 | |
| {
 | |
|     class ClipCurveEditor
 | |
|     {
 | |
|         static readonly GUIContent s_RemoveCurveContent = new GUIContent(L10n.Tr("Remove Curve"));
 | |
|         static readonly GUIContent s_RemoveCurvesContent = new GUIContent(L10n.Tr("Remove Curves"));
 | |
| 
 | |
|         internal readonly CurveEditor m_CurveEditor;
 | |
|         static readonly CurveEditorSettings s_CurveEditorSettings = new CurveEditorSettings
 | |
|         {
 | |
|             hSlider = false,
 | |
|             vSlider = false,
 | |
|             hRangeLocked = false,
 | |
|             vRangeLocked = false,
 | |
|             scaleWithWindow = true,
 | |
|             hRangeMin = 0.0f,
 | |
|             showAxisLabels = true,
 | |
|             allowDeleteLastKeyInCurve = true,
 | |
|             rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.MiniRectangleTool
 | |
|         };
 | |
| 
 | |
|         static readonly float s_GridLabelWidth = 40.0f;
 | |
| 
 | |
|         readonly BindingSelector m_BindingHierarchy;
 | |
|         public BindingSelector bindingHierarchy
 | |
|         {
 | |
|             get { return m_BindingHierarchy; }
 | |
|         }
 | |
| 
 | |
|         public Rect shownAreaInsideMargins
 | |
|         {
 | |
|             get { return m_CurveEditor != null ? m_CurveEditor.shownAreaInsideMargins : new Rect(1, 1, 1, 1); }
 | |
|         }
 | |
| 
 | |
|         Vector2 m_ScrollPosition = Vector2.zero;
 | |
| 
 | |
|         readonly CurveDataSource m_DataSource;
 | |
| 
 | |
|         float m_LastFrameRate = 30.0f;
 | |
| 
 | |
|         UInt64 m_LastClipVersion = UInt64.MaxValue;
 | |
| 
 | |
|         TrackViewModelData m_ViewModel;
 | |
|         bool m_ShouldRestoreShownArea;
 | |
| 
 | |
|         bool isNewSelection
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (m_ViewModel == null || m_DataSource == null)
 | |
|                     return true;
 | |
| 
 | |
|                 return m_ViewModel.lastInlineCurveDataID != m_DataSource.id;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal CurveEditor curveEditor
 | |
|         {
 | |
|             get { return m_CurveEditor; }
 | |
|         }
 | |
| 
 | |
|         public ClipCurveEditor(CurveDataSource dataSource, TimelineWindow parentWindow, TrackAsset hostTrack)
 | |
|         {
 | |
|             m_DataSource = dataSource;
 | |
| 
 | |
|             m_CurveEditor = new CurveEditor(new Rect(0, 0, 1000, 100), new CurveWrapper[0], false);
 | |
| 
 | |
|             s_CurveEditorSettings.vTickStyle = new TickStyle
 | |
|             {
 | |
|                 tickColor = { color = DirectorStyles.Instance.customSkin.colorInlineCurveVerticalLines },
 | |
|                 distLabel = 20,
 | |
|                 stubs = true
 | |
|             };
 | |
| 
 | |
|             s_CurveEditorSettings.hTickStyle = new TickStyle
 | |
|             {
 | |
|                 // hide horizontal lines by giving them a transparent color
 | |
|                 tickColor = { color = new Color(0.0f, 0.0f, 0.0f, 0.0f) },
 | |
|                 distLabel = 0
 | |
|             };
 | |
| 
 | |
|             m_CurveEditor.settings = s_CurveEditorSettings;
 | |
| 
 | |
|             m_ViewModel = TimelineWindowViewPrefs.GetTrackViewModelData(hostTrack);
 | |
| 
 | |
|             m_ShouldRestoreShownArea = true;
 | |
|             m_CurveEditor.ignoreScrollWheelUntilClicked = true;
 | |
|             m_CurveEditor.curvesUpdated = OnCurvesUpdated;
 | |
| 
 | |
|             m_BindingHierarchy = new BindingSelector(parentWindow, m_CurveEditor, m_ViewModel.inlineCurvesState);
 | |
|         }
 | |
| 
 | |
|         public void SelectAllKeys()
 | |
|         {
 | |
|             m_CurveEditor.SelectAll();
 | |
|         }
 | |
| 
 | |
|         public void FrameClip()
 | |
|         {
 | |
|             m_CurveEditor.InvalidateBounds();
 | |
|             m_CurveEditor.FrameClip(false, true);
 | |
|         }
 | |
| 
 | |
|         public CurveDataSource dataSource
 | |
|         {
 | |
|             get { return m_DataSource; }
 | |
|         }
 | |
| 
 | |
|         // called when curves are edited
 | |
|         internal void OnCurvesUpdated()
 | |
|         {
 | |
|             if (m_DataSource == null)
 | |
|                 return;
 | |
| 
 | |
|             if (m_CurveEditor == null)
 | |
|                 return;
 | |
| 
 | |
|             if (m_CurveEditor.animationCurves.Length == 0)
 | |
|                 return;
 | |
| 
 | |
|             List<CurveWrapper> curvesToUpdate = m_CurveEditor.animationCurves.Where(c => c.changed).ToList();
 | |
| 
 | |
|             // nothing changed, return.
 | |
|             if (curvesToUpdate.Count == 0)
 | |
|                 return;
 | |
| 
 | |
|             // something changed, manage the undo properly.
 | |
|             m_DataSource.ApplyCurveChanges(curvesToUpdate);
 | |
|             m_LastClipVersion = m_DataSource.GetClipVersion();
 | |
|         }
 | |
| 
 | |
|         public void DrawHeader(Rect headerRect)
 | |
|         {
 | |
|             m_BindingHierarchy.InitIfNeeded(headerRect, m_DataSource, isNewSelection);
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 GUILayout.BeginArea(headerRect);
 | |
|                 m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar);
 | |
|                 m_BindingHierarchy.OnGUI(new Rect(0, 0, headerRect.width, headerRect.height));
 | |
|                 if (m_BindingHierarchy.treeViewController != null)
 | |
|                     m_BindingHierarchy.treeViewController.contextClickItemCallback = ContextClickItemCallback;
 | |
|                 GUILayout.EndScrollView();
 | |
|                 GUILayout.EndArea();
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 Debug.LogException(e);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void ContextClickItemCallback(int obj)
 | |
|         {
 | |
|             GenerateContextMenu(obj);
 | |
|         }
 | |
| 
 | |
|         void GenerateContextMenu(int obj = -1)
 | |
|         {
 | |
|             if (Event.current.type != EventType.ContextClick)
 | |
|                 return;
 | |
| 
 | |
|             var selectedCurves = GetSelectedProperties().ToArray();
 | |
|             if (selectedCurves.Length > 0)
 | |
|             {
 | |
|                 var menu = new GenericMenu();
 | |
|                 var content = selectedCurves.Length == 1 ? s_RemoveCurveContent : s_RemoveCurvesContent;
 | |
|                 menu.AddItem(content,
 | |
|                     false,
 | |
|                     () => RemoveCurves(selectedCurves)
 | |
|                 );
 | |
|                 menu.ShowAsContext();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public IEnumerable<EditorCurveBinding> GetSelectedProperties(bool useForcedGroups = false)
 | |
|         {
 | |
|             var bindings = new HashSet<EditorCurveBinding>();
 | |
|             var bindingTree = m_BindingHierarchy.treeViewController.data as BindingTreeViewDataSource;
 | |
|             foreach (var selectedId in m_BindingHierarchy.treeViewController.GetSelection())
 | |
|             {
 | |
|                 var node = bindingTree.FindItem(selectedId) as CurveTreeViewNode;
 | |
|                 if (node == null)
 | |
|                     continue;
 | |
| 
 | |
|                 var curveNodeParent = node.parent as CurveTreeViewNode;
 | |
|                 if (useForcedGroups && node.forceGroup && curveNodeParent != null)
 | |
|                     bindings.UnionWith(curveNodeParent.bindings);
 | |
|                 else
 | |
|                     bindings.UnionWith(node.bindings);
 | |
|             }
 | |
|             return bindings;
 | |
|         }
 | |
| 
 | |
|         public void RemoveCurves(IEnumerable<EditorCurveBinding> bindings)
 | |
|         {
 | |
|             m_DataSource.RemoveCurves(bindings);
 | |
|             m_BindingHierarchy.RefreshTree();
 | |
|             TimelineWindow.instance.state.CalculateRowRects();
 | |
|             m_LastClipVersion = m_DataSource.GetClipVersion();
 | |
|         }
 | |
| 
 | |
|         class CurveEditorState : ICurveEditorState
 | |
|         {
 | |
|             public TimeArea.TimeFormat timeFormat { get; set; }
 | |
|             public Vector2 timeRange => new Vector2(0, 1);
 | |
|             public bool rippleTime => false;
 | |
|         }
 | |
| 
 | |
|         void UpdateCurveEditorIfNeeded(WindowState state)
 | |
|         {
 | |
|             if ((Event.current.type != EventType.Layout) || (m_DataSource == null) || (m_BindingHierarchy == null))
 | |
|                 return;
 | |
| 
 | |
|             // check if the curves have changed externally
 | |
|             var curveChange = m_DataSource.UpdateExternalChanges(ref m_LastClipVersion);
 | |
|             if (curveChange == CurveChangeType.None)
 | |
|                 return;
 | |
| 
 | |
|             if (curveChange == CurveChangeType.CurveAddedOrRemoved)
 | |
|                 m_BindingHierarchy.RefreshTree();
 | |
|             else // curve modified
 | |
|                 m_BindingHierarchy.RefreshCurves();
 | |
| 
 | |
|             m_CurveEditor.InvalidateSelectionBounds();
 | |
| 
 | |
|             m_CurveEditor.state = new CurveEditorState() { timeFormat = state.timeFormat.ToTimeAreaFormat() };
 | |
|             m_CurveEditor.invSnap = (float)state.referenceSequence.frameRate;
 | |
|         }
 | |
| 
 | |
|         public void DrawCurveEditor(Rect rect, WindowState state, Vector2 clipRange, bool loop, bool selected)
 | |
|         {
 | |
|             SetupMarginsAndRect(rect, state);
 | |
|             UpdateCurveEditorIfNeeded(state);
 | |
| 
 | |
|             if (m_ShouldRestoreShownArea)
 | |
|                 RestoreShownArea();
 | |
| 
 | |
|             var curveVisibleTimeRange = CalculateCurveVisibleTimeRange(state.timeAreaShownRange, m_DataSource);
 | |
|             m_CurveEditor.SetShownHRangeInsideMargins(curveVisibleTimeRange.x, curveVisibleTimeRange.y); //align the curve with the clip.
 | |
| 
 | |
|             if (m_LastFrameRate != state.referenceSequence.frameRate)
 | |
|             {
 | |
|                 m_CurveEditor.hTicks.SetTickModulosForFrameRate((float)state.referenceSequence.frameRate);
 | |
|                 m_LastFrameRate = (float)state.referenceSequence.frameRate;
 | |
|             }
 | |
| 
 | |
|             foreach (var cw in m_CurveEditor.animationCurves)
 | |
|                 cw.renderer.SetWrap(WrapMode.Default, loop ? WrapMode.Loop : WrapMode.Default);
 | |
| 
 | |
|             using (new GUIGroupScope(rect))
 | |
|             {
 | |
|                 var localRect = new Rect(0.0f, 0.0f, rect.width, rect.height);
 | |
|                 var localClipRange = new Vector2(Mathf.Floor(clipRange.x - rect.xMin), Mathf.Ceil(clipRange.y - rect.xMin));
 | |
|                 var curveStartPosX = Mathf.Floor(state.TimeToPixel(m_DataSource.start) - rect.xMin);
 | |
| 
 | |
|                 EditorGUI.DrawRect(new Rect(curveStartPosX, 0.0f, 1.0f, rect.height), new Color(1.0f, 1.0f, 1.0f, 0.5f));
 | |
|                 DrawCurveEditorBackground(localRect);
 | |
| 
 | |
|                 if (selected)
 | |
|                 {
 | |
|                     var selectionRect = new Rect(localClipRange.x, 0.0f, localClipRange.y - localClipRange.x, localRect.height);
 | |
|                     DrawOutline(selectionRect);
 | |
|                 }
 | |
| 
 | |
|                 EditorGUI.BeginChangeCheck();
 | |
|                 {
 | |
|                     var evt = Event.current;
 | |
|                     if (evt.type == EventType.Layout || evt.type == EventType.Repaint || selected)
 | |
|                         m_CurveEditor.CurveGUI();
 | |
|                 }
 | |
|                 if (EditorGUI.EndChangeCheck())
 | |
|                     OnCurvesUpdated();
 | |
| 
 | |
|                 DrawOverlay(localRect, localClipRange, DirectorStyles.Instance.customSkin.colorInlineCurveOutOfRangeOverlay);
 | |
|                 DrawGrid(localRect, curveStartPosX);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static Vector2 CalculateCurveVisibleTimeRange(Vector2 timeAreaShownRange, CurveDataSource curve)
 | |
|         {
 | |
|             var curveVisibleTimeRange = new Vector2
 | |
|             {
 | |
|                 x = Math.Max(0.0f, timeAreaShownRange.x - curve.start),
 | |
|                 y = timeAreaShownRange.y - curve.start
 | |
|             };
 | |
|             return curveVisibleTimeRange * curve.timeScale;
 | |
|         }
 | |
| 
 | |
|         void SetupMarginsAndRect(Rect rect, WindowState state)
 | |
|         {
 | |
|             var startX = state.TimeToPixel(m_DataSource.start) - rect.x;
 | |
|             var timelineWidth = state.timeAreaRect.width;
 | |
|             m_CurveEditor.rect = new Rect(0.0f, 0.0f, timelineWidth, rect.height);
 | |
|             m_CurveEditor.leftmargin = Math.Max(startX, 0.0f);
 | |
|             m_CurveEditor.rightmargin = 0.0f;
 | |
|             m_CurveEditor.topmargin = m_CurveEditor.bottommargin = CalculateTopMargin(rect.height);
 | |
|         }
 | |
| 
 | |
|         void RestoreShownArea()
 | |
|         {
 | |
|             if (isNewSelection)
 | |
|                 FrameClip();
 | |
|             else
 | |
|                 m_CurveEditor.shownAreaInsideMargins = m_ViewModel.inlineCurvesShownAreaInsideMargins;
 | |
|             m_ShouldRestoreShownArea = false;
 | |
|         }
 | |
| 
 | |
|         static void DrawCurveEditorBackground(Rect rect)
 | |
|         {
 | |
|             if (EditorGUIUtility.isProSkin)
 | |
|                 return;
 | |
| 
 | |
|             var animEditorBackgroundRect = Rect.MinMaxRect(0.0f, rect.yMin, rect.xMax, rect.yMax);
 | |
| 
 | |
|             // Curves are not legible in Personal Skin so we need to darken the background a bit.
 | |
|             EditorGUI.DrawRect(animEditorBackgroundRect, DirectorStyles.Instance.customSkin.colorInlineCurvesBackground);
 | |
|         }
 | |
| 
 | |
|         static float CalculateTopMargin(float height)
 | |
|         {
 | |
|             return Mathf.Clamp(0.15f * height, 10.0f, 40.0f);
 | |
|         }
 | |
| 
 | |
|         static void DrawOutline(Rect rect, float thickness = 2.0f)
 | |
|         {
 | |
|             // Draw top selected lines.
 | |
|             EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), Color.white);
 | |
| 
 | |
|             // Draw bottom selected lines.
 | |
|             EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), Color.white);
 | |
| 
 | |
|             // Draw Left Selected Lines
 | |
|             EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), Color.white);
 | |
| 
 | |
|             // Draw Right Selected Lines
 | |
|             EditorGUI.DrawRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), Color.white);
 | |
|         }
 | |
| 
 | |
|         static void DrawOverlay(Rect rect, Vector2 clipRange, Color color)
 | |
|         {
 | |
|             var leftSide = new Rect(rect.xMin, rect.yMin, clipRange.x - rect.xMin, rect.height);
 | |
|             EditorGUI.DrawRect(leftSide, color);
 | |
| 
 | |
|             var rightSide = new Rect(Mathf.Max(0.0f, clipRange.y), rect.yMin, rect.xMax, rect.height);
 | |
|             EditorGUI.DrawRect(rightSide, color);
 | |
|         }
 | |
| 
 | |
|         void DrawGrid(Rect rect, float curveXPosition)
 | |
|         {
 | |
|             var gridXPos = Mathf.Max(curveXPosition - s_GridLabelWidth, rect.xMin);
 | |
|             var gridRect = new Rect(gridXPos, rect.y, s_GridLabelWidth, rect.height);
 | |
|             var originalRect = m_CurveEditor.rect;
 | |
| 
 | |
|             m_CurveEditor.rect = new Rect(0.0f, 0.0f, rect.width, rect.height);
 | |
|             using (new GUIGroupScope(gridRect))
 | |
|                 m_CurveEditor.GridGUI();
 | |
|             m_CurveEditor.rect = originalRect;
 | |
|         }
 | |
|     }
 | |
| }
 |