A game about forced loneliness, made by TACStudios
at master 220 lines 8.5 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEditor.IMGUI.Controls; 5using UnityEditorInternal; 6using UnityEngine; 7using UnityEngine.Timeline; 8 9#if !UNITY_2020_2_OR_NEWER 10using L10n = UnityEditor.Timeline.L10n; 11#endif 12 13namespace UnityEditor.Timeline 14{ 15 class BindingTreeViewDataSource : TreeViewDataSource 16 { 17 struct BindingGroup : IEquatable<BindingGroup>, IComparable<BindingGroup> 18 { 19 public readonly string GroupName; 20 public readonly string Path; 21 public readonly Type Type; 22 23 public BindingGroup(string path, string groupName, Type type) 24 { 25 Path = path; 26 GroupName = groupName; 27 Type = type; 28 } 29 30 public string groupDisplayName => string.IsNullOrEmpty(Path) ? GroupName : string.Format($"{Path} : {GroupName}"); 31 public bool Equals(BindingGroup other) => GroupName == other.GroupName && Type == other.Type && Path == other.Path; 32 public int CompareTo(BindingGroup other) => GetHashCode() - other.GetHashCode(); 33 public override bool Equals(object obj) => obj is BindingGroup other && Equals(other); 34 public override int GetHashCode() 35 { 36 return HashUtility.CombineHash(GroupName != null ? GroupName.GetHashCode() : 0, Type != null ? Type.GetHashCode() : 0, Path != null ? Path.GetHashCode() : 0); 37 } 38 } 39 40 static readonly string s_DefaultValue = L10n.Tr("{0} (Default Value)"); 41 42 public const int RootID = int.MinValue; 43 public const int GroupID = -1; 44 45 private readonly AnimationClip m_Clip; 46 private readonly CurveDataSource m_CurveDataSource; 47 48 public BindingTreeViewDataSource( 49 TreeViewController treeView, AnimationClip clip, CurveDataSource curveDataSource) 50 : base(treeView) 51 { 52 m_Clip = clip; 53 showRootItem = false; 54 m_CurveDataSource = curveDataSource; 55 } 56 57 void SetupRootNodeSettings() 58 { 59 showRootItem = false; 60 SetExpanded(RootID, true); 61 SetExpanded(GroupID, true); 62 } 63 64 public override void FetchData() 65 { 66 if (m_Clip == null) 67 return; 68 69 var bindings = AnimationUtility.GetCurveBindings(m_Clip) 70 .Union(AnimationUtility.GetObjectReferenceCurveBindings(m_Clip)) 71 .ToArray(); 72 73 // a sorted linear list of nodes 74 var results = bindings.GroupBy(GetBindingGroup, p => p, CreateTuple) 75 .OrderBy(t => t.Item1.Path) 76 .ThenBy(NamePrioritySort) 77 // this makes component ordering match the animation window 78 .ThenBy(t => t.Item1.Type.ToString()) 79 .ThenBy(t => t.Item1.GroupName).ToArray(); 80 81 m_RootItem = new CurveTreeViewNode(RootID, null, "root", null) 82 { 83 children = new List<TreeViewItem>(1) 84 }; 85 86 if (results.Any()) 87 { 88 var groupingNode = new CurveTreeViewNode(GroupID, m_RootItem, m_CurveDataSource.groupingName, bindings) 89 { 90 children = new List<TreeViewItem>() 91 }; 92 93 m_RootItem.children.Add(groupingNode); 94 95 foreach (var r in results) 96 { 97 var key = r.Item1; 98 var nodeBindings = r.Item2; 99 100 FillMissingTransformCurves(nodeBindings); 101 if (nodeBindings.Count == 1) 102 groupingNode.children.Add(CreateLeafNode(nodeBindings[0], groupingNode, PropertyName(nodeBindings[0], true))); 103 else if (nodeBindings.Count > 1) 104 { 105 var childBindings = nodeBindings.OrderBy(BindingSort).ToArray(); 106 var parent = new CurveTreeViewNode(key.GetHashCode(), groupingNode, key.groupDisplayName, childBindings) { children = new List<TreeViewItem>() }; 107 groupingNode.children.Add(parent); 108 foreach (var b in childBindings) 109 parent.children.Add(CreateLeafNode(b, parent, PropertyName(b, false))); 110 } 111 } 112 SetupRootNodeSettings(); 113 } 114 115 m_NeedRefreshRows = true; 116 } 117 118 public void UpdateData() 119 { 120 m_TreeView.ReloadData(); 121 } 122 123 string GroupName(EditorCurveBinding binding) 124 { 125 var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName); 126 return CleanUpArrayBinding(AnimationWindowUtility.NicifyPropertyGroupName(binding.type, propertyName), true); 127 } 128 129 static string CleanUpArrayBinding(string propertyName, bool isGroup) 130 { 131 const string arrayIndicator = ".Array.data["; 132 const string arrayDisplay = ".data["; 133 134 var arrayIndex = propertyName.LastIndexOf(arrayIndicator, StringComparison.Ordinal); 135 if (arrayIndex == -1) 136 return propertyName; 137 if (isGroup) 138 propertyName = propertyName.Substring(0, arrayIndex); 139 return propertyName.Replace(arrayIndicator, arrayDisplay); 140 } 141 142 string PropertyName(EditorCurveBinding binding, bool prependPathName) 143 { 144 var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName); 145 propertyName = CleanUpArrayBinding(AnimationWindowUtility.GetPropertyDisplayName(propertyName), false); 146 if (binding.isPhantom) 147 propertyName = string.Format(s_DefaultValue, propertyName); 148 if (prependPathName && !string.IsNullOrEmpty(binding.path)) 149 propertyName = $"{binding.path} : {propertyName}"; 150 return propertyName; 151 } 152 153 BindingGroup GetBindingGroup(EditorCurveBinding binding) 154 { 155 return new BindingGroup(binding.path ?? string.Empty, GroupName(binding), binding.type); 156 } 157 158 static CurveTreeViewNode CreateLeafNode(EditorCurveBinding binding, TreeViewItem parent, string displayName) 159 { 160 return new CurveTreeViewNode(binding.GetHashCode(), parent, displayName, new[] { binding }, AnimationWindowUtility.ForceGrouping(binding)); 161 } 162 163 static void FillMissingTransformCurves(List<EditorCurveBinding> bindings) 164 { 165 if (!AnimationWindowUtility.IsActualTransformCurve(bindings[0]) || bindings.Count >= 3) 166 return; 167 168 var binding = bindings[0]; 169 var prefixPropertyName = binding.propertyName.Split('.').First(); 170 171 binding.isPhantom = true; 172 if (!bindings.Any(p => p.propertyName.EndsWith(".x"))) 173 { 174 binding.propertyName = prefixPropertyName + ".x"; 175 bindings.Insert(0, binding); 176 } 177 178 if (!bindings.Any(p => p.propertyName.EndsWith(".y"))) 179 { 180 binding.propertyName = prefixPropertyName + ".y"; 181 bindings.Insert(1, binding); 182 } 183 184 if (!bindings.Any(p => p.propertyName.EndsWith(".z"))) 185 { 186 binding.propertyName = prefixPropertyName + ".z"; 187 bindings.Insert(2, binding); 188 } 189 } 190 191 // make sure vectors and colors are sorted correctly in their subgroups 192 static int BindingSort(EditorCurveBinding b) 193 { 194 return AnimationWindowUtility.GetComponentIndex(b.propertyName); 195 } 196 197 static int NamePrioritySort(ValueTuple<BindingGroup, List<EditorCurveBinding>> group) 198 { 199 if (group.Item1.Type != typeof(Transform)) 200 return 0; 201 202 switch (group.Item1.GroupName) 203 { 204 case "Position": return Int32.MinValue; 205 case "Rotation": return Int32.MinValue + 1; 206 case "Scale": return Int32.MinValue + 2; 207 default: return 0; 208 } 209 } 210 211 static ValueTuple<BindingGroup, List<EditorCurveBinding>> CreateTuple(BindingGroup key, IEnumerable<EditorCurveBinding> items) 212 { 213 return new ValueTuple<BindingGroup, List<EditorCurveBinding>>() 214 { 215 Item1 = key, 216 Item2 = items.ToList() 217 }; 218 } 219 } 220}