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.
397 lines
12 KiB
397 lines
12 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using UnityEngine.Timeline; |
|
using UnityEngine.Playables; |
|
|
|
namespace UnityEditor.Timeline |
|
{ |
|
static class ClipModifier |
|
{ |
|
public static bool Delete(TimelineAsset timeline, TimelineClip clip) |
|
{ |
|
return timeline.DeleteClip(clip); |
|
} |
|
|
|
public static bool Tile(IEnumerable<TimelineClip> clips) |
|
{ |
|
if (clips.Count() < 2) |
|
return false; |
|
|
|
var clipsByTracks = clips.GroupBy(x => x.GetParentTrack()) |
|
.Select(track => new { track.Key, Items = track.OrderBy(c => c.start) }); |
|
|
|
foreach (var track in clipsByTracks) |
|
{ |
|
UndoExtensions.RegisterTrack(track.Key, L10n.Tr("Tile")); |
|
} |
|
|
|
foreach (var track in clipsByTracks) |
|
{ |
|
double newStart = track.Items.First().start; |
|
foreach (var c in track.Items) |
|
{ |
|
c.start = newStart; |
|
newStart += c.duration; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static bool TrimStart(IEnumerable<TimelineClip> clips, double trimTime) |
|
{ |
|
var result = false; |
|
|
|
foreach (var clip in clips) |
|
result |= TrimStart(clip, trimTime); |
|
|
|
return result; |
|
} |
|
|
|
public static bool TrimStart(TimelineClip clip, double trimTime) |
|
{ |
|
if (clip.asset == null) |
|
return false; |
|
|
|
if (clip.start > trimTime) |
|
return false; |
|
|
|
if (clip.end < trimTime) |
|
return false; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Start")); |
|
|
|
// Note: We are NOT using edit modes in this case because we want the same result |
|
// regardless of the selected EditMode: split at cursor and delete left part |
|
SetStart(clip, trimTime, false); |
|
clip.ConformEaseValues(); |
|
|
|
return true; |
|
} |
|
|
|
public static bool TrimEnd(IEnumerable<TimelineClip> clips, double trimTime) |
|
{ |
|
var result = false; |
|
|
|
foreach (var clip in clips) |
|
result |= TrimEnd(clip, trimTime); |
|
|
|
return result; |
|
} |
|
|
|
public static bool TrimEnd(TimelineClip clip, double trimTime) |
|
{ |
|
if (clip.asset == null) |
|
return false; |
|
|
|
if (clip.start > trimTime) |
|
return false; |
|
|
|
if (clip.end < trimTime) |
|
return false; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip End")); |
|
TrimClipWithEditMode(clip, TrimEdge.End, trimTime); |
|
|
|
return true; |
|
} |
|
|
|
public static bool MatchDuration(IEnumerable<TimelineClip> clips) |
|
{ |
|
double referenceDuration = clips.First().duration; |
|
UndoExtensions.RegisterClips(clips, L10n.Tr("Match Clip Duration")); |
|
foreach (var clip in clips) |
|
{ |
|
var newEnd = clip.start + referenceDuration; |
|
TrimClipWithEditMode(clip, TrimEdge.End, newEnd); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static bool Split(IEnumerable<TimelineClip> clips, double splitTime, PlayableDirector director) |
|
{ |
|
var result = false; |
|
|
|
foreach (var clip in clips) |
|
{ |
|
if (clip.start >= splitTime) |
|
continue; |
|
|
|
if (clip.end <= splitTime) |
|
continue; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Split Clip")); |
|
|
|
TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start); |
|
|
|
clip.easeInDuration = 0; |
|
newClip.easeOutDuration = 0; |
|
|
|
SetStart(clip, splitTime, false); |
|
SetEnd(newClip, splitTime, false); |
|
|
|
// Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes) |
|
clip.GetParentTrack().SortClips(); |
|
|
|
result = true; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
public static void SetStart(TimelineClip clip, double time, bool affectTimeScale) |
|
{ |
|
var supportsClipIn = clip.SupportsClipIn(); |
|
var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip); |
|
bool calculateTimeScale = (affectTimeScale && clip.SupportsSpeedMultiplier()); |
|
|
|
// treat empty recordable clips as not supporting clip in (there are no keys to modify) |
|
if (supportsPadding && (clip.animationClip == null || clip.animationClip.empty)) |
|
{ |
|
supportsClipIn = false; |
|
} |
|
|
|
if (supportsClipIn && !supportsPadding && !calculateTimeScale) |
|
{ |
|
var minStart = clip.FromLocalTimeUnbound(0.0); |
|
if (time < minStart) |
|
time = minStart; |
|
} |
|
|
|
var maxStart = clip.end - TimelineClip.kMinDuration; |
|
if (time > maxStart) |
|
time = maxStart; |
|
|
|
var timeOffset = time - clip.start; |
|
var duration = clip.duration - timeOffset; |
|
|
|
if (calculateTimeScale) |
|
{ |
|
var f = clip.duration / duration; |
|
clip.timeScale *= f; |
|
} |
|
|
|
|
|
if (supportsClipIn && !calculateTimeScale) |
|
{ |
|
if (supportsPadding) |
|
{ |
|
double clipInGlobal = clip.clipIn / clip.timeScale; |
|
double keyShift = -timeOffset; |
|
if (timeOffset < 0) // left drag, eliminate clipIn before shifting |
|
{ |
|
double clipInDelta = Math.Max(-clipInGlobal, timeOffset); |
|
keyShift = -Math.Min(0, timeOffset - clipInDelta); |
|
clip.clipIn += clipInDelta * clip.timeScale; |
|
} |
|
else if (timeOffset > 0) // right drag, elimate padding in animation clip before adding clip in |
|
{ |
|
var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip); |
|
double keyDelta = clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()) - clip.start; |
|
keyShift = -Math.Max(0, Math.Min(timeOffset, keyDelta)); |
|
clip.clipIn += Math.Max(timeOffset + keyShift, 0) * clip.timeScale; |
|
} |
|
if (keyShift != 0) |
|
{ |
|
AnimationTrackRecorder.ShiftAnimationClip(clip.animationClip, (float)(keyShift * clip.timeScale)); |
|
} |
|
} |
|
else |
|
{ |
|
clip.clipIn += timeOffset * clip.timeScale; |
|
} |
|
} |
|
|
|
clip.start = time; |
|
clip.duration = duration; |
|
} |
|
|
|
public static void SetEnd(TimelineClip clip, double time, bool affectTimeScale) |
|
{ |
|
var duration = Math.Max(time - clip.start, TimelineClip.kMinDuration); |
|
|
|
if (affectTimeScale && clip.SupportsSpeedMultiplier()) |
|
{ |
|
var f = clip.duration / duration; |
|
clip.timeScale *= f; |
|
} |
|
|
|
clip.duration = duration; |
|
} |
|
|
|
public static bool ResetEditing(IEnumerable<TimelineClip> clips) |
|
{ |
|
var result = false; |
|
|
|
foreach (var clip in clips) |
|
result = result || ResetEditing(clip); |
|
|
|
return result; |
|
} |
|
|
|
public static bool ResetEditing(TimelineClip clip) |
|
{ |
|
if (clip.asset == null) |
|
return false; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Editing")); |
|
|
|
clip.clipIn = 0.0; |
|
|
|
if (clip.clipAssetDuration < double.MaxValue) |
|
{ |
|
var duration = clip.clipAssetDuration / clip.timeScale; |
|
TrimClipWithEditMode(clip, TrimEdge.End, clip.start + duration); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static bool MatchContent(IEnumerable<TimelineClip> clips) |
|
{ |
|
var result = false; |
|
|
|
foreach (var clip in clips) |
|
result |= MatchContent(clip); |
|
|
|
return result; |
|
} |
|
|
|
public static bool MatchContent(TimelineClip clip) |
|
{ |
|
if (clip.asset == null) |
|
return false; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Match Clip Content")); |
|
|
|
var newStartCandidate = clip.start - clip.clipIn / clip.timeScale; |
|
var newStart = newStartCandidate < 0.0 ? 0.0 : newStartCandidate; |
|
|
|
TrimClipWithEditMode(clip, TrimEdge.Start, newStart); |
|
|
|
// In case resetting the start was blocked by edit mode or timeline start, we do the best we can |
|
clip.clipIn = (clip.start - newStartCandidate) * clip.timeScale; |
|
if (clip.clipAssetDuration > 0 && TimelineHelpers.HasUsableAssetDuration(clip)) |
|
{ |
|
var duration = TimelineHelpers.GetLoopDuration(clip); |
|
var offset = (clip.clipIn / clip.timeScale) % duration; |
|
TrimClipWithEditMode(clip, TrimEdge.End, clip.start - offset + duration); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static void TrimClipWithEditMode(TimelineClip clip, TrimEdge edge, double time) |
|
{ |
|
var clipItem = ItemsUtils.ToItem(clip); |
|
EditMode.BeginTrim(clipItem, edge); |
|
if (edge == TrimEdge.Start) |
|
EditMode.TrimStart(clipItem, time, false); |
|
else |
|
EditMode.TrimEnd(clipItem, time, false); |
|
EditMode.FinishTrim(); |
|
} |
|
|
|
public static bool CompleteLastLoop(IEnumerable<TimelineClip> clips) |
|
{ |
|
foreach (var clip in clips) |
|
{ |
|
CompleteLastLoop(clip); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static void CompleteLastLoop(TimelineClip clip) |
|
{ |
|
FixLoops(clip, true); |
|
} |
|
|
|
public static bool TrimLastLoop(IEnumerable<TimelineClip> clips) |
|
{ |
|
foreach (var clip in clips) |
|
{ |
|
TrimLastLoop(clip); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static void TrimLastLoop(TimelineClip clip) |
|
{ |
|
FixLoops(clip, false); |
|
} |
|
|
|
static void FixLoops(TimelineClip clip, bool completeLastLoop) |
|
{ |
|
if (!TimelineHelpers.HasUsableAssetDuration(clip)) |
|
return; |
|
|
|
var loopDuration = TimelineHelpers.GetLoopDuration(clip); |
|
var firstLoopDuration = loopDuration - clip.clipIn * (1.0 / clip.timeScale); |
|
|
|
// Making sure we don't trim to zero |
|
if (!completeLastLoop && firstLoopDuration > clip.duration) |
|
return; |
|
|
|
var numLoops = (clip.duration - firstLoopDuration) / loopDuration; |
|
var numCompletedLoops = Math.Floor(numLoops); |
|
|
|
if (!(numCompletedLoops < numLoops)) |
|
return; |
|
|
|
if (completeLastLoop) |
|
numCompletedLoops += 1; |
|
|
|
var newEnd = clip.start + firstLoopDuration + loopDuration * numCompletedLoops; |
|
|
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Last Loop")); |
|
|
|
TrimClipWithEditMode(clip, TrimEdge.End, newEnd); |
|
} |
|
|
|
public static bool DoubleSpeed(IEnumerable<TimelineClip> clips) |
|
{ |
|
foreach (var clip in clips) |
|
{ |
|
if (clip.SupportsSpeedMultiplier()) |
|
{ |
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Double Clip Speed")); |
|
clip.timeScale = clip.timeScale * 2.0f; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static bool HalfSpeed(IEnumerable<TimelineClip> clips) |
|
{ |
|
foreach (var clip in clips) |
|
{ |
|
if (clip.SupportsSpeedMultiplier()) |
|
{ |
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Half Clip Speed")); |
|
clip.timeScale = clip.timeScale * 0.5f; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public static bool ResetSpeed(IEnumerable<TimelineClip> clips) |
|
{ |
|
foreach (var clip in clips) |
|
{ |
|
if (clip.timeScale != 1.0) |
|
{ |
|
UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Speed")); |
|
clip.timeScale = 1.0; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
} |
|
}
|
|
|