159 lines
5.8 KiB
C#
159 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Animations;
|
|
using UnityEngine.Playables;
|
|
|
|
namespace UnityEngine.Timeline
|
|
{
|
|
public partial class TimelinePlayable
|
|
{
|
|
readonly Dictionary<AnimationTrack, List<ITimelineEvaluateCallback>> m_EvaluateCallbacks = new Dictionary<AnimationTrack, List<ITimelineEvaluateCallback>>();
|
|
readonly List<ITimelineEvaluateCallback> m_AlwaysEvaluateCallbacks = new List<ITimelineEvaluateCallback>();
|
|
readonly HashSet<ITimelineEvaluateCallback> m_ForceEvaluateNextEvaluate = new HashSet<ITimelineEvaluateCallback>();
|
|
readonly HashSet<ITimelineEvaluateCallback> m_InvokedThisFrame = new HashSet<ITimelineEvaluateCallback>();
|
|
readonly HashSet<AnimationTrack> m_ActiveTracksToEvaluateCache = new HashSet<AnimationTrack>();
|
|
|
|
readonly struct TrackCacheManager : IDisposable
|
|
{
|
|
public readonly HashSet<AnimationTrack> trackCache;
|
|
|
|
public TrackCacheManager(HashSet<AnimationTrack> cache, IReadOnlyList<RuntimeElement> activeRuntimeElements)
|
|
{
|
|
trackCache = cache;
|
|
GetTrackAssetsFromRuntimeElements(activeRuntimeElements);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
trackCache.Clear();
|
|
}
|
|
|
|
void GetTrackAssetsFromRuntimeElements(IReadOnlyList<RuntimeElement> activeRuntimeElements)
|
|
{
|
|
for (int index = 0; index < activeRuntimeElements.Count; index++)
|
|
{
|
|
if (activeRuntimeElements[index] is RuntimeClip rc)
|
|
{
|
|
if (rc.clip?.GetParentTrack() is AnimationTrack asset)
|
|
{
|
|
trackCache.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddPlayableOutputCallbacks(AnimationTrack track, PlayableOutput playableOutput)
|
|
{
|
|
AddOutputWeightProcessor(track, (AnimationPlayableOutput)playableOutput);
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying)
|
|
AddPreviewUpdateCallback(track, (AnimationPlayableOutput)playableOutput);
|
|
#endif
|
|
}
|
|
|
|
void AddOutputWeightProcessor(AnimationTrack track, AnimationPlayableOutput animOutput)
|
|
{
|
|
var processor = new AnimationOutputWeightProcessor(animOutput);
|
|
if (track.inClipMode)
|
|
AddEvaluateCallback(track, processor);
|
|
else
|
|
m_AlwaysEvaluateCallbacks.Add(processor);
|
|
|
|
m_ForceEvaluateNextEvaluate.Add(processor);
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
void AddPreviewUpdateCallback(AnimationTrack track, AnimationPlayableOutput animOutput)
|
|
{
|
|
var callback = new AnimationPreviewUpdateCallback(animOutput);
|
|
if (track.inClipMode)
|
|
AddEvaluateCallback(track, callback);
|
|
else
|
|
m_AlwaysEvaluateCallbacks.Add(callback);
|
|
|
|
m_ForceEvaluateNextEvaluate.Add(callback);
|
|
}
|
|
#endif
|
|
|
|
void AddEvaluateCallback(AnimationTrack track, ITimelineEvaluateCallback callback)
|
|
{
|
|
if (m_EvaluateCallbacks.TryGetValue(track, out var list))
|
|
{
|
|
list.Add(callback);
|
|
}
|
|
else
|
|
{
|
|
m_EvaluateCallbacks[track] = new List<ITimelineEvaluateCallback> { callback };
|
|
}
|
|
}
|
|
|
|
void InvokeOutputCallbacks(IReadOnlyList<RuntimeElement> activeRuntimeElements)
|
|
{
|
|
foreach (var callback in m_ForceEvaluateNextEvaluate)
|
|
{
|
|
callback.Evaluate();
|
|
m_InvokedThisFrame.Add(callback);
|
|
}
|
|
|
|
m_ForceEvaluateNextEvaluate.Clear();
|
|
|
|
if (activeRuntimeElements.Count > 0)
|
|
{
|
|
using (var activeTracksCache = new TrackCacheManager(m_ActiveTracksToEvaluateCache, activeRuntimeElements))
|
|
{
|
|
foreach (AnimationTrack asset in activeTracksCache.trackCache)
|
|
{
|
|
if (TryGetCallbackList(asset, out var callbacks))
|
|
{
|
|
foreach (ITimelineEvaluateCallback callback in callbacks)
|
|
{
|
|
if (m_InvokedThisFrame.Contains(callback)) // prevent double invocation
|
|
continue;
|
|
|
|
callback.Evaluate();
|
|
m_InvokedThisFrame.Add(callback);
|
|
m_ForceEvaluateNextEvaluate.Add(callback);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // evaluate all callbacks if there are no active clips
|
|
{
|
|
foreach (List<ITimelineEvaluateCallback> callbacks in m_EvaluateCallbacks.Values)
|
|
{
|
|
foreach (ITimelineEvaluateCallback callback in callbacks)
|
|
{
|
|
if (m_InvokedThisFrame.Contains(callback)) // prevent double invocation
|
|
continue;
|
|
|
|
callback.Evaluate();
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var callback in m_AlwaysEvaluateCallbacks)
|
|
{
|
|
callback.Evaluate();
|
|
}
|
|
|
|
m_InvokedThisFrame.Clear();
|
|
}
|
|
|
|
bool TryGetCallbackList(AnimationTrack track, out List<ITimelineEvaluateCallback> list)
|
|
{
|
|
if (track == null)
|
|
{
|
|
list = null;
|
|
return false;
|
|
}
|
|
|
|
if (m_EvaluateCallbacks.TryGetValue(track, out list))
|
|
return true;
|
|
|
|
return TryGetCallbackList(track.parent as AnimationTrack, out list);
|
|
}
|
|
}
|
|
}
|