123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- using System;
- using System.Linq;
- using UnityEngine;
- 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(TimelineClip[] clips)
- {
- if (clips.Length < 2)
- return false;
- var clipsByTracks = clips.GroupBy(x => x.parentTrack)
- .Select(track => new {track.Key, Items = track.OrderBy(c => c.start)});
- foreach (var track in clipsByTracks)
- {
- TimelineUndo.PushUndo(track.Key, "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(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;
- TimelineUndo.PushUndo(clip.parentTrack, "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);
- return true;
- }
- public static bool TrimEnd(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;
- TimelineUndo.PushUndo(clip.parentTrack, "Trim Clip End");
- TrimClipWithEditMode(clip, TrimEdge.End, trimTime);
- return true;
- }
- public static bool MatchDuration(TimelineClip[] clips)
- {
- double referenceDuration = clips[0].duration;
- foreach (var clip in clips)
- {
- TimelineUndo.PushUndo(clip.parentTrack, "Match Clip Duration");
- var newEnd = clip.start + referenceDuration;
- TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
- }
- return true;
- }
- public static bool Split(TimelineClip[] clips, double splitTime, PlayableDirector director)
- {
- var result = false;
- foreach (var clip in clips)
- {
- if (clip.start >= splitTime)
- continue;
- if (clip.end <= splitTime)
- continue;
- TimelineUndo.PushUndo(clip.parentTrack, "Split Clip");
- TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start);
- SetStart(clip, splitTime);
- SetEnd(newClip, splitTime, false);
- // Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes)
- clip.parentTrack.SortClips();
- result = true;
- }
- return result;
- }
- public static void SetStart(TimelineClip clip, double time)
- {
- var supportsClipIn = clip.SupportsClipIn();
- var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip);
- // 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)
- {
- 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 (supportsClipIn)
- {
- 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(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;
- TimelineUndo.PushUndo(clip.parentTrack, "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(TimelineClip[] clips)
- {
- var result = false;
- foreach (var clip in clips)
- result = result || MatchContent(clip);
- return result;
- }
- public static bool MatchContent(TimelineClip clip)
- {
- if (clip.asset == null)
- return false;
- TimelineUndo.PushUndo(clip.parentTrack, "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 (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);
- else
- EditMode.TrimEnd(clipItem, time, false);
- EditMode.FinishTrim();
- }
- public static bool CompleteLastLoop(TimelineClip[] clips)
- {
- foreach (var clip in clips)
- {
- CompleteLastLoop(clip);
- }
- return true;
- }
- public static void CompleteLastLoop(TimelineClip clip)
- {
- FixLoops(clip, true);
- }
- public static bool TrimLastLoop(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;
- TimelineUndo.PushUndo(clip.parentTrack, "Trim Clip Last Loop");
- TrimClipWithEditMode(clip, TrimEdge.End, newEnd);
- }
- public static bool DoubleSpeed(TimelineClip[] clips)
- {
- foreach (var clip in clips)
- {
- if (clip.SupportsSpeedMultiplier())
- {
- TimelineUndo.PushUndo(clip.parentTrack, "Double Clip Speed");
- clip.timeScale = clip.timeScale * 2.0f;
- }
- }
- return true;
- }
- public static bool HalfSpeed(TimelineClip[] clips)
- {
- foreach (var clip in clips)
- {
- if (clip.SupportsSpeedMultiplier())
- {
- TimelineUndo.PushUndo(clip.parentTrack, "Half Clip Speed");
- clip.timeScale = clip.timeScale * 0.5f;
- }
- }
- return true;
- }
- public static bool ResetSpeed(TimelineClip[] clips)
- {
- foreach (var clip in clips)
- {
- if (clip.timeScale != 1.0)
- {
- TimelineUndo.PushUndo(clip.parentTrack, "Reset Clip Speed");
- clip.timeScale = 1.0;
- }
- }
- return true;
- }
- }
- }
|