From c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Sun, 19 Apr 2020 17:19:32 -0400 Subject: Inital commit --- .../Editor/Animation/TimelineAnimationUtilities.cs | 435 +++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 Library/PackageCache/com.unity.timeline@1.2.13/Editor/Animation/TimelineAnimationUtilities.cs (limited to 'Library/PackageCache/com.unity.timeline@1.2.13/Editor/Animation/TimelineAnimationUtilities.cs') 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(); + var timeController = ScriptableObject.CreateInstance(); + timeController.Init(animationWindow.state, clip); + return timeController; + } + + public static TimelineWindowTimeControl CreateTimeController(WindowState state, TimelineWindowTimeControl.ClipData clipData) + { + var animationWindow = EditorWindow.GetWindow(); + var timeController = ScriptableObject.CreateInstance(); + timeController.Init(animationWindow.state, clipData); + return timeController; + } + + public static void EditAnimationClipWithTimeController(AnimationClip animationClip, TimelineWindowTimeControl timeController, Object sourceObject) + { + var animationWindow = EditorWindow.GetWindow(); + animationWindow.EditSequencerClip(animationClip, sourceObject, timeController); + } + + public static void UnlinkAnimationWindowFromTracks(IEnumerable tracks) + { + var clips = new List(); + 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 timelineClips) + { + var clips = new List(); + GetAnimationClips(timelineClips, clips); + UnlinkAnimationWindowFromAnimationClips(clips); + } + + public static void UnlinkAnimationWindowFromAnimationClips(ICollection clips) + { + if (clips.Count == 0) + return; + + UnityEngine.Object[] windows = Resources.FindObjectsOfTypeAll(typeof(AnimationWindow)); + foreach (var animWindow in windows.OfType()) + { + 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()) + { + if (animWindow != null && animWindow.state != null && animWindow.state.linkedWithSequencer) + animWindow.UnlinkSequencer(); + } + } + + private static void GetAnimationClips(IEnumerable timelineClips, List 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(); + if (animationWindow) + return animationWindow.state.currentFrame; + return -1; + } + + public static void SetAnimationWindowCurrentFrame(int frame) + { + var animationWindow = EditorWindow.GetWindow(); + 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; + } + } +} -- cgit v1.2.3