Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,96 @@
using System.ComponentModel;
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
using ClipAction = UnityEditor.Timeline.ItemAction<UnityEngine.Timeline.TimelineClip>;
namespace UnityEditor.Timeline
{
[MenuEntry("Match Offsets To Previous Clip", MenuOrder.CustomClipAction.AnimClipMatchPrevious), UsedImplicitly]
class MatchOffsetsPreviousAction : ClipAction
{
public override bool Execute(WindowState state, TimelineClip[] items)
{
AnimationOffsetMenu.MatchClipsToPrevious(state, items.Where(x => IsValidClip(x, TimelineEditor.inspectedDirector)).ToArray());
return true;
}
private static bool IsValidClip(TimelineClip clip, PlayableDirector director)
{
return clip != null &&
clip.parentTrack != null &&
(clip.asset as AnimationPlayableAsset) != null &&
clip.parentTrack.clips.Any(x => x.start < clip.start) &&
TimelineUtility.GetSceneGameObject(director, clip.parentTrack) != null;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] items)
{
if (!items.All(TimelineAnimationUtilities.IsAnimationClip))
return MenuActionDisplayState.Hidden;
var director = TimelineEditor.inspectedDirector;
if (TimelineEditor.inspectedDirector == null)
return MenuActionDisplayState.Hidden;
if (items.Any(c => IsValidClip(c, director)))
return MenuActionDisplayState.Visible;
return MenuActionDisplayState.Hidden;
}
}
[MenuEntry("Match Offsets To Next Clip", MenuOrder.CustomClipAction.AnimClipMatchNext), UsedImplicitly]
class MatchOffsetsNextAction : ClipAction
{
public override bool Execute(WindowState state, TimelineClip[] items)
{
AnimationOffsetMenu.MatchClipsToNext(state, items.Where(x => IsValidClip(x, TimelineEditor.inspectedDirector)).ToArray());
return true;
}
private static bool IsValidClip(TimelineClip clip, PlayableDirector director)
{
return clip != null &&
clip.parentTrack != null &&
(clip.asset as AnimationPlayableAsset) != null &&
clip.parentTrack.clips.Any(x => x.start > clip.start) &&
TimelineUtility.GetSceneGameObject(director, clip.parentTrack) != null;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] items)
{
if (!items.All(TimelineAnimationUtilities.IsAnimationClip))
return MenuActionDisplayState.Hidden;
var director = TimelineEditor.inspectedDirector;
if (TimelineEditor.inspectedDirector == null)
return MenuActionDisplayState.Hidden;
if (items.Any(c => IsValidClip(c, director)))
return MenuActionDisplayState.Visible;
return MenuActionDisplayState.Hidden;
}
}
[MenuEntry("Reset Offsets", MenuOrder.CustomClipAction.AnimClipResetOffset), UsedImplicitly]
class ResetOffsets : ClipAction
{
public override bool Execute(WindowState state, TimelineClip[] items)
{
AnimationOffsetMenu.ResetClipOffsets(state, items.Where(TimelineAnimationUtilities.IsAnimationClip).ToArray());
return true;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TimelineClip[] items)
{
if (!items.All(TimelineAnimationUtilities.IsAnimationClip))
return MenuActionDisplayState.Hidden;
return MenuActionDisplayState.Visible;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf22284ca28e7ef4490033b61e9b52cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,436 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
namespace UnityEditor.Timeline
{
struct CurveBindingPair
{
public EditorCurveBinding binding;
public AnimationCurve curve;
public ObjectReferenceKeyframe[] objectCurve;
}
class CurveBindingGroup
{
public CurveBindingPair[] curveBindingPairs { get; set; }
public Vector2 timeRange { get; set; }
public Vector2 valueRange { get; set; }
public bool isFloatCurve
{
get
{
return curveBindingPairs != null && curveBindingPairs.Length > 0 &&
curveBindingPairs[0].curve != null;
}
}
public bool isObjectCurve
{
get
{
return curveBindingPairs != null && curveBindingPairs.Length > 0 &&
curveBindingPairs[0].objectCurve != null;
}
}
public int count
{
get
{
if (curveBindingPairs == null)
return 0;
return curveBindingPairs.Length;
}
}
}
class AnimationClipCurveInfo
{
bool m_CurveDirty = true;
bool m_KeysDirty = true;
public bool dirty
{
get { return m_CurveDirty; }
set
{
m_CurveDirty = value;
if (m_CurveDirty)
{
m_KeysDirty = true;
if (m_groupings != null)
m_groupings.Clear();
}
}
}
public AnimationCurve[] curves;
public EditorCurveBinding[] bindings;
public EditorCurveBinding[] objectBindings;
public List<ObjectReferenceKeyframe[]> objectCurves;
Dictionary<string, CurveBindingGroup> m_groupings;
// to tell whether the cache has changed
public int version { get; private set; }
float[] m_KeyTimes;
Dictionary<EditorCurveBinding, float[]> m_individualBindinsKey;
public float[] keyTimes
{
get
{
if (m_KeysDirty || m_KeyTimes == null)
{
RebuildKeyCache();
}
return m_KeyTimes;
}
}
public float[] GetCurveTimes(EditorCurveBinding curve)
{
return GetCurveTimes(new[] { curve });
}
public float[] GetCurveTimes(EditorCurveBinding[] curves)
{
if (m_KeysDirty || m_KeyTimes == null)
{
RebuildKeyCache();
}
var keyTimes = new List<float>();
for (int i = 0; i < curves.Length; i++)
{
var c = curves[i];
if (m_individualBindinsKey.ContainsKey(c))
{
keyTimes.AddRange(m_individualBindinsKey[c]);
}
}
return keyTimes.ToArray();
}
void RebuildKeyCache()
{
m_individualBindinsKey = new Dictionary<EditorCurveBinding, float[]>();
List<float> keys = curves.SelectMany(y => y.keys).Select(z => z.time).ToList();
for (int i = 0; i < objectCurves.Count; i++)
{
var kf = objectCurves[i];
keys.AddRange(kf.Select(x => x.time));
}
for (int b = 0; b < bindings.Count(); b++)
{
m_individualBindinsKey.Add(bindings[b], curves[b].keys.Select(k => k.time).Distinct().ToArray());
}
m_KeyTimes = keys.OrderBy(x => x).Distinct().ToArray();
m_KeysDirty = false;
}
public void Update(AnimationClip clip)
{
List<EditorCurveBinding> postfilter = new List<EditorCurveBinding>();
var clipBindings = AnimationUtility.GetCurveBindings(clip);
for (int i = 0; i < clipBindings.Length; i++)
{
var bind = clipBindings[i];
if (!bind.propertyName.Contains("LocalRotation.w"))
postfilter.Add(RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(bind, clip));
}
bindings = postfilter.ToArray();
curves = new AnimationCurve[bindings.Length];
for (int i = 0; i < bindings.Length; i++)
{
curves[i] = AnimationUtility.GetEditorCurve(clip, bindings[i]);
}
objectBindings = AnimationUtility.GetObjectReferenceCurveBindings(clip);
objectCurves = new List<ObjectReferenceKeyframe[]>(objectBindings.Length);
for (int i = 0; i < objectBindings.Length; i++)
{
objectCurves.Add(AnimationUtility.GetObjectReferenceCurve(clip, objectBindings[i]));
}
m_CurveDirty = false;
m_KeysDirty = true;
version = version + 1;
}
public bool GetBindingForCurve(AnimationCurve curve, ref EditorCurveBinding binding)
{
for (int i = 0; i < curves.Length; i++)
{
if (curve == curves[i])
{
binding = bindings[i];
return true;
}
}
return false;
}
public AnimationCurve GetCurveForBinding(EditorCurveBinding binding)
{
for (int i = 0; i < curves.Length; i++)
{
if (binding.Equals(bindings[i]))
{
return curves[i];
}
}
return null;
}
public ObjectReferenceKeyframe[] GetObjectCurveForBinding(EditorCurveBinding binding)
{
if (objectCurves == null)
return null;
for (int i = 0; i < objectCurves.Count; i++)
{
if (binding.Equals(objectBindings[i]))
{
return objectCurves[i];
}
}
return null;
}
// given a groupID, get the list of curve bindings
public CurveBindingGroup GetGroupBinding(string groupID)
{
if (m_groupings == null)
m_groupings = new Dictionary<string, CurveBindingGroup>();
CurveBindingGroup result = null;
if (!m_groupings.TryGetValue(groupID, out result))
{
result = new CurveBindingGroup();
result.timeRange = new Vector2(float.MaxValue, float.MinValue);
result.valueRange = new Vector2(float.MaxValue, float.MinValue);
List<CurveBindingPair> found = new List<CurveBindingPair>();
for (int i = 0; i < bindings.Length; i++)
{
if (bindings[i].GetGroupID() == groupID)
{
CurveBindingPair pair = new CurveBindingPair();
pair.binding = bindings[i];
pair.curve = curves[i];
found.Add(pair);
for (int k = 0; k < curves[i].keys.Length; k++)
{
var key = curves[i].keys[k];
result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y));
result.valueRange = new Vector2(Mathf.Min(key.value, result.valueRange.x), Mathf.Max(key.value, result.valueRange.y));
}
}
}
for (int i = 0; i < objectBindings.Length; i++)
{
if (objectBindings[i].GetGroupID() == groupID)
{
CurveBindingPair pair = new CurveBindingPair();
pair.binding = objectBindings[i];
pair.objectCurve = objectCurves[i];
found.Add(pair);
for (int k = 0; k < objectCurves[i].Length; k++)
{
var key = objectCurves[i][k];
result.timeRange = new Vector2(Mathf.Min(key.time, result.timeRange.x), Mathf.Max(key.time, result.timeRange.y));
}
}
}
result.curveBindingPairs = found.OrderBy(x => AnimationWindowUtility.GetComponentIndex(x.binding.propertyName)).ToArray();
m_groupings.Add(groupID, result);
}
return result;
}
}
// Cache for storing the animation clip data
class AnimationClipCurveCache
{
static AnimationClipCurveCache s_Instance;
Dictionary<AnimationClip, AnimationClipCurveInfo> m_ClipCache = new Dictionary<AnimationClip, AnimationClipCurveInfo>();
bool m_IsEnabled;
public static AnimationClipCurveCache Instance
{
get
{
if (s_Instance == null)
{
s_Instance = new AnimationClipCurveCache();
}
return s_Instance;
}
}
public void OnEnable()
{
if (!m_IsEnabled)
{
AnimationUtility.onCurveWasModified += OnCurveWasModified;
m_IsEnabled = true;
}
}
public void OnDisable()
{
if (m_IsEnabled)
{
AnimationUtility.onCurveWasModified -= OnCurveWasModified;
m_IsEnabled = false;
}
}
// callback when a curve is edited. Force the cache to update next time it's accessed
void OnCurveWasModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType modification)
{
if (modification == AnimationUtility.CurveModifiedType.CurveDeleted)
{
m_ClipCache.Remove(clip);
}
else
{
AnimationClipCurveInfo data;
if (m_ClipCache.TryGetValue(clip, out data))
{
data.dirty = true;
}
}
}
public AnimationClipCurveInfo GetCurveInfo(AnimationClip clip)
{
AnimationClipCurveInfo data;
if (clip == null)
return null;
if (!m_ClipCache.TryGetValue(clip, out data))
{
data = new AnimationClipCurveInfo();
data.dirty = true;
m_ClipCache[clip] = data;
}
if (data.dirty)
{
data.Update(clip);
}
return data;
}
public void ClearCachedProxyClips()
{
var toRemove = new List<AnimationClip>();
foreach (var entry in m_ClipCache)
{
var clip = entry.Key;
if (clip != null && (clip.hideFlags & HideFlags.HideAndDontSave) == HideFlags.HideAndDontSave)
toRemove.Add(clip);
}
foreach (var clip in toRemove)
{
m_ClipCache.Remove(clip);
Object.DestroyImmediate(clip, true);
}
}
public void Clear()
{
ClearCachedProxyClips();
m_ClipCache.Clear();
}
}
static class EditorCurveBindingExtension
{
// identifier to generate an id thats the same for all curves in the same group
public static string GetGroupID(this EditorCurveBinding binding)
{
return binding.type + AnimationWindowUtility.GetPropertyGroupName(binding.propertyName);
}
}
static class CurveBindingGroupExtensions
{
// Extentions to determine curve types
public static bool IsEnableGroup(this CurveBindingGroup curves)
{
return curves.isFloatCurve && curves.count == 1 && curves.curveBindingPairs[0].binding.propertyName == "m_Enabled";
}
public static bool IsVectorGroup(this CurveBindingGroup curves)
{
if (!curves.isFloatCurve)
return false;
if (curves.count <= 1 || curves.count > 4)
return false;
char l = curves.curveBindingPairs[0].binding.propertyName.Last();
return l == 'x' || l == 'y' || l == 'z' || l == 'w';
}
public static bool IsColorGroup(this CurveBindingGroup curves)
{
if (!curves.isFloatCurve)
return false;
if (curves.count != 3 && curves.count != 4)
return false;
char l = curves.curveBindingPairs[0].binding.propertyName.Last();
return l == 'r' || l == 'g' || l == 'b' || l == 'a';
}
public static string GetDescription(this CurveBindingGroup group, float t)
{
string result = string.Empty;
if (group.isFloatCurve)
{
if (group.count > 1)
{
result += "(" + group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##");
for (int j = 1; j < group.curveBindingPairs.Length; j++)
{
result += "," + group.curveBindingPairs[j].curve.Evaluate(t).ToString("0.##");
}
result += ")";
}
else
{
result = group.curveBindingPairs[0].curve.Evaluate(t).ToString("0.##");
}
}
else if (group.isObjectCurve)
{
Object obj = null;
if (group.curveBindingPairs[0].objectCurve.Length > 0)
obj = CurveEditUtility.Evaluate(group.curveBindingPairs[0].objectCurve, t);
result = (obj == null ? "None" : obj.name);
}
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 07a967d2fca95324f8922df8394a5655
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,82 @@
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
static class AnimationOffsetMenu
{
public static GUIContent MatchPreviousMenuItem = EditorGUIUtility.TrTextContent("Match Offsets To Previous Clip");
public static GUIContent MatchNextMenuItem = EditorGUIUtility.TrTextContent("Match Offsets To Next Clip");
public static string MatchFieldsPrefix = "Match Offsets Fields/";
public static GUIContent ResetOffsetMenuItem = EditorGUIUtility.TrTextContent("Reset Offsets");
static bool EnforcePreviewMode(WindowState state)
{
state.previewMode = true; // try and set the preview mode
if (!state.previewMode)
{
Debug.LogError("Match clips cannot be completed because preview mode cannot be enabed");
return false;
}
return true;
}
internal static void MatchClipsToPrevious(WindowState state, TimelineClip[] clips)
{
if (!EnforcePreviewMode(state))
return;
clips = clips.OrderBy(x => x.start).ToArray();
foreach (var clip in clips)
{
var sceneObject = TimelineUtility.GetSceneGameObject(state.editSequence.director, clip.parentTrack);
if (sceneObject != null)
{
TimelineUndo.PushUndo(clip.asset, "Match Clip");
TimelineAnimationUtilities.MatchPrevious(clip, sceneObject.transform, state.editSequence.director);
}
}
InspectorWindow.RepaintAllInspectors();
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
internal static void MatchClipsToNext(WindowState state, TimelineClip[] clips)
{
if (!EnforcePreviewMode(state))
return;
clips = clips.OrderByDescending(x => x.start).ToArray();
foreach (var clip in clips)
{
var sceneObject = TimelineUtility.GetSceneGameObject(state.editSequence.director, clip.parentTrack);
if (sceneObject != null)
{
TimelineUndo.PushUndo(clip.asset, "Match Clip");
TimelineAnimationUtilities.MatchNext(clip, sceneObject.transform, state.editSequence.director);
}
}
InspectorWindow.RepaintAllInspectors();
TimelineEditor.Refresh(RefreshReason.ContentsModified);
}
public static void ResetClipOffsets(WindowState state, TimelineClip[] clips)
{
foreach (var clip in clips)
{
if (clip.asset is AnimationPlayableAsset)
{
TimelineUndo.PushUndo(clip.asset, "Reset Offsets");
var playableAsset = (AnimationPlayableAsset)clip.asset;
playableAsset.ResetOffsets();
}
}
state.rebuildGraph = true;
InspectorWindow.RepaintAllInspectors();
TimelineEditor.Refresh(RefreshReason.SceneNeedsUpdate);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ace5095cc37ed849b52109d2ee305d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[CustomTimelineEditor(typeof(AnimationPlayableAsset)), UsedImplicitly]
class AnimationPlayableAssetEditor : ClipEditor
{
public static readonly string k_NoClipAssignedError = LocalizationDatabase.GetLocalizedString("No animation clip assigned");
public static readonly string k_LegacyClipError = LocalizationDatabase.GetLocalizedString("Legacy animation clips are not supported");
static readonly string k_MotionCurveError = LocalizationDatabase.GetLocalizedString("You are using motion curves without applyRootMotion enabled on the Animator. The root transform will not be animated");
static readonly string k_RootCurveError = LocalizationDatabase.GetLocalizedString("You are using root curves without applyRootMotion enabled on the Animator. The root transform will not be animated");
/// <inheritdoc/>
public override ClipDrawOptions GetClipOptions(TimelineClip clip)
{
var clipOptions = base.GetClipOptions(clip);
var asset = clip.asset as AnimationPlayableAsset;
if (asset != null)
clipOptions.errorText = GetErrorText(asset, clip.parentTrack as AnimationTrack, clipOptions.errorText);
if (clip.recordable)
clipOptions.highlightColor = DirectorStyles.Instance.customSkin.colorAnimationRecorded;
return clipOptions;
}
/// <inheritdoc />
public override void OnCreate(TimelineClip clip, TrackAsset track, TimelineClip clonedFrom)
{
var asset = clip.asset as AnimationPlayableAsset;
if (asset != null && asset.clip != null && asset.clip.legacy)
{
asset.clip = null;
Debug.LogError("Legacy Animation Clips are not supported");
}
}
string GetErrorText(AnimationPlayableAsset animationAsset, AnimationTrack track, string defaultError)
{
if (animationAsset.clip == null)
return k_NoClipAssignedError;
if (animationAsset.clip.legacy)
return k_LegacyClipError;
if (animationAsset.clip.hasMotionCurves || animationAsset.clip.hasRootCurves)
{
if (track != null && track.trackOffset == TrackOffset.Auto)
{
var animator = track.GetBinding(TimelineEditor.inspectedDirector);
if (animator != null && !animator.applyRootMotion && !animationAsset.clip.hasGenericRootTransform)
{
if (animationAsset.clip.hasMotionCurves)
return k_MotionCurveError;
return k_RootCurveError;
}
}
}
return defaultError;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7fed0d9d0f7a7f41a8525aa79e790b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,151 @@
using System.ComponentModel;
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[MenuEntry("Add Override Track", MenuOrder.CustomTrackAction.AnimAddOverrideTrack), UsedImplicitly]
class AddOverrideTrackAction : TrackAction
{
public override bool Execute(WindowState state, TrackAsset[] tracks)
{
foreach (var animTrack in tracks.OfType<AnimationTrack>())
{
TimelineHelpers.CreateTrack(typeof(AnimationTrack), animTrack, "Override " + animTrack.GetChildTracks().Count());
}
return true;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks)
{
if (tracks.Any(t => t.isSubTrack || !t.GetType().IsAssignableFrom(typeof(AnimationTrack))))
return MenuActionDisplayState.Hidden;
if (tracks.Any(t => t.lockedInHierarchy))
return MenuActionDisplayState.Disabled;
return MenuActionDisplayState.Visible;
}
}
[MenuEntry("Convert To Clip Track", MenuOrder.CustomTrackAction.AnimConvertToClipMode), UsedImplicitly]
class ConvertToClipModeAction : TrackAction
{
public override bool Execute(WindowState state, TrackAsset[] tracks)
{
foreach (var animTrack in tracks.OfType<AnimationTrack>())
animTrack.ConvertToClipMode();
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
return true;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks)
{
if (tracks.Any(t => !t.GetType().IsAssignableFrom(typeof(AnimationTrack))))
return MenuActionDisplayState.Hidden;
if (tracks.Any(t => t.lockedInHierarchy))
return MenuActionDisplayState.Disabled;
if (tracks.OfType<AnimationTrack>().All(a => a.CanConvertToClipMode()))
return MenuActionDisplayState.Visible;
return MenuActionDisplayState.Hidden;
}
}
[MenuEntry("Convert To Infinite Clip", MenuOrder.CustomTrackAction.AnimConvertFromClipMode), UsedImplicitly]
class ConvertFromClipTrackAction : TrackAction
{
public override bool Execute(WindowState state, TrackAsset[] tracks)
{
foreach (var animTrack in tracks.OfType<AnimationTrack>())
animTrack.ConvertFromClipMode(state.editSequence.asset);
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
return true;
}
protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks)
{
if (tracks.Any(t => !t.GetType().IsAssignableFrom(typeof(AnimationTrack))))
return MenuActionDisplayState.Hidden;
if (tracks.Any(t => t.lockedInHierarchy))
return MenuActionDisplayState.Disabled;
if (tracks.OfType<AnimationTrack>().All(a => a.CanConvertFromClipMode()))
return MenuActionDisplayState.Visible;
return MenuActionDisplayState.Hidden;
}
}
abstract class TrackOffsetBaseAction : TrackAction
{
public abstract TrackOffset trackOffset { get; }
protected override MenuActionDisplayState GetDisplayState(WindowState state, TrackAsset[] tracks)
{
if (tracks.Any(t => !t.GetType().IsAssignableFrom(typeof(AnimationTrack))))
return MenuActionDisplayState.Hidden;
if (tracks.Any(t => t.lockedInHierarchy))
return MenuActionDisplayState.Disabled;
return MenuActionDisplayState.Visible;
}
protected override bool IsChecked(WindowState state, TrackAsset[] tracks)
{
return tracks.OfType<AnimationTrack>().All(t => t.trackOffset == trackOffset);
}
public override bool Execute(WindowState state, TrackAsset[] tracks)
{
foreach (var animTrack in tracks.OfType<AnimationTrack>())
{
state.UnarmForRecord(animTrack);
TimelineUndo.PushUndo(animTrack, "Set Transform Offsets");
animTrack.trackOffset = trackOffset;
}
TimelineEditor.Refresh(RefreshReason.ContentsModified);
return true;
}
}
[MenuEntry("Track Offsets/Apply Transform Offsets", MenuOrder.CustomTrackAction.AnimApplyTrackOffset), UsedImplicitly]
class ApplyTransformOffsetAction : TrackOffsetBaseAction
{
public override TrackOffset trackOffset
{
get { return TrackOffset.ApplyTransformOffsets; }
}
}
[MenuEntry("Track Offsets/Apply Scene Offsets", MenuOrder.CustomTrackAction.AnimApplySceneOffset), UsedImplicitly]
class ApplySceneOffsetAction : TrackOffsetBaseAction
{
public override TrackOffset trackOffset
{
get { return TrackOffset.ApplySceneOffsets; }
}
}
[MenuEntry("Track Offsets/Auto (Deprecated)", MenuOrder.CustomTrackAction.AnimApplyAutoOffset), UsedImplicitly]
class ApplyAutoAction : TrackOffsetBaseAction
{
public override TrackOffset trackOffset
{
get { return TrackOffset.Auto; }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4553f2006f48b6448553cb525d2876e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,224 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;
using UnityEditor.Timeline;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor
{
class BindingSelector
{
TreeViewController m_TreeView;
public TreeViewController treeViewController
{
get { return m_TreeView; }
}
TreeViewState m_TrackGlobalTreeViewState;
TreeViewState m_TreeViewState;
BindingTreeViewDataSource m_TreeViewDataSource;
CurveDataSource m_CurveDataSource;
TimelineWindow m_Window;
CurveEditor m_CurveEditor;
ReorderableList m_DopeLines;
string[] m_StringList = {};
int[] m_Selection;
bool m_PartOfSelection;
public BindingSelector(EditorWindow window, CurveEditor curveEditor, TreeViewState trackGlobalTreeViewState)
{
m_Window = window as TimelineWindow;
m_CurveEditor = curveEditor;
m_TrackGlobalTreeViewState = trackGlobalTreeViewState;
m_DopeLines = new ReorderableList(m_StringList, typeof(string), false, false, false, false);
m_DopeLines.drawElementBackgroundCallback = null;
m_DopeLines.showDefaultBackground = false;
m_DopeLines.index = 0;
m_DopeLines.headerHeight = 0;
m_DopeLines.elementHeight = 20;
m_DopeLines.draggable = false;
}
public bool selectable { get { return true; } }
public object selectableObject
{
get { return this; }
}
public bool selected
{
get { return m_PartOfSelection; }
set
{
m_PartOfSelection = value;
if (!m_PartOfSelection)
{
m_DopeLines.index = -1;
}
}
}
public virtual void Delete(WindowState state)
{
// we dont support deleting the summary
if (m_DopeLines.index < 1)
return;
if (m_CurveDataSource == null)
return;
var clip = m_CurveDataSource.animationClip;
if (clip == null)
return;
int curveIndexToDelete = m_DopeLines.index - 1;
var bindings = AnimationUtility.GetCurveBindings(clip);
if (curveIndexToDelete >= bindings.Length)
return;
TimelineUndo.PushUndo(clip, "Delete Curve");
AnimationUtility.SetEditorCurve(clip, bindings[m_DopeLines.index - 1], null);
state.rebuildGraph = true;
}
public void OnGUI(Rect targetRect)
{
if (m_TreeView == null)
return;
m_TreeView.OnEvent();
m_TreeView.OnGUI(targetRect, GUIUtility.GetControlID(FocusType.Passive));
}
public void InitIfNeeded(Rect rect, CurveDataSource dataSource, bool isNewSelection)
{
if (Event.current.type != EventType.Layout)
return;
m_CurveDataSource = dataSource;
var clip = dataSource.animationClip;
List<EditorCurveBinding> allBindings = new List<EditorCurveBinding>();
allBindings.Add(new EditorCurveBinding { propertyName = "Summary" });
if (clip != null)
allBindings.AddRange(AnimationUtility.GetCurveBindings(clip));
m_DopeLines.list = allBindings.ToArray();
if (m_TreeViewState != null)
{
if (isNewSelection)
RefreshAll();
return;
}
m_TreeViewState = m_TrackGlobalTreeViewState != null ? m_TrackGlobalTreeViewState : new TreeViewState();
m_TreeView = new TreeViewController(m_Window, m_TreeViewState)
{
useExpansionAnimation = false,
deselectOnUnhandledMouseDown = true
};
m_TreeView.selectionChangedCallback += OnItemSelectionChanged;
m_TreeViewDataSource = new BindingTreeViewDataSource(m_TreeView, clip, m_CurveDataSource);
m_TreeView.Init(rect, m_TreeViewDataSource, new BindingTreeViewGUI(m_TreeView), null);
m_TreeViewDataSource.UpdateData();
RefreshSelection();
}
void OnItemSelectionChanged(int[] selection)
{
RefreshSelection(selection);
}
void RefreshAll()
{
RefreshTree();
RefreshSelection();
}
void RefreshSelection()
{
RefreshSelection(m_TreeViewState.selectedIDs != null ? m_TreeViewState.selectedIDs.ToArray() : null);
}
void RefreshSelection(int[] selection)
{
if (selection == null || selection.Length == 0)
{
// select all.
if (m_TreeViewDataSource.GetRows().Count > 0)
{
m_Selection = m_TreeViewDataSource.GetRows().Select(r => r.id).ToArray();
}
}
else
{
m_Selection = selection;
}
RefreshCurves();
}
public void RefreshCurves()
{
if (m_CurveDataSource == null || m_Selection == null)
return;
var bindings = new List<EditorCurveBinding>();
foreach (int s in m_Selection)
{
var item = (CurveTreeViewNode)m_TreeView.FindItem(s);
if (item != null && item.bindings != null)
bindings.AddRange(item.bindings);
}
var wrappers = m_CurveDataSource.GenerateWrappers(bindings);
m_CurveEditor.animationCurves = wrappers.ToArray();
}
public void RefreshTree()
{
if (m_TreeViewDataSource == null)
return;
if (m_Selection == null)
m_Selection = new int[0];
// get the names of the previous items
var selected = m_Selection.Select(x => m_TreeViewDataSource.FindItem(x)).Where(t => t != null).Select(c => c.displayName).ToArray();
// update the source
m_TreeViewDataSource.UpdateData();
// find the same items
var reselected = m_TreeViewDataSource.GetRows().Where(x => selected.Contains(x.displayName)).Select(x => x.id).ToArray();
if (!reselected.Any())
{
if (m_TreeViewDataSource.GetRows().Count > 0)
{
reselected = new[] { m_TreeViewDataSource.GetItem(0).id };
}
}
// update the selection
OnItemSelectionChanged(reselected);
}
internal virtual bool IsRenamingNodeAllowed(TreeViewItem node)
{
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c171b9ca03610ea4faa426e082a1075d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditor.Timeline;
using UnityEngine;
namespace UnityEditorInternal
{
class BindingTreeViewDataSource : TreeViewDataSource
{
public const int RootID = int.MinValue;
public const int GroupID = -1;
AnimationClip m_Clip;
CurveDataSource m_CurveDataSource;
public BindingTreeViewDataSource(
TreeViewController treeView, AnimationClip clip, CurveDataSource curveDataSource)
: base(treeView)
{
m_Clip = clip;
showRootItem = false;
m_CurveDataSource = curveDataSource;
}
void SetupRootNodeSettings()
{
showRootItem = false;
SetExpanded(RootID, true);
SetExpanded(GroupID, true);
}
static string GroupName(EditorCurveBinding binding)
{
string property = AnimationWindowUtility.NicifyPropertyGroupName(binding.type, binding.propertyName);
if (!string.IsNullOrEmpty(binding.path))
{
property = binding.path + " : " + property;
}
int lastArrayIdx = property.LastIndexOf("Array.");
if (lastArrayIdx != -1)
{
property = property.Substring(0, lastArrayIdx - 1);
}
return property;
}
static string PropertyName(EditorCurveBinding binding, string arrayPrefixToRemove = "")
{
string propertyName = AnimationWindowUtility.GetPropertyDisplayName(binding.propertyName);
if (propertyName.Contains("Array"))
{
propertyName = propertyName.Replace("Array.", "");
propertyName = propertyName.Replace(arrayPrefixToRemove, "");
propertyName = propertyName.TrimStart('.');
}
return propertyName;
}
public override void FetchData()
{
if (m_Clip == null)
return;
var bindings = AnimationUtility.GetCurveBindings(m_Clip)
.Union(AnimationUtility.GetObjectReferenceCurveBindings(m_Clip))
.ToArray();
var results = bindings.GroupBy(p => GroupName(p), p => p, (key, g) => new
{
parent = key,
bindings = g.ToList()
}).OrderBy(t =>
{
//Force transform order first
if (t.parent == "Position") return -3;
if (t.parent == "Rotation") return -2;
if (t.parent == "Scale") return -1;
return 0;
}).ThenBy(t => t.parent);
m_RootItem = new CurveTreeViewNode(RootID, null, "root", null)
{
children = new List<TreeViewItem>(1)
};
var groupingNode = new CurveTreeViewNode(GroupID, m_RootItem, m_CurveDataSource.groupingName, bindings)
{
children = new List<TreeViewItem>()
};
m_RootItem.children.Add(groupingNode);
foreach (var r in results)
{
var newNode = new CurveTreeViewNode(r.parent.GetHashCode(), groupingNode, r.parent, r.bindings.ToArray());
groupingNode.children.Add(newNode);
if (r.bindings.Count > 1)
{
for (int b = 0; b < r.bindings.Count; b++)
{
if (newNode.children == null)
newNode.children = new List<TreeViewItem>();
var binding = r.bindings[b];
var bindingNode = new CurveTreeViewNode(binding.GetHashCode(), newNode, PropertyName(binding, newNode.displayName), new[] {binding});
newNode.children.Add(bindingNode);
}
}
}
SetupRootNodeSettings();
m_NeedRefreshRows = true;
}
public void UpdateData()
{
m_TreeView.ReloadData();
}
}
class CurveTreeViewNode : TreeViewItem
{
EditorCurveBinding[] m_Bindings;
public EditorCurveBinding[] bindings
{
get { return m_Bindings; }
}
public CurveTreeViewNode(int id, TreeViewItem parent, string displayName, EditorCurveBinding[] bindings)
: base(id, parent != null ? parent.depth + 1 : -1, parent, displayName)
{
m_Bindings = bindings;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c2177aaf0fde92439246adc2dc0bfa2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,80 @@
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace UnityEditorInternal
{
class BindingTreeViewGUI : TreeViewGUI
{
static readonly float s_RowRightOffset = 10;
static readonly float s_ColorIndicatorTopMargin = 3;
static readonly Color s_KeyColorForNonCurves = new Color(0.7f, 0.7f, 0.7f, 0.5f);
static readonly Color s_ChildrenCurveLabelColor = new Color(1.0f, 1.0f, 1.0f, 0.7f);
public BindingTreeViewGUI(TreeViewController treeView)
: base(treeView, true)
{
k_IconWidth = 13.0f;
}
public override void OnRowGUI(Rect rowRect, TreeViewItem node, int row, bool selected, bool focused)
{
Color originalColor = GUI.color;
GUI.color = node.parent == null ||
node.parent.id == BindingTreeViewDataSource.RootID ||
node.parent.id == BindingTreeViewDataSource.GroupID ?
Color.white :
s_ChildrenCurveLabelColor;
base.OnRowGUI(rowRect, node, row, selected, focused);
GUI.color = originalColor;
DoCurveColorIndicator(rowRect, node as CurveTreeViewNode);
}
protected override bool IsRenaming(int id)
{
return false;
}
public override bool BeginRename(TreeViewItem item, float delay)
{
return false;
}
void DoCurveColorIndicator(Rect rect, CurveTreeViewNode node)
{
if (node == null)
return;
if (Event.current.type != EventType.Repaint)
return;
Color originalColor = GUI.color;
if (node.bindings.Length == 1 && !node.bindings[0].isPPtrCurve)
GUI.color = CurveUtility.GetPropertyColor(node.bindings[0].propertyName);
else
GUI.color = s_KeyColorForNonCurves;
Texture icon = CurveUtility.GetIconCurve();
rect = new Rect(rect.xMax - s_RowRightOffset - (icon.width * 0.5f) - 5, rect.yMin + s_ColorIndicatorTopMargin, icon.width, icon.height);
GUI.DrawTexture(rect, icon, ScaleMode.ScaleToFit, true, 1);
GUI.color = originalColor;
}
protected override Texture GetIconForItem(TreeViewItem item)
{
var node = item as CurveTreeViewNode;
if (node == null)
return null;
if (node.bindings == null || node.bindings.Length == 0)
return null;
return AssetPreview.GetMiniTypeThumbnail(node.bindings[0].type);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c09dc5cd0a70cf40856b7d406106ee1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,342 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor
{
class ClipCurveEditor
{
internal readonly CurveEditor m_CurveEditor;
static readonly CurveEditorSettings s_CurveEditorSettings = new CurveEditorSettings
{
hSlider = false,
vSlider = false,
hRangeLocked = false,
vRangeLocked = false,
scaleWithWindow = true,
hRangeMin = 0.0f,
showAxisLabels = true,
allowDeleteLastKeyInCurve = true,
rectangleToolFlags = CurveEditorSettings.RectangleToolFlags.MiniRectangleTool
};
static readonly float s_GridLabelWidth = 40.0f;
readonly BindingSelector m_BindingHierarchy;
public BindingSelector bindingHierarchy
{
get { return m_BindingHierarchy; }
}
public Rect shownAreaInsideMargins
{
get { return m_CurveEditor != null ? m_CurveEditor.shownAreaInsideMargins : new Rect(1, 1, 1, 1); }
}
Vector2 m_ScrollPosition = Vector2.zero;
readonly CurveDataSource m_DataSource;
float m_LastFrameRate = 30.0f;
int m_LastClipVersion = -1;
int m_LastCurveCount = -1;
TrackViewModelData m_ViewModel;
bool m_ShouldRestoreShownArea;
bool isNewSelection
{
get
{
if (m_ViewModel == null || m_DataSource == null)
return true;
return m_ViewModel.lastInlineCurveDataID != m_DataSource.id;
}
}
internal CurveEditor curveEditor
{
get { return m_CurveEditor; }
}
public ClipCurveEditor(CurveDataSource dataSource, TimelineWindow parentWindow, TrackAsset hostTrack)
{
m_DataSource = dataSource;
m_CurveEditor = new CurveEditor(new Rect(0, 0, 1000, 100), new CurveWrapper[0], false);
s_CurveEditorSettings.vTickStyle = new TickStyle
{
tickColor = { color = DirectorStyles.Instance.customSkin.colorInlineCurveVerticalLines },
distLabel = 20,
stubs = true
};
s_CurveEditorSettings.hTickStyle = new TickStyle
{
// hide horizontal lines by giving them a transparent color
tickColor = { color = new Color(0.0f, 0.0f, 0.0f, 0.0f) },
distLabel = 0
};
m_CurveEditor.settings = s_CurveEditorSettings;
m_ViewModel = TimelineWindowViewPrefs.GetTrackViewModelData(hostTrack);
m_ShouldRestoreShownArea = true;
m_CurveEditor.ignoreScrollWheelUntilClicked = true;
m_CurveEditor.curvesUpdated = OnCurvesUpdated;
m_BindingHierarchy = new BindingSelector(parentWindow, m_CurveEditor, m_ViewModel.inlineCurvesState);
}
public void SelectAllKeys()
{
m_CurveEditor.SelectAll();
}
public void FrameClip()
{
m_CurveEditor.InvalidateBounds();
m_CurveEditor.FrameClip(false, true);
}
public CurveDataSource dataSource
{
get { return m_DataSource; }
}
internal void OnCurvesUpdated()
{
if (m_DataSource == null)
return;
if (m_CurveEditor == null)
return;
if (m_CurveEditor.animationCurves.Length == 0)
return;
List<CurveWrapper> curvesToUpdate = m_CurveEditor.animationCurves.Where(c => c.changed).ToList();
// nothing changed, return.
if (curvesToUpdate.Count == 0)
return;
AnimationClip clip = m_DataSource.animationClip;
// something changed, manage the undo properly.
Undo.RegisterCompleteObjectUndo(clip, "Edit Clip Curve");
foreach (CurveWrapper c in curvesToUpdate)
{
AnimationUtility.SetEditorCurve(clip, c.binding, c.curve);
c.changed = false;
}
m_DataSource.UpdateCurves(curvesToUpdate);
}
public void DrawHeader(Rect headerRect)
{
m_BindingHierarchy.InitIfNeeded(headerRect, m_DataSource, isNewSelection);
try
{
GUILayout.BeginArea(headerRect);
m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar);
m_BindingHierarchy.OnGUI(new Rect(0, 0, headerRect.width, headerRect.height));
GUILayout.EndScrollView();
GUILayout.EndArea();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
class FrameFormatCurveEditorState : ICurveEditorState
{
public TimeArea.TimeFormat timeFormat
{
get { return TimeArea.TimeFormat.Frame; }
}
public Vector2 timeRange { get { return new Vector2(0, 1); } }
public bool rippleTime { get { return false; } }
}
class UnformattedCurveEditorState : ICurveEditorState
{
public TimeArea.TimeFormat timeFormat
{
get { return TimeArea.TimeFormat.None; }
}
public Vector2 timeRange { get { return new Vector2(0, 1); } }
public bool rippleTime { get { return false; } }
}
void UpdateCurveEditorIfNeeded(WindowState state)
{
if ((Event.current.type != EventType.Layout) || (m_DataSource == null) || (m_BindingHierarchy == null) || (m_DataSource.animationClip == null))
return;
AnimationClipCurveInfo curveInfo = AnimationClipCurveCache.Instance.GetCurveInfo(m_DataSource.animationClip);
int version = curveInfo.version;
if (version != m_LastClipVersion)
{
// tree has changed
if (m_LastCurveCount != curveInfo.curves.Length)
{
m_BindingHierarchy.RefreshTree();
m_LastCurveCount = curveInfo.curves.Length;
}
else
{
// update just the curves
m_BindingHierarchy.RefreshCurves();
}
m_LastClipVersion = version;
}
if (state.timeInFrames)
m_CurveEditor.state = new FrameFormatCurveEditorState();
else
m_CurveEditor.state = new UnformattedCurveEditorState();
m_CurveEditor.invSnap = state.referenceSequence.frameRate;
}
public void DrawCurveEditor(Rect rect, WindowState state, Vector2 clipRange, bool loop, bool selected)
{
SetupMarginsAndRect(rect, state);
UpdateCurveEditorIfNeeded(state);
if (m_ShouldRestoreShownArea)
RestoreShownArea();
var curveVisibleTimeRange = CalculateCurveVisibleTimeRange(state.timeAreaShownRange, m_DataSource);
m_CurveEditor.SetShownHRangeInsideMargins(curveVisibleTimeRange.x, curveVisibleTimeRange.y); //align the curve with the clip.
if (m_LastFrameRate != state.referenceSequence.frameRate)
{
m_CurveEditor.hTicks.SetTickModulosForFrameRate(state.referenceSequence.frameRate);
m_LastFrameRate = state.referenceSequence.frameRate;
}
foreach (var cw in m_CurveEditor.animationCurves)
cw.renderer.SetWrap(WrapMode.Default, loop ? WrapMode.Loop : WrapMode.Default);
using (new GUIGroupScope(rect))
{
var localRect = new Rect(0.0f, 0.0f, rect.width, rect.height);
var localClipRange = new Vector2(Mathf.Floor(clipRange.x - rect.xMin), Mathf.Ceil(clipRange.y - rect.xMin));
var curveStartPosX = Mathf.Floor(state.TimeToPixel(m_DataSource.start) - rect.xMin);
EditorGUI.DrawRect(new Rect(curveStartPosX, 0.0f, 1.0f, rect.height), new Color(1.0f, 1.0f, 1.0f, 0.5f));
DrawCurveEditorBackground(localRect);
if (selected)
{
var selectionRect = new Rect(localClipRange.x, 0.0f, localClipRange.y - localClipRange.x, localRect.height);
DrawOutline(selectionRect);
}
EditorGUI.BeginChangeCheck();
{
var evt = Event.current;
if (evt.type == EventType.Layout || evt.type == EventType.Repaint || selected)
m_CurveEditor.CurveGUI();
}
if (EditorGUI.EndChangeCheck())
OnCurvesUpdated();
DrawOverlay(localRect, localClipRange, DirectorStyles.Instance.customSkin.colorInlineCurveOutOfRangeOverlay);
DrawGrid(localRect, curveStartPosX);
}
}
static Vector2 CalculateCurveVisibleTimeRange(Vector2 timeAreaShownRange, CurveDataSource curve)
{
var curveVisibleTimeRange = new Vector2
{
x = Math.Max(0.0f, timeAreaShownRange.x - curve.start),
y = timeAreaShownRange.y - curve.start
};
return curveVisibleTimeRange * curve.timeScale;
}
void SetupMarginsAndRect(Rect rect, WindowState state)
{
var startX = state.TimeToPixel(m_DataSource.start) - rect.x;
var timelineWidth = state.timeAreaRect.width;
m_CurveEditor.rect = new Rect(0.0f, 0.0f, timelineWidth, rect.height);
m_CurveEditor.leftmargin = Math.Max(startX, 0.0f);
m_CurveEditor.rightmargin = 0.0f;
m_CurveEditor.topmargin = m_CurveEditor.bottommargin = CalculateTopMargin(rect.height);
}
void RestoreShownArea()
{
if (isNewSelection)
FrameClip();
else
m_CurveEditor.shownAreaInsideMargins = m_ViewModel.inlineCurvesShownAreaInsideMargins;
m_ShouldRestoreShownArea = false;
}
static void DrawCurveEditorBackground(Rect rect)
{
if (EditorGUIUtility.isProSkin)
return;
var animEditorBackgroundRect = Rect.MinMaxRect(0.0f, rect.yMin, rect.xMax, rect.yMax);
// Curves are not legible in Personal Skin so we need to darken the background a bit.
EditorGUI.DrawRect(animEditorBackgroundRect, DirectorStyles.Instance.customSkin.colorInlineCurvesBackground);
}
static float CalculateTopMargin(float height)
{
return Mathf.Clamp(0.15f * height, 10.0f, 40.0f);
}
static void DrawOutline(Rect rect, float thickness = 2.0f)
{
// Draw top selected lines.
EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), Color.white);
// Draw bottom selected lines.
EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), Color.white);
// Draw Left Selected Lines
EditorGUI.DrawRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), Color.white);
// Draw Right Selected Lines
EditorGUI.DrawRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), Color.white);
}
static void DrawOverlay(Rect rect, Vector2 clipRange, Color color)
{
var leftSide = new Rect(rect.xMin, rect.yMin, clipRange.x - rect.xMin, rect.height);
EditorGUI.DrawRect(leftSide, color);
var rightSide = new Rect(Mathf.Max(0.0f, clipRange.y), rect.yMin, rect.xMax, rect.height);
EditorGUI.DrawRect(rightSide, color);
}
void DrawGrid(Rect rect, float curveXPosition)
{
var gridXPos = Mathf.Max(curveXPosition - s_GridLabelWidth, rect.xMin);
var gridRect = new Rect(gridXPos, rect.y, s_GridLabelWidth, rect.height);
var originalRect = m_CurveEditor.rect;
m_CurveEditor.rect = new Rect(0.0f, 0.0f, rect.width, rect.height);
using (new GUIGroupScope(gridRect))
m_CurveEditor.GridGUI();
m_CurveEditor.rect = originalRect;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d49b2ed20045e034f9cdf6a6d95e6183
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,272 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
abstract class CurveDataSource
{
public static CurveDataSource Create(IRowGUI trackGUI)
{
if (trackGUI.asset is AnimationTrack)
return new InfiniteClipCurveDataSource(trackGUI);
return new TrackParametersCurveDataSource(trackGUI);
}
public static CurveDataSource Create(TimelineClipGUI clipGUI)
{
if (clipGUI.clip.animationClip != null)
return new ClipAnimationCurveDataSource(clipGUI);
return new ClipParametersCurveDataSource(clipGUI);
}
int? m_ID = null;
public int id
{
get
{
if (!m_ID.HasValue)
m_ID = CreateHashCode();
return m_ID.Value;
}
}
readonly IRowGUI m_TrackGUI;
protected IRowGUI trackGUI { get { return m_TrackGUI; } }
protected CurveDataSource(IRowGUI trackGUI)
{
m_TrackGUI = trackGUI;
}
public abstract AnimationClip animationClip { get; }
public abstract float start { get; }
public abstract float timeScale { get; }
public abstract string groupingName { get; }
public virtual void UpdateCurves(List<CurveWrapper> updatedCurves) {}
public virtual void RebuildCurves() {} // Only necessary when using proxies
public Rect GetBackgroundRect(WindowState state)
{
var trackRect = m_TrackGUI.boundingRect;
return new Rect(
state.timeAreaTranslation.x + trackRect.xMin,
trackRect.y,
(float)state.editSequence.asset.duration * state.timeAreaScale.x,
trackRect.height
);
}
public List<CurveWrapper> GenerateWrappers(List<EditorCurveBinding> bindings)
{
var wrappers = new List<CurveWrapper>(bindings.Count);
int curveWrapperId = 0;
foreach (EditorCurveBinding b in bindings)
{
// General configuration
var wrapper = new CurveWrapper
{
id = curveWrapperId++,
binding = b,
groupId = -1,
hidden = false,
readOnly = false,
getAxisUiScalarsCallback = () => new Vector2(1, 1)
};
// Specific configuration
ConfigureCurveWrapper(wrapper);
wrappers.Add(wrapper);
}
return wrappers;
}
protected virtual void ConfigureCurveWrapper(CurveWrapper wrapper)
{
wrapper.color = CurveUtility.GetPropertyColor(wrapper.binding.propertyName);
wrapper.renderer = new NormalCurveRenderer(AnimationUtility.GetEditorCurve(animationClip, wrapper.binding));
wrapper.renderer.SetCustomRange(0.0f, animationClip.length);
}
protected virtual int CreateHashCode()
{
return m_TrackGUI.asset.GetHashCode();
}
}
class ClipAnimationCurveDataSource : CurveDataSource
{
static readonly string k_GroupingName = L10n.Tr("Animated Values");
readonly TimelineClipGUI m_ClipGUI;
public ClipAnimationCurveDataSource(TimelineClipGUI clipGUI) : base(clipGUI.parent)
{
m_ClipGUI = clipGUI;
}
public override AnimationClip animationClip
{
get { return m_ClipGUI.clip.animationClip; }
}
public override float start
{
get { return (float)m_ClipGUI.clip.FromLocalTimeUnbound(0.0); }
}
public override float timeScale
{
get { return (float)m_ClipGUI.clip.timeScale; }
}
public override string groupingName
{
get { return k_GroupingName; }
}
protected override int CreateHashCode()
{
return base.CreateHashCode().CombineHash(m_ClipGUI.clip.GetHashCode());
}
}
class ClipParametersCurveDataSource : CurveDataSource
{
static readonly string k_GroupingName = L10n.Tr("Clip Properties");
readonly TimelineClipGUI m_ClipGUI;
readonly CurvesProxy m_CurvesProxy;
public ClipParametersCurveDataSource(TimelineClipGUI clipGUI) : base(clipGUI.parent)
{
m_ClipGUI = clipGUI;
m_CurvesProxy = new CurvesProxy(clipGUI.clip);
}
public override AnimationClip animationClip
{
get { return m_CurvesProxy.curves; }
}
public override float start
{
get { return (float)m_ClipGUI.clip.FromLocalTimeUnbound(0.0); }
}
public override float timeScale
{
get { return (float)m_ClipGUI.clip.timeScale; }
}
public override string groupingName
{
get { return k_GroupingName; }
}
public override void UpdateCurves(List<CurveWrapper> updatedCurves)
{
m_CurvesProxy.UpdateCurves(updatedCurves);
}
public override void RebuildCurves()
{
m_CurvesProxy.RebuildCurves();
}
protected override void ConfigureCurveWrapper(CurveWrapper wrapper)
{
m_CurvesProxy.ConfigureCurveWrapper(wrapper);
}
protected override int CreateHashCode()
{
return base.CreateHashCode().CombineHash(m_ClipGUI.clip.GetHashCode());
}
}
class InfiniteClipCurveDataSource : CurveDataSource
{
static readonly string k_GroupingName = L10n.Tr("Animated Values");
readonly AnimationTrack m_AnimationTrack;
public InfiniteClipCurveDataSource(IRowGUI trackGui) : base(trackGui)
{
m_AnimationTrack = trackGui.asset as AnimationTrack;
}
public override AnimationClip animationClip
{
get { return m_AnimationTrack.infiniteClip; }
}
public override float start
{
get { return 0.0f; }
}
public override float timeScale
{
get { return 1.0f; }
}
public override string groupingName
{
get { return k_GroupingName; }
}
}
class TrackParametersCurveDataSource : CurveDataSource
{
static readonly string k_GroupingName = L10n.Tr("Track Properties");
readonly CurvesProxy m_CurvesProxy;
public TrackParametersCurveDataSource(IRowGUI trackGui) : base(trackGui)
{
m_CurvesProxy = new CurvesProxy(trackGui.asset);
}
public override AnimationClip animationClip
{
get { return m_CurvesProxy.curves; }
}
public override float start
{
get { return 0.0f; }
}
public override float timeScale
{
get { return 1.0f; }
}
public override string groupingName
{
get { return k_GroupingName; }
}
public override void UpdateCurves(List<CurveWrapper> updatedCurves)
{
m_CurvesProxy.UpdateCurves(updatedCurves);
}
public override void RebuildCurves()
{
m_CurvesProxy.RebuildCurves();
}
protected override void ConfigureCurveWrapper(CurveWrapper wrapper)
{
m_CurvesProxy.ConfigureCurveWrapper(wrapper);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87a1ae9719ec25d44a4dbec20ec0f892
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class CurvesProxy : ICurvesOwner
{
public AnimationClip curves
{
get { return proxyCurves != null ? proxyCurves : m_OriginalOwner.curves; }
}
public bool hasCurves
{
get { return m_IsAnimatable || m_OriginalOwner.hasCurves; }
}
public double duration
{
get { return m_OriginalOwner.duration; }
}
public string defaultCurvesName
{
get { return m_OriginalOwner.defaultCurvesName; }
}
public UnityObject asset
{
get { return m_OriginalOwner.asset; }
}
public UnityObject assetOwner
{
get { return m_OriginalOwner.assetOwner; }
}
public TrackAsset targetTrack
{
get { return m_OriginalOwner.targetTrack; }
}
readonly ICurvesOwner m_OriginalOwner;
readonly bool m_IsAnimatable;
readonly Dictionary<EditorCurveBinding, SerializedProperty> m_PropertiesMap = new Dictionary<EditorCurveBinding, SerializedProperty>();
int m_ProxyIsRebuilding = 0;
AnimationClip m_ProxyCurves;
AnimationClip proxyCurves
{
get
{
if (!m_IsAnimatable) return null;
if (m_ProxyCurves == null)
RebuildProxyCurves();
return m_ProxyCurves;
}
}
List<SerializedProperty> m_AllAnimatableParameters;
List<SerializedProperty> allAnimatableParameters
{
get
{
var so = AnimatedParameterUtility.GetSerializedPlayableAsset(m_OriginalOwner.asset);
if (so == null)
return null;
so.UpdateIfRequiredOrScript();
if (m_AllAnimatableParameters == null)
m_AllAnimatableParameters = m_OriginalOwner.GetAllAnimatableParameters().ToList();
return m_AllAnimatableParameters;
}
}
public CurvesProxy([NotNull] ICurvesOwner originalOwner)
{
m_OriginalOwner = originalOwner;
m_IsAnimatable = originalOwner.HasAnyAnimatableParameters();
RebuildProxyCurves();
}
public void CreateCurves(string curvesClipName)
{
m_OriginalOwner.CreateCurves(curvesClipName);
}
public void ConfigureCurveWrapper(CurveWrapper wrapper)
{
var color = CurveUtility.GetPropertyColor(wrapper.binding.propertyName);
wrapper.color = color;
float h, s, v;
Color.RGBToHSV(color, out h, out s, out v);
wrapper.wrapColorMultiplier = Color.HSVToRGB(h, s * 0.33f, v * 1.15f);
var curve = AnimationUtility.GetEditorCurve(proxyCurves, wrapper.binding);
wrapper.renderer = new NormalCurveRenderer(curve);
// Use curve length instead of animation clip length
wrapper.renderer.SetCustomRange(0.0f, curve.keys.Last().time);
}
public void RebuildCurves()
{
RebuildProxyCurves();
}
public void UpdateCurves(List<CurveWrapper> updatedCurves)
{
if (m_ProxyIsRebuilding > 0)
return;
Undo.RegisterCompleteObjectUndo(m_OriginalOwner.asset, "Edit Clip Curve");
if (m_OriginalOwner.curves != null)
Undo.RegisterCompleteObjectUndo(m_OriginalOwner.curves, "Edit Clip Curve");
foreach (var curve in updatedCurves)
{
UpdateCurve(curve.binding, curve.curve);
}
AnimatedParameterUtility.UpdateSerializedPlayableAsset(m_OriginalOwner.asset);
}
void UpdateCurve(EditorCurveBinding binding, AnimationCurve curve)
{
ApplyConstraints(binding, curve);
if (curve.length == 0)
{
HandleAllKeysDeleted(binding);
}
else if (curve.length == 1)
{
HandleConstantCurveValueChanged(binding, curve);
}
else
{
HandleCurveUpdated(binding, curve);
}
}
void ApplyConstraints(EditorCurveBinding binding, AnimationCurve curve)
{
if (curve.length == 0)
return;
var curveUpdated = false;
var property = m_PropertiesMap[binding];
if (property.propertyType == SerializedPropertyType.Boolean)
{
TimelineAnimationUtilities.ConstrainCurveToBooleanValues(curve);
curveUpdated = true;
}
else
{
var range = AnimatedParameterUtility.GetAttributeForProperty<RangeAttribute>(property);
if (range != null)
{
TimelineAnimationUtilities.ConstrainCurveToRange(curve, range.min, range.max);
curveUpdated = true;
}
}
if (!curveUpdated)
return;
using (new RebuildGuard(this))
{
AnimationUtility.SetEditorCurve(m_ProxyCurves, binding, curve);
}
}
void HandleCurveUpdated(EditorCurveBinding binding, AnimationCurve updatedCurve)
{
if (!m_OriginalOwner.hasCurves)
m_OriginalOwner.CreateCurves(null);
AnimationUtility.SetEditorCurve(m_OriginalOwner.curves, binding, updatedCurve);
}
void HandleConstantCurveValueChanged(EditorCurveBinding binding, AnimationCurve updatedCurve)
{
var prop = m_PropertiesMap[binding];
if (prop == null)
return;
Undo.RegisterCompleteObjectUndo(prop.serializedObject.targetObject, "Edit Clip Curve");
prop.serializedObject.UpdateIfRequiredOrScript();
CurveEditUtility.SetFromKeyValue(prop, updatedCurve.keys[0].value);
prop.serializedObject.ApplyModifiedProperties();
}
void HandleAllKeysDeleted(EditorCurveBinding binding)
{
if (m_OriginalOwner.hasCurves)
{
// Remove curve from original asset
AnimationUtility.SetEditorCurve(m_OriginalOwner.curves, binding, null);
m_OriginalOwner.SanitizeCurvesData();
}
// Ensure proxy still has constant value
RebuildProxyCurves();
}
void RebuildProxyCurves()
{
if (!m_IsAnimatable)
return;
using (new RebuildGuard(this))
{
if (m_ProxyCurves == null)
{
m_ProxyCurves = new AnimationClip
{
legacy = true,
name = "Constant Curves",
hideFlags = HideFlags.HideAndDontSave,
frameRate = m_OriginalOwner.targetTrack.timelineAsset == null
? TimelineAsset.EditorSettings.kDefaultFps
: m_OriginalOwner.targetTrack.timelineAsset.editorSettings.fps
};
}
else
{
m_ProxyCurves.ClearCurves();
}
m_OriginalOwner.SanitizeCurvesData();
AnimatedParameterUtility.UpdateSerializedPlayableAsset(m_OriginalOwner.asset);
foreach (var param in allAnimatableParameters)
CreateProxyCurve(param, m_ProxyCurves, m_OriginalOwner.asset, param.propertyPath);
AnimationClipCurveCache.Instance.GetCurveInfo(m_ProxyCurves).dirty = true;
}
}
void CreateProxyCurve(SerializedProperty prop, AnimationClip clip, UnityObject owner, string propertyName)
{
var binding = AnimatedParameterUtility.GetCurveBinding(owner, propertyName);
var originalCurve = m_OriginalOwner.hasCurves
? AnimationUtility.GetEditorCurve(m_OriginalOwner.curves, binding)
: null;
if (originalCurve != null)
{
AnimationUtility.SetEditorCurve(clip, binding, originalCurve);
}
else
{
var curve = new AnimationCurve();
CurveEditUtility.AddKeyFrameToCurve(
curve, 0.0f, clip.frameRate, CurveEditUtility.GetKeyValue(prop),
prop.propertyType == SerializedPropertyType.Boolean);
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
m_PropertiesMap[binding] = prop;
}
struct RebuildGuard : IDisposable
{
CurvesProxy m_Owner;
AnimationUtility.OnCurveWasModified m_Callback;
public RebuildGuard(CurvesProxy owner)
{
m_Callback = AnimationUtility.onCurveWasModified;
AnimationUtility.onCurveWasModified = null;
m_Owner = owner;
m_Owner.m_ProxyIsRebuilding++;
}
public void Dispose()
{
AnimationUtility.onCurveWasModified = m_Callback;
m_Owner.m_ProxyIsRebuilding--;
m_Owner = null;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d72ccd2c66ea846fc842adf682b11526
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,435 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEngineInternal;
using UnityEngine.Timeline;
using UnityEngine.Playables;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class TimelineAnimationUtilities
{
public enum OffsetEditMode
{
None = -1,
Translation = 0,
Rotation = 1
}
public static bool ValidateOffsetAvailabitity(PlayableDirector director, Animator animator)
{
if (director == null || animator == null)
return false;
return true;
}
public static TimelineClip GetPreviousClip(TimelineClip clip)
{
TimelineClip previousClip = null;
foreach (var c in clip.parentTrack.clips)
{
if (c.start < clip.start && (previousClip == null || c.start >= previousClip.start))
previousClip = c;
}
return previousClip;
}
public static TimelineClip GetNextClip(TimelineClip clip)
{
return clip.parentTrack.clips.Where(c => c.start > clip.start).OrderBy(c => c.start).FirstOrDefault();
}
public struct RigidTransform
{
public Vector3 position;
public Quaternion rotation;
public static RigidTransform Compose(Vector3 pos, Quaternion rot)
{
RigidTransform ret;
ret.position = pos;
ret.rotation = rot;
return ret;
}
public static RigidTransform Mul(RigidTransform a, RigidTransform b)
{
RigidTransform ret;
ret.rotation = a.rotation * b.rotation;
ret.position = a.position + a.rotation * b.position;
return ret;
}
public static RigidTransform Inverse(RigidTransform a)
{
RigidTransform ret;
ret.rotation = Quaternion.Inverse(a.rotation);
ret.position = ret.rotation * (-a.position);
return ret;
}
public static RigidTransform identity
{
get { return Compose(Vector3.zero, Quaternion.identity); }
}
}
private static Matrix4x4 GetTrackMatrix(Transform transform, AnimationTrack track)
{
Matrix4x4 trackMatrix = Matrix4x4.TRS(track.position, track.rotation, Vector3.one);
// in scene off mode, the track offsets are set to the preview position which is stored in the track
if (track.trackOffset == TrackOffset.ApplySceneOffsets)
{
trackMatrix = Matrix4x4.TRS(track.sceneOffsetPosition, Quaternion.Euler(track.sceneOffsetRotation), Vector3.one);
}
// put the parent transform on to the track matrix
if (transform.parent != null)
{
trackMatrix = transform.parent.localToWorldMatrix * trackMatrix;
}
return trackMatrix;
}
// Given a world space position and rotation, updates the clip offsets to match that
public static RigidTransform UpdateClipOffsets(AnimationPlayableAsset asset, AnimationTrack track, Transform transform, Vector3 globalPosition, Quaternion globalRotation)
{
Matrix4x4 worldToLocal = transform.worldToLocalMatrix;
Matrix4x4 clipMatrix = Matrix4x4.TRS(asset.position, asset.rotation, Vector3.one);
Matrix4x4 trackMatrix = GetTrackMatrix(transform, track);
// Use the transform to find the proper goal matrix with scale taken into account
var oldPos = transform.position;
var oldRot = transform.rotation;
transform.position = globalPosition;
transform.rotation = globalRotation;
Matrix4x4 goal = transform.localToWorldMatrix;
transform.position = oldPos;
transform.rotation = oldRot;
// compute the new clip matrix.
Matrix4x4 newClip = trackMatrix.inverse * goal * worldToLocal * trackMatrix * clipMatrix;
return RigidTransform.Compose(newClip.GetColumn(3), MathUtils.QuaternionFromMatrix(newClip));
}
public static RigidTransform GetTrackOffsets(AnimationTrack track, Transform transform)
{
Vector3 position = track.position;
Quaternion rotation = track.rotation;
if (transform != null && transform.parent != null)
{
position = transform.parent.TransformPoint(position);
rotation = transform.parent.rotation * rotation;
MathUtils.QuaternionNormalize(ref rotation);
}
return RigidTransform.Compose(position, rotation);
}
public static void UpdateTrackOffset(AnimationTrack track, Transform transform, RigidTransform offsets)
{
if (transform != null && transform.parent != null)
{
offsets.position = transform.parent.InverseTransformPoint(offsets.position);
offsets.rotation = Quaternion.Inverse(transform.parent.rotation) * offsets.rotation;
MathUtils.QuaternionNormalize(ref offsets.rotation);
}
track.position = offsets.position;
track.eulerAngles = AnimationUtility.GetClosestEuler(offsets.rotation, track.eulerAngles, RotationOrder.OrderZXY);
track.UpdateClipOffsets();
}
static MatchTargetFields GetMatchFields(TimelineClip clip)
{
var track = clip.parentTrack as AnimationTrack;
if (track == null)
return MatchTargetFieldConstants.None;
var asset = clip.asset as AnimationPlayableAsset;
var fields = track.matchTargetFields;
if (asset != null && !asset.useTrackMatchFields)
fields = asset.matchTargetFields;
return fields;
}
static void WriteMatchFields(AnimationPlayableAsset asset, RigidTransform result, MatchTargetFields fields)
{
Vector3 position = asset.position;
position.x = fields.HasAny(MatchTargetFields.PositionX) ? result.position.x : position.x;
position.y = fields.HasAny(MatchTargetFields.PositionY) ? result.position.y : position.y;
position.z = fields.HasAny(MatchTargetFields.PositionZ) ? result.position.z : position.z;
asset.position = position;
// check first to avoid unnecessary conversion errors
if (fields.HasAny(MatchTargetFieldConstants.Rotation))
{
Vector3 eulers = asset.eulerAngles;
Vector3 resultEulers = result.rotation.eulerAngles;
eulers.x = fields.HasAny(MatchTargetFields.RotationX) ? resultEulers.x : eulers.x;
eulers.y = fields.HasAny(MatchTargetFields.RotationY) ? resultEulers.y : eulers.y;
eulers.z = fields.HasAny(MatchTargetFields.RotationZ) ? resultEulers.z : eulers.z;
asset.eulerAngles = AnimationUtility.GetClosestEuler(Quaternion.Euler(eulers), asset.eulerAngles, RotationOrder.OrderZXY);
}
}
public static void MatchPrevious(TimelineClip currentClip, Transform matchPoint, PlayableDirector director)
{
const double timeEpsilon = 0.00001;
MatchTargetFields matchFields = GetMatchFields(currentClip);
if (matchFields == MatchTargetFieldConstants.None || matchPoint == null)
return;
double cachedTime = director.time;
// finds previous clip
TimelineClip previousClip = GetPreviousClip(currentClip);
if (previousClip == null || currentClip == previousClip)
return;
// make sure the transform is properly updated before modifying the graph
director.Evaluate();
var parentTrack = currentClip.parentTrack as AnimationTrack;
var blendIn = currentClip.blendInDuration;
currentClip.blendInDuration = 0;
var blendOut = previousClip.blendOutDuration;
previousClip.blendOutDuration = 0;
//evaluate previous without current
parentTrack.RemoveClip(currentClip);
director.RebuildGraph();
double previousEndTime = currentClip.start > previousClip.end ? previousClip.end : currentClip.start;
director.time = previousEndTime - timeEpsilon;
director.Evaluate(); // add port to evaluate only track
var targetPosition = matchPoint.position;
var targetRotation = matchPoint.rotation;
// evaluate current without previous
parentTrack.AddClip(currentClip);
parentTrack.RemoveClip(previousClip);
director.RebuildGraph();
director.time = currentClip.start + timeEpsilon;
director.Evaluate();
//////////////////////////////////////////////////////////////////////
//compute offsets
var animationPlayable = currentClip.asset as AnimationPlayableAsset;
var match = UpdateClipOffsets(animationPlayable, parentTrack, matchPoint, targetPosition, targetRotation);
WriteMatchFields(animationPlayable, match, matchFields);
//////////////////////////////////////////////////////////////////////
currentClip.blendInDuration = blendIn;
previousClip.blendOutDuration = blendOut;
parentTrack.AddClip(previousClip);
director.RebuildGraph();
director.time = cachedTime;
director.Evaluate();
}
public static void MatchNext(TimelineClip currentClip, Transform matchPoint, PlayableDirector director)
{
const double timeEpsilon = 0.00001;
MatchTargetFields matchFields = GetMatchFields(currentClip);
if (matchFields == MatchTargetFieldConstants.None || matchPoint == null)
return;
double cachedTime = director.time;
// finds next clip
TimelineClip nextClip = GetNextClip(currentClip);
if (nextClip == null || currentClip == nextClip)
return;
// make sure the transform is properly updated before modifying the graph
director.Evaluate();
var parentTrack = currentClip.parentTrack as AnimationTrack;
var blendOut = currentClip.blendOutDuration;
var blendIn = nextClip.blendInDuration;
currentClip.blendOutDuration = 0;
nextClip.blendInDuration = 0;
//evaluate previous without current
parentTrack.RemoveClip(currentClip);
director.RebuildGraph();
director.time = nextClip.start + timeEpsilon;
director.Evaluate(); // add port to evaluate only track
var targetPosition = matchPoint.position;
var targetRotation = matchPoint.rotation;
// evaluate current without next
parentTrack.AddClip(currentClip);
parentTrack.RemoveClip(nextClip);
director.RebuildGraph();
director.time = Math.Min(nextClip.start, currentClip.end - timeEpsilon);
director.Evaluate();
//////////////////////////////////////////////////////////////////////
//compute offsets
var animationPlayable = currentClip.asset as AnimationPlayableAsset;
var match = UpdateClipOffsets(animationPlayable, parentTrack, matchPoint, targetPosition, targetRotation);
WriteMatchFields(animationPlayable, match, matchFields);
//////////////////////////////////////////////////////////////////////
currentClip.blendOutDuration = blendOut;
nextClip.blendInDuration = blendIn;
parentTrack.AddClip(nextClip);
director.RebuildGraph();
director.time = cachedTime;
director.Evaluate();
}
public static TimelineWindowTimeControl CreateTimeController(WindowState state, TimelineClip clip)
{
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
var timeController = ScriptableObject.CreateInstance<TimelineWindowTimeControl>();
timeController.Init(animationWindow.state, clip);
return timeController;
}
public static TimelineWindowTimeControl CreateTimeController(WindowState state, TimelineWindowTimeControl.ClipData clipData)
{
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
var timeController = ScriptableObject.CreateInstance<TimelineWindowTimeControl>();
timeController.Init(animationWindow.state, clipData);
return timeController;
}
public static void EditAnimationClipWithTimeController(AnimationClip animationClip, TimelineWindowTimeControl timeController, Object sourceObject)
{
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
animationWindow.EditSequencerClip(animationClip, sourceObject, timeController);
}
public static void UnlinkAnimationWindowFromTracks(IEnumerable<TrackAsset> tracks)
{
var clips = new List<AnimationClip>();
foreach (var track in tracks)
{
var animationTrack = track as AnimationTrack;
if (animationTrack != null && animationTrack.infiniteClip != null)
clips.Add(animationTrack.infiniteClip);
GetAnimationClips(track.GetClips(), clips);
}
UnlinkAnimationWindowFromAnimationClips(clips);
}
public static void UnlinkAnimationWindowFromClips(IEnumerable<TimelineClip> timelineClips)
{
var clips = new List<AnimationClip>();
GetAnimationClips(timelineClips, clips);
UnlinkAnimationWindowFromAnimationClips(clips);
}
public static void UnlinkAnimationWindowFromAnimationClips(ICollection<AnimationClip> clips)
{
if (clips.Count == 0)
return;
UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(typeof(AnimationWindow));
foreach (var animWindow in windows.OfType<AnimationWindow>())
{
if (animWindow != null && animWindow.state != null && animWindow.state.linkedWithSequencer && clips.Contains(animWindow.state.activeAnimationClip))
animWindow.UnlinkSequencer();
}
}
public static void UnlinkAnimationWindow()
{
UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(typeof(AnimationWindow));
foreach (var animWindow in windows.OfType<AnimationWindow>())
{
if (animWindow != null && animWindow.state != null && animWindow.state.linkedWithSequencer)
animWindow.UnlinkSequencer();
}
}
private static void GetAnimationClips(IEnumerable<TimelineClip> timelineClips, List<AnimationClip> clips)
{
foreach (var timelineClip in timelineClips)
{
if (timelineClip.curves != null)
clips.Add(timelineClip.curves);
AnimationPlayableAsset apa = timelineClip.asset as AnimationPlayableAsset;
if (apa != null && apa.clip != null)
clips.Add(apa.clip);
}
}
public static int GetAnimationWindowCurrentFrame()
{
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
if (animationWindow)
return animationWindow.state.currentFrame;
return -1;
}
public static void SetAnimationWindowCurrentFrame(int frame)
{
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
if (animationWindow)
animationWindow.state.currentFrame = frame;
}
public static void ConstrainCurveToBooleanValues(AnimationCurve curve)
{
// Clamp the values first
var keys = curve.keys;
for (var i = 0; i < keys.Length; i++)
{
var key = keys[i];
key.value = key.value < 0.5f ? 0.0f : 1.0f;
keys[i] = key;
}
curve.keys = keys;
// Update the tangents once all the values are clamped
for (var i = 0; i < curve.length; i++)
{
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Constant);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Constant);
}
}
public static void ConstrainCurveToRange(AnimationCurve curve, float minValue, float maxValue)
{
var keys = curve.keys;
for (var i = 0; i < keys.Length; i++)
{
var key = keys[i];
key.value = Mathf.Clamp(key.value, minValue, maxValue);
keys[i] = key;
}
curve.keys = keys;
}
public static bool IsAnimationClip(TimelineClip clip)
{
return clip != null && (clip.asset as AnimationPlayableAsset) != null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9685354eb873b8d4699078b307b0f260
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: