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.GetParentTrack(), 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();
}
}
}