228 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| using UnityEditorInternal;
 | |
| using UnityEngine;
 | |
| using UnityEngine.Timeline;
 | |
| 
 | |
| #if !UNITY_2020_2_OR_NEWER
 | |
| using L10n = UnityEditor.Timeline.L10n;
 | |
| #endif
 | |
| 
 | |
| #if UNITY_6000_2_OR_NEWER
 | |
| using TreeViewController = UnityEditor.IMGUI.Controls.TreeViewController<int>;
 | |
| using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
 | |
| using TreeViewDataSource = UnityEditor.IMGUI.Controls.TreeViewDataSource<int>;
 | |
| #else
 | |
| using UnityEditor.IMGUI.Controls;
 | |
| #endif
 | |
| 
 | |
| namespace UnityEditor.Timeline
 | |
| {
 | |
|     class BindingTreeViewDataSource : TreeViewDataSource
 | |
|     {
 | |
|         struct BindingGroup : IEquatable<BindingGroup>, IComparable<BindingGroup>
 | |
|         {
 | |
|             public readonly string GroupName;
 | |
|             public readonly string Path;
 | |
|             public readonly Type Type;
 | |
| 
 | |
|             public BindingGroup(string path, string groupName, Type type)
 | |
|             {
 | |
|                 Path = path;
 | |
|                 GroupName = groupName;
 | |
|                 Type = type;
 | |
|             }
 | |
| 
 | |
|             public string groupDisplayName => string.IsNullOrEmpty(Path) ? GroupName : string.Format($"{Path} : {GroupName}");
 | |
|             public bool Equals(BindingGroup other) => GroupName == other.GroupName && Type == other.Type && Path == other.Path;
 | |
|             public int CompareTo(BindingGroup other) => GetHashCode() - other.GetHashCode();
 | |
|             public override bool Equals(object obj) => obj is BindingGroup other && Equals(other);
 | |
|             public override int GetHashCode()
 | |
|             {
 | |
|                 return HashUtility.CombineHash(GroupName != null ? GroupName.GetHashCode() : 0, Type != null ? Type.GetHashCode() : 0, Path != null ? Path.GetHashCode() : 0);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static readonly string s_DefaultValue = L10n.Tr("{0} (Default Value)");
 | |
| 
 | |
|         public const int RootID = int.MinValue;
 | |
|         public const int GroupID = -1;
 | |
| 
 | |
|         private readonly AnimationClip m_Clip;
 | |
|         private readonly CurveDataSource m_CurveDataSource;
 | |
| 
 | |
|         public BindingTreeViewDataSource(
 | |
|             TreeViewController treeView, AnimationClip clip, CurveDataSource curveDataSource)
 | |
|             : base(treeView)
 | |
|         {
 | |
|             m_Clip = clip;
 | |
|             showRootItem = false;
 | |
|             m_CurveDataSource = curveDataSource;
 | |
|         }
 | |
| 
 | |
|         void SetupRootNodeSettings()
 | |
|         {
 | |
|             showRootItem = false;
 | |
|             SetExpanded(RootID, true);
 | |
|             SetExpanded(GroupID, true);
 | |
|         }
 | |
| 
 | |
|         public override void FetchData()
 | |
|         {
 | |
|             if (m_Clip == null)
 | |
|                 return;
 | |
| 
 | |
|             var bindings = AnimationUtility.GetCurveBindings(m_Clip)
 | |
|                 .Union(AnimationUtility.GetObjectReferenceCurveBindings(m_Clip))
 | |
|                 .ToArray();
 | |
| 
 | |
|             // a sorted linear list of nodes
 | |
|             var results = bindings.GroupBy(GetBindingGroup, p => p, CreateTuple)
 | |
|                 .OrderBy(t => t.Item1.Path)
 | |
|                 .ThenBy(NamePrioritySort)
 | |
|                 // this makes component ordering match the animation window
 | |
|                 .ThenBy(t => t.Item1.Type.ToString())
 | |
|                 .ThenBy(t => t.Item1.GroupName).ToArray();
 | |
| 
 | |
|             m_RootItem = new CurveTreeViewNode(RootID, null, "root", null)
 | |
|             {
 | |
|                 children = new List<TreeViewItem>(1)
 | |
|             };
 | |
| 
 | |
|             if (results.Any())
 | |
|             {
 | |
|                 var groupingNode = new CurveTreeViewNode(GroupID, m_RootItem, m_CurveDataSource.groupingName, bindings)
 | |
|                 {
 | |
|                     children = new List<TreeViewItem>()
 | |
|                 };
 | |
| 
 | |
|                 m_RootItem.children.Add(groupingNode);
 | |
| 
 | |
|                 foreach (var r in results)
 | |
|                 {
 | |
|                     var key = r.Item1;
 | |
|                     var nodeBindings = r.Item2;
 | |
| 
 | |
|                     FillMissingTransformCurves(nodeBindings);
 | |
|                     if (nodeBindings.Count == 1)
 | |
|                         groupingNode.children.Add(CreateLeafNode(nodeBindings[0], groupingNode, PropertyName(nodeBindings[0], true)));
 | |
|                     else if (nodeBindings.Count > 1)
 | |
|                     {
 | |
|                         var childBindings = nodeBindings.OrderBy(BindingSort).ToArray();
 | |
|                         var parent = new CurveTreeViewNode(key.GetHashCode(), groupingNode, key.groupDisplayName, childBindings) { children = new List<TreeViewItem>() };
 | |
|                         groupingNode.children.Add(parent);
 | |
|                         foreach (var b in childBindings)
 | |
|                             parent.children.Add(CreateLeafNode(b, parent, PropertyName(b, false)));
 | |
|                     }
 | |
|                 }
 | |
|                 SetupRootNodeSettings();
 | |
|             }
 | |
| 
 | |
|             m_NeedRefreshRows = true;
 | |
|         }
 | |
| 
 | |
|         public void UpdateData()
 | |
|         {
 | |
|             m_TreeView.ReloadData();
 | |
|         }
 | |
| 
 | |
|         string GroupName(EditorCurveBinding binding)
 | |
|         {
 | |
|             var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName);
 | |
|             return CleanUpArrayBinding(AnimationWindowUtility.NicifyPropertyGroupName(binding.type, propertyName), true);
 | |
|         }
 | |
| 
 | |
|         static string CleanUpArrayBinding(string propertyName, bool isGroup)
 | |
|         {
 | |
|             const string arrayIndicator = ".Array.data[";
 | |
|             const string arrayDisplay = ".data[";
 | |
| 
 | |
|             var arrayIndex = propertyName.LastIndexOf(arrayIndicator, StringComparison.Ordinal);
 | |
|             if (arrayIndex == -1)
 | |
|                 return propertyName;
 | |
|             if (isGroup)
 | |
|                 propertyName = propertyName.Substring(0, arrayIndex);
 | |
|             return propertyName.Replace(arrayIndicator, arrayDisplay);
 | |
|         }
 | |
| 
 | |
|         string PropertyName(EditorCurveBinding binding, bool prependPathName)
 | |
|         {
 | |
|             var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName);
 | |
|             propertyName = CleanUpArrayBinding(AnimationWindowUtility.GetPropertyDisplayName(propertyName), false);
 | |
|             if (binding.isPhantom)
 | |
|                 propertyName = string.Format(s_DefaultValue, propertyName);
 | |
|             if (prependPathName && !string.IsNullOrEmpty(binding.path))
 | |
|                 propertyName = $"{binding.path} : {propertyName}";
 | |
|             return propertyName;
 | |
|         }
 | |
| 
 | |
|         BindingGroup GetBindingGroup(EditorCurveBinding binding)
 | |
|         {
 | |
|             return new BindingGroup(binding.path ?? string.Empty, GroupName(binding), binding.type);
 | |
|         }
 | |
| 
 | |
|         static CurveTreeViewNode CreateLeafNode(EditorCurveBinding binding, TreeViewItem parent, string displayName)
 | |
|         {
 | |
|             return new CurveTreeViewNode(binding.GetHashCode(), parent, displayName, new[] { binding }, AnimationWindowUtility.ForceGrouping(binding));
 | |
|         }
 | |
| 
 | |
|         static void FillMissingTransformCurves(List<EditorCurveBinding> bindings)
 | |
|         {
 | |
|             if (!AnimationWindowUtility.IsActualTransformCurve(bindings[0]) || bindings.Count >= 3)
 | |
|                 return;
 | |
| 
 | |
|             var binding = bindings[0];
 | |
|             var prefixPropertyName = binding.propertyName.Split('.').First();
 | |
| 
 | |
|             binding.isPhantom = true;
 | |
|             if (!bindings.Any(p => p.propertyName.EndsWith(".x")))
 | |
|             {
 | |
|                 binding.propertyName = prefixPropertyName + ".x";
 | |
|                 bindings.Insert(0, binding);
 | |
|             }
 | |
| 
 | |
|             if (!bindings.Any(p => p.propertyName.EndsWith(".y")))
 | |
|             {
 | |
|                 binding.propertyName = prefixPropertyName + ".y";
 | |
|                 bindings.Insert(1, binding);
 | |
|             }
 | |
| 
 | |
|             if (!bindings.Any(p => p.propertyName.EndsWith(".z")))
 | |
|             {
 | |
|                 binding.propertyName = prefixPropertyName + ".z";
 | |
|                 bindings.Insert(2, binding);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // make sure vectors and colors are sorted correctly in their subgroups
 | |
|         static int BindingSort(EditorCurveBinding b)
 | |
|         {
 | |
|             return AnimationWindowUtility.GetComponentIndex(b.propertyName);
 | |
|         }
 | |
| 
 | |
|         static int NamePrioritySort(ValueTuple<BindingGroup, List<EditorCurveBinding>> group)
 | |
|         {
 | |
|             if (group.Item1.Type != typeof(Transform))
 | |
|                 return 0;
 | |
| 
 | |
|             switch (group.Item1.GroupName)
 | |
|             {
 | |
|                 case "Position": return Int32.MinValue;
 | |
|                 case "Rotation": return Int32.MinValue + 1;
 | |
|                 case "Scale": return Int32.MinValue + 2;
 | |
|                 default: return 0;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static ValueTuple<BindingGroup, List<EditorCurveBinding>> CreateTuple(BindingGroup key, IEnumerable<EditorCurveBinding> items)
 | |
|         {
 | |
|             return new ValueTuple<BindingGroup, List<EditorCurveBinding>>()
 | |
|             {
 | |
|                 Item1 = key,
 | |
|                 Item2 = items.ToList()
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| }
 |