Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -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.GetParentTrack().clips)
|
||||
{
|
||||
if (c.start < clip.start && (previousClip == null || c.start >= previousClip.start))
|
||||
previousClip = c;
|
||||
}
|
||||
return previousClip;
|
||||
}
|
||||
|
||||
public static TimelineClip GetNextClip(TimelineClip clip)
|
||||
{
|
||||
return clip.GetParentTrack().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.GetParentTrack() 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.GetParentTrack() 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.GetParentTrack() 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(TimelineClip clip)
|
||||
{
|
||||
var animationWindow = EditorWindow.GetWindow<AnimationWindow>();
|
||||
var timeController = ScriptableObject.CreateInstance<TimelineWindowTimeControl>();
|
||||
timeController.Init(animationWindow.state, clip);
|
||||
return timeController;
|
||||
}
|
||||
|
||||
public static TimelineWindowTimeControl CreateTimeController(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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue