FUtility.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditor.Animations;
  4. using UnityEditor.SceneManagement;
  5. //using UnityEditorInternal;
  6. using System;
  7. using System.IO;
  8. using System.Reflection;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using Flux;
  12. namespace FluxEditor
  13. {
  14. public static class FUtility
  15. {
  16. #region Preferences
  17. private static bool _preferencesLoaded = false;
  18. // keys
  19. private const string FLUX_KEY = "Flux.";
  20. private const string TIME_FORMAT_KEY = FLUX_KEY + "TimeFormat";
  21. private const string FRAME_RATE_KEY = FLUX_KEY + "FrameRate";
  22. private const string OPEN_SEQUENCE_ON_SELECT_KEY = FLUX_KEY + "OpenSequenceOnSelect";
  23. private const string RENDER_ON_EDITOR_PLAY_KEY = FLUX_KEY + "ReplainOnEditorPlay";
  24. private const string COLLAPSE_COMMENT_TRACKS_KEY = FLUX_KEY + "CollapseCommentTracks";
  25. // defaults
  26. private const TimeFormat DEFAULT_TIME_FORMAT = TimeFormat.Frames;
  27. private const int DEFAULT_FRAME_RATE = FSequence.DEFAULT_FRAMES_PER_SECOND;
  28. private const bool DEFAULT_OPEN_SEQUENCE_ON_SELECT = false;
  29. private const bool DEFAULT_RENDER_ON_EDITOR_PLAY = true;
  30. private const bool DEFAULT_COLLAPSE_COMMENT_TRACKS = false;
  31. // current values
  32. private static TimeFormat _timeFormat = DEFAULT_TIME_FORMAT;
  33. public static TimeFormat TimeFormat { get { return _timeFormat; } }
  34. private static int _frameRate = DEFAULT_FRAME_RATE;
  35. public static int FrameRate { get { return _frameRate; } }
  36. private static bool _openSequenceOnSelect = DEFAULT_OPEN_SEQUENCE_ON_SELECT;
  37. public static bool OpenSequenceOnSelect { get { return _openSequenceOnSelect; } }
  38. private static bool _renderOnEditorPlay = DEFAULT_RENDER_ON_EDITOR_PLAY;
  39. public static bool RenderOnEditorPlay { get { return _renderOnEditorPlay; } }
  40. private static bool _collapseCommentTracks = DEFAULT_COLLAPSE_COMMENT_TRACKS;
  41. public static bool CollapseCommentTracks { get { return _collapseCommentTracks; } }
  42. [PreferenceItem("Flux Editor")]
  43. public static void PreferencesGUI()
  44. {
  45. if( !_preferencesLoaded )
  46. LoadPreferences();
  47. EditorGUI.BeginChangeCheck();
  48. _timeFormat = (TimeFormat)EditorGUILayout.EnumPopup( new GUIContent("Time Format", "In what format is time represented? E.g. for 60fps:\n\tFrames: 127\n\tSeconds: 2.12\n\tSeconds Formatted: 2.7"), _timeFormat );
  49. _frameRate = EditorGUILayout.IntSlider( new GUIContent("Frame Rate", "How many frames per second have the Sequences by default?"), _frameRate, 1, 120 );
  50. _openSequenceOnSelect = EditorGUILayout.Toggle( new GUIContent("Open Sequence On Select", "Should it open Sequences in Flux Editor window when they are selected?"), _openSequenceOnSelect );
  51. _renderOnEditorPlay = EditorGUILayout.Toggle( new GUIContent("Render On Editor Play", "Render Flux Editor window when editor is in Play mode? Turn it off it you want to avoid the redraw costs of Flux when selected sequence is playing."), _renderOnEditorPlay );
  52. _collapseCommentTracks = EditorGUILayout.Toggle( new GUIContent("Collapse Comment Tracks", "Collapse the comment tracks so that they are slimmer and don't build preview textures."), _collapseCommentTracks );
  53. if( EditorGUI.EndChangeCheck() )
  54. SavePreferences();
  55. }
  56. public static void LoadPreferences()
  57. {
  58. _preferencesLoaded = true;
  59. _timeFormat = (TimeFormat)EditorPrefs.GetInt( TIME_FORMAT_KEY, (int)DEFAULT_TIME_FORMAT );
  60. _frameRate = EditorPrefs.GetInt( FRAME_RATE_KEY, DEFAULT_FRAME_RATE );
  61. _openSequenceOnSelect = EditorPrefs.GetBool( OPEN_SEQUENCE_ON_SELECT_KEY, DEFAULT_OPEN_SEQUENCE_ON_SELECT );
  62. _renderOnEditorPlay = EditorPrefs.GetBool( RENDER_ON_EDITOR_PLAY_KEY, DEFAULT_RENDER_ON_EDITOR_PLAY );
  63. _collapseCommentTracks = EditorPrefs.GetBool( COLLAPSE_COMMENT_TRACKS_KEY, DEFAULT_COLLAPSE_COMMENT_TRACKS );
  64. }
  65. public static void SavePreferences()
  66. {
  67. EditorPrefs.SetInt( TIME_FORMAT_KEY, (int)_timeFormat );
  68. EditorPrefs.SetInt( FRAME_RATE_KEY, _frameRate );
  69. EditorPrefs.SetBool( OPEN_SEQUENCE_ON_SELECT_KEY, _openSequenceOnSelect );
  70. EditorPrefs.SetBool( RENDER_ON_EDITOR_PLAY_KEY, _renderOnEditorPlay );
  71. EditorPrefs.SetBool( COLLAPSE_COMMENT_TRACKS_KEY, _collapseCommentTracks );
  72. }
  73. #endregion Preferences
  74. #region Paths / Resource Loading
  75. private static string _fluxPath = null;
  76. private static string _fluxEditorPath = null;
  77. private static string _fluxSkinPath = null;
  78. private static EditorWindow _gameView = null;
  79. private static GUISkin _fluxSkin = null;
  80. private static GUIStyle _evtStyle = null;
  81. private static GUIStyle _simpleButtonStyle = null;
  82. // private static GUIStyle _commentEvtStyle = null;
  83. public static string FindFluxDirectory()
  84. {
  85. string[] directories = Directory.GetDirectories("Assets", "Flux", SearchOption.AllDirectories);
  86. return directories.Length > 0 ? directories[0] : string.Empty;
  87. }
  88. public static string GetFluxPath()
  89. {
  90. if( _fluxPath == null )
  91. {
  92. _fluxPath = FindFluxDirectory()+'/';
  93. _fluxEditorPath = _fluxPath + "Editor/";
  94. _fluxSkinPath = _fluxEditorPath + "Skin/";
  95. }
  96. return _fluxPath;
  97. }
  98. public static string GetFluxEditorPath()
  99. {
  100. if( _fluxEditorPath == null ) GetFluxPath();
  101. return _fluxEditorPath;
  102. }
  103. public static string GetFluxSkinPath()
  104. {
  105. if( _fluxSkinPath == null ) GetFluxPath();
  106. return _fluxSkinPath;
  107. }
  108. public static GUISkin GetFluxSkin()
  109. {
  110. if( _fluxSkin == null )
  111. _fluxSkin = (GUISkin)AssetDatabase.LoadAssetAtPath( GetFluxSkinPath()+"FSkin.guiskin", typeof(GUISkin) );
  112. return _fluxSkin;
  113. }
  114. public static Texture2D GetFluxTexture( string textureFile )
  115. {
  116. return (Texture2D)AssetDatabase.LoadAssetAtPath( GetFluxSkinPath() + textureFile, typeof(Texture2D) );
  117. }
  118. public static GUIStyle GetEventStyle()
  119. {
  120. if( _evtStyle == null )
  121. _evtStyle = GetFluxSkin().GetStyle("Event");
  122. return _evtStyle;
  123. }
  124. public static GUIStyle GetCommentEventStyle()
  125. {
  126. return GetEventStyle();
  127. // if( _commentEvtStyle == null )
  128. // _commentEvtStyle = GetFluxSkin().GetStyle("CommentEvent");
  129. // return _commentEvtStyle;
  130. }
  131. public static GUIStyle GetSimpleButtonStyle()
  132. {
  133. if( _simpleButtonStyle == null )
  134. _simpleButtonStyle = GetFluxSkin().GetStyle("SimpleButton");
  135. return _simpleButtonStyle;
  136. }
  137. private static FSettings _settings = null;
  138. public static Color GetEventColor( string eventTypeStr )
  139. {
  140. return GetSettings().GetEventColor( eventTypeStr );
  141. }
  142. public static FSettings GetSettings()
  143. {
  144. if( _settings == null )
  145. _settings = (FSettings)AssetDatabase.LoadAssetAtPath( GetFluxEditorPath() + "FluxSettings.asset", typeof(FSettings) );
  146. return _settings;
  147. }
  148. #endregion Paths / Resource Loading
  149. #region Events
  150. // public static void Resize( FEvent evt, FrameRange newFrameRange )
  151. // {
  152. // if( evt.FrameRange == newFrameRange || newFrameRange.Start > newFrameRange.End )
  153. // return;
  154. //
  155. // if( newFrameRange.Length < evt.GetMinLength() || newFrameRange.Length > evt.GetMaxLength() )
  156. // {
  157. // Debug.LogError( string.Format("Trying to resize an Event to [{0},{1}] (length: {2}) which isn't a valid length, should be between [{3},{4}]", newFrameRange.Start, newFrameRange.End, newFrameRange.Length, evt.GetMinLength(), evt.GetMaxLength() ), evt );
  158. // return;
  159. // }
  160. //
  161. // bool changedLength = evt.Length != newFrameRange.Length;
  162. //
  163. // if( !evt.GetTrack().CanAdd( evt, newFrameRange ) )
  164. // return;
  165. //
  166. // Undo.RecordObject( evt, changedLength ? "Resize Event" : "Move Event" );
  167. //
  168. // evt.Start = newFrameRange.Start;
  169. // evt.End = newFrameRange.End;
  170. //
  171. // if( changedLength )
  172. // {
  173. // if( evt is FPlayAnimationEvent )
  174. // {
  175. // FPlayAnimationEvent animEvt = (FPlayAnimationEvent)evt;
  176. //
  177. // if( animEvt.IsAnimationEditable() )
  178. // {
  179. // FAnimationEventInspector.ScaleAnimationClip( animEvt._animationClip, animEvt.FrameRange );
  180. // }
  181. // }
  182. // else if( evt is FTimescaleEvent )
  183. // {
  184. // ResizeAnimationCurve( (FTimescaleEvent)evt, newFrameRange );
  185. // }
  186. // }
  187. //
  188. // EditorUtility.SetDirty( evt );
  189. //
  190. // if( FSequenceEditorWindow.instance != null )
  191. // FSequenceEditorWindow.instance.Repaint();
  192. // }
  193. public static void Rescale( FEvent evt, FrameRange newFrameRange )
  194. {
  195. if( evt.FrameRange == newFrameRange )
  196. return;
  197. Undo.RecordObject( evt, string.Empty );
  198. evt.Start = newFrameRange.Start;
  199. evt.End = newFrameRange.End;
  200. if( evt is FPlayAnimationEvent )
  201. {
  202. FPlayAnimationEvent animEvt = (FPlayAnimationEvent)evt;
  203. if( animEvt._animationClip != null )
  204. {
  205. if( Flux.FUtility.IsAnimationEditable(animEvt._animationClip) )
  206. {
  207. animEvt._animationClip.frameRate = animEvt.Sequence.FrameRate;
  208. EditorUtility.SetDirty( animEvt._animationClip );
  209. }
  210. else if( Mathf.RoundToInt(animEvt._animationClip.frameRate) != animEvt.Sequence.FrameRate )
  211. {
  212. Debug.LogError( string.Format("Removed AnimationClip '{0}' ({1} fps) from Animation Event '{2}'",
  213. animEvt._animationClip.name,
  214. animEvt._animationClip.frameRate,
  215. animEvt.name ), animEvt );
  216. animEvt._animationClip = null;
  217. }
  218. }
  219. }
  220. EditorUtility.SetDirty( evt );
  221. }
  222. #endregion Events
  223. public static void RepaintGameView()
  224. {
  225. if( _gameView == null )
  226. _gameView = EditorWindow.GetWindow(typeof(EditorWindow).Assembly.GetType( "UnityEditor.GameView" ));
  227. _gameView.Repaint();
  228. SceneView.RepaintAll();
  229. // UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
  230. }
  231. public static string GetTime( int frame, int frameRate )
  232. {
  233. switch( _timeFormat )
  234. {
  235. case TimeFormat.Frames:
  236. return frame.ToString();
  237. case TimeFormat.Seconds:
  238. return ((float)frame / frameRate).ToString("0.##");
  239. case TimeFormat.SecondsFormatted:
  240. return string.Format( "{0}:{1}", frame / frameRate, frame % frameRate );
  241. }
  242. return frame.ToString();
  243. }
  244. #region Audio Utilities
  245. public static float[] GetAudioMinMaxData( AudioClip clip )
  246. {
  247. if( clip == null )
  248. return null;
  249. AudioImporter importer = (AudioImporter)AssetImporter.GetAtPath( AssetDatabase.GetAssetPath( clip ) );
  250. MethodInfo getMinMaxdata = typeof(Editor).Assembly.GetType("UnityEditor.AudioUtil").GetMethod("GetMinMaxData", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod);
  251. return (float[])getMinMaxdata.Invoke( null, new object[]{importer} );
  252. }
  253. #endregion
  254. #region Editor Window
  255. public static EditorWindow GetWindowIfExists( Type windowType )
  256. {
  257. UnityEngine.Object[] objs = Resources.FindObjectsOfTypeAll( windowType );
  258. foreach( UnityEngine.Object o in objs )
  259. {
  260. if( o.GetType() == windowType )
  261. return (EditorWindow)o;
  262. }
  263. return null;
  264. }
  265. // public static EditorWindow GetAnimationWindow( bool showWindow )
  266. // {
  267. // System.Type t = typeof(EditorWindow).Assembly.GetType("UnityEditor.AnimationWindow");
  268. // if( showWindow )
  269. // {
  270. // return EditorWindow.GetWindow( t );
  271. // }
  272. //
  273. // System.Reflection.MethodInfo getWindow = typeof(EditorWindow).GetMethod( "GetWindowDontShow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static );
  274. // System.Reflection.MethodInfo getWindowGeneric = getWindow.MakeGenericMethod( t );
  275. // return (EditorWindow)getWindowGeneric.Invoke(null, null);
  276. // }
  277. #endregion
  278. #region Upgrade Sequences
  279. public static void Upgrade( FSequence sequence )
  280. {
  281. const int fluxVersion = Flux.FUtility.FLUX_VERSION;
  282. if( sequence.Version == fluxVersion )
  283. return;
  284. Debug.LogFormat( "Upgrading sequence '{0}' from version '{1}' to '{2}'", sequence.name, sequence.Version, fluxVersion );
  285. // don't allow to undo
  286. // Undo.RecordObject( sequence, "Upgrading Sequence" );
  287. // is it before 2.0.0 release?
  288. if( sequence.Version < 200 )
  289. Upgrade100To200( sequence );
  290. if( sequence.Version < 210 )
  291. Upgrade200To210( sequence );
  292. sequence.Version = fluxVersion;
  293. if( PrefabUtility.GetPrefabType(sequence) == PrefabType.Prefab )
  294. EditorUtility.SetDirty(sequence);
  295. else
  296. EditorSceneManager.MarkAllScenesDirty(); //@TODO make sure it only dirties it's scene
  297. }
  298. private static void Upgrade100To200( FSequence sequence )
  299. {
  300. // set TimelineContainer as Content
  301. Transform timelineContainer = null;
  302. foreach( Transform t in sequence.transform )
  303. {
  304. if( t.name == "Timeline Container" )
  305. {
  306. timelineContainer = t;
  307. break;
  308. }
  309. }
  310. if( timelineContainer == null )
  311. {
  312. timelineContainer = new GameObject( "SequenceContent" ).transform;
  313. timelineContainer.hideFlags |= HideFlags.HideInHierarchy;
  314. }
  315. sequence.Content = timelineContainer;
  316. // create a container, and add it to the sequence
  317. FContainer container = FContainer.Create( FContainer.DEFAULT_COLOR );
  318. sequence.Add( container );
  319. FTimeline[] timelines = timelineContainer.GetComponentsInChildren<FTimeline>();
  320. foreach( FTimeline timeline in timelines )
  321. {
  322. container.Add( timeline );
  323. List<FTrack> tracks = timeline.Tracks;
  324. foreach( FTrack track in tracks )
  325. {
  326. if( track is FAnimationTrack )
  327. {
  328. FAnimationTrack animTrack = (FAnimationTrack)track;
  329. if( animTrack.AnimatorController != null )
  330. {
  331. FAnimationTrackInspector inspector = (FAnimationTrackInspector)FAnimationTrackInspector.CreateEditor( animTrack );
  332. AnimatorController controller = (AnimatorController)animTrack.AnimatorController;
  333. inspector.UpdateLayer( controller == null || controller.layers.Length == 0 ? null : controller.layers[0] );
  334. inspector.serializedObject.ApplyModifiedProperties();
  335. if( controller.layers.Length > 0 )
  336. {
  337. while( controller.layers[0].stateMachine.stateMachines.Length > 0 )
  338. controller.layers[0].stateMachine.RemoveStateMachine( controller.layers[0].stateMachine.stateMachines[controller.layers[0].stateMachine.stateMachines.Length-1].stateMachine );
  339. while( controller.layers[0].stateMachine.states.Length > 0 )
  340. controller.layers[0].stateMachine.RemoveState( controller.layers[0].stateMachine.states[controller.layers[0].stateMachine.states.Length-1].state );
  341. }
  342. Editor.DestroyImmediate( inspector );
  343. FAnimationTrackInspector.RebuildStateMachine( animTrack );
  344. }
  345. }
  346. track.CacheMode = track.RequiredCacheMode;
  347. }
  348. // foreach( FTrack track in tracks )
  349. // {
  350. // track.CacheType = track.DefaultCacheType();
  351. // }
  352. }
  353. }
  354. private static void Upgrade200To210( FSequence sequence )
  355. {
  356. MethodInfo setEventType = typeof(FTrack).GetMethod("SetEventType", BindingFlags.NonPublic | BindingFlags.Instance);
  357. foreach( FContainer container in sequence.Containers )
  358. {
  359. foreach( FTimeline timeline in container.Timelines )
  360. {
  361. for( int i = 0; i != timeline.Tracks.Count; ++i )
  362. {
  363. FTrack track = timeline.Tracks[i];
  364. Type evtType = track.GetEventType();
  365. object[] customAttributes = evtType.GetCustomAttributes(typeof(FEventAttribute), true);
  366. foreach( FEventAttribute customAttribute in customAttributes )
  367. {
  368. if(customAttribute.trackType == typeof(FTransformTrack) && !(track is FTransformTrack))
  369. {
  370. FTransformTrack transformTrack = track.gameObject.AddComponent<FTransformTrack>();
  371. setEventType.Invoke( transformTrack, new object[]{track.GetEventType()} );
  372. Editor.DestroyImmediate( track );
  373. break;
  374. }
  375. else if( customAttribute.trackType == typeof(FCommentTrack) && !(track is FCommentTrack) )
  376. {
  377. FCommentTrack commentTrack = track.gameObject.AddComponent<FCommentTrack>();
  378. setEventType.Invoke( commentTrack, new object[]{track.GetEventType()} );
  379. Editor.DestroyImmediate( track );
  380. break;
  381. }
  382. }
  383. }
  384. }
  385. }
  386. }
  387. #endregion
  388. }
  389. }