207 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			207 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Linq; | ||
|  | using UnityEngine; | ||
|  | using UnityEngine.Timeline; | ||
|  | 
 | ||
|  | namespace UnityEditor.Timeline | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// Extension Methods for AnimationTracks that require the Unity Editor, and may require the Timeline containing the Animation Track to be currently loaded in the Timeline Editor Window. | ||
|  |     /// </summary> | ||
|  |     public static class AnimationTrackExtensions | ||
|  |     { | ||
|  |         /// <summary> | ||
|  |         /// Determines whether the Timeline window can enable recording mode on an AnimationTrack. | ||
|  |         /// For a track to support recording, it needs to have a valid scene binding, | ||
|  |         /// its offset mode should not be Auto and needs to be currently visible in the Timeline Window. | ||
|  |         /// </summary> | ||
|  |         /// <param name="track">The track to query.</param> | ||
|  |         /// <returns>True if recording can start, False otherwise.</returns> | ||
|  |         public static bool CanStartRecording(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (track == null) | ||
|  |             { | ||
|  |                 throw new ArgumentNullException(nameof(track)); | ||
|  |             } | ||
|  |             if (TimelineEditor.state == null) | ||
|  |             { | ||
|  |                 return false; | ||
|  |             } | ||
|  | 
 | ||
|  |             var director = TimelineEditor.inspectedDirector; | ||
|  |             var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack; | ||
|  |             return animTrack != null && animTrack.trackOffset != TrackOffset.Auto && | ||
|  |                 TimelineEditor.inspectedAsset == animTrack.timelineAsset && | ||
|  |                 director != null && TimelineUtility.GetSceneGameObject(director, animTrack) != null; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Method that allows querying if a track is current enabled for animation recording. | ||
|  |         /// </summary> | ||
|  |         /// <param name="track">The track to query.</param> | ||
|  |         /// <returns>True if currently recording and False otherwise.</returns> | ||
|  |         public static bool IsRecording(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (track == null) | ||
|  |             { | ||
|  |                 throw new ArgumentNullException(nameof(track)); | ||
|  |             } | ||
|  |             return TimelineEditor.state != null && TimelineEditor.state.IsArmedForRecord(track); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Method that enables animation recording for an AnimationTrack. | ||
|  |         /// </summary> | ||
|  |         /// <param name="track">The AnimationTrack which will be put in recording mode.</param> | ||
|  |         /// <returns>True if track was put successfully in recording mode, False otherwise. </returns> | ||
|  |         public static bool StartRecording(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (!CanStartRecording(track)) | ||
|  |             { | ||
|  |                 return false; | ||
|  |             } | ||
|  |             TimelineEditor.state.ArmForRecord(track); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Disables recording mode of an AnimationTrack. | ||
|  |         /// </summary> | ||
|  |         /// <param name="track">The AnimationTrack which will be taken out of recording mode.</param> | ||
|  |         public static void StopRecording(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (!IsRecording(track) || TimelineEditor.state == null) | ||
|  |             { | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             TimelineEditor.state.UnarmForRecord(track); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void ConvertToClipMode(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (!track.CanConvertToClipMode()) | ||
|  |                 return; | ||
|  | 
 | ||
|  |             UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip")); | ||
|  | 
 | ||
|  |             if (!track.infiniteClip.empty) | ||
|  |             { | ||
|  |                 var animClip = track.infiniteClip; | ||
|  |                 TimelineUndo.PushUndo(animClip, L10n.Tr("Convert To Clip")); | ||
|  |                 UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip")); | ||
|  |                 var start = AnimationClipCurveCache.Instance.GetCurveInfo(animClip).keyTimes.FirstOrDefault(); | ||
|  |                 animClip.ShiftBySeconds(-start); | ||
|  | 
 | ||
|  |                 track.infiniteClip = null; | ||
|  |                 var clip = track.CreateClip(animClip); | ||
|  | 
 | ||
|  |                 clip.start = start; | ||
|  |                 clip.preExtrapolationMode = track.infiniteClipPreExtrapolation; | ||
|  |                 clip.postExtrapolationMode = track.infiniteClipPostExtrapolation; | ||
|  |                 clip.recordable = true; | ||
|  |                 if (Mathf.Abs(animClip.length) < TimelineClip.kMinDuration) | ||
|  |                 { | ||
|  |                     clip.duration = 1; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var animationAsset = clip.asset as AnimationPlayableAsset; | ||
|  |                 if (animationAsset) | ||
|  |                 { | ||
|  |                     animationAsset.position = track.infiniteClipOffsetPosition; | ||
|  |                     animationAsset.eulerAngles = track.infiniteClipOffsetEulerAngles; | ||
|  | 
 | ||
|  |                     // going to / from infinite mode should reset this. infinite mode | ||
|  |                     animationAsset.removeStartOffset = track.infiniteClipRemoveOffset; | ||
|  |                     animationAsset.applyFootIK = track.infiniteClipApplyFootIK; | ||
|  |                     animationAsset.loop = track.infiniteClipLoop; | ||
|  | 
 | ||
|  |                     track.infiniteClipOffsetPosition = Vector3.zero; | ||
|  |                     track.infiniteClipOffsetEulerAngles = Vector3.zero; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 track.CalculateExtrapolationTimes(); | ||
|  |             } | ||
|  | 
 | ||
|  |             track.infiniteClip = null; | ||
|  | 
 | ||
|  |             EditorUtility.SetDirty(track); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static void ConvertFromClipMode(this AnimationTrack track, TimelineAsset timeline) | ||
|  |         { | ||
|  |             if (!track.CanConvertFromClipMode()) | ||
|  |                 return; | ||
|  | 
 | ||
|  |             UndoExtensions.RegisterTrack(track, L10n.Tr("Convert From Clip")); | ||
|  | 
 | ||
|  |             var clip = track.clips[0]; | ||
|  |             var delta = (float)clip.start; | ||
|  |             track.infiniteClipTimeOffset = 0.0f; | ||
|  |             track.infiniteClipPreExtrapolation = clip.preExtrapolationMode; | ||
|  |             track.infiniteClipPostExtrapolation = clip.postExtrapolationMode; | ||
|  | 
 | ||
|  |             var animAsset = clip.asset as AnimationPlayableAsset; | ||
|  |             if (animAsset) | ||
|  |             { | ||
|  |                 track.infiniteClipOffsetPosition = animAsset.position; | ||
|  |                 track.infiniteClipOffsetEulerAngles = animAsset.eulerAngles; | ||
|  |                 track.infiniteClipRemoveOffset = animAsset.removeStartOffset; | ||
|  |                 track.infiniteClipApplyFootIK = animAsset.applyFootIK; | ||
|  |                 track.infiniteClipLoop = animAsset.loop; | ||
|  |             } | ||
|  | 
 | ||
|  |             // clone it, it may not be in the same asset | ||
|  |             var animClip = clip.animationClip; | ||
|  | 
 | ||
|  |             float scale = (float)clip.timeScale; | ||
|  |             if (!Mathf.Approximately(scale, 1.0f)) | ||
|  |             { | ||
|  |                 if (!Mathf.Approximately(scale, 0.0f)) | ||
|  |                     scale = 1.0f / scale; | ||
|  |                 animClip.ScaleTime(scale); | ||
|  |             } | ||
|  | 
 | ||
|  |             TimelineUndo.PushUndo(animClip, L10n.Tr("Convert From Clip")); | ||
|  |             animClip.ShiftBySeconds(delta); | ||
|  | 
 | ||
|  |             // manually delete the clip | ||
|  |             var asset = clip.asset; | ||
|  |             clip.asset = null; | ||
|  | 
 | ||
|  |             // Remove the clip, remove old assets | ||
|  |             ClipModifier.Delete(timeline, clip); | ||
|  |             TimelineUndo.PushDestroyUndo(null, track, asset); | ||
|  | 
 | ||
|  |             track.infiniteClip = animClip; | ||
|  | 
 | ||
|  |             EditorUtility.SetDirty(track); | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool CanConvertToClipMode(this AnimationTrack track) | ||
|  |         { | ||
|  |             if (track == null || track.inClipMode) | ||
|  |                 return false; | ||
|  |             return (track.infiniteClip != null && !track.infiniteClip.empty); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Requirements to go from clip mode | ||
|  |         //  - one clip, recordable, and animation clip belongs to the same asset as the track | ||
|  |         internal static bool CanConvertFromClipMode(this AnimationTrack track) | ||
|  |         { | ||
|  |             if ((track == null) || | ||
|  |                 (!track.inClipMode) || | ||
|  |                 (track.clips.Length != 1) || | ||
|  |                 (track.clips[0].start < 0) || | ||
|  |                 (!track.clips[0].recordable)) | ||
|  |                 return false; | ||
|  | 
 | ||
|  |             var asset = track.clips[0].asset as AnimationPlayableAsset; | ||
|  |             if (asset == null) | ||
|  |                 return false; | ||
|  | 
 | ||
|  |             return TimelineHelpers.HaveSameContainerAsset(track, asset.clip); | ||
|  |         } | ||
|  |     } | ||
|  | } |