A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditorInternal;
5#if UNITY_2021_2_OR_NEWER
6using UnityEditor.SceneManagement;
7#else
8using UnityEditor.Experimental.SceneManagement;
9#endif
10using UnityEngine;
11using UnityEngine.Playables;
12using UnityEngine.Timeline;
13#if !UNITY_2020_2_OR_NEWER
14using UnityEngine.Experimental.Animations;
15#endif
16using UnityEngine.Animations;
17using Object = System.Object;
18
19namespace UnityEditor.Timeline
20{
21 delegate bool PendingUpdateDelegate(WindowState state, Event currentEvent);
22
23 /// <summary>
24 /// Interface for faking purposes
25 /// </summary>
26 interface IWindowState
27 {
28 ISequenceState masterSequence { get; }
29 ISequenceState editSequence { get; }
30 IEnumerable<ISequenceState> allSequences { get; }
31 PlayRange playRange { get; set; }
32 void SetCurrentSequence(TimelineAsset timelineAsset, PlayableDirector director, TimelineClip hostClip);
33 void PopSequencesUntilCount(int count);
34 IEnumerable<SequenceContext> GetSubSequences();
35 void SetPlaying(bool start);
36 }
37 class WindowState : IWindowState
38 {
39 const int k_TimeCodeTextFieldId = 3790;
40
41 readonly TimelineWindow m_Window;
42 bool m_Recording;
43
44 readonly SpacePartitioner m_SpacePartitioner = new SpacePartitioner();
45 readonly SpacePartitioner m_HeaderSpacePartitioner = new SpacePartitioner();
46 readonly List<Manipulator> m_CaptureSession = new List<Manipulator>();
47
48 int m_DirtyStamp;
49 float m_BindingAreaWidth = WindowConstants.defaultBindingAreaWidth;
50
51 bool m_MustRebuildGraph;
52
53 float m_LastTime;
54
55 readonly PropertyCollector m_PropertyCollector = new PropertyCollector();
56
57 static AnimationModeDriver s_PreviewDriver;
58 PreviewedBindings<Animator> m_PreviewedAnimators;
59 List<Component> m_PreviewedComponents;
60 IEnumerable<IAnimationWindowPreview> previewedComponents =>
61 m_PreviewedComponents.Where(component => component != null).Cast<IAnimationWindowPreview>();
62
63 public static double kTimeEpsilon { get { return TimeUtility.kTimeEpsilon; } }
64 public static readonly float kMaxShownTime = (float)TimeUtility.k_MaxTimelineDurationInSeconds;
65
66 static readonly ISequenceState k_NullSequenceState = new NullSequenceState();
67
68 // which tracks are armed for record - only one allowed per 'actor'
69 Dictionary<TrackAsset, TrackAsset> m_ArmedTracks = new Dictionary<TrackAsset, TrackAsset>();
70
71 TimelineWindow.TimelineWindowPreferences m_Preferences;
72
73 List<PendingUpdateDelegate> m_OnStartFrameUpdates;
74 List<PendingUpdateDelegate> m_OnEndFrameUpdates;
75
76 readonly SequenceHierarchy m_SequenceHierarchy;
77
78 public event Action<WindowState, Event> windowOnGuiStarted;
79
80 public event Action<bool> OnPlayStateChange;
81 public event System.Action OnDirtyStampChange;
82 public event System.Action OnRebuildGraphChange;
83 public event System.Action OnTimeChange;
84 public event System.Action OnRecordingChange;
85
86 public event System.Action OnBeforeSequenceChange;
87 public event System.Action OnAfterSequenceChange;
88
89 public WindowState(TimelineWindow w, SequenceHierarchy hierarchy)
90 {
91 m_Window = w;
92 m_Preferences = w.preferences;
93 hierarchy.Init(this);
94 m_SequenceHierarchy = hierarchy;
95 TimelinePlayable.muteAudioScrubbing = muteAudioScrubbing;
96 }
97
98 public static AnimationModeDriver previewDriver
99 {
100 get
101 {
102 if (s_PreviewDriver == null)
103 {
104 s_PreviewDriver = ScriptableObject.CreateInstance<AnimationModeDriver>();
105 AnimationPreviewUtilities.s_PreviewDriver = s_PreviewDriver;
106 }
107 return s_PreviewDriver;
108 }
109 }
110
111 public EditorWindow editorWindow
112 {
113 get { return m_Window; }
114 }
115
116 public ISequenceState editSequence
117 {
118 get
119 {
120 // Using "null" ISequenceState to avoid checking against null all the time.
121 // This *should* be removed in a phase 2 of refactoring, where we make sure
122 // to pass around the correct state object instead of letting clients dig
123 // into the WindowState for whatever they want.
124 return m_SequenceHierarchy.editSequence ?? k_NullSequenceState;
125 }
126 }
127
128 public ISequenceState masterSequence
129 {
130 get { return m_SequenceHierarchy.masterSequence ?? k_NullSequenceState; }
131 }
132
133 public ISequenceState referenceSequence
134 {
135 get { return timeReferenceMode == TimeReferenceMode.Local ? editSequence : masterSequence; }
136 }
137
138 public IEnumerable<ISequenceState> allSequences
139 {
140 get { return m_SequenceHierarchy.allSequences; }
141 }
142
143 public bool rebuildGraph
144 {
145 get { return m_MustRebuildGraph; }
146 set { SyncNotifyValue(ref m_MustRebuildGraph, value, OnRebuildGraphChange); }
147 }
148
149 public float mouseDragLag { get; set; }
150
151 public SpacePartitioner spacePartitioner
152 {
153 get { return m_SpacePartitioner; }
154 }
155
156 public SpacePartitioner headerSpacePartitioner
157 {
158 get { return m_HeaderSpacePartitioner; }
159 }
160
161 public List<Manipulator> captured
162 {
163 get { return m_CaptureSession; }
164 }
165
166 public void AddCaptured(Manipulator manipulator)
167 {
168 if (!m_CaptureSession.Contains(manipulator))
169 m_CaptureSession.Add(manipulator);
170 }
171
172 public void RemoveCaptured(Manipulator manipulator)
173 {
174 m_CaptureSession.Remove(manipulator);
175 }
176
177 public bool isJogging { get; set; }
178
179 public int viewStateHash { get; private set; }
180
181 public float bindingAreaWidth
182 {
183 get { return m_BindingAreaWidth; }
184 set { m_BindingAreaWidth = value; }
185 }
186
187 public float sequencerHeaderWidth
188 {
189 get { return editSequence.viewModel.sequencerHeaderWidth; }
190 set
191 {
192 editSequence.viewModel.sequencerHeaderWidth = Mathf.Clamp(value, WindowConstants.minHeaderWidth, WindowConstants.maxHeaderWidth);
193 }
194 }
195
196 public float mainAreaWidth { get; set; }
197
198 public float trackScale
199 {
200 get { return editSequence.viewModel.trackScale; }
201 set
202 {
203 editSequence.viewModel.trackScale = value;
204 m_Window.treeView.CalculateRowRects();
205 }
206 }
207
208 public int dirtyStamp
209 {
210 get { return m_DirtyStamp; }
211 private set { SyncNotifyValue(ref m_DirtyStamp, value, OnDirtyStampChange); }
212 }
213
214 public bool showQuadTree { get; set; }
215
216 public bool canRecord
217 {
218 get { return AnimationMode.InAnimationMode(previewDriver) || !AnimationMode.InAnimationMode(); }
219 }
220
221 public bool recording
222 {
223 get
224 {
225 if (!previewMode)
226 m_Recording = false;
227 return m_Recording;
228 }
229 // set can only be used to disable recording
230 set
231 {
232 if (ignorePreview)
233 return;
234
235 // force preview mode on
236 if (value)
237 previewMode = true;
238
239 bool newValue = value;
240 if (!previewMode)
241 newValue = false;
242
243 if (newValue && m_ArmedTracks.Count == 0)
244 {
245 Debug.LogError("Cannot enable recording without an armed track");
246 newValue = false;
247 }
248
249 if (!newValue)
250 m_ArmedTracks.Clear();
251
252 if (newValue != m_Recording)
253 {
254 if (newValue)
255 AnimationMode.StartAnimationRecording();
256 else
257 AnimationMode.StopAnimationRecording();
258
259 InspectorWindow.RepaintAllInspectors();
260 }
261
262 SyncNotifyValue(ref m_Recording, newValue, OnRecordingChange);
263 }
264 }
265
266 public bool previewMode
267 {
268 get { return ignorePreview || AnimationMode.InAnimationMode(previewDriver); }
269 set
270 {
271 if (ignorePreview)
272 return;
273 bool inAnimationMode = AnimationMode.InAnimationMode(previewDriver);
274 if (!value)
275 {
276 if (inAnimationMode)
277 {
278 Stop();
279
280 OnStopPreview();
281
282 AnimationMode.StopAnimationMode(previewDriver);
283
284 AnimationPropertyContextualMenu.Instance.SetResponder(null);
285 previewedDirectors = null;
286 }
287 }
288 else if (!inAnimationMode)
289 {
290 editSequence.time = editSequence.viewModel.windowTime;
291 EvaluateImmediate(); // does appropriate caching prior to enabling
292 }
293 }
294 }
295
296 public bool playing
297 {
298 get
299 {
300 return masterSequence.director != null && masterSequence.director.state == PlayState.Playing;
301 }
302 }
303
304 public float playbackSpeed { get; set; }
305
306 public bool frameSnap
307 {
308 get { return TimelinePreferences.instance.snapToFrame; }
309 set { TimelinePreferences.instance.snapToFrame = value; }
310 }
311
312 public bool edgeSnaps
313 {
314 get { return TimelinePreferences.instance.edgeSnap; }
315 set { TimelinePreferences.instance.edgeSnap = value; }
316 }
317
318 public bool muteAudioScrubbing
319 {
320 get { return !TimelinePreferences.instance.audioScrubbing; }
321 set
322 {
323 TimelinePreferences.instance.audioScrubbing = !value;
324 TimelinePlayable.muteAudioScrubbing = value;
325 RebuildPlayableGraph();
326 }
327 }
328
329 public TimeReferenceMode timeReferenceMode
330 {
331 get { return m_Preferences.timeReferenceMode; }
332 set { m_Preferences.timeReferenceMode = value; }
333 }
334
335 public TimeFormat timeFormat
336 {
337 get { return TimelinePreferences.instance.timeFormat; }
338 set { TimelinePreferences.instance.timeFormat = value; }
339 }
340
341 public bool showAudioWaveform
342 {
343 get { return TimelinePreferences.instance.showAudioWaveform; }
344 set { TimelinePreferences.instance.showAudioWaveform = value; }
345 }
346
347 public PlayRange playRange
348 {
349 get { return masterSequence.viewModel.timeAreaPlayRange; }
350 set { masterSequence.viewModel.timeAreaPlayRange = ValidatePlayRange(value, masterSequence); }
351 }
352
353 public bool showMarkerHeader
354 {
355 get { return editSequence.asset != null && editSequence.asset.markerTrack != null && editSequence.asset.markerTrack.GetShowMarkers(); }
356 set { GetWindow().SetShowMarkerHeader(value); }
357 }
358
359 public EditMode.EditType editType
360 {
361 get { return m_Preferences.editType; }
362 set { m_Preferences.editType = value; }
363 }
364
365 public PlaybackScrollMode autoScrollMode
366 {
367 get { return TimelinePreferences.instance.playbackScrollMode; }
368 set { TimelinePreferences.instance.playbackScrollMode = value; }
369 }
370
371 public List<PlayableDirector> previewedDirectors { get; private set; }
372
373 public void OnDestroy()
374 {
375 if (!ignorePreview)
376 Stop();
377
378 if (m_OnStartFrameUpdates != null)
379 m_OnStartFrameUpdates.Clear();
380
381 if (m_OnEndFrameUpdates != null)
382 m_OnEndFrameUpdates.Clear();
383
384 m_SequenceHierarchy.Clear();
385 windowOnGuiStarted = null;
386 }
387
388 public void OnSceneSaved()
389 {
390 // the director will reset it's time when the scene is saved.
391 EnsureWindowTimeConsistency();
392 }
393
394 public void SetCurrentSequence(TimelineAsset timelineAsset, PlayableDirector director, TimelineClip hostClip)
395 {
396 if (OnBeforeSequenceChange != null)
397 OnBeforeSequenceChange.Invoke();
398
399 OnCurrentDirectorWillChange();
400
401 if (hostClip == null || timelineAsset == null)
402 {
403 m_PropertyCollector.Clear();
404 m_SequenceHierarchy.Clear();
405 }
406
407 if (timelineAsset != null)
408 m_SequenceHierarchy.Add(timelineAsset, director, hostClip);
409
410 if (OnAfterSequenceChange != null)
411 OnAfterSequenceChange.Invoke();
412 }
413
414 public void PopSequencesUntilCount(int count)
415 {
416 if (count >= m_SequenceHierarchy.count) return;
417 if (count < 1) return;
418
419 if (OnBeforeSequenceChange != null)
420 OnBeforeSequenceChange.Invoke();
421
422 var nextDirector = m_SequenceHierarchy.GetStateAtIndex(count - 1).director;
423 OnCurrentDirectorWillChange();
424
425 m_SequenceHierarchy.RemoveUntilCount(count);
426
427 EnsureWindowTimeConsistency();
428
429 if (OnAfterSequenceChange != null)
430 OnAfterSequenceChange.Invoke();
431 }
432
433 public SequencePath GetCurrentSequencePath()
434 {
435 return m_SequenceHierarchy.ToSequencePath();
436 }
437
438 public void SetCurrentSequencePath(SequencePath path, bool forceRebuild)
439 {
440 if (!m_SequenceHierarchy.NeedsUpdate(path, forceRebuild))
441 return;
442
443 if (OnBeforeSequenceChange != null)
444 OnBeforeSequenceChange.Invoke();
445
446 m_SequenceHierarchy.FromSequencePath(path, forceRebuild);
447
448 if (OnAfterSequenceChange != null)
449 OnAfterSequenceChange.Invoke();
450 }
451
452 public IEnumerable<ISequenceState> GetAllSequences()
453 {
454 return m_SequenceHierarchy.allSequences;
455 }
456
457 public IEnumerable<SequenceContext> GetSubSequences()
458 {
459 var contexts =
460 editSequence.asset?.flattenedTracks
461 .SelectMany(x => x.clips)
462 .Where((TimelineUtility.HasCustomEditor))
463 .SelectMany((clip =>
464 TimelineUtility.GetSubTimelines(clip, TimelineEditor.inspectedDirector)
465 .Select(director => new SequenceContext(director, clip))));
466
467 return contexts;
468 }
469
470 public void Reset()
471 {
472 recording = false;
473 previewMode = false;
474 }
475
476 public double GetSnappedTimeAtMousePosition(Vector2 mousePos)
477 {
478 return TimeReferenceUtility.SnapToFrameIfRequired(ScreenSpacePixelToTimeAreaTime(mousePos.x));
479 }
480
481 static void SyncNotifyValue<T>(ref T oldValue, T newValue, System.Action changeStateCallback)
482 {
483 var stateChanged = false;
484
485 if (oldValue == null)
486 {
487 oldValue = newValue;
488 stateChanged = true;
489 }
490 else
491 {
492 if (!oldValue.Equals(newValue))
493 {
494 oldValue = newValue;
495 stateChanged = true;
496 }
497 }
498
499 if (stateChanged && changeStateCallback != null)
500 {
501 changeStateCallback.Invoke();
502 }
503 }
504
505 public TimelineWindowAnalytics analytics = new TimelineWindowAnalytics();
506
507 public void SetTimeAreaTransform(Vector2 newTranslation, Vector2 newScale)
508 {
509 m_Window.timeArea.SetTransform(newTranslation, newScale);
510 TimeAreaChanged();
511 }
512
513 public void SetTimeAreaShownRange(float min, float max)
514 {
515 m_Window.timeArea.SetShownHRange(min, max);
516 TimeAreaChanged();
517 }
518
519 internal void TimeAreaChanged()
520 {
521 if (editSequence.asset != null)
522 {
523 editSequence.viewModel.timeAreaShownRange = new Vector2(m_Window.timeArea.shownArea.x, m_Window.timeArea.shownArea.xMax);
524 }
525 }
526
527 public void ResetPreviewMode()
528 {
529 var mode = previewMode;
530 previewMode = false;
531 previewMode = mode;
532 }
533
534 public bool TimeIsInRange(float value)
535 {
536 Rect shownArea = m_Window.timeArea.shownArea;
537 return value >= shownArea.x && value <= shownArea.xMax;
538 }
539
540 public bool RangeIsVisible(Range range)
541 {
542 var shownArea = m_Window.timeArea.shownArea;
543 return range.start < shownArea.xMax && range.end > shownArea.xMin;
544 }
545
546 public void EnsurePlayHeadIsVisible()
547 {
548 double minDisplayedTime = PixelToTime(timeAreaRect.xMin);
549 double maxDisplayedTime = PixelToTime(timeAreaRect.xMax);
550
551 double currentTime = editSequence.time;
552 if (currentTime >= minDisplayedTime && currentTime <= maxDisplayedTime)
553 return;
554
555 float displayedTimeRange = (float)(maxDisplayedTime - minDisplayedTime);
556 float minimumTimeToDisplay = (float)currentTime - displayedTimeRange / 2.0f;
557 float maximumTimeToDisplay = (float)currentTime + displayedTimeRange / 2.0f;
558 SetTimeAreaShownRange(minimumTimeToDisplay, maximumTimeToDisplay);
559 }
560
561 public void SetPlayHeadToMiddle()
562 {
563 double minDisplayedTime = PixelToTime(timeAreaRect.xMin);
564 double maxDisplayedTime = PixelToTime(timeAreaRect.xMax);
565
566 double currentTime = editSequence.time;
567 float displayedTimeRange = (float)(maxDisplayedTime - minDisplayedTime);
568
569 if (currentTime >= minDisplayedTime && currentTime <= maxDisplayedTime)
570 {
571 if (currentTime < minDisplayedTime + displayedTimeRange / 2)
572 return;
573 }
574
575 const float kCatchUpSpeed = 3f;
576 float realDelta = Mathf.Clamp(Time.realtimeSinceStartup - m_LastTime, 0f, 1f) * kCatchUpSpeed;
577 float scrollCatchupAmount = kCatchUpSpeed * realDelta * displayedTimeRange / 2;
578
579 if (currentTime < minDisplayedTime)
580 {
581 SetTimeAreaShownRange((float)currentTime, (float)currentTime + displayedTimeRange);
582 }
583 else if (currentTime > maxDisplayedTime)
584 {
585 SetTimeAreaShownRange((float)currentTime - displayedTimeRange + scrollCatchupAmount, (float)currentTime + scrollCatchupAmount);
586 }
587 else if (currentTime > minDisplayedTime + displayedTimeRange / 2)
588 {
589 float targetMinDisplayedTime = Mathf.Min((float)minDisplayedTime + scrollCatchupAmount,
590 (float)(currentTime - displayedTimeRange / 2));
591 SetTimeAreaShownRange(targetMinDisplayedTime, targetMinDisplayedTime + displayedTimeRange);
592 }
593 }
594
595 internal void UpdateLastFrameTime()
596 {
597 m_LastTime = Time.realtimeSinceStartup;
598 }
599
600 public Vector2 timeAreaShownRange
601 {
602 get
603 {
604 if (m_Window.state.editSequence.asset != null)
605 return editSequence.viewModel.timeAreaShownRange;
606
607 return TimelineAssetViewModel.TimeAreaDefaultRange;
608 }
609 set
610 {
611 SetTimeAreaShownRange(value.x, value.y);
612 }
613 }
614
615 public Vector2 timeAreaTranslation
616 {
617 get { return m_Window.timeArea.translation; }
618 }
619
620 public Vector2 timeAreaScale
621 {
622 get { return m_Window.timeArea.scale; }
623 }
624
625 public Rect timeAreaRect
626 {
627 get
628 {
629 var sequenceContentRect = m_Window.sequenceContentRect;
630 return new Rect(
631 sequenceContentRect.x,
632 WindowConstants.timeAreaYPosition,
633 Mathf.Max(sequenceContentRect.width, WindowConstants.timeAreaMinWidth),
634 WindowConstants.timeAreaHeight
635 );
636 }
637 }
638
639 public float windowHeight
640 {
641 get { return m_Window.position.height; }
642 }
643
644 public bool playRangeEnabled
645 {
646 get { return !ignorePreview && masterSequence.viewModel.playRangeEnabled && !IsEditingASubTimeline(); }
647 set
648 {
649 if (!ignorePreview)
650 masterSequence.viewModel.playRangeEnabled = value;
651 }
652 }
653
654 public bool ignorePreview
655 {
656 get
657 {
658 var shouldIgnorePreview = masterSequence.asset != null && !masterSequence.asset.editorSettings.scenePreview;
659 return Application.isPlaying || shouldIgnorePreview;
660 }
661 }
662
663
664 public TimelineWindow GetWindow()
665 {
666 return m_Window;
667 }
668
669 public void Play()
670 {
671 if (masterSequence.director == null)
672 return;
673
674 if (!previewMode)
675 previewMode = true;
676
677 if (previewMode)
678 {
679 if (masterSequence.time > masterSequence.duration)
680 masterSequence.time = 0;
681#if TIMELINE_FRAMEACCURATE
682 if (TimelinePreferences.instance.playbackLockedToFrame)
683 {
684 FrameRate frameRate = FrameRate.DoubleToFrameRate(masterSequence.asset.editorSettings.frameRate);
685 masterSequence.director.Play(frameRate);
686 }
687 else
688 {
689 masterSequence.director.Play();
690 }
691#else
692 masterSequence.director.Play();
693#endif
694 masterSequence.director.ProcessPendingGraphChanges();
695 PlayableDirector.ResetFrameTiming();
696 InvokePlayStateChangeCallback(true);
697 }
698 }
699
700 public void Pause()
701 {
702 if (masterSequence.director != null)
703 {
704 masterSequence.director.Pause();
705 masterSequence.director.ProcessPendingGraphChanges();
706 SynchronizeSequencesAfterPlayback();
707 InvokePlayStateChangeCallback(false);
708 }
709 }
710
711 public void SetPlaying(bool start)
712 {
713 if (start && !playing)
714 {
715 Play();
716 }
717
718 if (!start && playing)
719 {
720 Pause();
721 }
722
723 analytics.SendPlayEvent(start);
724 }
725
726 public void Stop()
727 {
728 if (masterSequence.director != null)
729 {
730 masterSequence.director.Stop();
731 masterSequence.director.ProcessPendingGraphChanges();
732 InvokePlayStateChangeCallback(false);
733 }
734 }
735
736 void InvokePlayStateChangeCallback(bool isPlaying)
737 {
738 if (OnPlayStateChange != null)
739 OnPlayStateChange.Invoke(isPlaying);
740 }
741
742 public void RebuildPlayableGraph()
743 {
744 if (masterSequence.director != null)
745 {
746 masterSequence.director.RebuildGraph();
747 // rebuild both the parent and the edit sequences. control tracks don't necessary
748 // rebuild the subdirector on recreation
749 if (editSequence.director != null && editSequence.director != masterSequence.director)
750 {
751 editSequence.director.RebuildGraph();
752 }
753 }
754 }
755
756 public void Evaluate()
757 {
758 if (masterSequence.director != null)
759 {
760 if (!EditorApplication.isPlaying && !previewMode)
761 GatherProperties(masterSequence.director);
762
763 ForceTimeOnDirector(masterSequence.director);
764 masterSequence.director.DeferredEvaluate();
765
766 if (EditorApplication.isPlaying == false)
767 {
768 PlayModeView.RepaintAll();
769 SceneView.RepaintAll();
770 AudioMixerWindow.RepaintAudioMixerWindow();
771 }
772 }
773 }
774
775 public void EvaluateImmediate()
776 {
777 if (masterSequence.director != null && masterSequence.director.isActiveAndEnabled)
778 {
779 if (!EditorApplication.isPlaying && !previewMode)
780 GatherProperties(masterSequence.director);
781
782 if (previewMode)
783 {
784 ForceTimeOnDirector(masterSequence.director);
785 masterSequence.director.ProcessPendingGraphChanges();
786 masterSequence.director.Evaluate();
787 }
788 }
789 }
790
791 public void Refresh()
792 {
793 CheckRecordingState();
794 dirtyStamp = dirtyStamp + 1;
795
796 rebuildGraph = true;
797 }
798
799 public void UpdateViewStateHash()
800 {
801 viewStateHash = timeAreaTranslation.GetHashCode()
802 .CombineHash(timeAreaScale.GetHashCode())
803 .CombineHash(trackScale.GetHashCode());
804 }
805
806 public bool IsEditingASubTimeline()
807 {
808 return editSequence != masterSequence;
809 }
810
811 public bool IsEditingAnEmptyTimeline()
812 {
813 return editSequence.asset == null;
814 }
815
816 public bool IsEditingAPrefabAsset()
817 {
818 var stage = PrefabStageUtility.GetCurrentPrefabStage();
819 return stage != null && editSequence.director != null && stage.IsPartOfPrefabContents(editSequence.director.gameObject);
820 }
821
822 public bool IsCurrentEditingASequencerTextField()
823 {
824 if (editSequence.asset == null)
825 return false;
826
827 if (k_TimeCodeTextFieldId == GUIUtility.keyboardControl)
828 return true;
829
830 return editSequence.asset.flattenedTracks.Count(t => t.GetInstanceID() == GUIUtility.keyboardControl) != 0;
831 }
832
833 public float TimeToTimeAreaPixel(double t) // TimeToTimeAreaPixel
834 {
835 float pixelX = (float)t;
836 pixelX *= timeAreaScale.x;
837 pixelX += timeAreaTranslation.x + sequencerHeaderWidth;
838 return pixelX;
839 }
840
841 public float TimeToScreenSpacePixel(double time)
842 {
843 float pixelX = (float)time;
844 pixelX *= timeAreaScale.x;
845 pixelX += timeAreaTranslation.x;
846 return pixelX;
847 }
848
849 public float TimeToPixel(double time)
850 {
851 return m_Window.timeArea.TimeToPixel((float)time, timeAreaRect);
852 }
853
854 public float PixelToTime(float pixel)
855 {
856 return m_Window.timeArea.PixelToTime(pixel, timeAreaRect);
857 }
858
859 public float PixelDeltaToDeltaTime(float p)
860 {
861 return PixelToTime(p) - PixelToTime(0);
862 }
863
864 public float TimeAreaPixelToTime(float pixel)
865 {
866 return PixelToTime(pixel);
867 }
868
869 public float ScreenSpacePixelToTimeAreaTime(float p)
870 {
871 // transform into track space by offsetting the pixel by the screen-space offset of the time area
872 p -= timeAreaRect.x;
873 return TrackSpacePixelToTimeAreaTime(p);
874 }
875
876 public float TrackSpacePixelToTimeAreaTime(float p)
877 {
878 p -= timeAreaTranslation.x;
879
880 if (timeAreaScale.x > 0.0f)
881 return p / timeAreaScale.x;
882
883 return p;
884 }
885
886 public void OffsetTimeArea(int pixels)
887 {
888 Vector3 tx = timeAreaTranslation;
889 tx.x += pixels;
890 SetTimeAreaTransform(tx, timeAreaScale);
891 }
892
893 public GameObject GetSceneReference(TrackAsset asset)
894 {
895 if (editSequence.director == null)
896 return null; // no player bound
897
898 return TimelineUtility.GetSceneGameObject(editSequence.director, asset);
899 }
900
901 public void CalculateRowRects()
902 {
903 // arming a track might add inline curve tracks, recalc track heights
904 if (m_Window != null && m_Window.treeView != null)
905 m_Window.treeView.CalculateRowRects();
906 }
907
908 // Only one track within a 'track' hierarchy can be armed
909 public void ArmForRecord(TrackAsset track)
910 {
911 m_ArmedTracks[TimelineUtility.GetSceneReferenceTrack(track)] = track;
912 if (track != null && !recording)
913 recording = true;
914 if (!recording)
915 return;
916
917 track.OnRecordingArmed(editSequence.director);
918 CalculateRowRects();
919 }
920
921 public void UnarmForRecord(TrackAsset track)
922 {
923 m_ArmedTracks.Remove(TimelineUtility.GetSceneReferenceTrack(track));
924 if (m_ArmedTracks.Count == 0)
925 recording = false;
926 track.OnRecordingUnarmed(editSequence.director);
927 }
928
929 public void UpdateRecordingState()
930 {
931 if (recording)
932 {
933 foreach (var track in m_ArmedTracks.Values)
934 {
935 if (track != null)
936 track.OnRecordingTimeChanged(editSequence.director);
937 }
938 }
939 }
940
941 public bool IsTrackRecordable(TrackAsset track)
942 {
943 // A track with animated parameters can always be recorded to
944 return IsArmedForRecord(track) || track.HasAnyAnimatableParameters();
945 }
946
947 public bool IsArmedForRecord(TrackAsset track)
948 {
949 return track == GetArmedTrack(track);
950 }
951
952 public TrackAsset GetArmedTrack(TrackAsset track)
953 {
954 TrackAsset outTrack;
955 m_ArmedTracks.TryGetValue(TimelineUtility.GetSceneReferenceTrack(track), out outTrack);
956 return outTrack;
957 }
958
959 void CheckRecordingState()
960 {
961 // checks for deleted tracks, and makes sure the recording state matches
962 if (m_ArmedTracks.Any(t => t.Value == null))
963 {
964 m_ArmedTracks = m_ArmedTracks.Where(t => t.Value != null).ToDictionary(t => t.Key, t => t.Value);
965 if (m_ArmedTracks.Count == 0)
966 recording = false;
967 }
968 }
969
970 void OnCurrentDirectorWillChange()
971 {
972 if (ignorePreview)
973 return;
974
975 SynchronizeViewModelTime(editSequence);
976 Stop();
977 rebuildGraph = true; // needed for asset previews
978 }
979
980 public void GatherProperties(PlayableDirector director)
981 {
982 if (director == null || Application.isPlaying)
983 return;
984
985 var asset = director.playableAsset as TimelineAsset;
986 if (asset != null && !asset.editorSettings.scenePreview)
987 return;
988
989 if (!previewMode)
990 {
991 AnimationMode.StartAnimationMode(previewDriver);
992
993 OnStartPreview(director);
994
995 AnimationPropertyContextualMenu.Instance.SetResponder(new TimelineRecordingContextualResponder(this));
996 if (!previewMode)
997 return;
998 EnsureWindowTimeConsistency();
999 }
1000
1001 if (asset != null)
1002 {
1003 m_PropertyCollector.Reset();
1004 m_PropertyCollector.PushActiveGameObject(null); // avoid overflow on unbound tracks
1005 asset.GatherProperties(director, m_PropertyCollector);
1006 }
1007 }
1008
1009 void OnStartPreview(PlayableDirector director)
1010 {
1011 previewedDirectors = TimelineUtility.GetAllDirectorsInHierarchy(director).ToList();
1012
1013 if (previewedDirectors == null)
1014 return;
1015
1016 m_PreviewedAnimators = PreviewedBindings<Animator>.GetPreviewedBindings(previewedDirectors);
1017
1018 m_PreviewedComponents = m_PreviewedAnimators.GetUniqueBindings()
1019 .SelectMany(animator => animator.GetComponents<IAnimationWindowPreview>()
1020 .Cast<Component>())
1021 .ToList();
1022
1023 foreach (var previewedComponent in previewedComponents)
1024 {
1025 previewedComponent.StartPreview();
1026 }
1027#if UNITY_2022_2_OR_NEWER
1028 PrefabUtility.allowRecordingPrefabPropertyOverridesFor += AllowRecordingPrefabPropertyOverridesFor;
1029#endif //UNITY_2022_2_OR_NEWER
1030 }
1031
1032 internal bool AllowRecordingPrefabPropertyOverridesFor(Object componentOrGameObject)
1033 {
1034 if (componentOrGameObject == null)
1035 throw new ArgumentNullException(nameof(componentOrGameObject));
1036
1037 if (previewMode == false)
1038 return true;
1039
1040 if (m_ArmedTracks.Count == 0)
1041 return true;
1042
1043 GameObject inputGameObject = null;
1044 if (componentOrGameObject is Component component)
1045 {
1046 inputGameObject = component.gameObject;
1047 }
1048 else if (componentOrGameObject is GameObject gameObject)
1049 {
1050 inputGameObject = gameObject;
1051 }
1052
1053 if (inputGameObject == null)
1054 return true;
1055
1056 var armedTracks = m_ArmedTracks.Keys;
1057 foreach (var track in armedTracks)
1058 {
1059 var animators = m_PreviewedAnimators.GetBindingsForObject(track);
1060 foreach (var animator in animators)
1061 {
1062 if (animator == null)
1063 continue;
1064
1065 if (inputGameObject.transform.IsChildOf(animator.transform))
1066 return false;
1067 }
1068 }
1069
1070 return true;
1071 }
1072
1073 void OnStopPreview()
1074 {
1075 if (m_PreviewedComponents != null)
1076 {
1077 foreach (var previewComponent in previewedComponents)
1078 {
1079 previewComponent.StopPreview();
1080 }
1081
1082 m_PreviewedComponents = null;
1083 }
1084
1085 foreach (var previewAnimator in m_PreviewedAnimators.GetUniqueBindings())
1086 {
1087 if (previewAnimator != null)
1088 previewAnimator.UnbindAllHandles();
1089 }
1090 m_PreviewedAnimators = default;
1091
1092#if UNITY_2022_2_OR_NEWER
1093 PrefabUtility.allowRecordingPrefabPropertyOverridesFor -= AllowRecordingPrefabPropertyOverridesFor;
1094#endif //UNITY_2022_2_OR_NEWER
1095 }
1096
1097 internal void ProcessStartFramePendingUpdates()
1098 {
1099 if (m_OnStartFrameUpdates != null)
1100 m_OnStartFrameUpdates.RemoveAll(callback => callback.Invoke(this, Event.current));
1101 }
1102
1103 internal void ProcessEndFramePendingUpdates()
1104 {
1105 if (m_OnEndFrameUpdates != null)
1106 m_OnEndFrameUpdates.RemoveAll(callback => callback.Invoke(this, Event.current));
1107 }
1108
1109 public void AddStartFrameDelegate(PendingUpdateDelegate updateDelegate)
1110 {
1111 if (m_OnStartFrameUpdates == null)
1112 m_OnStartFrameUpdates = new List<PendingUpdateDelegate>();
1113 if (m_OnStartFrameUpdates.Contains(updateDelegate))
1114 return;
1115 m_OnStartFrameUpdates.Add(updateDelegate);
1116 }
1117
1118 public void AddEndFrameDelegate(PendingUpdateDelegate updateDelegate)
1119 {
1120 if (m_OnEndFrameUpdates == null)
1121 m_OnEndFrameUpdates = new List<PendingUpdateDelegate>();
1122 if (m_OnEndFrameUpdates.Contains(updateDelegate))
1123 return;
1124 m_OnEndFrameUpdates.Add(updateDelegate);
1125 }
1126
1127 internal void InvokeWindowOnGuiStarted(Event evt)
1128 {
1129 if (windowOnGuiStarted != null)
1130 windowOnGuiStarted.Invoke(this, evt);
1131 }
1132
1133 public void UpdateRootPlayableDuration(double duration)
1134 {
1135 if (editSequence.director != null)
1136 {
1137 if (editSequence.director.playableGraph.IsValid())
1138 {
1139 if (editSequence.director.playableGraph.GetRootPlayableCount() > 0)
1140 {
1141 var rootPlayable = editSequence.director.playableGraph.GetRootPlayable(0);
1142 if (rootPlayable.IsValid())
1143 rootPlayable.SetDuration(duration);
1144 }
1145 }
1146 }
1147 }
1148
1149 public void InvokeTimeChangeCallback()
1150 {
1151 if (OnTimeChange != null)
1152 OnTimeChange.Invoke();
1153 }
1154
1155 PlayRange ValidatePlayRange(PlayRange range, ISequenceState sequenceState)
1156 {
1157 if (range == TimelineAssetViewModel.NoPlayRangeSet)
1158 return range;
1159
1160 double minimumPlayRangeTime = (0.01 / Math.Max(1.0, sequenceState.frameRate));
1161
1162 // Validate min
1163 if (range.end - range.start < minimumPlayRangeTime)
1164 range.start = range.end - minimumPlayRangeTime;
1165
1166 if (range.start < 0.0)
1167 range.start = 0.0;
1168
1169 // Validate max
1170 if (range.end > sequenceState.duration)
1171 range.end = sequenceState.duration;
1172
1173 if (range.end - range.start < minimumPlayRangeTime)
1174 range.end = Math.Min(range.start + minimumPlayRangeTime, sequenceState.duration);
1175
1176 return range;
1177 }
1178
1179 void EnsureWindowTimeConsistency()
1180 {
1181 if (masterSequence.director != null && masterSequence.viewModel != null && !ignorePreview)
1182 masterSequence.time = masterSequence.viewModel.windowTime;
1183 }
1184
1185 void SynchronizeSequencesAfterPlayback()
1186 {
1187 // Synchronizing editSequence will synchronize all view models up to the master
1188 SynchronizeViewModelTime(editSequence);
1189 }
1190
1191 static void SynchronizeViewModelTime(ISequenceState state)
1192 {
1193 if (state.director == null || state.viewModel == null)
1194 return;
1195
1196 var t = state.time;
1197 state.time = t;
1198 }
1199
1200 // because we may be evaluating outside the duration of the root playable
1201 // we explicitly set the time - this causes the graph to not 'advance' the time
1202 // because advancing it can force it to change due to wrapping to the duration
1203 // This can happen if the graph is force evaluated outside it's duration
1204 // case 910114, 936844 and 943377
1205 static void ForceTimeOnDirector(PlayableDirector director)
1206 {
1207 var directorTime = director.time;
1208 director.time = directorTime;
1209 }
1210
1211 public bool IsPlayableGraphDone()
1212 {
1213 return masterSequence.director != null
1214 && masterSequence.director.playableGraph.IsValid()
1215 && masterSequence.director.playableGraph.IsDone();
1216 }
1217 }
1218}