diff options
| author | Andrew Lee <alee14498@protonmail.com> | 2020-04-19 17:19:32 -0400 |
|---|---|---|
| committer | Andrew Lee <alee14498@protonmail.com> | 2020-04-19 17:19:32 -0400 |
| commit | c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 (patch) | |
| tree | ee4d51c7c1d633e11f46453ef1edd3c77c4ef9f7 /Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording | |
| download | Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.gz Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.bz2 Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.zip | |
Inital commit
Diffstat (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/Recording')
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: |
