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