TypeUtility.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Reflection;
  6. using UnityEngine;
  7. using UnityEngine.Playables;
  8. using UnityEngine.Timeline;
  9. using Component = UnityEngine.Component;
  10. using Object = UnityEngine.Object;
  11. namespace UnityEditor.Timeline
  12. {
  13. static class TypeUtility
  14. {
  15. private static Type[] s_AllTrackTypes;
  16. private static Type[] s_AllClipTypes;
  17. private static Type[] s_MarkerTypes;
  18. private static Dictionary<Type, Type[]> s_TrackTypeToVisibleClipType = new Dictionary<Type, Type[]>();
  19. private static Dictionary<Type, Type[]> s_TrackTypeToAllClipType = new Dictionary<Type, Type[]>();
  20. private static Dictionary<Type, TrackBindingTypeAttribute> s_TrackToBindingCache = new Dictionary<Type, TrackBindingTypeAttribute>();
  21. public static bool IsConcretePlayableAsset(Type t)
  22. {
  23. return typeof(IPlayableAsset).IsAssignableFrom(t)
  24. && IsConcreteAsset(t);
  25. }
  26. private static bool IsConcreteAsset(Type t)
  27. {
  28. return typeof(ScriptableObject).IsAssignableFrom(t)
  29. && !t.IsAbstract
  30. && !t.IsGenericType
  31. && !t.IsInterface
  32. && !typeof(TrackAsset).IsAssignableFrom(t)
  33. && !typeof(TimelineAsset).IsAssignableFrom(t);
  34. }
  35. /// <summary>
  36. /// List of all PlayableAssets
  37. /// </summary>
  38. public static IEnumerable<Type> AllClipTypes()
  39. {
  40. if (s_AllClipTypes == null)
  41. {
  42. s_AllClipTypes = TypeCache.GetTypesDerivedFrom<IPlayableAsset>().
  43. Where(t => IsConcreteAsset(t)).
  44. ToArray();
  45. }
  46. return s_AllClipTypes;
  47. }
  48. public static IEnumerable<Type> AllTrackTypes()
  49. {
  50. if (s_AllTrackTypes == null)
  51. {
  52. s_AllTrackTypes = TypeCache.GetTypesDerivedFrom<TrackAsset>()
  53. .Where(x => !x.IsAbstract)
  54. .ToArray();
  55. }
  56. return s_AllTrackTypes;
  57. }
  58. public static IEnumerable<Type> GetVisiblePlayableAssetsHandledByTrack(Type trackType)
  59. {
  60. if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
  61. return Enumerable.Empty<Type>();
  62. Type[] types;
  63. if (s_TrackTypeToVisibleClipType.TryGetValue(trackType, out types))
  64. {
  65. return types;
  66. }
  67. // special case -- the playable track handles all types not handled by other tracks
  68. if (trackType == typeof(PlayableTrack))
  69. {
  70. types = GetUnhandledClipTypes().ToArray();
  71. s_TrackTypeToVisibleClipType[trackType] = types;
  72. return types;
  73. }
  74. var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
  75. var baseClasses = attributes.
  76. OfType<TrackClipTypeAttribute>().
  77. Where(t => t.allowAutoCreate).
  78. Select(a => a.inspectedType);
  79. types = AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t))).ToArray();
  80. s_TrackTypeToVisibleClipType[trackType] = types;
  81. return types;
  82. }
  83. public static IEnumerable<Type> GetPlayableAssetsHandledByTrack(Type trackType)
  84. {
  85. if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
  86. return Enumerable.Empty<Type>();
  87. Type[] types;
  88. if (s_TrackTypeToAllClipType.TryGetValue(trackType, out types))
  89. {
  90. return types;
  91. }
  92. // special case -- the playable track handles all types not handled by other tracks
  93. if (trackType == typeof(PlayableTrack))
  94. {
  95. types = GetUnhandledClipTypes().ToArray();
  96. s_TrackTypeToAllClipType[trackType] = types;
  97. return types;
  98. }
  99. var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
  100. var baseClasses = attributes.
  101. OfType<TrackClipTypeAttribute>().
  102. Select(a => a.inspectedType);
  103. types = AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t))).ToArray();
  104. s_TrackTypeToAllClipType[trackType] = types;
  105. return types;
  106. }
  107. /// <summary>
  108. /// Returns the binding attribute attrached to the track
  109. /// </summary>
  110. public static TrackBindingTypeAttribute GetTrackBindingAttribute(Type trackType)
  111. {
  112. if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
  113. return null;
  114. TrackBindingTypeAttribute attribute = null;
  115. if (!s_TrackToBindingCache.TryGetValue(trackType, out attribute))
  116. {
  117. attribute = (TrackBindingTypeAttribute)Attribute.GetCustomAttribute(trackType, typeof(TrackBindingTypeAttribute));
  118. s_TrackToBindingCache.Add(trackType, attribute);
  119. }
  120. return attribute;
  121. }
  122. /// <summary>
  123. /// True if the given track has a clip type that handles the given object
  124. /// </summary>
  125. public static bool TrackHasClipForObject(Type trackType, Object obj)
  126. {
  127. return GetPlayableAssetsHandledByTrack(trackType)
  128. .Any(c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj)));
  129. }
  130. /// <summary>
  131. /// Get the list of markers that have fields for the object
  132. /// </summary>
  133. public static IEnumerable<Type> MarkerTypesWithFieldForObject(Object obj)
  134. {
  135. return GetAllMarkerTypes().Where(
  136. c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj))
  137. );
  138. }
  139. /// <summary>
  140. /// Get the list of tracks that can handle this object as clips
  141. /// </summary>
  142. public static IEnumerable<Type> GetTrackTypesForObject(Object obj)
  143. {
  144. if (obj == null)
  145. return Enumerable.Empty<Type>();
  146. return AllTrackTypes().Where(t => TrackHasClipForObject(t, obj));
  147. }
  148. /// <summary>
  149. /// Given a trackType and an object, does the binding type match
  150. /// Takes into account whether creating a missing component is permitted
  151. /// </summary>
  152. public static bool IsTrackCreatableFromObject(Object obj, Type trackType)
  153. {
  154. if (obj == null || obj.IsPrefab())
  155. return false;
  156. var attribute = GetTrackBindingAttribute(trackType);
  157. if (attribute == null || attribute.type == null)
  158. return false;
  159. if (attribute.type.IsAssignableFrom(obj.GetType()))
  160. return true;
  161. var gameObject = obj as GameObject;
  162. if (gameObject != null && typeof(Component).IsAssignableFrom(attribute.type))
  163. {
  164. return gameObject.GetComponent(attribute.type) != null ||
  165. (attribute.flags & TrackBindingFlags.AllowCreateComponent) != 0;
  166. }
  167. return false;
  168. }
  169. /// <summary>
  170. /// Given an object, get the list of track that are creatable from it. Takes
  171. /// binding flags into account
  172. /// </summary>
  173. public static IEnumerable<Type> GetTracksCreatableFromObject(Object obj)
  174. {
  175. if (obj == null)
  176. return Enumerable.Empty<Type>();
  177. return AllTrackTypes().Where(t => !IsHiddenInMenu(t) && IsTrackCreatableFromObject(obj, t));
  178. }
  179. /// <summary>
  180. /// Get the list of playable assets that can handle an object for a particular track
  181. /// </summary>
  182. /// <param name="trackType">The type of the track</param>
  183. /// <param name="obj">The object to handle</param>
  184. /// <returns></returns>
  185. public static IEnumerable<Type> GetAssetTypesForObject(Type trackType, Object obj)
  186. {
  187. if (obj == null)
  188. return Enumerable.Empty<Type>();
  189. return GetPlayableAssetsHandledByTrack(trackType).Where(
  190. c => ObjectReferenceField.FindObjectReferences(c).Any(o => o.IsAssignable(obj))
  191. );
  192. }
  193. // get the track types for a track from it's attributes
  194. private static IEnumerable<Type> GetTrackClipTypesFromAttributes(Type trackType)
  195. {
  196. if (trackType == null || !typeof(TrackAsset).IsAssignableFrom(trackType))
  197. return Enumerable.Empty<Type>();
  198. var attributes = trackType.GetCustomAttributes(typeof(TrackClipTypeAttribute), true);
  199. var baseClasses = attributes.
  200. OfType<TrackClipTypeAttribute>().
  201. Select(a => a.inspectedType);
  202. return AllClipTypes().Where(t => baseClasses.Any(x => x.IsAssignableFrom(t)));
  203. }
  204. // find the playable asset types that are unhandled
  205. private static IEnumerable<Type> GetUnhandledClipTypes()
  206. {
  207. var typesHandledByTrack = AllTrackTypes().SelectMany(t => GetTrackClipTypesFromAttributes(t));
  208. // exclude anything in the timeline assembly, handled by tracks, has a hide in menu attribute
  209. // or is explicity ignored
  210. return AllClipTypes()
  211. .Except(typesHandledByTrack)
  212. .Where(t => !TypeUtility.IsBuiltIn(t)) // exclude built-in
  213. .Where(t => !typeof(TrackAsset).IsAssignableFrom(t)) // exclude track types (they are playable assets)
  214. .Where(t => !t.IsDefined(typeof(HideInMenuAttribute), false) && !t.IsDefined(typeof(IgnoreOnPlayableTrackAttribute), true))
  215. .Distinct();
  216. }
  217. public static IEnumerable<Type> GetAllMarkerTypes()
  218. {
  219. if (s_MarkerTypes == null)
  220. {
  221. s_MarkerTypes = TypeCache.GetTypesDerivedFrom<IMarker>()
  222. .Where(x =>
  223. typeof(ScriptableObject).IsAssignableFrom(x)
  224. && !x.IsAbstract
  225. && !x.IsGenericType
  226. && !x.IsInterface)
  227. .ToArray();
  228. }
  229. return s_MarkerTypes;
  230. }
  231. public static IEnumerable<Type> GetUserMarkerTypes()
  232. {
  233. return GetAllMarkerTypes().Where(x => !IsBuiltIn(x) && !IsHiddenInMenu(x));
  234. }
  235. public static IEnumerable<Type> GetBuiltInMarkerTypes()
  236. {
  237. return GetAllMarkerTypes().Where(TypeUtility.IsBuiltIn);
  238. }
  239. public static bool DoesTrackSupportMarkerType(TrackAsset track, Type type)
  240. {
  241. if (track.supportsNotifications)
  242. {
  243. return true;
  244. }
  245. return !typeof(INotification).IsAssignableFrom(type);
  246. }
  247. internal static string GetDisplayName(Type t)
  248. {
  249. var displayName = ObjectNames.NicifyVariableName(t.Name);
  250. var attr = Attribute.GetCustomAttribute(t, typeof(DisplayNameAttribute)) as DisplayNameAttribute;
  251. if (attr != null)
  252. displayName = attr.DisplayName;
  253. return displayName;
  254. }
  255. public static bool IsHiddenInMenu(Type type)
  256. {
  257. var attr = type.GetCustomAttributes(typeof(HideInMenuAttribute), false);
  258. return attr.Length > 0;
  259. }
  260. public struct ObjectReference
  261. {
  262. public Type type;
  263. public bool isSceneReference;
  264. }
  265. public static IEnumerable<ObjectReference> ObjectReferencesForType(Type type)
  266. {
  267. var objectReferences = ObjectReferenceField.FindObjectReferences(type);
  268. var uniqueTypes = objectReferences.Select(objRef => objRef.type).Distinct();
  269. foreach (var refType in uniqueTypes)
  270. {
  271. var isSceneReference = objectReferences.Any(objRef => objRef.type == refType && objRef.isSceneReference);
  272. yield return new ObjectReference { type = refType, isSceneReference = isSceneReference };
  273. }
  274. }
  275. /// <summary>
  276. /// Checks whether a type has an overridden method with a specific name. This method also checks overridden members in parent classes.
  277. /// </summary>
  278. public static bool HasOverrideMethod(System.Type t, string name)
  279. {
  280. const MethodAttributes mask = MethodAttributes.Virtual | MethodAttributes.NewSlot;
  281. const MethodAttributes expectedResult = MethodAttributes.Virtual;
  282. var method = t.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  283. return method != null && (method.Attributes & mask) == expectedResult;
  284. }
  285. /// <summary>
  286. /// Returns whether the given type resides in the timeline assembly
  287. /// </summary>
  288. public static bool IsBuiltIn(System.Type t)
  289. {
  290. return t != null && t.Assembly.Equals(typeof(TimelineAsset).Assembly);
  291. }
  292. }
  293. }