A game about forced loneliness, made by TACStudios
at master 1218 lines 40 kB view raw
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}