first commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeGUIUtils
|
||||
{
|
||||
public static void DrawBoundsEdge(Rect bounds, Color color, TrimEdge edge, float width = 4.0f)
|
||||
{
|
||||
var r = bounds;
|
||||
r.yMin += 2.0f;
|
||||
r.yMax -= 2.0f;
|
||||
r.width = width;
|
||||
|
||||
r.x = edge == TrimEdge.End ? bounds.xMax : bounds.xMin - width;
|
||||
|
||||
EditorGUI.DrawRect(r, color);
|
||||
}
|
||||
|
||||
public static void DrawOverlayRect(Rect bounds, Color overlayColor)
|
||||
{
|
||||
var c = overlayColor;
|
||||
c.a = 0.2f;
|
||||
EditorGUI.DrawRect(bounds, c);
|
||||
EditorGUI.DrawOutline(bounds, 1.0f, new Color(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8938e753b3f47374889d5cf3265b563c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,138 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeMixUtils
|
||||
{
|
||||
static readonly List<PlacementValidity> k_UnrecoverablePlacements = new List<PlacementValidity>
|
||||
{
|
||||
PlacementValidity.InvalidIsWithin,
|
||||
PlacementValidity.InvalidStartsInBlend,
|
||||
PlacementValidity.InvalidContainsBlend,
|
||||
PlacementValidity.InvalidOverlapWithNonBlendableClip
|
||||
};
|
||||
|
||||
public static bool CanInsert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
List<ITimelineItem> siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items).ToList();
|
||||
foreach (var item in itemsGroup.items)
|
||||
{
|
||||
var placementValidity = GetPlacementValidity(item, siblings);
|
||||
|
||||
if (k_UnrecoverablePlacements.Contains(placementValidity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Corrects clips durations to fit at insertion point, if needed
|
||||
public static void PrepareItemsForInsertion(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
var siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items);
|
||||
foreach (var item in itemsGroup.items.OfType<ITrimmable>())
|
||||
{
|
||||
var eatenItems = siblings.Where(c => EditModeUtils.IsItemWithinRange(c, item.start, item.end)).ToList();
|
||||
|
||||
var intersectedItem = EditModeUtils.GetFirstIntersectedItem(siblings, item.end);
|
||||
if (intersectedItem != null)
|
||||
eatenItems.Add(intersectedItem);
|
||||
|
||||
var blendableItems = eatenItems.OfType<IBlendable>();
|
||||
if (blendableItems.Any())
|
||||
{
|
||||
var minTime = blendableItems.Min(c => c.end - c.rightBlendDuration);
|
||||
|
||||
if (item.end > minTime)
|
||||
item.SetEnd(minTime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PlacementValidity GetPlacementValidity(ITimelineItem item, IEnumerable<ITimelineItem> otherItems)
|
||||
{
|
||||
if (item.duration <= 0.0)
|
||||
return PlacementValidity.Valid; //items without any duration can always be placed
|
||||
|
||||
var sortedItems = otherItems.Where(i => i.duration > 0.0).OrderBy(c => c.start);
|
||||
var candidates = new List<ITimelineItem>();
|
||||
foreach (var sortedItem in sortedItems)
|
||||
{
|
||||
if ((DiscreteTime)sortedItem.start >= (DiscreteTime)item.end)
|
||||
{
|
||||
// No need to process further
|
||||
break;
|
||||
}
|
||||
|
||||
if ((DiscreteTime)sortedItem.end <= (DiscreteTime)item.start)
|
||||
{
|
||||
// Skip
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(sortedItem);
|
||||
}
|
||||
|
||||
var discreteStart = (DiscreteTime)item.start;
|
||||
var discreteEnd = (DiscreteTime)item.end;
|
||||
|
||||
// Note: Order of tests matters
|
||||
for (int i = 0, n = candidates.Count; i < n; i++)
|
||||
{
|
||||
var candidate = candidates[i];
|
||||
|
||||
var blendItem = item as IBlendable;
|
||||
if (blendItem != null && blendItem.supportsBlending)
|
||||
{
|
||||
if (EditModeUtils.Contains(candidate.start, candidate.end, item))
|
||||
return PlacementValidity.InvalidIsWithin;
|
||||
|
||||
if (i < n - 1)
|
||||
{
|
||||
var nextCandidate = candidates[i + 1];
|
||||
|
||||
var discreteNextCandidateStart = (DiscreteTime)nextCandidate.start;
|
||||
var discreteCandidateEnd = (DiscreteTime)candidate.end;
|
||||
|
||||
if (discreteCandidateEnd > discreteNextCandidateStart)
|
||||
{
|
||||
if (discreteStart >= discreteNextCandidateStart)
|
||||
{
|
||||
// Note: In case the placement is fully within a blend,
|
||||
// InvalidStartsInBlend MUST have priority
|
||||
return PlacementValidity.InvalidStartsInBlend;
|
||||
}
|
||||
|
||||
if (discreteEnd > discreteNextCandidateStart && discreteEnd <= discreteCandidateEnd)
|
||||
return PlacementValidity.InvalidEndsInBlend;
|
||||
|
||||
if (discreteStart < discreteNextCandidateStart && discreteEnd > discreteCandidateEnd)
|
||||
return PlacementValidity.InvalidContainsBlend;
|
||||
}
|
||||
}
|
||||
|
||||
if (EditModeUtils.Contains(item.start, item.end, candidate))
|
||||
return PlacementValidity.InvalidContains;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EditModeUtils.Overlaps(item, candidate.start, candidate.end)
|
||||
|| EditModeUtils.Overlaps(candidate, item.start, item.end))
|
||||
return PlacementValidity.InvalidOverlapWithNonBlendableClip;
|
||||
}
|
||||
}
|
||||
|
||||
return PlacementValidity.Valid;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 505965fb9ab352b4d88882d7c8d822bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeReplaceUtils
|
||||
{
|
||||
public static void Insert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
Insert(itemsGroup.targetTrack, itemsGroup.items);
|
||||
}
|
||||
}
|
||||
|
||||
static void Insert(TrackAsset track, IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (track == null) return;
|
||||
var orderedItems = ItemsUtils.GetItemsExcept(track, items)
|
||||
.OfType<ITrimmable>()
|
||||
.OrderBy(i => i.start).ToArray();
|
||||
|
||||
foreach (var item in items.OfType<ITrimmable>())
|
||||
{
|
||||
var from = item.start;
|
||||
var to = item.end;
|
||||
|
||||
var overlappedItems = orderedItems.Where(i => EditModeUtils.Overlaps(i, from, to));
|
||||
|
||||
foreach (var overlappedItem in overlappedItems)
|
||||
{
|
||||
if (EditModeUtils.IsItemWithinRange(overlappedItem, from, to))
|
||||
{
|
||||
overlappedItem.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (overlappedItem.start >= from)
|
||||
overlappedItem.TrimStart(to);
|
||||
else
|
||||
overlappedItem.TrimEnd(from);
|
||||
}
|
||||
}
|
||||
|
||||
var includingItems = orderedItems.Where(c => c.start < from && c.end > to);
|
||||
foreach (var includingItem in includingItems)
|
||||
{
|
||||
var newItem = includingItem.CloneTo(track, includingItem.start) as ITrimmable;
|
||||
includingItem.TrimStart(to);
|
||||
if (newItem != null)
|
||||
newItem.SetEnd(from, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ca745fb561cbf640b6e603f95662fa0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeRippleUtils
|
||||
{
|
||||
public static void Insert(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
var start = double.MaxValue;
|
||||
var end = double.MinValue;
|
||||
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
start = Math.Min(start, itemsGroup.items.Min(c => c.start));
|
||||
end = Math.Max(end, itemsGroup.items.Max(c => c.end));
|
||||
}
|
||||
|
||||
var offset = 0.0;
|
||||
var discreteStart = (DiscreteTime)start;
|
||||
var discreteEnd = (DiscreteTime)end;
|
||||
var itemTypes = ItemsUtils.GetItemTypes(itemsGroups);
|
||||
var siblingsToRipple = new List<ITimelineItem>();
|
||||
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
{
|
||||
//can only ripple items of the same type as those selected
|
||||
siblingsToRipple.AddRange(ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items).Where(i => itemTypes.Contains(i.GetType())));
|
||||
foreach (var item in siblingsToRipple)
|
||||
{
|
||||
var discreteItemStart = (DiscreteTime)item.start;
|
||||
var discreteItemEnd = (DiscreteTime)item.end;
|
||||
|
||||
if ((discreteItemStart < discreteStart && discreteItemEnd > discreteStart) || (discreteItemStart >= discreteStart && discreteItemStart < discreteEnd))
|
||||
offset = Math.Max(offset, end - item.start);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0.0)
|
||||
{
|
||||
foreach (var sibling in siblingsToRipple)
|
||||
{
|
||||
if ((DiscreteTime)sibling.end > (DiscreteTime)start)
|
||||
sibling.start += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Remove(IEnumerable<ItemsPerTrack> itemsGroups)
|
||||
{
|
||||
foreach (var itemsGroup in itemsGroups)
|
||||
Remove(itemsGroup.targetTrack, itemsGroup.items);
|
||||
}
|
||||
|
||||
static void Remove(TrackAsset track, IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (track == null) return;
|
||||
|
||||
//can only ripple items of the same type as those selected
|
||||
var itemTypes = ItemsUtils.GetItemTypes(items);
|
||||
var siblingsToRipple = ItemsUtils.GetItemsExcept(track, items)
|
||||
.Where(i => itemTypes.Contains(i.GetType()))
|
||||
.OrderBy(c => c.start)
|
||||
.ToArray();
|
||||
|
||||
var orderedItems = items
|
||||
.OrderBy(c => c.start)
|
||||
.ToArray();
|
||||
|
||||
var cumulativeOffset = 0.0;
|
||||
|
||||
foreach (var item in orderedItems)
|
||||
{
|
||||
var offset = item.end - item.start;
|
||||
var start = item.start - cumulativeOffset;
|
||||
var end = item.end - cumulativeOffset;
|
||||
|
||||
var nextItem = siblingsToRipple.FirstOrDefault(c => (DiscreteTime)c.start > (DiscreteTime)start && (DiscreteTime)c.start < (DiscreteTime)end);
|
||||
if (nextItem != null)
|
||||
{
|
||||
offset -= end - nextItem.start;
|
||||
}
|
||||
|
||||
var prevItem = siblingsToRipple.FirstOrDefault(c => (DiscreteTime)c.end > (DiscreteTime)start && (DiscreteTime)c.end < (DiscreteTime)end);
|
||||
if (prevItem != null)
|
||||
{
|
||||
offset -= prevItem.end - start;
|
||||
}
|
||||
|
||||
if (offset <= 0.0)
|
||||
continue;
|
||||
|
||||
cumulativeOffset += offset;
|
||||
|
||||
for (int i = siblingsToRipple.Length - 1; i >= 0; --i)
|
||||
{
|
||||
var c = siblingsToRipple[i];
|
||||
if ((DiscreteTime)c.start < (DiscreteTime)start)
|
||||
break;
|
||||
|
||||
c.start = c.start - offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 495e2738ac7d88a41a158cd2e237d70b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class EditModeUtils
|
||||
{
|
||||
public static void Delete(IEnumerable<ITimelineItem> items)
|
||||
{
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
foreach (var item in items)
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
public static void SetStart(IEnumerable<ITimelineItem> items, double time)
|
||||
{
|
||||
var offset = time - items.Min(c => c.start);
|
||||
|
||||
foreach (var item in items)
|
||||
item.start += offset;
|
||||
}
|
||||
|
||||
public static void SetParentTrack(IEnumerable<ITimelineItem> items, TrackAsset parentTrack)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.parentTrack == parentTrack)
|
||||
continue;
|
||||
|
||||
item.parentTrack = parentTrack;
|
||||
|
||||
var clipGUI = item.gui as TimelineClipGUI;
|
||||
if (clipGUI != null)
|
||||
{
|
||||
clipGUI.clipCurveEditor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ITimelineItem GetFirstIntersectedItem(IEnumerable<ITimelineItem> items, double time)
|
||||
{
|
||||
return items.FirstOrDefault(c => Intersects(time, c.start, c.end));
|
||||
}
|
||||
|
||||
static bool Intersects(double time, double start, double end)
|
||||
{
|
||||
var discreteTime = (DiscreteTime)time;
|
||||
return discreteTime > (DiscreteTime)start && discreteTime < (DiscreteTime)end;
|
||||
}
|
||||
|
||||
public static bool Overlaps(ITimelineItem item, double from, double to)
|
||||
{
|
||||
var discreteFrom = (DiscreteTime)from;
|
||||
var discreteTo = (DiscreteTime)to;
|
||||
var discreteStart = (DiscreteTime)item.start;
|
||||
|
||||
if (discreteStart >= discreteFrom && discreteStart < discreteTo)
|
||||
return true;
|
||||
|
||||
var discreteEnd = (DiscreteTime)item.end;
|
||||
|
||||
if (discreteEnd > discreteFrom && discreteEnd <= discreteTo)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsItemWithinRange(ITimelineItem item, double from, double to)
|
||||
{
|
||||
return (DiscreteTime)item.start >= (DiscreteTime)from && (DiscreteTime)item.end <= (DiscreteTime)to;
|
||||
}
|
||||
|
||||
public static bool IsRangeWithinItem(double from, double to, ITimelineItem item)
|
||||
{
|
||||
return (DiscreteTime)from >= (DiscreteTime)item.start && (DiscreteTime)to <= (DiscreteTime)item.end;
|
||||
}
|
||||
|
||||
public static bool Contains(double from, double to, ITimelineItem item)
|
||||
{
|
||||
return (DiscreteTime)from < (DiscreteTime)item.start && (DiscreteTime)to > (DiscreteTime)item.end;
|
||||
}
|
||||
|
||||
public static bool HasBlends(ITimelineItem item, TrimEdge edge)
|
||||
{
|
||||
var blendable = item as IBlendable;
|
||||
if (blendable == null) return false;
|
||||
|
||||
return edge == TrimEdge.Start && blendable.hasLeftBlend || edge == TrimEdge.End && blendable.hasRightBlend;
|
||||
}
|
||||
|
||||
public static double BlendDuration(ITimelineItem item, TrimEdge edge)
|
||||
{
|
||||
var blendable = item as IBlendable;
|
||||
if (blendable == null) return 0.0;
|
||||
|
||||
return edge == TrimEdge.Start ? blendable.leftBlendDuration : blendable.rightBlendDuration;
|
||||
}
|
||||
|
||||
public static bool IsInfiniteTrack(TrackAsset track)
|
||||
{
|
||||
var aTrack = track as AnimationTrack;
|
||||
return aTrack != null && aTrack.CanConvertToClipMode();
|
||||
}
|
||||
|
||||
public static void GetInfiniteClipBoundaries(TrackAsset track, out double start, out double end)
|
||||
{
|
||||
var info = AnimationClipCurveCache.Instance.GetCurveInfo(((AnimationTrack)track).infiniteClip);
|
||||
if (info.keyTimes.Length > 0)
|
||||
{
|
||||
start = info.keyTimes.Min();
|
||||
end = info.keyTimes.Max();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = end = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70eae1897c9d308448eb3bb0b5be9f58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
static class ManipulatorsUtils
|
||||
{
|
||||
public static EventModifiers actionModifier
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Application.platform == RuntimePlatform.OSXEditor ||
|
||||
Application.platform == RuntimePlatform.OSXPlayer)
|
||||
return EventModifiers.Command;
|
||||
|
||||
return EventModifiers.Control;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e177382a693dea644acd34e3e7a3feb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
enum PlacementValidity
|
||||
{
|
||||
Valid,
|
||||
InvalidContains,
|
||||
InvalidIsWithin,
|
||||
InvalidStartsInBlend,
|
||||
InvalidEndsInBlend,
|
||||
InvalidContainsBlend,
|
||||
InvalidOverlapWithNonBlendableClip
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 335020228a0fe124897f51f25f6350ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user