playtest-unity/playtest/Library/PackageCache/com.unity.timeline@1.7.4/Editor/State/WindowState.cs

1222 lines
39 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
#if !UNITY_2020_2_OR_NEWER
using UnityEngine.Experimental.Animations;
#endif
using UnityEngine.Animations;
using Object = System.Object;
namespace UnityEditor.Timeline
{
delegate bool PendingUpdateDelegate(WindowState state, Event currentEvent);
/// <summary>
/// Interface for faking purposes
/// </summary>
interface IWindowState
{
ISequenceState masterSequence { get; }
ISequenceState editSequence { get; }
IEnumerable<ISequenceState> allSequences { get; }
PlayRange playRange { get; set; }
void SetCurrentSequence(TimelineAsset timelineAsset, PlayableDirector director, TimelineClip hostClip);
void PopSequencesUntilCount(int count);
IEnumerable<SequenceContext> GetSubSequences();
void SetPlaying(bool start);
}
class WindowState : IWindowState
{
const int k_TimeCodeTextFieldId = 3790;
readonly TimelineWindow m_Window;
bool m_Recording;
readonly SpacePartitioner m_SpacePartitioner = new SpacePartitioner();
readonly SpacePartitioner m_HeaderSpacePartitioner = new SpacePartitioner();
readonly List<Manipulator> m_CaptureSession = new List<Manipulator>();
int m_DirtyStamp;
float m_BindingAreaWidth = WindowConstants.defaultBindingAreaWidth;
bool m_MustRebuildGraph;
float m_LastTime;
readonly PropertyCollector m_PropertyCollector = new PropertyCollector();
static AnimationModeDriver s_PreviewDriver;
Dictionary<UnityEngine.Object, Animator> m_PreviewedAnimators;
List<Component> m_PreviewedComponents;
IEnumerable<IAnimationWindowPreview> previewedComponents =>
m_PreviewedComponents.Where(component => component != null).Cast<IAnimationWindowPreview>();
public static double kTimeEpsilon { get { return TimeUtility.kTimeEpsilon; } }
public static readonly float kMaxShownTime = (float)TimeUtility.k_MaxTimelineDurationInSeconds;
static readonly ISequenceState k_NullSequenceState = new NullSequenceState();
// which tracks are armed for record - only one allowed per 'actor'
Dictionary<TrackAsset, TrackAsset> m_ArmedTracks = new Dictionary<TrackAsset, TrackAsset>();
TimelineWindow.TimelineWindowPreferences m_Preferences;
List<PendingUpdateDelegate> m_OnStartFrameUpdates;
List<PendingUpdateDelegate> m_OnEndFrameUpdates;
readonly SequenceHierarchy m_SequenceHierarchy;
public event Action<WindowState, Event> windowOnGuiStarted;
public event Action<bool> OnPlayStateChange;
public event System.Action OnDirtyStampChange;
public event System.Action OnRebuildGraphChange;
public event System.Action OnTimeChange;
public event System.Action OnRecordingChange;
public event System.Action OnBeforeSequenceChange;
public event System.Action OnAfterSequenceChange;
public WindowState(TimelineWindow w, SequenceHierarchy hierarchy)
{
m_Window = w;
m_Preferences = w.preferences;
hierarchy.Init(this);
m_SequenceHierarchy = hierarchy;
TimelinePlayable.muteAudioScrubbing = muteAudioScrubbing;
}
public static AnimationModeDriver previewDriver
{
get
{
if (s_PreviewDriver == null)
{
s_PreviewDriver = ScriptableObject.CreateInstance<AnimationModeDriver>();
AnimationPreviewUtilities.s_PreviewDriver = s_PreviewDriver;
}
return s_PreviewDriver;
}
}
public EditorWindow editorWindow
{
get { return m_Window; }
}
public ISequenceState editSequence
{
get
{
// Using "null" ISequenceState to avoid checking against null all the time.
// This *should* be removed in a phase 2 of refactoring, where we make sure
// to pass around the correct state object instead of letting clients dig
// into the WindowState for whatever they want.
return m_SequenceHierarchy.editSequence ?? k_NullSequenceState;
}
}
public ISequenceState masterSequence
{
get { return m_SequenceHierarchy.masterSequence ?? k_NullSequenceState; }
}
public ISequenceState referenceSequence
{
get { return timeReferenceMode == TimeReferenceMode.Local ? editSequence : masterSequence; }
}
public IEnumerable<ISequenceState> allSequences
{
get { return m_SequenceHierarchy.allSequences; }
}
public bool rebuildGraph
{
get { return m_MustRebuildGraph; }
set { SyncNotifyValue(ref m_MustRebuildGraph, value, OnRebuildGraphChange); }
}
public float mouseDragLag { get; set; }
public SpacePartitioner spacePartitioner
{
get { return m_SpacePartitioner; }
}
public SpacePartitioner headerSpacePartitioner
{
get { return m_HeaderSpacePartitioner; }
}
public List<Manipulator> captured
{
get { return m_CaptureSession; }
}
public void AddCaptured(Manipulator manipulator)
{
if (!m_CaptureSession.Contains(manipulator))
m_CaptureSession.Add(manipulator);
}
public void RemoveCaptured(Manipulator manipulator)
{
m_CaptureSession.Remove(manipulator);
}
public bool isJogging { get; set; }
public int viewStateHash { get; private set; }
public float bindingAreaWidth
{
get { return m_BindingAreaWidth; }
set { m_BindingAreaWidth = value; }
}
public float sequencerHeaderWidth
{
get { return editSequence.viewModel.sequencerHeaderWidth; }
set
{
editSequence.viewModel.sequencerHeaderWidth = Mathf.Clamp(value, WindowConstants.minHeaderWidth, WindowConstants.maxHeaderWidth);
}
}
public float mainAreaWidth { get; set; }
public float trackScale
{
get { return editSequence.viewModel.trackScale; }
set
{
editSequence.viewModel.trackScale = value;
m_Window.treeView.CalculateRowRects();
}
}
public int dirtyStamp
{
get { return m_DirtyStamp; }
private set { SyncNotifyValue(ref m_DirtyStamp, value, OnDirtyStampChange); }
}
public bool showQuadTree { get; set; }
public bool canRecord
{
get { return AnimationMode.InAnimationMode(previewDriver) || !AnimationMode.InAnimationMode(); }
}
public bool recording
{
get
{
if (!previewMode)
m_Recording = false;
return m_Recording;
}
// set can only be used to disable recording
set
{
if (ignorePreview)
return;
// force preview mode on
if (value)
previewMode = true;
bool newValue = value;
if (!previewMode)
newValue = false;
if (newValue && m_ArmedTracks.Count == 0)
{
Debug.LogError("Cannot enable recording without an armed track");
newValue = false;
}
if (!newValue)
m_ArmedTracks.Clear();
if (newValue != m_Recording)
{
if (newValue)
AnimationMode.StartAnimationRecording();
else
AnimationMode.StopAnimationRecording();
InspectorWindow.RepaintAllInspectors();
}
SyncNotifyValue(ref m_Recording, newValue, OnRecordingChange);
}
}
public bool previewMode
{
get { return ignorePreview || AnimationMode.InAnimationMode(previewDriver); }
set
{
if (ignorePreview)
return;
bool inAnimationMode = AnimationMode.InAnimationMode(previewDriver);
if (!value)
{
if (inAnimationMode)
{
Stop();
OnStopPreview();
AnimationMode.StopAnimationMode(previewDriver);
AnimationPropertyContextualMenu.Instance.SetResponder(null);
previewedDirectors = null;
}
}
else if (!inAnimationMode)
{
editSequence.time = editSequence.viewModel.windowTime;
EvaluateImmediate(); // does appropriate caching prior to enabling
}
}
}
public bool playing
{
get
{
return masterSequence.director != null && masterSequence.director.state == PlayState.Playing;
}
}
public float playbackSpeed { get; set; }
public bool frameSnap
{
get { return TimelinePreferences.instance.snapToFrame; }
set { TimelinePreferences.instance.snapToFrame = value; }
}
public bool edgeSnaps
{
get { return TimelinePreferences.instance.edgeSnap; }
set { TimelinePreferences.instance.edgeSnap = value; }
}
public bool muteAudioScrubbing
{
get { return !TimelinePreferences.instance.audioScrubbing; }
set
{
TimelinePreferences.instance.audioScrubbing = !value;
TimelinePlayable.muteAudioScrubbing = value;
RebuildPlayableGraph();
}
}
public TimeReferenceMode timeReferenceMode
{
get { return m_Preferences.timeReferenceMode; }
set { m_Preferences.timeReferenceMode = value; }
}
public TimeFormat timeFormat
{
get { return TimelinePreferences.instance.timeFormat; }
set { TimelinePreferences.instance.timeFormat = value; }
}
public bool showAudioWaveform
{
get { return TimelinePreferences.instance.showAudioWaveform; }
set { TimelinePreferences.instance.showAudioWaveform = value; }
}
public PlayRange playRange
{
get { return masterSequence.viewModel.timeAreaPlayRange; }
set { masterSequence.viewModel.timeAreaPlayRange = ValidatePlayRange(value, masterSequence); }
}
public bool showMarkerHeader
{
get { return editSequence.asset != null && editSequence.asset.markerTrack != null && editSequence.asset.markerTrack.GetShowMarkers(); }
set { GetWindow().SetShowMarkerHeader(value); }
}
public EditMode.EditType editType
{
get { return m_Preferences.editType; }
set { m_Preferences.editType = value; }
}
public PlaybackScrollMode autoScrollMode
{
get { return TimelinePreferences.instance.playbackScrollMode; }
set { TimelinePreferences.instance.playbackScrollMode = value; }
}
public List<PlayableDirector> previewedDirectors { get; private set; }
public void OnDestroy()
{
if (!ignorePreview)
Stop();
if (m_OnStartFrameUpdates != null)
m_OnStartFrameUpdates.Clear();
if (m_OnEndFrameUpdates != null)
m_OnEndFrameUpdates.Clear();
m_SequenceHierarchy.Clear();
windowOnGuiStarted = null;
}
public void OnSceneSaved()
{
// the director will reset it's time when the scene is saved.
EnsureWindowTimeConsistency();
}
public void SetCurrentSequence(TimelineAsset timelineAsset, PlayableDirector director, TimelineClip hostClip)
{
if (OnBeforeSequenceChange != null)
OnBeforeSequenceChange.Invoke();
OnCurrentDirectorWillChange();
if (hostClip == null || timelineAsset == null)
{
m_PropertyCollector.Clear();
m_SequenceHierarchy.Clear();
}
if (timelineAsset != null)
m_SequenceHierarchy.Add(timelineAsset, director, hostClip);
if (OnAfterSequenceChange != null)
OnAfterSequenceChange.Invoke();
}
public void PopSequencesUntilCount(int count)
{
if (count >= m_SequenceHierarchy.count) return;
if (count < 1) return;
if (OnBeforeSequenceChange != null)
OnBeforeSequenceChange.Invoke();
var nextDirector = m_SequenceHierarchy.GetStateAtIndex(count - 1).director;
OnCurrentDirectorWillChange();
m_SequenceHierarchy.RemoveUntilCount(count);
EnsureWindowTimeConsistency();
if (OnAfterSequenceChange != null)
OnAfterSequenceChange.Invoke();
}
public SequencePath GetCurrentSequencePath()
{
return m_SequenceHierarchy.ToSequencePath();
}
public void SetCurrentSequencePath(SequencePath path, bool forceRebuild)
{
if (!m_SequenceHierarchy.NeedsUpdate(path, forceRebuild))
return;
if (OnBeforeSequenceChange != null)
OnBeforeSequenceChange.Invoke();
m_SequenceHierarchy.FromSequencePath(path, forceRebuild);
if (OnAfterSequenceChange != null)
OnAfterSequenceChange.Invoke();
}
public IEnumerable<ISequenceState> GetAllSequences()
{
return m_SequenceHierarchy.allSequences;
}
public IEnumerable<SequenceContext> GetSubSequences()
{
var contexts =
editSequence.asset?.flattenedTracks
.SelectMany(x => x.clips)
.Where((TimelineUtility.HasCustomEditor))
.SelectMany((clip =>
TimelineUtility.GetSubTimelines(clip, TimelineEditor.inspectedDirector)
.Select(director => new SequenceContext(director, clip))));
return contexts;
}
public void Reset()
{
recording = false;
previewMode = false;
}
public double GetSnappedTimeAtMousePosition(Vector2 mousePos)
{
return TimeReferenceUtility.SnapToFrameIfRequired(ScreenSpacePixelToTimeAreaTime(mousePos.x));
}
static void SyncNotifyValue<T>(ref T oldValue, T newValue, System.Action changeStateCallback)
{
var stateChanged = false;
if (oldValue == null)
{
oldValue = newValue;
stateChanged = true;
}
else
{
if (!oldValue.Equals(newValue))
{
oldValue = newValue;
stateChanged = true;
}
}
if (stateChanged && changeStateCallback != null)
{
changeStateCallback.Invoke();
}
}
public TimelineWindowAnalytics analytics = new TimelineWindowAnalytics();
public void SetTimeAreaTransform(Vector2 newTranslation, Vector2 newScale)
{
m_Window.timeArea.SetTransform(newTranslation, newScale);
TimeAreaChanged();
}
public void SetTimeAreaShownRange(float min, float max)
{
m_Window.timeArea.SetShownHRange(min, max);
TimeAreaChanged();
}
internal void TimeAreaChanged()
{
if (editSequence.asset != null)
{
editSequence.viewModel.timeAreaShownRange = new Vector2(m_Window.timeArea.shownArea.x, m_Window.timeArea.shownArea.xMax);
}
}
public void ResetPreviewMode()
{
var mode = previewMode;
previewMode = false;
previewMode = mode;
}
public bool TimeIsInRange(float value)
{
Rect shownArea = m_Window.timeArea.shownArea;
return value >= shownArea.x && value <= shownArea.xMax;
}
public bool RangeIsVisible(Range range)
{
var shownArea = m_Window.timeArea.shownArea;
return range.start < shownArea.xMax && range.end > shownArea.xMin;
}
public void EnsurePlayHeadIsVisible()
{
double minDisplayedTime = PixelToTime(timeAreaRect.xMin);
double maxDisplayedTime = PixelToTime(timeAreaRect.xMax);
double currentTime = editSequence.time;
if (currentTime >= minDisplayedTime && currentTime <= maxDisplayedTime)
return;
float displayedTimeRange = (float)(maxDisplayedTime - minDisplayedTime);
float minimumTimeToDisplay = (float)currentTime - displayedTimeRange / 2.0f;
float maximumTimeToDisplay = (float)currentTime + displayedTimeRange / 2.0f;
SetTimeAreaShownRange(minimumTimeToDisplay, maximumTimeToDisplay);
}
public void SetPlayHeadToMiddle()
{
double minDisplayedTime = PixelToTime(timeAreaRect.xMin);
double maxDisplayedTime = PixelToTime(timeAreaRect.xMax);
double currentTime = editSequence.time;
float displayedTimeRange = (float)(maxDisplayedTime - minDisplayedTime);
if (currentTime >= minDisplayedTime && currentTime <= maxDisplayedTime)
{
if (currentTime < minDisplayedTime + displayedTimeRange / 2)
return;
}
const float kCatchUpSpeed = 3f;
float realDelta = Mathf.Clamp(Time.realtimeSinceStartup - m_LastTime, 0f, 1f) * kCatchUpSpeed;
float scrollCatchupAmount = kCatchUpSpeed * realDelta * displayedTimeRange / 2;
if (currentTime < minDisplayedTime)
{
SetTimeAreaShownRange((float)currentTime, (float)currentTime + displayedTimeRange);
}
else if (currentTime > maxDisplayedTime)
{
SetTimeAreaShownRange((float)currentTime - displayedTimeRange + scrollCatchupAmount, (float)currentTime + scrollCatchupAmount);
}
else if (currentTime > minDisplayedTime + displayedTimeRange / 2)
{
float targetMinDisplayedTime = Mathf.Min((float)minDisplayedTime + scrollCatchupAmount,
(float)(currentTime - displayedTimeRange / 2));
SetTimeAreaShownRange(targetMinDisplayedTime, targetMinDisplayedTime + displayedTimeRange);
}
}
internal void UpdateLastFrameTime()
{
m_LastTime = Time.realtimeSinceStartup;
}
public Vector2 timeAreaShownRange
{
get
{
if (m_Window.state.editSequence.asset != null)
return editSequence.viewModel.timeAreaShownRange;
return TimelineAssetViewModel.TimeAreaDefaultRange;
}
set
{
SetTimeAreaShownRange(value.x, value.y);
}
}
public Vector2 timeAreaTranslation
{
get { return m_Window.timeArea.translation; }
}
public Vector2 timeAreaScale
{
get { return m_Window.timeArea.scale; }
}
public Rect timeAreaRect
{
get
{
var sequenceContentRect = m_Window.sequenceContentRect;
return new Rect(
sequenceContentRect.x,
WindowConstants.timeAreaYPosition,
Mathf.Max(sequenceContentRect.width, WindowConstants.timeAreaMinWidth),
WindowConstants.timeAreaHeight
);
}
}
public float windowHeight
{
get { return m_Window.position.height; }
}
public bool playRangeEnabled
{
get { return !ignorePreview && masterSequence.viewModel.playRangeEnabled && !IsEditingASubTimeline(); }
set
{
if (!ignorePreview)
masterSequence.viewModel.playRangeEnabled = value;
}
}
public bool ignorePreview
{
get
{
var shouldIgnorePreview = masterSequence.asset != null && !masterSequence.asset.editorSettings.scenePreview;
return Application.isPlaying || shouldIgnorePreview;
}
}
public TimelineWindow GetWindow()
{
return m_Window;
}
public void Play()
{
if (masterSequence.director == null)
return;
if (!previewMode)
previewMode = true;
if (previewMode)
{
if (masterSequence.time > masterSequence.duration)
masterSequence.time = 0;
#if TIMELINE_FRAMEACCURATE
if (TimelinePreferences.instance.playbackLockedToFrame)
{
FrameRate frameRate = FrameRate.DoubleToFrameRate(masterSequence.asset.editorSettings.frameRate);
masterSequence.director.Play(frameRate);
}
else
{
masterSequence.director.Play();
}
#else
masterSequence.director.Play();
#endif
masterSequence.director.ProcessPendingGraphChanges();
PlayableDirector.ResetFrameTiming();
InvokePlayStateChangeCallback(true);
}
}
public void Pause()
{
if (masterSequence.director != null)
{
masterSequence.director.Pause();
masterSequence.director.ProcessPendingGraphChanges();
SynchronizeSequencesAfterPlayback();
InvokePlayStateChangeCallback(false);
}
}
public void SetPlaying(bool start)
{
if (start && !playing)
{
Play();
}
if (!start && playing)
{
Pause();
}
analytics.SendPlayEvent(start);
}
public void Stop()
{
if (masterSequence.director != null)
{
masterSequence.director.Stop();
masterSequence.director.ProcessPendingGraphChanges();
InvokePlayStateChangeCallback(false);
}
}
void InvokePlayStateChangeCallback(bool isPlaying)
{
if (OnPlayStateChange != null)
OnPlayStateChange.Invoke(isPlaying);
}
public void RebuildPlayableGraph()
{
if (masterSequence.director != null)
{
masterSequence.director.RebuildGraph();
// rebuild both the parent and the edit sequences. control tracks don't necessary
// rebuild the subdirector on recreation
if (editSequence.director != null && editSequence.director != masterSequence.director)
{
editSequence.director.RebuildGraph();
}
}
}
public void Evaluate()
{
if (masterSequence.director != null)
{
if (!EditorApplication.isPlaying && !previewMode)
GatherProperties(masterSequence.director);
ForceTimeOnDirector(masterSequence.director);
masterSequence.director.DeferredEvaluate();
if (EditorApplication.isPlaying == false)
{
PlayModeView.RepaintAll();
SceneView.RepaintAll();
AudioMixerWindow.RepaintAudioMixerWindow();
}
}
}
public void EvaluateImmediate()
{
if (masterSequence.director != null && masterSequence.director.isActiveAndEnabled)
{
if (!EditorApplication.isPlaying && !previewMode)
GatherProperties(masterSequence.director);
if (previewMode)
{
ForceTimeOnDirector(masterSequence.director);
masterSequence.director.ProcessPendingGraphChanges();
masterSequence.director.Evaluate();
}
}
}
public void Refresh()
{
CheckRecordingState();
dirtyStamp = dirtyStamp + 1;
rebuildGraph = true;
}
public void UpdateViewStateHash()
{
viewStateHash = timeAreaTranslation.GetHashCode()
.CombineHash(timeAreaScale.GetHashCode())
.CombineHash(trackScale.GetHashCode());
}
public bool IsEditingASubTimeline()
{
return editSequence != masterSequence;
}
public bool IsEditingAnEmptyTimeline()
{
return editSequence.asset == null;
}
public bool IsEditingAPrefabAsset()
{
var stage = PrefabStageUtility.GetCurrentPrefabStage();
return stage != null && editSequence.director != null && stage.IsPartOfPrefabContents(editSequence.director.gameObject);
}
public bool IsCurrentEditingASequencerTextField()
{
if (editSequence.asset == null)
return false;
if (k_TimeCodeTextFieldId == GUIUtility.keyboardControl)
return true;
return editSequence.asset.flattenedTracks.Count(t => t.GetInstanceID() == GUIUtility.keyboardControl) != 0;
}
public float TimeToTimeAreaPixel(double t) // TimeToTimeAreaPixel
{
float pixelX = (float)t;
pixelX *= timeAreaScale.x;
pixelX += timeAreaTranslation.x + sequencerHeaderWidth;
return pixelX;
}
public float TimeToScreenSpacePixel(double time)
{
float pixelX = (float)time;
pixelX *= timeAreaScale.x;
pixelX += timeAreaTranslation.x;
return pixelX;
}
public float TimeToPixel(double time)
{
return m_Window.timeArea.TimeToPixel((float)time, timeAreaRect);
}
public float PixelToTime(float pixel)
{
return m_Window.timeArea.PixelToTime(pixel, timeAreaRect);
}
public float PixelDeltaToDeltaTime(float p)
{
return PixelToTime(p) - PixelToTime(0);
}
public float TimeAreaPixelToTime(float pixel)
{
return PixelToTime(pixel);
}
public float ScreenSpacePixelToTimeAreaTime(float p)
{
// transform into track space by offsetting the pixel by the screen-space offset of the time area
p -= timeAreaRect.x;
return TrackSpacePixelToTimeAreaTime(p);
}
public float TrackSpacePixelToTimeAreaTime(float p)
{
p -= timeAreaTranslation.x;
if (timeAreaScale.x > 0.0f)
return p / timeAreaScale.x;
return p;
}
public void OffsetTimeArea(int pixels)
{
Vector3 tx = timeAreaTranslation;
tx.x += pixels;
SetTimeAreaTransform(tx, timeAreaScale);
}
public GameObject GetSceneReference(TrackAsset asset)
{
if (editSequence.director == null)
return null; // no player bound
return TimelineUtility.GetSceneGameObject(editSequence.director, asset);
}
public void CalculateRowRects()
{
// arming a track might add inline curve tracks, recalc track heights
if (m_Window != null && m_Window.treeView != null)
m_Window.treeView.CalculateRowRects();
}
// Only one track within a 'track' hierarchy can be armed
public void ArmForRecord(TrackAsset track)
{
m_ArmedTracks[TimelineUtility.GetSceneReferenceTrack(track)] = track;
if (track != null && !recording)
recording = true;
if (!recording)
return;
track.OnRecordingArmed(editSequence.director);
CalculateRowRects();
}
public void UnarmForRecord(TrackAsset track)
{
m_ArmedTracks.Remove(TimelineUtility.GetSceneReferenceTrack(track));
if (m_ArmedTracks.Count == 0)
recording = false;
track.OnRecordingUnarmed(editSequence.director);
}
public void UpdateRecordingState()
{
if (recording)
{
foreach (var track in m_ArmedTracks.Values)
{
if (track != null)
track.OnRecordingTimeChanged(editSequence.director);
}
}
}
public bool IsTrackRecordable(TrackAsset track)
{
// A track with animated parameters can always be recorded to
return IsArmedForRecord(track) || track.HasAnyAnimatableParameters();
}
public bool IsArmedForRecord(TrackAsset track)
{
return track == GetArmedTrack(track);
}
public TrackAsset GetArmedTrack(TrackAsset track)
{
TrackAsset outTrack;
m_ArmedTracks.TryGetValue(TimelineUtility.GetSceneReferenceTrack(track), out outTrack);
return outTrack;
}
void CheckRecordingState()
{
// checks for deleted tracks, and makes sure the recording state matches
if (m_ArmedTracks.Any(t => t.Value == null))
{
m_ArmedTracks = m_ArmedTracks.Where(t => t.Value != null).ToDictionary(t => t.Key, t => t.Value);
if (m_ArmedTracks.Count == 0)
recording = false;
}
}
void OnCurrentDirectorWillChange()
{
if (ignorePreview)
return;
SynchronizeViewModelTime(editSequence);
Stop();
rebuildGraph = true; // needed for asset previews
}
public void GatherProperties(PlayableDirector director)
{
if (director == null || Application.isPlaying)
return;
var asset = director.playableAsset as TimelineAsset;
if (asset != null && !asset.editorSettings.scenePreview)
return;
if (!previewMode)
{
AnimationMode.StartAnimationMode(previewDriver);
OnStartPreview(director);
AnimationPropertyContextualMenu.Instance.SetResponder(new TimelineRecordingContextualResponder(this));
if (!previewMode)
return;
EnsureWindowTimeConsistency();
}
if (asset != null)
{
m_PropertyCollector.Reset();
m_PropertyCollector.PushActiveGameObject(null); // avoid overflow on unbound tracks
asset.GatherProperties(director, m_PropertyCollector);
}
}
void OnStartPreview(PlayableDirector director)
{
previewedDirectors = TimelineUtility.GetAllDirectorsInHierarchy(director).ToList();
if (previewedDirectors == null)
return;
m_PreviewedAnimators = TimelineUtility.GetBindingPairsFromDirectors<Animator>(previewedDirectors);
m_PreviewedComponents = m_PreviewedAnimators.Values
.SelectMany(animator => animator.GetComponents<IAnimationWindowPreview>()
.Cast<Component>())
.ToList();
foreach (var previewedComponent in previewedComponents)
{
previewedComponent.StartPreview();
}
#if UNITY_2022_2_OR_NEWER
PrefabUtility.allowRecordingPrefabPropertyOverridesFor += AllowRecordingPrefabPropertyOverridesFor;
#endif //UNITY_2022_2_OR_NEWER
}
internal bool AllowRecordingPrefabPropertyOverridesFor(Object componentOrGameObject)
{
if (componentOrGameObject == null)
throw new ArgumentNullException(nameof(componentOrGameObject));
if (previewMode == false)
return true;
if (m_ArmedTracks.Count == 0)
return true;
GameObject inputGameObject = null;
if (componentOrGameObject is Component component)
{
inputGameObject = component.gameObject;
}
else if (componentOrGameObject is GameObject gameObject)
{
inputGameObject = gameObject;
}
if (inputGameObject == null)
return true;
var armedTracks = m_ArmedTracks.Keys;
foreach (var track in armedTracks)
{
if (m_PreviewedAnimators.TryGetValue(track, out Animator animator))
{
if (inputGameObject.transform.IsChildOf(animator.transform))
{
return false;
}
}
}
return true;
}
void OnStopPreview()
{
if (m_PreviewedComponents != null)
{
foreach (var previewComponent in previewedComponents)
{
previewComponent.StopPreview();
}
m_PreviewedComponents = null;
}
if (m_PreviewedAnimators != null)
{
foreach (var previewAnimator in m_PreviewedAnimators.Values)
{
if (previewAnimator != null)
{
previewAnimator.UnbindAllHandles();
}
}
m_PreviewedAnimators = null;
}
#if UNITY_2022_2_OR_NEWER
PrefabUtility.allowRecordingPrefabPropertyOverridesFor -= AllowRecordingPrefabPropertyOverridesFor;
#endif //UNITY_2022_2_OR_NEWER
}
internal void ProcessStartFramePendingUpdates()
{
if (m_OnStartFrameUpdates != null)
m_OnStartFrameUpdates.RemoveAll(callback => callback.Invoke(this, Event.current));
}
internal void ProcessEndFramePendingUpdates()
{
if (m_OnEndFrameUpdates != null)
m_OnEndFrameUpdates.RemoveAll(callback => callback.Invoke(this, Event.current));
}
public void AddStartFrameDelegate(PendingUpdateDelegate updateDelegate)
{
if (m_OnStartFrameUpdates == null)
m_OnStartFrameUpdates = new List<PendingUpdateDelegate>();
if (m_OnStartFrameUpdates.Contains(updateDelegate))
return;
m_OnStartFrameUpdates.Add(updateDelegate);
}
public void AddEndFrameDelegate(PendingUpdateDelegate updateDelegate)
{
if (m_OnEndFrameUpdates == null)
m_OnEndFrameUpdates = new List<PendingUpdateDelegate>();
if (m_OnEndFrameUpdates.Contains(updateDelegate))
return;
m_OnEndFrameUpdates.Add(updateDelegate);
}
internal void InvokeWindowOnGuiStarted(Event evt)
{
if (windowOnGuiStarted != null)
windowOnGuiStarted.Invoke(this, evt);
}
public void UpdateRootPlayableDuration(double duration)
{
if (editSequence.director != null)
{
if (editSequence.director.playableGraph.IsValid())
{
if (editSequence.director.playableGraph.GetRootPlayableCount() > 0)
{
var rootPlayable = editSequence.director.playableGraph.GetRootPlayable(0);
if (rootPlayable.IsValid())
rootPlayable.SetDuration(duration);
}
}
}
}
public void InvokeTimeChangeCallback()
{
if (OnTimeChange != null)
OnTimeChange.Invoke();
}
PlayRange ValidatePlayRange(PlayRange range, ISequenceState sequenceState)
{
if (range == TimelineAssetViewModel.NoPlayRangeSet)
return range;
double minimumPlayRangeTime = (0.01 / Math.Max(1.0, sequenceState.frameRate));
// Validate min
if (range.end - range.start < minimumPlayRangeTime)
range.start = range.end - minimumPlayRangeTime;
if (range.start < 0.0)
range.start = 0.0;
// Validate max
if (range.end > sequenceState.duration)
range.end = sequenceState.duration;
if (range.end - range.start < minimumPlayRangeTime)
range.end = Math.Min(range.start + minimumPlayRangeTime, sequenceState.duration);
return range;
}
void EnsureWindowTimeConsistency()
{
if (masterSequence.director != null && masterSequence.viewModel != null && !ignorePreview)
masterSequence.time = masterSequence.viewModel.windowTime;
}
void SynchronizeSequencesAfterPlayback()
{
// Synchronizing editSequence will synchronize all view models up to the master
SynchronizeViewModelTime(editSequence);
}
static void SynchronizeViewModelTime(ISequenceState state)
{
if (state.director == null || state.viewModel == null)
return;
var t = state.time;
state.time = t;
}
// because we may be evaluating outside the duration of the root playable
// we explicitly set the time - this causes the graph to not 'advance' the time
// because advancing it can force it to change due to wrapping to the duration
// This can happen if the graph is force evaluated outside it's duration
// case 910114, 936844 and 943377
static void ForceTimeOnDirector(PlayableDirector director)
{
var directorTime = director.time;
director.time = directorTime;
}
public bool IsPlayableGraphDone()
{
return masterSequence.director != null
&& masterSequence.director.playableGraph.IsValid()
&& masterSequence.director.playableGraph.IsDone();
}
}
}