You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
8.0 KiB
206 lines
8.0 KiB
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); |
|
} |
|
} |
|
}
|
|
|