summaryrefslogtreecommitdiff
path: root/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording
diff options
context:
space:
mode:
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording')
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs306
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs497
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs134
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs709
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs115
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs191
-rw-r--r--Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs.meta11
12 files changed, 2018 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs
new file mode 100644
index 0000000..d09cc62
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs
@@ -0,0 +1,306 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Timeline;
+
+namespace UnityEditor.Timeline
+{
+ class AnimationTrackRecorder
+ {
+ public static readonly string kRecordClipDefaultName = L10n.Tr("Recorded");
+
+ AnimationClip m_TargetClip;
+ int m_CurveCount = 0;
+
+ double m_ClipTime;
+ bool m_needRebuildRects;
+
+ bool m_TrackHasPreviewComponents;
+
+ public TimelineClip recordClip { get; private set; }
+
+ public void PrepareForRecord(WindowState state)
+ {
+ m_CurveCount = 0;
+ m_TargetClip = null;
+ m_TrackHasPreviewComponents = false;
+ }
+
+ public AnimationClip PrepareTrack(TrackAsset track, WindowState state, GameObject gameObject, out double startTime)
+ {
+ AnimationClip animationClip = null;
+
+ // if we are not in clip mode, we simply use the track clip
+ var animationTrack = (AnimationTrack)track;
+
+ // ignore recording if we are in Legacy auto mode
+ startTime = -1;
+ var parentTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
+ if (parentTrack != null && parentTrack.trackOffset == TrackOffset.Auto)
+ return null;
+
+ if (!animationTrack.inClipMode)
+ {
+ var trackClip = animationTrack.GetOrCreateClip();
+ startTime = trackClip.frameRate * state.editSequence.time;
+
+ // Make the first key be at time 0 of the clip
+ if (trackClip.empty)
+ {
+ animationTrack.infiniteClipTimeOffset = 0; // state.time;
+ animationTrack.infiniteClipPreExtrapolation = TimelineClip.ClipExtrapolation.Hold;
+ animationTrack.infiniteClipPostExtrapolation = TimelineClip.ClipExtrapolation.Hold;
+ }
+
+ animationClip = trackClip;
+ }
+ else
+ {
+ TimelineClip activeClip = null;
+
+ // if it fails, but returns no clip, we can add one.
+ if (!track.FindRecordingClipAtTime(state.editSequence.time, out activeClip) && activeClip != null)
+ {
+ return null;
+ }
+
+ if (activeClip == null)
+ {
+ activeClip = AddRecordableClip(track, state, state.editSequence.time);
+ }
+
+ var clip = activeClip.animationClip;
+
+ // flags this as the clip being recorded for the track
+ var clipTime = state.editSequence.time - activeClip.start;
+
+ // if we are in the past
+ if (clipTime < 0)
+ {
+ Undo.RegisterCompleteObjectUndo(clip, "Record Key");
+ TimelineUndo.PushUndo(track, "Prepend Key");
+ ShiftAnimationClip(clip, (float)-clipTime);
+ activeClip.start = state.editSequence.time;
+ activeClip.duration += -clipTime;
+ clipTime = 0;
+ }
+
+ m_ClipTime = clipTime;
+ recordClip = activeClip;
+ startTime = TimeUtility.ToFrames(recordClip.ToLocalTimeUnbound(state.editSequence.time), clip.frameRate);
+ m_needRebuildRects = clip.empty;
+
+ animationClip = clip;
+ }
+
+ m_TargetClip = animationClip;
+ m_CurveCount = GetCurveCount(animationClip);
+ m_TrackHasPreviewComponents = animationTrack.hasPreviewComponents;
+
+ return animationClip;
+ }
+
+ static int GetCurveCount(AnimationClip animationClip)
+ {
+ int count = 0;
+ if (animationClip != null)
+ {
+ var clipCache = AnimationClipCurveCache.Instance.GetCurveInfo(animationClip);
+ count = clipCache.curves.Length + clipCache.objectCurves.Count;
+ }
+
+ return count;
+ }
+
+ public void FinializeTrack(TrackAsset track, WindowState state)
+ {
+ // make sure we dirty the clip if we are in non clip mode
+ var animTrack = track as AnimationTrack;
+ if (!animTrack.inClipMode)
+ {
+ EditorUtility.SetDirty(animTrack.GetOrCreateClip());
+ }
+
+ // in clip mode we need to do some extra work
+ if (recordClip != null)
+ {
+ // stretch the clip out to meet the new recording time
+ if (m_ClipTime > recordClip.duration)
+ {
+ TimelineUndo.PushUndo(track, "Add Key");
+ recordClip.duration = m_ClipTime;
+ }
+
+ track.CalculateExtrapolationTimes();
+ }
+
+ recordClip = null;
+ m_ClipTime = 0;
+ if (m_needRebuildRects)
+ {
+ state.CalculateRowRects();
+ m_needRebuildRects = false;
+ }
+ }
+
+ public void FinalizeRecording(WindowState state)
+ {
+ // rebuild the graph if we add/remove a clip. Rebuild the graph with an evaluation immediately
+ // so previews and scene position is maintained.
+ if (m_CurveCount != GetCurveCount(m_TargetClip))
+ {
+ state.rebuildGraph = true;
+ state.GetWindow().RebuildGraphIfNecessary(true);
+ }
+ else if (m_TrackHasPreviewComponents)
+ {
+ // Track with preview components potentially has modifications impacting other properties that need
+ // to be refreshed before inspector or scene view to not interfere with manipulation.
+ state.EvaluateImmediate();
+ }
+ }
+
+ // For a given track asset get a unique clip name
+ public static string GetUniqueRecordedClipName(Object owner, string name)
+ {
+ // first attempt -- uniquely named in file
+ var path = AssetDatabase.GetAssetPath(owner);
+ if (!string.IsNullOrEmpty(path))
+ {
+ var names = AssetDatabase.LoadAllAssetsAtPath(path).Where(x => x != null).Select(x => x.name);
+ return ObjectNames.GetUniqueName(names.ToArray(), name);
+ }
+
+ TrackAsset asset = owner as TrackAsset;
+ if (asset == null || asset.clips.Length == 0)
+ return name;
+
+ // final attempt - uniquely named in track
+ return ObjectNames.GetUniqueName(asset.clips.Select(x => x.displayName).ToArray(), name);
+ }
+
+ // Given an appropriate parent track, create a recordable clip
+ public static TimelineClip AddRecordableClip(TrackAsset parentTrack, WindowState state, double atTime)
+ {
+ var sequenceAsset = state.editSequence.asset;
+ if (sequenceAsset == null)
+ {
+ Debug.LogError("Parent Track needs to be bound to an asset to add a recordable");
+ return null;
+ }
+
+ var animTrack = parentTrack as AnimationTrack;
+ if (animTrack == null)
+ {
+ Debug.LogError("Recordable clips are only valid on Animation Tracks");
+ return null;
+ }
+
+ var newClip = animTrack.CreateRecordableClip(GetUniqueRecordedClipName(parentTrack, kRecordClipDefaultName));
+ if (newClip == null)
+ {
+ Debug.LogError("Could not create a recordable clip");
+ return null;
+ }
+
+ newClip.mixInCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
+ newClip.mixOutCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
+
+ newClip.preExtrapolationMode = TimelineClip.ClipExtrapolation.Hold;
+ newClip.postExtrapolationMode = TimelineClip.ClipExtrapolation.Hold;
+
+ double startTime = 0;
+ double endTime = 0;
+
+ GetAddedRecordingClipRange(animTrack, state, atTime, out startTime, out endTime);
+
+ newClip.start = startTime;
+ newClip.duration = endTime - startTime;
+
+ state.Refresh();
+
+ return newClip;
+ }
+
+ // get the start and end times of what an added recording clip at a given time would be
+ internal static void GetAddedRecordingClipRange(TrackAsset track, WindowState state, double atTime, out double start, out double end)
+ {
+ // size to make the clip in pixels. Reasonably big so that both handles are easily manipulated,
+ // and the full title is normally visible
+ double defaultDuration = state.PixelDeltaToDeltaTime(100);
+
+ start = atTime;
+ end = atTime + defaultDuration;
+
+ double gapStart = 0;
+ double gapEnd = 0;
+
+ // no gap, pick are reasonable amount
+ if (!track.GetGapAtTime(atTime, out gapStart, out gapEnd))
+ {
+ start = atTime;
+ return;
+ }
+
+ if (!double.IsInfinity(gapEnd))
+ end = gapEnd;
+
+ start = state.SnapToFrameIfRequired(start);
+ end = state.SnapToFrameIfRequired(end);
+ }
+
+ // Given a clip, shifts the keys in that clip by the given amount.
+ internal static void ShiftAnimationClip(AnimationClip clip, float amount)
+ {
+ if (clip == null)
+ return;
+
+ var curveBindings = AnimationUtility.GetCurveBindings(clip);
+ var objectCurveBindings = AnimationUtility.GetObjectReferenceCurveBindings(clip);
+
+ foreach (var binding in curveBindings)
+ {
+ AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);
+ curve.keys = ShiftKeys(curve.keys, amount);
+ AnimationUtility.SetEditorCurve(clip, binding, curve);
+ }
+
+ foreach (var binding in objectCurveBindings)
+ {
+ ObjectReferenceKeyframe[] keyframes = AnimationUtility.GetObjectReferenceCurve(clip, binding);
+ keyframes = ShiftObjectKeys(keyframes, amount);
+ AnimationUtility.SetObjectReferenceCurve(clip, binding, keyframes);
+ }
+
+ EditorUtility.SetDirty(clip);
+ }
+
+ // shift all the keys over by the given time, stretching the time 0 key
+ static Keyframe[] ShiftKeys(Keyframe[] keys, float time)
+ {
+ if (keys == null || keys.Length == 0 || time == 0)
+ return keys;
+
+ for (int i = 0; i < keys.Length; i++)
+ {
+ keys[i].time += time;
+ }
+
+ return keys;
+ }
+
+ // Shift object keys over by the appropriate amount
+ static ObjectReferenceKeyframe[] ShiftObjectKeys(ObjectReferenceKeyframe[] keys, float time)
+ {
+ if (keys == null || keys.Length == 0 || time == 0)
+ return keys;
+
+ for (int i = 0; i < keys.Length; i++)
+ {
+ keys[i].time += time;
+ }
+
+ return keys;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs.meta
new file mode 100644
index 0000000..e80ec10
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/AnimationTrackRecorder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a0a02e768c802b641b6793fa864f1c2c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs
new file mode 100644
index 0000000..e63ad6a
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs
@@ -0,0 +1,497 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Timeline;
+using UnityEngine.Playables;
+
+namespace UnityEditor.Timeline
+{
+ // Handles Undo animated properties on Monobehaviours to create track clips
+ static partial class TimelineRecording
+ {
+ static readonly List<PropertyModification> s_TempPropertyModifications = new List<PropertyModification>(6);
+
+ internal static UndoPropertyModification[] ProcessUndoModification(UndoPropertyModification[] modifications, WindowState state)
+ {
+ if (HasAnyPlayableAssetModifications(modifications))
+ return ProcessPlayableAssetModification(modifications, state);
+ return ProcessMonoBehaviourModification(modifications, state);
+ }
+
+ static UnityEngine.Object GetTarget(UndoPropertyModification undo)
+ {
+ if (undo.currentValue != null)
+ return undo.currentValue.target;
+ if (undo.previousValue != null)
+ return undo.previousValue.target;
+ return null;
+ }
+
+ // Gets the appropriate track for a given game object
+ static TrackAsset GetTrackForGameObject(GameObject gameObject, WindowState state)
+ {
+ if (gameObject == null)
+ return null;
+
+ var director = state.editSequence.director;
+ if (director == null)
+ return null;
+
+ var level = int.MaxValue;
+
+ TrackAsset result = null;
+
+ // search the output tracks
+ var outputTracks = state.editSequence.asset.flattenedTracks;
+ foreach (var track in outputTracks)
+ {
+ if (track.GetType() != typeof(AnimationTrack))
+ continue;
+ if (!state.IsTrackRecordable(track))
+ continue;
+
+ var obj = TimelineUtility.GetSceneGameObject(director, track);
+ if (obj != null)
+ {
+ // checks if the effected gameobject is our child
+ var childLevel = GetChildLevel(obj, gameObject);
+ if (childLevel != -1 && childLevel < level)
+ {
+ result = track;
+ level = childLevel;
+ }
+ }
+ }
+
+ // the resulting track is not armed. checking here avoids accidentally recording objects with their own
+ // tracks
+ if (result && !state.IsTrackRecordable(result))
+ {
+ result = null;
+ }
+
+ return result;
+ }
+
+ // Gets the track this property would record to.
+ // Returns null if there is a track, but it's not currently active for recording
+ public static TrackAsset GetRecordingTrack(SerializedProperty property, WindowState state)
+ {
+ var serializedObject = property.serializedObject;
+ var component = serializedObject.targetObject as Component;
+ if (component == null)
+ return null;
+
+ var gameObject = component.gameObject;
+ return GetTrackForGameObject(gameObject, state);
+ }
+
+ // Given a serialized property, gathers all animatable properties
+ static void GatherModifications(SerializedProperty property, List<PropertyModification> modifications)
+ {
+ // handles child properties (Vector3 is 3 recordable properties)
+ if (property.hasChildren)
+ {
+ var iter = property.Copy();
+ var end = property.GetEndProperty(false);
+
+ // recurse over all children properties
+ while (iter.Next(true) && !SerializedProperty.EqualContents(iter, end))
+ {
+ GatherModifications(iter, modifications);
+ }
+ }
+
+ var isObject = property.propertyType == SerializedPropertyType.ObjectReference;
+ var isFloat = property.propertyType == SerializedPropertyType.Float ||
+ property.propertyType == SerializedPropertyType.Boolean ||
+ property.propertyType == SerializedPropertyType.Integer;
+
+ if (isObject || isFloat)
+ {
+ var serializedObject = property.serializedObject;
+ var modification = new PropertyModification();
+
+ modification.target = serializedObject.targetObject;
+ modification.propertyPath = property.propertyPath;
+ if (isObject)
+ {
+ modification.value = string.Empty;
+ modification.objectReference = property.objectReferenceValue;
+ }
+ else
+ {
+ modification.value = TimelineUtility.PropertyToString(property);
+ }
+
+ // Path for monobehaviour based - better to grab the component to get the curvebinding to allow validation
+ if (serializedObject.targetObject is Component)
+ {
+ EditorCurveBinding temp;
+ var go = ((Component)serializedObject.targetObject).gameObject;
+ if (AnimationUtility.PropertyModificationToEditorCurveBinding(modification, go, out temp) != null)
+ {
+ modifications.Add(modification);
+ }
+ }
+ else
+ {
+ modifications.Add(modification);
+ }
+ }
+ }
+
+ public static bool CanRecord(SerializedProperty property, WindowState state)
+ {
+ if (IsPlayableAssetProperty(property))
+ return AnimatedParameterUtility.IsTypeAnimatable(property.propertyType);
+
+ if (GetRecordingTrack(property, state) == null)
+ return false;
+
+ s_TempPropertyModifications.Clear();
+ GatherModifications(property, s_TempPropertyModifications);
+ return s_TempPropertyModifications.Any();
+ }
+
+ public static void AddKey(SerializedProperty prop, WindowState state)
+ {
+ s_TempPropertyModifications.Clear();
+ GatherModifications(prop, s_TempPropertyModifications);
+ if (s_TempPropertyModifications.Any())
+ {
+ AddKey(s_TempPropertyModifications, state);
+ }
+ }
+
+ public static void AddKey(IEnumerable<PropertyModification> modifications, WindowState state)
+ {
+ var undos = modifications.Select(PropertyModificationToUndoPropertyModification).ToArray();
+ ProcessUndoModification(undos, state);
+ }
+
+ static UndoPropertyModification PropertyModificationToUndoPropertyModification(PropertyModification prop)
+ {
+ return new UndoPropertyModification
+ {
+ previousValue = prop,
+ currentValue = new PropertyModification
+ {
+ objectReference = prop.objectReference,
+ propertyPath = prop.propertyPath,
+ target = prop.target,
+ value = prop.value
+ },
+ keepPrefabOverride = true
+ };
+ }
+
+ // Given an animation track, return the clip that we are currently recording to
+ static AnimationClip GetRecordingClip(TrackAsset asset, WindowState state, out double startTime, out double timeScale)
+ {
+ startTime = 0;
+ timeScale = 1;
+
+ TimelineClip displayBackground = null;
+ asset.FindRecordingClipAtTime(state.editSequence.time, out displayBackground);
+ var animClip = asset.FindRecordingAnimationClipAtTime(state.editSequence.time);
+
+ if (displayBackground != null)
+ {
+ startTime = displayBackground.start;
+ timeScale = displayBackground.timeScale;
+ }
+
+ return animClip;
+ }
+
+ // Helper that finds the animation clip we are recording and the relative time to that clip
+ static bool GetClipAndRelativeTime(UnityEngine.Object target, WindowState state,
+ out AnimationClip outClip, out double keyTime, out bool keyInRange)
+ {
+ const float floatToDoubleError = 0.00001f;
+ outClip = null;
+ keyTime = 0;
+ keyInRange = false;
+
+ double startTime = 0;
+ double timeScale = 1;
+ AnimationClip clip = null;
+
+ IPlayableAsset playableAsset = target as IPlayableAsset;
+ Component component = target as Component;
+
+ // Handle recordable playable assets
+ if (playableAsset != null)
+ {
+ var curvesOwner = AnimatedParameterUtility.ToCurvesOwner(playableAsset, state.editSequence.asset);
+ if (curvesOwner != null && state.IsTrackRecordable(curvesOwner.targetTrack))
+ {
+ if (curvesOwner.curves == null)
+ curvesOwner.CreateCurves(curvesOwner.GetUniqueRecordedClipName());
+
+ clip = curvesOwner.curves;
+
+ var timelineClip = curvesOwner as TimelineClip;
+ if (timelineClip != null)
+ {
+ startTime = timelineClip.start;
+ timeScale = timelineClip.timeScale;
+ }
+ }
+ }
+ // Handle recording components, including infinite clip
+ else if (component != null)
+ {
+ var asset = GetTrackForGameObject(component.gameObject, state);
+ if (asset != null)
+ {
+ clip = GetRecordingClip(asset, state, out startTime, out timeScale);
+ }
+ }
+
+ if (clip == null)
+ return false;
+
+ keyTime = (state.editSequence.time - startTime) * timeScale;
+ outClip = clip;
+ keyInRange = keyTime >= 0 && keyTime <= (clip.length * timeScale + floatToDoubleError);
+
+ return true;
+ }
+
+ public static bool HasCurve(IEnumerable<PropertyModification> modifications, UnityEngine.Object target,
+ WindowState state)
+ {
+ return GetKeyTimes(target, modifications, state).Any();
+ }
+
+ public static bool HasKey(IEnumerable<PropertyModification> modifications, UnityEngine.Object target,
+ WindowState state)
+ {
+ AnimationClip clip;
+ double keyTime;
+ bool inRange;
+ if (!GetClipAndRelativeTime(target, state, out clip, out keyTime, out inRange))
+ return false;
+
+ return GetKeyTimes(target, modifications, state).Any(t => (CurveEditUtility.KeyCompare((float)state.editSequence.time, (float)t, clip.frameRate) == 0));
+ }
+
+ // Checks if a key already exists for this property
+ static bool HasBinding(UnityEngine.Object target, PropertyModification modification, AnimationClip clip, out EditorCurveBinding binding)
+ {
+ var component = target as Component;
+ var playableAsset = target as IPlayableAsset;
+
+ if (component != null)
+ {
+ var type = AnimationUtility.PropertyModificationToEditorCurveBinding(modification, component.gameObject, out binding);
+ binding = RotationCurveInterpolation.RemapAnimationBindingForRotationCurves(binding, clip);
+ return type != null;
+ }
+
+ if (playableAsset != null)
+ {
+ binding = EditorCurveBinding.FloatCurve(string.Empty, target.GetType(),
+ AnimatedParameterUtility.GetAnimatedParameterBindingName(target, modification.propertyPath));
+ }
+ else
+ {
+ binding = new EditorCurveBinding();
+ return false;
+ }
+
+ return true;
+ }
+
+ public static void RemoveKey(UnityEngine.Object target, IEnumerable<PropertyModification> modifications,
+ WindowState state)
+ {
+ AnimationClip clip;
+ double keyTime;
+ bool inRange;
+ if (!GetClipAndRelativeTime(target, state, out clip, out keyTime, out inRange) || !inRange)
+ return;
+ var refreshPreview = false;
+ TimelineUndo.PushUndo(clip, "Remove Key");
+ foreach (var mod in modifications)
+ {
+ EditorCurveBinding temp;
+ if (HasBinding(target, mod, clip, out temp))
+ {
+ if (temp.isPPtrCurve)
+ {
+ CurveEditUtility.RemoveObjectKey(clip, temp, keyTime);
+ if (CurveEditUtility.GetObjectKeyCount(clip, temp) == 0)
+ {
+ refreshPreview = true;
+ }
+ }
+ else
+ {
+ AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, temp);
+ if (curve != null)
+ {
+ CurveEditUtility.RemoveKeyFrameFromCurve(curve, (float)keyTime, clip.frameRate);
+ AnimationUtility.SetEditorCurve(clip, temp, curve);
+ if (curve.length == 0)
+ {
+ AnimationUtility.SetEditorCurve(clip, temp, null);
+ refreshPreview = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (refreshPreview)
+ {
+ state.ResetPreviewMode();
+ }
+ }
+
+ static HashSet<double> GetKeyTimes(UnityEngine.Object target, IEnumerable<PropertyModification> modifications, WindowState state)
+ {
+ var keyTimes = new HashSet<double>();
+
+ AnimationClip animationClip;
+ double keyTime;
+ bool inRange;
+ GetClipAndRelativeTime(target, state, out animationClip, out keyTime, out inRange);
+ if (animationClip == null)
+ return keyTimes;
+
+ var component = target as Component;
+ var playableAsset = target as IPlayableAsset;
+ var info = AnimationClipCurveCache.Instance.GetCurveInfo(animationClip);
+
+ TimelineClip clip = null;
+ if (component != null)
+ {
+ GetTrackForGameObject(component.gameObject, state).FindRecordingClipAtTime(state.editSequence.time, out clip);
+ }
+ else if (playableAsset != null)
+ {
+ clip = FindClipWithAsset(state.editSequence.asset, playableAsset);
+ }
+
+ foreach (var mod in modifications)
+ {
+ EditorCurveBinding temp;
+ if (HasBinding(target, mod, animationClip, out temp))
+ {
+ IEnumerable<double> keys = new HashSet<double>();
+ if (temp.isPPtrCurve)
+ {
+ var curve = info.GetObjectCurveForBinding(temp);
+ if (curve != null)
+ {
+ keys = curve.Select(x => (double)x.time);
+ }
+ }
+ else
+ {
+ var curve = info.GetCurveForBinding(temp);
+ if (curve != null)
+ {
+ keys = curve.keys.Select(x => (double)x.time);
+ }
+ }
+
+ // Transform the times in to 'global' space using the clip
+ if (clip != null)
+ {
+ foreach (var k in keys)
+ {
+ var time = clip.FromLocalTimeUnbound(k);
+ const double eps = 1e-5;
+ if (time >= clip.start - eps && time <= clip.end + eps)
+ {
+ keyTimes.Add(time);
+ }
+ }
+ }
+ // infinite clip mode, global == local space
+ else
+ {
+ keyTimes.UnionWith(keys);
+ }
+ }
+ }
+
+ return keyTimes;
+ }
+
+ public static void NextKey(UnityEngine.Object target, IEnumerable<PropertyModification> modifications, WindowState state)
+ {
+ const double eps = 1e-5;
+ var keyTimes = GetKeyTimes(target, modifications, state);
+ if (keyTimes.Count == 0)
+ return;
+ var nextKeys = keyTimes.Where(x => x > state.editSequence.time + eps);
+ if (nextKeys.Any())
+ {
+ state.editSequence.time = nextKeys.Min();
+ }
+ }
+
+ public static void PrevKey(UnityEngine.Object target, IEnumerable<PropertyModification> modifications, WindowState state)
+ {
+ const double eps = 1e-5;
+ var keyTimes = GetKeyTimes(target, modifications, state);
+ if (keyTimes.Count == 0)
+ return;
+ var prevKeys = keyTimes.Where(x => x < state.editSequence.time - eps);
+ if (prevKeys.Any())
+ {
+ state.editSequence.time = prevKeys.Max();
+ }
+ }
+
+ public static void RemoveCurve(UnityEngine.Object target, IEnumerable<PropertyModification> modifications, WindowState state)
+ {
+ AnimationClip clip = null;
+ double keyTime = 0;
+ var inRange = false; // not used for curves
+ if (!GetClipAndRelativeTime(target, state, out clip, out keyTime, out inRange))
+ return;
+
+ TimelineUndo.PushUndo(clip, "Remove Curve");
+ foreach (var mod in modifications)
+ {
+ EditorCurveBinding temp;
+ if (HasBinding(target, mod, clip, out temp))
+ {
+ if (temp.isPPtrCurve)
+ AnimationUtility.SetObjectReferenceCurve(clip, temp, null);
+ else
+ AnimationUtility.SetEditorCurve(clip, temp, null);
+ }
+ }
+
+ state.ResetPreviewMode();
+ }
+
+ public static IEnumerable<GameObject> GetRecordableGameObjects(WindowState state)
+ {
+ if (state == null || state.editSequence.asset == null || state.editSequence.director == null)
+ yield break;
+
+ var outputTracks = state.editSequence.asset.GetOutputTracks();
+ foreach (var track in outputTracks)
+ {
+ if (track.GetType() != typeof(AnimationTrack))
+ continue;
+ if (!state.IsTrackRecordable(track) && !track.GetChildTracks().Any(state.IsTrackRecordable))
+ continue;
+
+ var obj = TimelineUtility.GetSceneGameObject(state.editSequence.director, track);
+ if (obj != null)
+ {
+ yield return obj;
+ }
+ }
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs.meta
new file mode 100644
index 0000000..d8bca18
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ef4c81c9368d5a340b14c2fec1cad345
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs
new file mode 100644
index 0000000..e55ff56
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs
@@ -0,0 +1,134 @@
+using UnityEditorInternal;
+using UnityEngine;
+using UnityEngine.Playables;
+using UnityEngine.Timeline;
+
+namespace UnityEditor.Timeline
+{
+ class TimelineRecordingContextualResponder : IAnimationContextualResponder
+ {
+ public WindowState state { get; internal set; }
+
+ public TimelineRecordingContextualResponder(WindowState _state)
+ {
+ state = _state;
+ }
+
+ //Unsupported stuff
+ public bool HasAnyCandidates() { return false; }
+ public bool HasAnyCurves() {return false; }
+ public void AddCandidateKeys() {}
+ public void AddAnimatedKeys() {}
+
+ public bool IsAnimatable(PropertyModification[] modifications)
+ {
+ // search playable assets
+ for (int i = 0; i < modifications.Length; i++)
+ {
+ var iAsset = modifications[i].target as IPlayableAsset;
+ if (iAsset != null)
+ {
+ var curvesOwner = AnimatedParameterUtility.ToCurvesOwner(iAsset, state.editSequence.asset);
+ if (curvesOwner != null && curvesOwner.HasAnyAnimatableParameters() && curvesOwner.IsParameterAnimatable(modifications[i].propertyPath))
+ return true;
+ }
+ }
+
+ // search recordable game objects
+ foreach (var gameObject in TimelineRecording.GetRecordableGameObjects(state))
+ {
+ for (int i = 0; i < modifications.Length; ++i)
+ {
+ var modification = modifications[i];
+ if (AnimationWindowUtility.PropertyIsAnimatable(modification.target, modification.propertyPath, gameObject))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool IsEditable(Object targetObject)
+ {
+ return true; // i.e. all animatable properties are editable
+ }
+
+ public bool KeyExists(PropertyModification[] modifications)
+ {
+ if (modifications.Length == 0 || modifications[0].target == null)
+ return false;
+
+ return TimelineRecording.HasKey(modifications, modifications[0].target, state);
+ }
+
+ public bool CandidateExists(PropertyModification[] modifications)
+ {
+ return true;
+ }
+
+ public bool CurveExists(PropertyModification[] modifications)
+ {
+ if (modifications.Length == 0 || modifications[0].target == null)
+ return false;
+
+ return TimelineRecording.HasCurve(modifications, modifications[0].target, state);
+ }
+
+ public void AddKey(PropertyModification[] modifications)
+ {
+ TimelineRecording.AddKey(modifications, state);
+ state.Refresh();
+ }
+
+ public void RemoveKey(PropertyModification[] modifications)
+ {
+ if (modifications.Length == 0)
+ return;
+
+ var target = modifications[0].target;
+ if (target == null)
+ return;
+
+ TimelineRecording.RemoveKey(modifications[0].target, modifications, state);
+
+ var curvesOwner = target as ICurvesOwner;
+ if (curvesOwner != null)
+ curvesOwner.SanitizeCurvesData();
+
+ state.Refresh();
+ }
+
+ public void RemoveCurve(PropertyModification[] modifications)
+ {
+ if (modifications.Length == 0)
+ return;
+
+ var target = modifications[0].target;
+ if (target == null)
+ return;
+
+ TimelineRecording.RemoveCurve(target, modifications, state);
+
+ var curvesOwner = target as ICurvesOwner;
+ if (curvesOwner != null)
+ curvesOwner.SanitizeCurvesData();
+
+ state.Refresh();
+ }
+
+ public void GoToNextKeyframe(PropertyModification[] modifications)
+ {
+ if (modifications.Length == 0 || modifications[0].target == null)
+ return;
+
+ TimelineRecording.NextKey(modifications[0].target, modifications, state);
+ state.Refresh();
+ }
+
+ public void GoToPreviousKeyframe(PropertyModification[] modifications)
+ {
+ TimelineRecording.PrevKey(modifications[0].target, modifications, state);
+ state.Refresh();
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs.meta
new file mode 100644
index 0000000..2e8e492
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecordingContextualResponder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a59c2e62fbd97f84f92c3b546e3903cb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs
new file mode 100644
index 0000000..0d2da17
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs
@@ -0,0 +1,709 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditorInternal;
+using UnityEngine;
+using UnityEngine.Timeline;
+using System.Globalization;
+
+namespace UnityEditor.Timeline
+{
+ // Methods and data for handling recording to monobehaviours
+ static partial class TimelineRecording
+ {
+ internal class RecordingState : IAnimationRecordingState
+ {
+ public GameObject activeGameObject { get; set; }
+ public GameObject activeRootGameObject { get; set; }
+ public AnimationClip activeAnimationClip { get; set; }
+
+ public void SaveCurve(AnimationWindowCurve curve)
+ {
+ Undo.RegisterCompleteObjectUndo(activeAnimationClip, "Edit Curve");
+ AnimationWindowUtility.SaveCurve(activeAnimationClip, curve);
+ }
+
+ public void AddPropertyModification(EditorCurveBinding binding, PropertyModification propertyModification, bool keepPrefabOverride)
+ {
+ AnimationMode.AddPropertyModification(binding, propertyModification, keepPrefabOverride);
+ }
+
+ public bool addZeroFrame
+ {
+ get { return false; }
+ }
+
+ public int currentFrame { get; set; }
+
+ public bool DiscardModification(PropertyModification modification)
+ {
+ return false;
+ }
+ }
+
+ static readonly RecordingState s_RecordState = new RecordingState();
+ static readonly AnimationTrackRecorder s_TrackRecorder = new AnimationTrackRecorder();
+ static readonly List<UndoPropertyModification> s_UnprocessedMods = new List<UndoPropertyModification>();
+ static readonly List<UndoPropertyModification> s_ModsToProcess = new List<UndoPropertyModification>();
+ static AnimationTrack s_LastTrackWarning;
+
+ public const string kLocalPosition = "m_LocalPosition";
+ public const string kLocalRotation = "m_LocalRotation";
+ public const string kLocalEulerHint = "m_LocalEulerAnglesHint";
+ const string kRotationWarning = "You are recording with an initial rotation offset. This may result in a misrepresentation of euler angles. When recording transform properties, it is recommended to reset rotation prior to recording";
+
+
+ public static bool IsRecordingAnimationTrack { get; private set; }
+
+
+ internal static UndoPropertyModification[] ProcessMonoBehaviourModification(UndoPropertyModification[] modifications, WindowState state)
+ {
+ if (state == null || state.editSequence.director == null)
+ return modifications;
+
+ s_UnprocessedMods.Clear();
+
+ s_TrackRecorder.PrepareForRecord(state);
+
+ s_ModsToProcess.Clear();
+ s_ModsToProcess.AddRange(modifications.Reverse());
+
+ while (s_ModsToProcess.Count > 0)
+ {
+ var modification = s_ModsToProcess[s_ModsToProcess.Count - 1];
+ s_ModsToProcess.RemoveAt(s_ModsToProcess.Count - 1);
+
+ // grab the clip we need to apply to
+ var modifiedGO = GetGameObjectFromModification(modification);
+ var track = GetTrackForGameObject(modifiedGO, state);
+ if (track != null)
+ {
+ IsRecordingAnimationTrack = true;
+
+ double startTime = 0;
+ var clip = s_TrackRecorder.PrepareTrack(track, state, modifiedGO, out startTime);
+ if (clip == null)
+ {
+ s_ModsToProcess.Reverse();
+ return s_ModsToProcess.ToArray();
+ }
+ s_RecordState.activeAnimationClip = clip;
+ s_RecordState.activeRootGameObject = state.GetSceneReference(track);
+ s_RecordState.activeGameObject = modifiedGO;
+ s_RecordState.currentFrame = Mathf.RoundToInt((float)startTime);
+
+ EditorUtility.SetDirty(clip);
+ var toProcess = GatherRelatedModifications(modification, s_ModsToProcess);
+
+ var animator = s_RecordState.activeRootGameObject.GetComponent<Animator>();
+ var animTrack = track as AnimationTrack;
+
+ // update preview mode before recording so the correct values get placed (in case we modify offsets)
+ // Case 900624
+ UpdatePreviewMode(toProcess, modifiedGO);
+
+ // if this is the first position/rotation recording, copy the current position / rotation to the track offset
+ AddTrackOffset(animTrack, toProcess, clip, animator);
+
+ // same for clip mod clips being created
+ AddClipOffset(animTrack, toProcess, s_TrackRecorder.recordClip, animator);
+
+ // Check if we need to handle position/rotation offsets
+ var handleOffsets = animator != null && modification.currentValue != null &&
+ modification.currentValue.target == s_RecordState.activeRootGameObject.transform &&
+ HasOffsets(animTrack, s_TrackRecorder.recordClip);
+ if (handleOffsets)
+ {
+ toProcess = HandleEulerModifications(animTrack, s_TrackRecorder.recordClip, clip, s_RecordState.currentFrame * clip.frameRate, toProcess);
+ RemoveOffsets(modification, animTrack, s_TrackRecorder.recordClip, toProcess);
+ }
+
+ var remaining = AnimationRecording.Process(s_RecordState, toProcess);
+ if (remaining != null && remaining.Length != 0)
+ {
+ s_UnprocessedMods.AddRange(remaining);
+ }
+
+ if (handleOffsets)
+ {
+ ReapplyOffsets(modification, animTrack, s_TrackRecorder.recordClip, toProcess);
+ }
+
+ s_TrackRecorder.FinializeTrack(track, state);
+
+ IsRecordingAnimationTrack = false;
+ }
+ else
+ {
+ s_UnprocessedMods.Add(modification);
+ }
+ }
+
+
+ s_TrackRecorder.FinalizeRecording(state);
+
+ return s_UnprocessedMods.ToArray();
+ }
+
+ internal static bool IsPosition(UndoPropertyModification modification)
+ {
+ if (modification.currentValue != null)
+ return modification.currentValue.propertyPath.StartsWith(kLocalPosition);
+ else if (modification.previousValue != null)
+ return modification.previousValue.propertyPath.StartsWith(kLocalPosition);
+ return false;
+ }
+
+ internal static bool IsRotation(UndoPropertyModification modification)
+ {
+ if (modification.currentValue != null)
+ return modification.currentValue.propertyPath.StartsWith(kLocalRotation) ||
+ modification.currentValue.propertyPath.StartsWith(kLocalEulerHint);
+ if (modification.previousValue != null)
+ return modification.previousValue.propertyPath.StartsWith(kLocalRotation) ||
+ modification.previousValue.propertyPath.StartsWith(kLocalEulerHint);
+ return false;
+ }
+
+ // Test if this modification position or rotation
+ internal static bool IsPositionOrRotation(UndoPropertyModification modification)
+ {
+ return IsPosition(modification) || IsRotation(modification);
+ }
+
+ internal static void UpdatePreviewMode(UndoPropertyModification[] mods, GameObject go)
+ {
+ if (mods.Any(x => IsPositionOrRotation(x) && IsRootModification(x)))
+ {
+ bool hasPosition = false;
+ bool hasRotation = false;
+
+ foreach (var mod in mods)
+ {
+ EditorCurveBinding binding = new EditorCurveBinding();
+ if (AnimationUtility.PropertyModificationToEditorCurveBinding(mod.previousValue, go, out binding) != null)
+ {
+ hasPosition |= IsPosition(mod);
+ hasRotation |= IsRotation(mod);
+ AnimationMode.AddPropertyModification(binding, mod.previousValue, true);
+ }
+ }
+
+ // case 931859 - if we are only changing one field, all fields must be registered before
+ // any recording modifications
+ var driver = WindowState.previewDriver;
+ if (driver != null && AnimationMode.InAnimationMode(driver))
+ {
+ if (hasPosition)
+ {
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalPosition + ".x");
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalPosition + ".y");
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalPosition + ".z");
+ }
+ else if (hasRotation)
+ {
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalRotation + ".x");
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalRotation + ".y");
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalRotation + ".z");
+ DrivenPropertyManager.RegisterProperty(driver, go.transform, kLocalRotation + ".w");
+ }
+ }
+ }
+ }
+
+ internal static bool IsRootModification(UndoPropertyModification modification)
+ {
+ string path = string.Empty;
+ if (modification.currentValue != null)
+ path = modification.currentValue.propertyPath;
+ else if (modification.previousValue != null)
+ path = modification.previousValue.propertyPath;
+
+ return !path.Contains('/') && !path.Contains('\\');
+ }
+
+ // test if the clip has any position or rotation bindings
+ internal static bool ClipHasPositionOrRotation(AnimationClip clip)
+ {
+ if (clip == null || clip.empty)
+ return false;
+
+ var info = AnimationClipCurveCache.Instance.GetCurveInfo(clip);
+ for (var i = 0; i < info.bindings.Length; i++)
+ {
+ bool isPositionOrRotation =
+ info.bindings[i].type != null &&
+ typeof(Transform).IsAssignableFrom(info.bindings[i].type) &&
+ (
+ info.bindings[i].propertyName.StartsWith(kLocalPosition) ||
+ info.bindings[i].propertyName.StartsWith(kLocalRotation) ||
+ info.bindings[i].propertyName.StartsWith("localEuler")
+ );
+
+ if (isPositionOrRotation)
+ return true;
+ }
+
+ return false;
+ }
+
+ internal static TimelineAnimationUtilities.RigidTransform ComputeInitialClipOffsets(AnimationTrack track, UndoPropertyModification[] mods, Animator animator)
+ {
+ // take into account the track transform
+ var target = GetInitialTransform(mods, animator);
+ var trackToClip = TimelineAnimationUtilities.RigidTransform.identity;
+ if (track.trackOffset == TrackOffset.ApplyTransformOffsets)
+ trackToClip = TimelineAnimationUtilities.RigidTransform.Compose(track.position, track.rotation);
+ else if (track.trackOffset == TrackOffset.ApplySceneOffsets)
+ trackToClip = TimelineAnimationUtilities.RigidTransform.Compose(track.sceneOffsetPosition, Quaternion.Euler(track.sceneOffsetRotation));
+
+ target = TimelineAnimationUtilities.RigidTransform.Mul(TimelineAnimationUtilities.RigidTransform.Inverse(trackToClip), target);
+
+ // set the previous position in case the animation system adds a default key
+ SetPreviousPositionAndRotation(mods, animator, trackToClip.position, trackToClip.rotation);
+ return target;
+ }
+
+ internal static TimelineAnimationUtilities.RigidTransform GetInitialTransform(UndoPropertyModification[] mods, Animator animator)
+ {
+ var pos = Vector3.zero;
+ var rot = Quaternion.identity;
+
+ // if we are operating on the root, grab the transform from the undo
+ if (mods[0].previousValue.target == animator.transform)
+ {
+ GetPreviousPositionAndRotation(mods, ref pos, ref rot);
+ }
+ // otherwise we need to grab it from the root object, which is the one with the actual animator
+ else
+ {
+ pos = animator.transform.localPosition;
+ rot = animator.transform.localRotation;
+ }
+
+ // take into account the track transform
+ return TimelineAnimationUtilities.RigidTransform.Compose(pos, rot);
+ }
+
+ internal static void SetPreviousPositionAndRotation(UndoPropertyModification[] mods, Animator animator, Vector3 pos, Quaternion rot)
+ {
+ if (mods[0].previousValue.target == animator.transform)
+ {
+ SetPreviousPositionAndRotation(mods, pos, rot);
+ }
+ }
+
+ // If we are adding to an infinite clip, strip the objects position and rotation and set it as the clip offset
+ internal static void AddTrackOffset(AnimationTrack track, UndoPropertyModification[] mods, AnimationClip clip, Animator animator)
+ {
+ var copyTrackOffset = !track.inClipMode &&
+ !ClipHasPositionOrRotation(clip) &&
+ mods.Any(x => IsPositionOrRotation(x) && IsRootModification(x)) &&
+ animator != null;
+ if (copyTrackOffset)
+ {
+ // in scene offset mode, makes sure we have the correct initial transform set
+ if (track.trackOffset == TrackOffset.ApplySceneOffsets)
+ {
+ var rigidTransform = GetInitialTransform(mods, animator);
+ track.sceneOffsetPosition = rigidTransform.position;
+ track.sceneOffsetRotation = rigidTransform.rotation.eulerAngles;
+ SetPreviousPositionAndRotation(mods, animator, rigidTransform.position, rigidTransform.rotation);
+ }
+ else
+ {
+ var rigidTransform = ComputeInitialClipOffsets(track, mods, animator);
+ track.infiniteClipOffsetPosition = rigidTransform.position;
+ track.infiniteClipOffsetEulerAngles = rigidTransform.rotation.eulerAngles;
+ }
+ }
+ }
+
+ internal static void AddClipOffset(AnimationTrack track, UndoPropertyModification[] mods, TimelineClip clip, Animator animator)
+ {
+ if (clip == null || clip.asset == null)
+ return;
+
+ var clipAsset = clip.asset as AnimationPlayableAsset;
+ var copyClipOffset = track.inClipMode &&
+ clipAsset != null && !ClipHasPositionOrRotation(clipAsset.clip) &&
+ mods.Any(x => IsPositionOrRotation(x) && IsRootModification(x)) &&
+ animator != null;
+ if (copyClipOffset)
+ {
+ var rigidTransform = ComputeInitialClipOffsets(track, mods, animator);
+
+ clipAsset.position = rigidTransform.position;
+ clipAsset.rotation = rigidTransform.rotation;
+ }
+ }
+
+ internal static TimelineAnimationUtilities.RigidTransform GetLocalToTrack(AnimationTrack track, TimelineClip clip)
+ {
+ if (track == null)
+ return TimelineAnimationUtilities.RigidTransform.Compose(Vector3.zero, Quaternion.identity);
+
+ var trackPos = track.position;
+ var trackRot = track.rotation;
+
+ if (track.trackOffset == TrackOffset.ApplySceneOffsets)
+ {
+ trackPos = track.sceneOffsetPosition;
+ trackRot = Quaternion.Euler(track.sceneOffsetRotation);
+ }
+
+ var clipWrapper = clip == null ? null : clip.asset as AnimationPlayableAsset;
+ var clipTransform = TimelineAnimationUtilities.RigidTransform.Compose(Vector3.zero, Quaternion.identity);
+ if (clipWrapper != null)
+ {
+ clipTransform = TimelineAnimationUtilities.RigidTransform.Compose(clipWrapper.position, clipWrapper.rotation);
+ }
+ else
+ {
+ clipTransform = TimelineAnimationUtilities.RigidTransform.Compose(track.infiniteClipOffsetPosition, track.infiniteClipOffsetRotation);
+ }
+
+ var trackTransform = TimelineAnimationUtilities.RigidTransform.Compose(trackPos, trackRot);
+
+ return TimelineAnimationUtilities.RigidTransform.Mul(trackTransform, clipTransform);
+ }
+
+ // Checks whether there are any offsets applied to a clip
+ internal static bool HasOffsets(AnimationTrack track, TimelineClip clip)
+ {
+ if (track == null)
+ return false;
+
+ bool hasClipOffsets = false;
+ bool hasTrackOffsets = false;
+
+ var clipWrapper = clip == null ? null : clip.asset as AnimationPlayableAsset;
+ if (clipWrapper != null)
+ hasClipOffsets |= clipWrapper.position != Vector3.zero || clipWrapper.rotation != Quaternion.identity;
+
+ if (track.trackOffset == TrackOffset.ApplySceneOffsets)
+ {
+ hasTrackOffsets = track.sceneOffsetPosition != Vector3.zero || track.sceneOffsetRotation != Vector3.zero;
+ }
+ else
+ {
+ hasTrackOffsets = (track.position != Vector3.zero || track.rotation != Quaternion.identity);
+ if (!track.inClipMode)
+ hasClipOffsets |= track.infiniteClipOffsetPosition != Vector3.zero || track.infiniteClipOffsetRotation != Quaternion.identity;
+ }
+
+ return hasTrackOffsets || hasClipOffsets;
+ }
+
+ internal static void RemoveOffsets(UndoPropertyModification modification, AnimationTrack track, TimelineClip clip, UndoPropertyModification[] mods)
+ {
+ if (IsPositionOrRotation(modification))
+ {
+ var modifiedGO = GetGameObjectFromModification(modification);
+ var target = TimelineAnimationUtilities.RigidTransform.Compose(modifiedGO.transform.localPosition, modifiedGO.transform.localRotation);
+ var localToTrack = GetLocalToTrack(track, clip);
+ var trackToLocal = TimelineAnimationUtilities.RigidTransform.Inverse(localToTrack);
+ var localSpace = TimelineAnimationUtilities.RigidTransform.Mul(trackToLocal, target);
+
+ // Update the undo call values
+ var prevPos = modifiedGO.transform.localPosition;
+ var prevRot = modifiedGO.transform.localRotation;
+ GetPreviousPositionAndRotation(mods, ref prevPos, ref prevRot);
+ var previousRigidTransform = TimelineAnimationUtilities.RigidTransform.Mul(trackToLocal, TimelineAnimationUtilities.RigidTransform.Compose(prevPos, prevRot));
+ SetPreviousPositionAndRotation(mods, previousRigidTransform.position, previousRigidTransform.rotation);
+
+ var currentPos = modifiedGO.transform.localPosition;
+ var currentRot = modifiedGO.transform.localRotation;
+ GetCurrentPositionAndRotation(mods, ref currentPos, ref currentRot);
+ var currentRigidTransform = TimelineAnimationUtilities.RigidTransform.Mul(trackToLocal, TimelineAnimationUtilities.RigidTransform.Compose(currentPos, currentRot));
+ SetCurrentPositionAndRotation(mods, currentRigidTransform.position, currentRigidTransform.rotation);
+
+ modifiedGO.transform.localPosition = localSpace.position;
+ modifiedGO.transform.localRotation = localSpace.rotation;
+ }
+ }
+
+ internal static void ReapplyOffsets(UndoPropertyModification modification, AnimationTrack track, TimelineClip clip, UndoPropertyModification[] mods)
+ {
+ if (IsPositionOrRotation(modification))
+ {
+ var modifiedGO = GetGameObjectFromModification(modification);
+ var target = TimelineAnimationUtilities.RigidTransform.Compose(modifiedGO.transform.localPosition, modifiedGO.transform.localRotation);
+ var localToTrack = GetLocalToTrack(track, clip);
+ var trackSpace = TimelineAnimationUtilities.RigidTransform.Mul(localToTrack, target);
+
+ // Update the undo call values
+ var prevPos = modifiedGO.transform.localPosition;
+ var prevRot = modifiedGO.transform.localRotation;
+ GetPreviousPositionAndRotation(mods, ref prevPos, ref prevRot);
+ var previousRigidTransform = TimelineAnimationUtilities.RigidTransform.Mul(localToTrack, TimelineAnimationUtilities.RigidTransform.Compose(prevPos, prevRot));
+ SetPreviousPositionAndRotation(mods, previousRigidTransform.position, previousRigidTransform.rotation);
+
+ var currentPos = modifiedGO.transform.localPosition;
+ var currentRot = modifiedGO.transform.localRotation;
+ GetCurrentPositionAndRotation(mods, ref currentPos, ref currentRot);
+ var currentRigidTransform = TimelineAnimationUtilities.RigidTransform.Mul(localToTrack, TimelineAnimationUtilities.RigidTransform.Compose(currentPos, currentRot));
+ SetCurrentPositionAndRotation(mods, currentRigidTransform.position, currentRigidTransform.rotation);
+
+ modifiedGO.transform.localPosition = trackSpace.position;
+ modifiedGO.transform.localRotation = trackSpace.rotation;
+ }
+ }
+
+ // This will gather the modifications that modify the same property on the same object (rgba of a color, xyzw of a vector)
+ // Note: This will modify the list, removing any elements that match
+ static UndoPropertyModification[] GatherRelatedModifications(UndoPropertyModification toMatch, List<UndoPropertyModification> list)
+ {
+ var matching = new List<UndoPropertyModification> {toMatch};
+
+ for (var i = list.Count - 1; i >= 0; i--)
+ {
+ var undo = list[i];
+ if (undo.previousValue.target == toMatch.previousValue.target &&
+ DoesPropertyPathMatch(undo.previousValue.propertyPath, toMatch.previousValue.propertyPath))
+ {
+ matching.Add(undo);
+ list.RemoveAt(i);
+ }
+ }
+
+ return matching.ToArray();
+ }
+
+ // Grab the game object out of the modification object
+ static GameObject GetGameObjectFromModification(UndoPropertyModification mod)
+ {
+ // grab the GO this is modifying
+ GameObject modifiedGO = null;
+ if (mod.previousValue.target is GameObject)
+ modifiedGO = mod.previousValue.target as GameObject;
+ else if (mod.previousValue.target is Component)
+ modifiedGO = (mod.previousValue.target as Component).gameObject;
+
+ return modifiedGO;
+ }
+
+ // returns the level of the child in the hierarchy relative to the parent,
+ // or -1 if the child is not the parent or a descendent of it
+ static int GetChildLevel(GameObject parent, GameObject child)
+ {
+ var level = 0;
+ while (child != null)
+ {
+ if (parent == child)
+ break;
+ if (child.transform.parent == null)
+ return -1;
+ child = child.transform.parent.gameObject;
+ level++;
+ }
+
+ if (child != null)
+ return level;
+ return -1;
+ }
+
+ static bool DoesPropertyPathMatch(string a, string b)
+ {
+ return AnimationWindowUtility.GetPropertyGroupName(a).Equals(AnimationWindowUtility.GetPropertyGroupName(a));
+ }
+
+ internal static void GetPreviousPositionAndRotation(UndoPropertyModification[] mods, ref Vector3 position, ref Quaternion rotation)
+ {
+ var t = mods[0].previousValue.target as Transform;
+ if (t == null)
+ t = (Transform)mods[0].currentValue.target;
+
+ position = t.localPosition;
+ rotation = t.localRotation;
+
+ foreach (var mod in mods)
+ {
+ switch (mod.previousValue.propertyPath)
+ {
+ case kLocalPosition + ".x":
+ position.x = ParseFloat(mod.previousValue.value, position.x);
+ break;
+ case kLocalPosition + ".y":
+ position.y = ParseFloat(mod.previousValue.value, position.y);
+ break;
+ case kLocalPosition + ".z":
+ position.z = ParseFloat(mod.previousValue.value, position.z);
+ break;
+ case kLocalRotation + ".x":
+ rotation.x = ParseFloat(mod.previousValue.value, rotation.x);
+ break;
+ case kLocalRotation + ".y":
+ rotation.y = ParseFloat(mod.previousValue.value, rotation.y);
+ break;
+ case kLocalRotation + ".z":
+ rotation.z = ParseFloat(mod.previousValue.value, rotation.z);
+ break;
+ case kLocalRotation + ".w":
+ rotation.w = ParseFloat(mod.previousValue.value, rotation.w);
+ break;
+ }
+ }
+ }
+
+ internal static void GetCurrentPositionAndRotation(UndoPropertyModification[] mods, ref Vector3 position, ref Quaternion rotation)
+ {
+ var t = (Transform)mods[0].currentValue.target;
+ position = t.localPosition;
+ rotation = t.localRotation;
+
+ foreach (var mod in mods)
+ {
+ switch (mod.currentValue.propertyPath)
+ {
+ case kLocalPosition + ".x":
+ position.x = ParseFloat(mod.currentValue.value, position.x);
+ break;
+ case kLocalPosition + ".y":
+ position.y = ParseFloat(mod.currentValue.value, position.y);
+ break;
+ case kLocalPosition + ".z":
+ position.z = ParseFloat(mod.currentValue.value, position.z);
+ break;
+ case kLocalRotation + ".x":
+ rotation.x = ParseFloat(mod.currentValue.value, rotation.x);
+ break;
+ case kLocalRotation + ".y":
+ rotation.y = ParseFloat(mod.currentValue.value, rotation.y);
+ break;
+ case kLocalRotation + ".z":
+ rotation.z = ParseFloat(mod.currentValue.value, rotation.z);
+ break;
+ case kLocalRotation + ".w":
+ rotation.w = ParseFloat(mod.currentValue.value, rotation.w);
+ break;
+ }
+ }
+ }
+
+ // when making the previous position and rotation
+ internal static void SetPreviousPositionAndRotation(UndoPropertyModification[] mods, Vector3 pos, Quaternion rot)
+ {
+ foreach (var mod in mods)
+ {
+ switch (mod.previousValue.propertyPath)
+ {
+ case kLocalPosition + ".x":
+ mod.previousValue.value = pos.x.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalPosition + ".y":
+ mod.previousValue.value = pos.y.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalPosition + ".z":
+ mod.previousValue.value = pos.z.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".x":
+ mod.previousValue.value = rot.x.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".y":
+ mod.previousValue.value = rot.y.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".z":
+ mod.previousValue.value = rot.z.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".w":
+ mod.previousValue.value = rot.w.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ }
+ }
+ }
+
+ internal static void SetCurrentPositionAndRotation(UndoPropertyModification[] mods, Vector3 pos, Quaternion rot)
+ {
+ foreach (var mod in mods)
+ {
+ switch (mod.previousValue.propertyPath)
+ {
+ case kLocalPosition + ".x":
+ mod.currentValue.value = pos.x.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalPosition + ".y":
+ mod.currentValue.value = pos.y.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalPosition + ".z":
+ mod.currentValue.value = pos.z.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".x":
+ mod.currentValue.value = rot.x.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".y":
+ mod.currentValue.value = rot.y.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".z":
+ mod.currentValue.value = rot.z.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ case kLocalRotation + ".w":
+ mod.currentValue.value = rot.w.ToString(EditorGUI.kFloatFieldFormatString);
+ break;
+ }
+ }
+ }
+
+ internal static float ParseFloat(string str, float defaultVal)
+ {
+ float temp = 0.0f;
+ if (float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out temp))
+ return temp;
+ return defaultVal;
+ }
+
+ internal static UndoPropertyModification[] HandleEulerModifications(AnimationTrack track, TimelineClip clip, AnimationClip animClip, float time, UndoPropertyModification[] mods)
+ {
+ if (mods.Any(x => x.currentValue.propertyPath.StartsWith(kLocalEulerHint) || x.currentValue.propertyPath.StartsWith(kLocalRotation)))
+ {
+ // if there is a rotational offsets, we need to strip the euler hints, since they are used by the animation recording system
+ // over the quaternion.
+ var localToTrack = GetLocalToTrack(track, clip);
+ if (localToTrack.rotation != Quaternion.identity)
+ {
+ if (s_LastTrackWarning != track)
+ {
+ s_LastTrackWarning = track;
+ Debug.LogWarning(kRotationWarning);
+ }
+
+ Transform transform = mods[0].currentValue.target as Transform;
+ if (transform != null)
+ {
+ var trackToLocal = TimelineAnimationUtilities.RigidTransform.Inverse(localToTrack);
+ // since the euler angles are going to be transformed, we do a best guess at a euler that gives the shortest path
+ var quatMods = mods.Where(x => !x.currentValue.propertyPath.StartsWith(kLocalEulerHint));
+ var eulerMods = FindBestEulerHint(trackToLocal.rotation * transform.localRotation, animClip, time, transform);
+ return quatMods.Union(eulerMods).ToArray();
+ }
+ return mods.Where(x => !x.currentValue.propertyPath.StartsWith(kLocalEulerHint)).ToArray();
+ }
+ }
+ return mods;
+ }
+
+ internal static IEnumerable<UndoPropertyModification> FindBestEulerHint(Quaternion rotation, AnimationClip clip, float time, Transform transform)
+ {
+ Vector3 euler = rotation.eulerAngles;
+
+ var xCurve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(string.Empty, typeof(Transform), "localEulerAnglesRaw.x"));
+ var yCurve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(string.Empty, typeof(Transform), "localEulerAnglesRaw.y"));
+ var zCurve = AnimationUtility.GetEditorCurve(clip, EditorCurveBinding.FloatCurve(string.Empty, typeof(Transform), "localEulerAnglesRaw.z"));
+
+ if (xCurve != null)
+ euler.x = xCurve.Evaluate(time);
+ if (yCurve != null)
+ euler.y = yCurve.Evaluate(time);
+ if (zCurve != null)
+ euler.z = zCurve.Evaluate(time);
+
+ euler = QuaternionCurveTangentCalculation.GetEulerFromQuaternion(rotation, euler);
+
+ return new[]
+ {
+ PropertyModificationToUndoPropertyModification(new PropertyModification {target = transform, propertyPath = kLocalEulerHint + ".x", value = euler.x.ToString() }),
+ PropertyModificationToUndoPropertyModification(new PropertyModification {target = transform, propertyPath = kLocalEulerHint + ".y", value = euler.y.ToString() }),
+ PropertyModificationToUndoPropertyModification(new PropertyModification {target = transform, propertyPath = kLocalEulerHint + ".z", value = euler.z.ToString() })
+ };
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs.meta
new file mode 100644
index 0000000..346e5bd
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_Monobehaviour.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9805855c8e379ed4cad77f639aaddb73
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs
new file mode 100644
index 0000000..940854e
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs
@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Timeline;
+using UnityEngine.Playables;
+
+namespace UnityEditor.Timeline
+{
+ // Handles Undo animated properties on PlayableAssets from clips to create parameter animation
+
+ static partial class TimelineRecording
+ {
+ internal static bool HasAnyPlayableAssetModifications(UndoPropertyModification[] modifications)
+ {
+ return modifications.Any(x => GetTarget(x) as IPlayableAsset != null);
+ }
+
+ internal static UndoPropertyModification[] ProcessPlayableAssetModification(UndoPropertyModification[] modifications, WindowState state)
+ {
+ // can't record without a director since the asset being modified might be a scene instance
+ if (state == null || state.editSequence.director == null)
+ return modifications;
+
+ var remaining = new List<UndoPropertyModification>();
+ foreach (UndoPropertyModification mod in modifications)
+ {
+ if (!ProcessPlayableAssetModification(mod, state))
+ remaining.Add(mod);
+ }
+
+ if (remaining.Count != modifications.Length)
+ {
+ state.rebuildGraph = true;
+ state.GetWindow().Repaint();
+ }
+
+ return remaining.ToArray();
+ }
+
+ static bool ProcessPlayableAssetModification(UndoPropertyModification mod, WindowState state)
+ {
+ var target = GetTarget(mod) as IPlayableAsset;
+ if (target == null)
+ return false;
+
+ var curvesOwner = AnimatedParameterUtility.ToCurvesOwner(target, state.editSequence.asset);
+ if (curvesOwner == null || !curvesOwner.HasAnyAnimatableParameters())
+ return false;
+
+ return ProcessPlayableAssetRecording(mod, state, curvesOwner);
+ }
+
+ internal static TimelineClip FindClipWithAsset(TimelineAsset asset, IPlayableAsset target)
+ {
+ if (target == null || asset == null)
+ return null;
+
+ var clips = asset.flattenedTracks.SelectMany(x => x.clips);
+ return clips.FirstOrDefault(x => x != null && x.asset != null && target == x.asset as IPlayableAsset);
+ }
+
+ static bool ProcessPlayableAssetRecording(UndoPropertyModification mod, WindowState state, ICurvesOwner curvesOwner)
+ {
+ if (mod.currentValue == null)
+ return false;
+
+ if (!curvesOwner.IsParameterAnimatable(mod.currentValue.propertyPath))
+ return false;
+
+ var localTime = state.editSequence.time;
+ var timelineClip = curvesOwner as TimelineClip;
+ if (timelineClip != null)
+ {
+ // don't use time global to local since it will possibly loop.
+ localTime = timelineClip.ToLocalTimeUnbound(state.editSequence.time);
+ }
+
+ if (localTime < 0)
+ return false;
+
+ // grab the value from the current modification
+ float fValue = 0;
+ if (!float.TryParse(mod.currentValue.value, out fValue))
+ {
+ // case 916913 -- 'Add Key' menu item will passes 'True' or 'False' (instead of 1, 0)
+ // so we need a special case to parse the boolean string
+ bool bValue;
+ if (!bool.TryParse(mod.currentValue.value, out bValue))
+ {
+ Debug.Assert(false, "Invalid type in PlayableAsset recording");
+ return false;
+ }
+
+ fValue = bValue ? 1 : 0;
+ }
+
+ var added = curvesOwner.AddAnimatedParameterValueAt(mod.currentValue.propertyPath, fValue, (float)localTime);
+ if (added && AnimationMode.InAnimationMode())
+ {
+ EditorCurveBinding binding = curvesOwner.GetCurveBinding(mod.previousValue.propertyPath);
+ AnimationMode.AddPropertyModification(binding, mod.previousValue, true);
+ curvesOwner.targetTrack.SetShowInlineCurves(true);
+ if (state.GetWindow() != null && state.GetWindow().treeView != null)
+ state.GetWindow().treeView.CalculateRowRects();
+ }
+
+ return added;
+ }
+
+ static bool IsPlayableAssetProperty(SerializedProperty property)
+ {
+ return property.serializedObject.targetObject is IPlayableAsset;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs.meta
new file mode 100644
index 0000000..e78220d
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TimelineRecording_PlayableAsset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7341c0cd0aad4994e8fa461cb443aa7d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs
new file mode 100644
index 0000000..f44b82b
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs
@@ -0,0 +1,191 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Playables;
+using UnityEngine.Timeline;
+
+namespace UnityEditor.Timeline
+{
+ static class TrackAssetRecordingExtensions
+ {
+ static readonly Dictionary<TrackAsset, AnimationClip> s_ActiveClips = new Dictionary<TrackAsset, AnimationClip>();
+
+ internal static void OnRecordingArmed(this TrackAsset track, PlayableDirector director)
+ {
+ if (track == null)
+ return;
+
+ var animClip = track.FindRecordingAnimationClipAtTime(director.time);
+ if (animClip == null)
+ return;
+
+ s_ActiveClips[track] = animClip;
+ }
+
+ internal static void OnRecordingTimeChanged(this TrackAsset track, PlayableDirector director)
+ {
+ if (track == null)
+ return;
+
+ var animClip = track.FindRecordingAnimationClipAtTime(director.time);
+ AnimationClip prevClip = track.GetActiveRecordingAnimationClip();
+ if (prevClip != animClip)
+ {
+ s_ActiveClips[track] = animClip;
+ }
+ }
+
+ internal static void OnRecordingUnarmed(this TrackAsset track, PlayableDirector director)
+ {
+ s_ActiveClips.Remove(track);
+ }
+
+ internal static bool CanRecordAtTime(this TrackAsset track, double time)
+ {
+ // Animation Track
+ var animTrack = track as AnimationTrack;
+ if (animTrack != null)
+ {
+ if (!animTrack.inClipMode)
+ return true;
+
+ TimelineClip clip = null;
+ return FindRecordingClipAtTime(track, time, out clip);
+ }
+
+ // Custom track
+ return track.clips.Any(x => x.start < time + TimeUtility.kTimeEpsilon && x.HasAnyAnimatableParameters());
+ }
+
+ internal static AnimationClip GetActiveRecordingAnimationClip(this TrackAsset track)
+ {
+ AnimationClip clip = null;
+ s_ActiveClips.TryGetValue(track, out clip);
+ return clip;
+ }
+
+ internal static bool IsRecordingToClip(this TrackAsset track, TimelineClip clip)
+ {
+ if (track == null || clip == null)
+ return false;
+ var animClip = track.GetActiveRecordingAnimationClip();
+ if (animClip == null)
+ return false;
+ if (animClip == clip.curves)
+ return true;
+
+ var animAsset = clip.asset as AnimationPlayableAsset;
+ return animAsset != null && animClip == animAsset.clip;
+ }
+
+ // Finds the clip at the given time that recording should use
+ // returns whether recording at this particular point is valid
+ // The target clip will be returned, even if recording at that time is invalid
+ // in case of recording in a blend OR recording to a non-timeline clip
+ internal static bool FindRecordingClipAtTime(this TrackAsset track, double time, out TimelineClip target)
+ {
+ target = null;
+ if (track == null)
+ {
+ return false;
+ }
+
+ var discreteTime = new DiscreteTime(time);
+
+ // only animation tracks require the recordable flag as they are recording
+ // to an animation clip
+ bool requiresRecordable = (track as AnimationTrack) != null;
+ if (requiresRecordable)
+ {
+ track.SortClips();
+ var sortedByStartTime = track.clips;
+ int i = 0;
+ for (i = 0; i < sortedByStartTime.Length; i++)
+ {
+ var clip = sortedByStartTime[i];
+ if (new DiscreteTime(clip.start) <= discreteTime && new DiscreteTime(clip.end) > discreteTime)
+ {
+ target = clip;
+ // not recordable
+ if (!clip.recordable)
+ return false;
+
+ // in a blend
+ if (!Mathf.Approximately(1.0f, clip.EvaluateMixIn(time) * clip.EvaluateMixOut(time)))
+ return false;
+
+ return true;
+ }
+
+ if (new DiscreteTime(clip.start) > discreteTime)
+ {
+ break;
+ }
+ }
+
+ return false;
+ }
+
+
+ // Recordable playable assets -- takes the last clip that matches
+ track.SortClips();
+ for (int i = 0; i < track.clips.Length; i++)
+ {
+ var clip = track.clips[i];
+ if (clip.start <= time && clip.end >= time && clip.HasAnyAnimatableParameters())
+ target = clip;
+
+ if (clip.start > time)
+ break;
+ }
+
+ return target != null;
+ }
+
+ // Given a track, return the animation clip
+ internal static AnimationClip FindRecordingAnimationClipAtTime(this TrackAsset trackAsset, double time)
+ {
+ if (trackAsset == null)
+ return null;
+
+ AnimationTrack animTrack = trackAsset as AnimationTrack;
+ if (animTrack != null && !animTrack.inClipMode)
+ {
+ return animTrack.infiniteClip;
+ }
+
+ TimelineClip displayBackground;
+ trackAsset.FindRecordingClipAtTime(time, out displayBackground);
+ if (displayBackground != null)
+ {
+ if (displayBackground.recordable)
+ {
+ AnimationPlayableAsset asset = displayBackground.asset as AnimationPlayableAsset;
+ if (asset != null)
+ return asset.clip;
+ }
+ else if (animTrack == null)
+ {
+ if (displayBackground.curves == null)
+ displayBackground.CreateCurves(AnimationTrackRecorder.GetUniqueRecordedClipName(displayBackground.parentTrack, TimelineClip.kDefaultCurvesName));
+
+ return displayBackground.curves;
+ }
+ }
+ else if (trackAsset.HasAnyAnimatableParameters())
+ {
+ if (trackAsset.curves == null)
+ trackAsset.CreateCurves(AnimationTrackRecorder.GetUniqueRecordedClipName(trackAsset.timelineAsset, TrackAsset.kDefaultCurvesName));
+
+ return trackAsset.curves;
+ }
+
+ return null;
+ }
+
+ internal static void ClearRecordingState()
+ {
+ s_ActiveClips.Clear();
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs.meta b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs.meta
new file mode 100644
index 0000000..edbe4da
--- /dev/null
+++ b/Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording/TrackAssetRecordingExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 49f1d2c7420db4444b011955726d0046
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: