// Copyright Epic Games, Inc. All Rights Reserved. #include "SequencerEdMode.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/SequenceModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/ObjectBindingModelStorageExtension.h" #include "MVVM/ViewModels/ChannelModel.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/Views/STrackAreaView.h" #include "MVVM/Selection/Selection.h" #include "EditorViewportClient.h" #include "Curves/KeyHandle.h" #include "ISequencer.h" #include "MovieSceneSequence.h" #include "MovieScene.h" #include "IKeyArea.h" #include "SceneView.h" #include "Sequencer.h" #include "Framework/Application/SlateApplication.h" #include "Evaluation/MovieSceneEvaluationTrack.h" #include "SequencerCommonHelpers.h" #include "MovieSceneHitProxy.h" #include "Tracks/MovieScene3DTransformTrack.h" #include "Sections/MovieScene3DTransformSection.h" #include "Tracks/MovieSceneAudioTrack.h" #include "Sections/MovieSceneAudioSection.h" #include "Compilation/MovieSceneCompiledDataManager.h" #include "SubtitleManager.h" #include "SequencerMeshTrail.h" #include "SequencerKeyActor.h" #include "EditorWorldExtension.h" #include "ViewportWorldInteraction.h" #include "SSequencer.h" #include "MovieSceneTracksComponentTypes.h" #include "EntitySystem/Interrogation/MovieSceneInterrogationLinker.h" #include "SequencerSectionPainter.h" #include "MovieSceneTimeHelpers.h" #include "MovieSceneToolHelpers.h" #include "Tools/MotionTrailOptions.h" #include "SequencerCommands.h" #include "SnappingUtils.h" #include "SequencerNodeTree.h" #include "SequencerSettings.h" #include "UnrealEdGlobals.h" #include "UnrealEdMisc.h" #include "Editor/UnrealEdEngine.h" #include "TextureResource.h" #include "EditorModeManager.h" #include "IMovieScenePlaybackClient.h" #include "Components/PrimitiveComponent.h" #include "SequencerSelectabilityTool.h" const FEditorModeID FSequencerEdMode::EM_SequencerMode(TEXT("EM_SequencerMode")); static TAutoConsoleVariable CVarDrawMeshTrails( TEXT("Sequencer.DrawMeshTrails"), true, TEXT("Toggle to show or hide Level Sequence VR Editor trails")); //DEPRECRATED TAutoConsoleVariable CVarUseOldSequencerMotionTrails( TEXT("Sequencer.UseOldSequencerTrails"), true, TEXT("DEPRECRATED: Will show old trails outside animation mode, otherwise will show newer version for all objects.")); namespace UE { namespace SequencerEdMode { static const float DrawTrackTimeRes = 0.1f; struct FTrackTransforms { TArray Times; TArray Transforms; void Initialize(UObject* BoundObject, TArrayView TrajectoryKeys, ISequencer* Sequencer) { using namespace UE::MovieScene; // Hack: static system interrogator for now to avoid re-allocating UObjects all the time static FSystemInterrogator Interrogator; Interrogator.Reset(); USceneComponent* SceneComponent = Cast(BoundObject); if (!SceneComponent) { AActor* Actor = Cast(BoundObject); SceneComponent = Actor ? Actor->GetRootComponent() : nullptr; } if (!SceneComponent) { return; } FFrameRate TickResolution = Sequencer->GetFocusedTickResolution(); TRange ViewRange(TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetLowerBoundValue()), TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetUpperBoundValue())); Times.Reserve(TrajectoryKeys.Num()); FInterrogationChannel Channel = Interrogator.ImportTransformHierarchy(SceneComponent, Sequencer, Sequencer->GetFocusedTemplateID()); TArray SubsequenceHierarchy = Sequencer->GetSubSequenceHierarchy(); const FMovieSceneRootEvaluationTemplateInstance& RootInstance = Sequencer->GetEvaluationTemplate(); const FMovieSceneSequenceHierarchy* Hierarchy = RootInstance.GetCompiledDataManager()->FindHierarchy(RootInstance.GetCompiledDataID()); if (Hierarchy) { Interrogator.SetHierarchy(const_cast(Hierarchy)); FMovieSceneSequenceID OwningSequenceID = MovieSceneSequenceID::Root; for (const FMovieSceneSequenceID SubSequenceID : SubsequenceHierarchy) { if (const UMovieSceneSubSection* SubSection = Sequencer->FindSubSection(SubSequenceID)) { if (UMovieSceneTrack* SubTrack = Cast(SubSection->GetOuter())) { Interrogator.ImportTrack(SubTrack, FInterrogationChannel::Default(), OwningSequenceID); } } OwningSequenceID = SubSequenceID; } } const int32 NumTrajectoryKeys = TrajectoryKeys.Num(); for (int32 Index = 0; Index < TrajectoryKeys.Num(); ++Index) { const FTrajectoryKey& ThisKey = TrajectoryKeys[Index]; Times.Add(ThisKey.Time); Interrogator.AddInterrogation(ThisKey.Time); const bool bIsConstantKey = ThisKey.Is(ERichCurveInterpMode::RCIM_Constant); if (!bIsConstantKey && Index != NumTrajectoryKeys-1) { const FTrajectoryKey& NextKey = TrajectoryKeys[Index+1]; FFrameTime Diff = NextKey.Time - ThisKey.Time; int32 NumSteps = FMath::CeilToInt(TickResolution.AsSeconds(Diff) / DrawTrackTimeRes); // Limit the number of steps to prevent a rendering performance hit NumSteps = FMath::Min( 100, NumSteps ); // Ensure that sub steps evaluate at equal points between the key times such that a NumSteps=2 results in: // PrevKey step1 step2 ThisKey // | ' ' | NumSteps += 1; for (int32 Substep = 1; Substep < NumSteps; ++Substep) { FFrameTime Time = ThisKey.Time + (Diff * float(Substep)/NumSteps); Times.Add(Time); Interrogator.AddInterrogation(Time); } } } TArray OriginTransforms; // Get the Instance Data override from the world context object. const IMovieScenePlaybackClient* Client = Sequencer->GetPlaybackClient(); const UObject* InstanceData = Client ? Client->GetInstanceData() : nullptr; Interrogator.Update(); Interrogator.QueryWorldSpaceTransforms(Channel, Transforms); Interrogator.QueryTransformOrigins(OriginTransforms, SubsequenceHierarchy, InstanceData); for (int32 Index = 0; Index < Times.Num(); ++Index) { if (Transforms.IsValidIndex(Index)) { Transforms[Index] *= OriginTransforms[Index]; } } check(Transforms.Num() == Times.Num()); Interrogator.Reset(); } }; } // namespace SequencerEdMode } // namespace UE FSequencerEdMode::FSequencerEdMode() { DefaultTool = MakeShared(this); SelectabilityTool = MakeShared(FOnGetWorld::CreateRaw(this, &FSequencerEdMode::GetWorld) , FOnIsObjectSelectableInViewport::CreateRaw(this, &FSequencerEdMode::IsObjectSelectableInViewport)); Tools.Add(DefaultTool.Get()); Tools.Add(SelectabilityTool.Get()); SetCurrentTool(DefaultTool.Get()); bDrawMeshTrails = CVarDrawMeshTrails->GetBool(); CVarDrawMeshTrails.AsVariable()->SetOnChangedCallback(FConsoleVariableDelegate::CreateLambda([this](IConsoleVariable* Var) { bDrawMeshTrails = Var->GetBool(); })); AudioTexture = LoadObject(NULL, TEXT("/Engine/EditorResources/AudioIcons/S_AudioComponent.S_AudioComponent")); check(AudioTexture); } FSequencerEdMode::~FSequencerEdMode() { CVarDrawMeshTrails.AsVariable()->SetOnChangedCallback(FConsoleVariableDelegate()); } void FSequencerEdMode::Enter() { bIsTracking = false;; StartXValue.Reset(); FEdMode::Enter(); } void FSequencerEdMode::Exit() { CleanUpMeshTrails(); Sequencers.Reset(); FEdMode::Exit(); } bool FSequencerEdMode::IsCompatibleWith(FEditorModeID OtherModeID) const { // Compatible with all modes so that we can take over with the sequencer hotkeys return true; } bool FSequencerEdMode::InputKey( FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event ) { if (Event != IE_Released) { if (const TSharedPtr ActiveSequencer = GetFirstActiveSequencer()) { FModifierKeysState KeyState = FSlateApplication::Get().GetModifierKeys(); if (ActiveSequencer->GetCommandBindings(ESequencerCommandBindings::Shared).Get()->ProcessCommandBindings(Key, KeyState, (Event == IE_Repeat) )) { return true; } if (IsPressingMoveTimeSlider(Viewport)) //this is needed to make sure we get all of the processed mouse events, for some reason the above may not return true { return true; } } } return FEdMode::InputKey(ViewportClient, Viewport, Key, Event); } bool FSequencerEdMode::IsPressingMoveTimeSlider(FViewport* InViewport) const { const FSequencerCommands& Commands = FSequencerCommands::Get(); bool bIsMovingTimeSlider = false; // Need to iterate through primary and secondary to make sure they are all pressed. for (uint32 i = 0; i < static_cast(EMultipleKeyBindingIndex::NumChords); ++i) { EMultipleKeyBindingIndex ChordIndex = static_cast(i); const FInputChord& Chord = *Commands.ScrubTimeViewport->GetActiveChord(ChordIndex); bIsMovingTimeSlider |= Chord.IsValidChord() && InViewport->KeyState(Chord.Key) && (Chord.NeedsAlt() ? IsAltDown(InViewport) : !IsAltDown(InViewport)) && (Chord.NeedsShift() ? IsShiftDown(InViewport) : !IsShiftDown(InViewport)) && (Chord.NeedsControl() ? IsCtrlDown(InViewport) : !IsCtrlDown(InViewport)); } return bIsMovingTimeSlider; } //just get the first one. USequencerSettings* FSequencerEdMode:: GetSequencerSettings() const { if (const TSharedPtr ActiveSequencer = GetFirstActiveSequencer()) { return ActiveSequencer->GetSequencerSettings(); } return nullptr; } bool FSequencerEdMode::IsMovingCamera(FViewport* InViewport) const { const bool LeftMouseButtonDown = InViewport->KeyState(EKeys::LeftMouseButton); const bool bIsAltKeyDown = InViewport->KeyState(EKeys::LeftAlt) || InViewport->KeyState(EKeys::RightAlt); const USequencerSettings* SequencerSettings = GetSequencerSettings(); return ((SequencerSettings ? SequencerSettings->GetLeftMouseDragDoesMarquee() : false) && LeftMouseButtonDown && bIsAltKeyDown); } bool FSequencerEdMode::IsDoingDrag(FViewport* InViewport) const { const USequencerSettings* SequencerSettings = GetSequencerSettings(); const bool LeftMouseButtonDown = InViewport->KeyState(EKeys::LeftMouseButton); const bool bIsCtrlKeyDown = InViewport->KeyState(EKeys::LeftControl) || InViewport->KeyState(EKeys::RightControl); const bool bIsAltKeyDown = InViewport->KeyState(EKeys::LeftAlt) || InViewport->KeyState(EKeys::RightAlt); EAxisList::Type CurrentAxis = GetCurrentWidgetAxis(); //if shift is down we still want to drag return LeftMouseButtonDown && (CurrentAxis == EAxisList::None) && !bIsCtrlKeyDown && !bIsAltKeyDown && (SequencerSettings ? SequencerSettings->GetLeftMouseDragDoesMarquee() : false); } TSharedPtr FSequencerEdMode::GetFirstActiveSequencer() const { for (const TWeakPtr SequencerWeak : Sequencers) { if (const TSharedPtr Sequencer = SequencerWeak.Pin()) { return Sequencer; } } return nullptr; } bool FSequencerEdMode::StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport) { if (SelectabilityTool->IsSelectionLimited()) { SelectabilityTool->StartTracking(InViewportClient, InViewport); } if (IsPressingMoveTimeSlider(InViewport)) { TSharedPtr ActiveSequencer; for (TWeakPtr WeakSequencer : Sequencers) { ActiveSequencer = WeakSequencer.Pin(); if (ActiveSequencer.IsValid()) { break; } } if (ActiveSequencer.IsValid()) { ActiveSequencer->SetPlaybackStatus(EMovieScenePlayerStatus::Scrubbing); ActiveSequencer->OnBeginScrubbingEvent().Broadcast(); } return true; } else if (IsMovingCamera(InViewport)) { bUpdatePivot = true; InViewportClient->SetCurrentWidgetAxis(EAxisList::None); return true; } else if (IsDoingDrag(InViewport)) { bUpdatePivot = true; DragToolHandler.MakeDragTool(InViewportClient); return DragToolHandler.StartTracking(InViewportClient, InViewport); } return FEdMode::StartTracking(InViewportClient, InViewport); } bool FSequencerEdMode::EndTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport) { if (SelectabilityTool->IsSelectionLimited()) { SelectabilityTool->EndTracking(InViewportClient, InViewport); } if (IsPressingMoveTimeSlider(InViewport)) { return true; } else if (IsMovingCamera(InViewport)) { bUpdatePivot = false; return true; } else if (DragToolHandler.EndTracking(InViewportClient, InViewport)) { bUpdatePivot = false; return true; } return FEdMode::EndTracking(InViewportClient, InViewport); } void FSequencerEdMode::Tick(FEditorViewportClient* ViewportClient,float DeltaTime) { if (bUpdatePivot) { GUnrealEd->UpdatePivotLocationForSelection(); } return FEdMode::Tick(ViewportClient,DeltaTime); } bool FSequencerEdMode::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) { if (IsPressingMoveTimeSlider(InViewport)) { return true; } else if (IsMovingCamera(InViewport)) { if (InDrag.IsNearlyZero() == false || InRot.IsNearlyZero() == false || InScale.IsNearlyZero() == false) { InViewportClient->SetCurrentWidgetAxis(EAxisList::None); InViewportClient->PeformDefaultCameraMovement(InDrag, InRot, InScale); GUnrealEd->UpdatePivotLocationForSelection(); GUnrealEd->RedrawLevelEditingViewports(); } return true; } else if (IsDoingDrag(InViewport)) { return DragToolHandler.InputDelta(InViewportClient, InViewport, InDrag, InRot, InScale); } return FEdMode::InputDelta(InViewportClient, InViewport, InDrag, InRot, InScale); } bool FSequencerEdMode::ProcessCapturedMouseMoves(FEditorViewportClient* InViewportClient, FViewport* InViewport, const TArrayView& CapturedMouseMoves) { const TSharedPtr ActiveSequencer = GetFirstActiveSequencer(); const bool bTimeChange = IsPressingMoveTimeSlider(InViewport); if (CapturedMouseMoves.Num() > 0) { if (ActiveSequencer.IsValid()) { if (bTimeChange) { for (int32 Index = 0; Index < CapturedMouseMoves.Num(); ++Index) { int32 X = CapturedMouseMoves[Index].X; if (StartXValue.IsSet() == false) { StartXValue = X; FQualifiedFrameTime CurrentTime = ActiveSequencer->GetLocalTime(); StartFrameNumber = CurrentTime.Time.GetFrame(); } else { int32 Diff = X - StartXValue.GetValue(); if (Diff != 0) { FIntPoint Origin, Size; InViewportClient->GetViewportDimensions(Origin, Size); const float ViewPortSize = (float)Size.X; const float FloatViewDiff = (float)(Diff) / ViewPortSize; FFrameRate TickResolution = ActiveSequencer->GetFocusedTickResolution(); TPair ViewRange(TickResolution.AsFrameNumber(ActiveSequencer->GetViewRange().GetLowerBoundValue()), TickResolution.AsFrameNumber(ActiveSequencer->GetViewRange().GetUpperBoundValue())); FFrameNumber FrameDiff = ViewRange.Value - ViewRange.Key; FrameDiff = FrameDiff * FloatViewDiff; FFrameTime ScrubTime = StartFrameNumber + FrameDiff; ActiveSequencer->SnapSequencerTime(ScrubTime); ActiveSequencer->SetLocalTime(ScrubTime.GetFrame()); } } if (bIsTracking == false) { ActiveSequencer->SetPlaybackStatus(EMovieScenePlayerStatus::Scrubbing); ActiveSequencer->OnBeginScrubbingEvent().Broadcast(); bIsTracking = true; } } } else if(bIsTracking) { bIsTracking = false; ActiveSequencer->SetPlaybackStatus(EMovieScenePlayerStatus::Stopped); ActiveSequencer->OnEndScrubbingEvent().Broadcast(); StartXValue.Reset(); } } else { bIsTracking = false; StartXValue.Reset(); } return bIsTracking; } else if (bTimeChange == false && bIsTracking) { if (ActiveSequencer.IsValid()) { bIsTracking = false; ActiveSequencer->SetPlaybackStatus(EMovieScenePlayerStatus::Stopped); ActiveSequencer->OnEndScrubbingEvent().Broadcast(); StartXValue.Reset(); } else { bIsTracking = false; StartXValue.Reset(); } } return false; } bool FSequencerEdMode::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy *InHitProxy, const FViewportClick &InClick) { if (SelectabilityTool->IsSelectionLimited()) { return SelectabilityTool->HandleClick(InViewportClient, InHitProxy, InClick); } return FEdMode::HandleClick(InViewportClient, InHitProxy, InClick); } bool FSequencerEdMode::BoxSelect(FBox& InBox, bool InSelect) { if (SelectabilityTool->IsSelectionLimited()) { return SelectabilityTool->BoxSelect(InBox, InSelect); } return FEdMode::BoxSelect(InBox, InSelect); } bool FSequencerEdMode::FrustumSelect(const FConvexVolume& InFrustum, FEditorViewportClient* InViewportClient, bool InSelect) { if (SelectabilityTool->IsSelectionLimited()) { return SelectabilityTool->FrustumSelect(InFrustum, InViewportClient, InSelect); } return FEdMode::FrustumSelect(InFrustum, InViewportClient, InSelect); } bool FSequencerEdMode::GetCursor(EMouseCursor::Type& OutCursor) const { if (SelectabilityTool->GetCursorForHovered(OutCursor)) { return true; } return FEdMode::GetCursor(OutCursor); } bool FSequencerEdMode::MouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InX, int32 InY) { if (InViewportClient && InViewportClient->Viewport && SelectabilityTool->IsSelectionLimited()) { HHitProxy* const HitResult = InViewportClient->Viewport->GetHitProxy(InX, InY); SelectabilityTool->UpdateHoverFromHitProxy(HitResult); } return FEdMode::MouseMove(InViewportClient, InViewport, InX, InY); } void FSequencerEdMode::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) { FEdMode::Render(View, Viewport, PDI); DragToolHandler.Render3DDragTool(View, PDI); #if WITH_EDITORONLY_DATA if (PDI) { DrawAudioTracks(PDI); } // Draw spline trails using the PDI if (View->Family->EngineShowFlags.Splines) { DrawTracks3D(PDI); } // Draw mesh trails (doesn't use the PDI) else if (bDrawMeshTrails) { PDI = nullptr; DrawTracks3D(PDI); } #endif } void FSequencerEdMode::DrawHUD(FEditorViewportClient* ViewportClient, FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) { FEdMode::DrawHUD(ViewportClient,Viewport,View,Canvas); DragToolHandler.RenderDragTool(View, Canvas); if( ViewportClient->AllowsCinematicControl() ) { // Get the size of the viewport const int32 SizeX = Viewport->GetSizeXY().X; const int32 SizeY = Viewport->GetSizeXY().Y; // Draw subtitles (toggle is handled internally) FVector2D MinPos(0.f, 0.f); FVector2D MaxPos(1.f, .9f); FIntRect SubtitleRegion(FMath::TruncToInt(SizeX * MinPos.X), FMath::TruncToInt(SizeY * MinPos.Y), FMath::TruncToInt(SizeX * MaxPos.X), FMath::TruncToInt(SizeY * MaxPos.Y)); FSubtitleManager::GetSubtitleManager()->DisplaySubtitles( Canvas, SubtitleRegion, ViewportClient->GetWorld()->GetAudioTimeSeconds() ); } if (SelectabilityTool->IsSelectionLimited()) { SelectabilityTool->DrawHUD(ViewportClient, Viewport, View, Canvas); } } void FSequencerEdMode::AddReferencedObjects(FReferenceCollector& Collector) { for (FMeshTrailData& MeshTrail : MeshTrails) { Collector.AddReferencedObject(MeshTrail.Track); Collector.AddReferencedObject(MeshTrail.Trail); } } void FSequencerEdMode::OnKeySelected(FViewport* Viewport, HMovieSceneKeyProxy* KeyProxy) { using namespace UE::Sequencer; if (!KeyProxy) { return; } const bool bToggleSelection = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); const bool bAddToSelection = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift); for (TWeakPtr WeakSequencer : Sequencers) { TSharedPtr Sequencer = WeakSequencer.Pin(); if (Sequencer.IsValid()) { Sequencer->SetLocalTimeDirectly(KeyProxy->Key.Time); FSequencerSelection& Selection = *Sequencer->GetViewModel()->GetSelection(); FSelectionEventSuppressor EventSuppressor = Selection.SuppressEvents(); if (!bAddToSelection && !bToggleSelection) { Selection.KeySelection.Empty(); } for (const FTrajectoryKey::FData& KeyData : KeyProxy->Key.KeyData) { UMovieSceneSection* Section = KeyData.Section.Get(); TSharedPtr SectionHandle = Sequencer->GetNodeTree()->GetSectionModel(Section); if (SectionHandle && KeyData.KeyHandle.IsSet()) { TParentFirstChildIterator KeyAreaNodes = SectionHandle->GetParentTrackModel().AsModel()->GetDescendantsOfType(); for (const TViewModelPtr& KeyAreaNode : KeyAreaNodes) { TSharedPtr Channel = KeyAreaNode->GetChannel(Section); if (Channel && Channel->GetKeyArea()->GetName() == KeyData.ChannelName) { if (bToggleSelection && Selection.KeySelection.IsSelected(KeyData.KeyHandle.GetValue())) { Selection.KeySelection.Deselect(KeyData.KeyHandle.GetValue()); } else { Selection.KeySelection.Select(Channel, KeyData.KeyHandle.GetValue()); } break; } } } } } } } void FSequencerEdMode::DrawMeshTransformTrailFromKey(const class ASequencerKeyActor* KeyActor) { ASequencerMeshTrail* Trail = Cast(KeyActor->GetOwner()); if(Trail != nullptr) { FMeshTrailData* TrailPtr = MeshTrails.FindByPredicate([Trail](const FMeshTrailData InTrail) { return Trail == InTrail.Trail; }); if(TrailPtr != nullptr) { // From the key, get the mesh trail, and then the track associated with that mesh trail UMovieScene3DTransformTrack* Track = TrailPtr->Track; // Draw a mesh trail for the key's associated actor TArray> KeyObjects; AActor* TrailActor = KeyActor->GetAssociatedActor(); KeyObjects.Add(TrailActor); FPrimitiveDrawInterface* PDI = nullptr; for (TWeakPtr WeakSequencer : Sequencers) { TSharedPtr Sequencer = WeakSequencer.Pin(); if (Sequencer.IsValid()) { DrawTransformTrack(Sequencer, PDI, Track, KeyObjects, true); } } } } } void FSequencerEdMode::CleanUpMeshTrails() { // Clean up any existing trails for (FMeshTrailData& MeshTrail : MeshTrails) { if (MeshTrail.Trail) { MeshTrail.Trail->Cleanup(); } } MeshTrails.Empty(); } void FSequencerEdMode::DrawTransformTrack(const TSharedPtr& Sequencer, FPrimitiveDrawInterface* PDI, UMovieScene3DTransformTrack* TransformTrack, TArrayView> BoundObjects, const bool bIsSelected) { using namespace UE::MovieScene; using namespace UE::Sequencer; const bool bHitTesting = PDI && PDI->IsHitTesting(); ASequencerMeshTrail* TrailActor = nullptr; // Get the Trail Actor associated with this track if we are drawing mesh trails if (bDrawMeshTrails) { FMeshTrailData* TrailPtr = MeshTrails.FindByPredicate([TransformTrack](const FMeshTrailData InTrail) { return InTrail.Track == TransformTrack; }); if (TrailPtr != nullptr) { TrailActor = TrailPtr->Trail; } } bool bShowTrajectory = TransformTrack->GetAllSections().ContainsByPredicate( [bIsSelected](UMovieSceneSection* Section) { UMovieScene3DTransformSection* TransformSection = Cast(Section); if (TransformSection) { switch (TransformSection->GetShow3DTrajectory()) { case EShow3DTrajectory::EST_Always: return true; case EShow3DTrajectory::EST_Never: return false; case EShow3DTrajectory::EST_OnlyWhenSelected: return bIsSelected; } } return false; } ); FFrameRate TickResolution = Sequencer->GetFocusedTickResolution(); if (!bShowTrajectory || !TransformTrack->GetAllSections().ContainsByPredicate([](UMovieSceneSection* In){ return In->IsActive(); })) { return; } TArray AllSectionsScratch; FLinearColor TrackColor = STrackAreaView::BlendDefaultTrackColor(TransformTrack->GetColorTint()); FColor KeyColor = TrackColor.ToFColor(true); // Draw one line per-track (should only really ever be one) TRange ViewRange(TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetLowerBoundValue()), TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetUpperBoundValue())); TArray TrajectoryKeys = TransformTrack->GetTrajectoryData(Sequencer->GetLocalTime().Time.FrameNumber, Sequencer->GetSequencerSettings()->GetTrajectoryPathCap(), ViewRange); for (TWeakObjectPtr<> WeakBinding : BoundObjects) { UObject* BoundObject = WeakBinding.Get(); if (!BoundObject) { continue; } UE::SequencerEdMode::FTrackTransforms TrackTransforms; TrackTransforms.Initialize(BoundObject, TrajectoryKeys, Sequencer.Get()); int32 TransformIndex = 0; for (int32 TrajectoryIndex = 0; TrajectoryIndex < TrajectoryKeys.Num(); ++TrajectoryIndex) { const FTrajectoryKey& ThisKey = TrajectoryKeys[TrajectoryIndex]; if (TransformIndex >= TrackTransforms.Transforms.Num()) { continue; } FTransform ThisTransform = TrackTransforms.Transforms[TransformIndex]; if (TrajectoryIndex < TrajectoryKeys.Num()-1) { FFrameTime NextKeyTime = TrajectoryKeys[TrajectoryIndex+1].Time; // Draw all the interpolated times between this and the next key FVector StartPosition = TrackTransforms.Transforms[TransformIndex].GetTranslation(); ++TransformIndex; const bool bIsConstantKey = ThisKey.Is(ERichCurveInterpMode::RCIM_Constant); if (bIsConstantKey) { if (PDI) { FVector EndPosition = TrackTransforms.Transforms[TransformIndex].GetTranslation(); DrawDashedLine(PDI, StartPosition, EndPosition, TrackColor, 20, SDPG_Foreground); } } else { // Draw intermediate segments for ( ; TransformIndex < TrackTransforms.Times.Num() && TrackTransforms.Times[TransformIndex] < NextKeyTime; ++TransformIndex ) { FTransform EndTransform = TrackTransforms.Transforms[TransformIndex]; if (PDI) { PDI->DrawLine(StartPosition, EndTransform.GetTranslation(), TrackColor, SDPG_Foreground); } else if (TrailActor) { FTransform FrameTransform = EndTransform; FrameTransform.SetScale3D(FVector(3.0f)); FFrameTime FrameTime = TrackTransforms.Times[TransformIndex]; TrailActor->AddFrameMeshComponent(FrameTime / TickResolution, FrameTransform); } StartPosition = EndTransform.GetTranslation(); } // Draw the final segment if (PDI && TrackTransforms.Times[TransformIndex] == NextKeyTime) { FTransform EndTransform = TrackTransforms.Transforms[TransformIndex]; PDI->DrawLine(StartPosition, EndTransform.GetTranslation(), TrackColor, SDPG_Foreground); } } } // If this trajectory key does not have any key handles associated with it, we've nothing left to do if (ThisKey.KeyData.Num() == 0) { continue; } if (bHitTesting && PDI) { PDI->SetHitProxy(new HMovieSceneKeyProxy(TransformTrack, ThisKey)); } // Drawing keys if (PDI != nullptr) { if (bHitTesting) { PDI->SetHitProxy(new HMovieSceneKeyProxy(TransformTrack, ThisKey)); } PDI->DrawPoint(ThisTransform.GetTranslation(), KeyColor, 6.f, SDPG_Foreground); if (bHitTesting) { PDI->SetHitProxy(nullptr); } } else if (TrailActor != nullptr) { AllSectionsScratch.Reset(); for (const FTrajectoryKey::FData& Value : ThisKey.KeyData) { UMovieScene3DTransformSection* Section = Value.Section.Get(); if (Section && !AllSectionsScratch.Contains(Section)) { FTransform MeshTransform = ThisTransform; MeshTransform.SetScale3D(FVector(3.0f)); TrailActor->AddKeyMeshActor(ThisKey.Time / TickResolution, MeshTransform, Section); AllSectionsScratch.Add(Section); } } } } } } void FSequencerEdMode::DrawTracks3D(FPrimitiveDrawInterface* PDI) { using namespace UE::Sequencer; //if in control rig mode(aka animation mode) we show new trails const FName ControlRigEditModeModeName("EditMode.ControlRig"); const bool bIsInControlRigEditMode = GLevelEditorModeTools().GetActiveMode(ControlRigEditModeModeName) != nullptr; if (bIsInControlRigEditMode == true) { return; } for (TWeakPtr WeakSequencer : Sequencers) { TSharedPtr Sequencer = WeakSequencer.Pin(); if (!Sequencer.IsValid()) { continue; } UMovieSceneSequence* Sequence = Sequencer->GetFocusedMovieSceneSequence(); if (!Sequence) { continue; } // Gather a map of object bindings to their implict selection state TMap ObjectBindingNodesSelectionMap; FObjectBindingModelStorageExtension* ObjectStorage = Sequencer->GetViewModel()->GetRootModel()->CastDynamic(); check(ObjectStorage); const FSequencerSelection& Selection = *Sequencer->GetViewModel()->GetSelection(); const TSharedRef& NodeTree = Sequencer->GetNodeTree(); for (const FMovieSceneBinding& Binding : Sequence->GetMovieScene()->GetBindings()) { TSharedPtr ObjectBindingNode = ObjectStorage->FindModelForObjectBinding(Binding.GetObjectGuid()); if (!ObjectBindingNode) { continue; } bool bSelected = Selection.Outliner.IsSelected(ObjectBindingNode); if (!bSelected) { for (TViewModelPtr Child : ObjectBindingNode->GetDescendantsOfType()) { if (Selection.Outliner.IsSelected(Child) || Selection.NodeHasSelectedKeysOrSections(Child)) { bSelected = true; // Stop traversing break; } } // If one of our parent is selected, we're considered selected for (TViewModelPtr Parent : ObjectBindingNode->GetAncestorsOfType()) { if (Selection.Outliner.IsSelected(Parent) || Selection.NodeHasSelectedKeysOrSections(Parent)) { bSelected = true; break; } } } ObjectBindingNodesSelectionMap.Add(&Binding, bSelected); } // Gather up the transform track nodes from the object binding nodes for (TTuple& Pair : ObjectBindingNodesSelectionMap) { for (UMovieSceneTrack* Track : Pair.Key->GetTracks()) { UMovieScene3DTransformTrack* TransformTrack = Cast(Track); if (!TransformTrack) { continue; } // Ensure that we've got a mesh trail for this track if (bDrawMeshTrails) { const bool bHasMeshTrail = Algo::FindBy(MeshTrails, TransformTrack, &FMeshTrailData::Track) != nullptr; if (!bHasMeshTrail) { UViewportWorldInteraction* WorldInteraction = Cast( GEditor->GetEditorWorldExtensionsManager()->GetEditorWorldExtensions( GetWorld() )->FindExtension( UViewportWorldInteraction::StaticClass() ) ); if( WorldInteraction != nullptr ) { ASequencerMeshTrail* TrailActor = WorldInteraction->SpawnTransientSceneActor(TEXT("SequencerMeshTrail"), true); FMeshTrailData MeshTrail = FMeshTrailData(TransformTrack, TrailActor); MeshTrails.Add(MeshTrail); } } } const bool bIsSelected = Pair.Value; DrawTransformTrack(Sequencer, PDI, TransformTrack, Sequencer->FindObjectsInCurrentSequence(Pair.Key->GetObjectGuid()), bIsSelected); } } } } void FSequencerEdMode::DrawAudioTracks(FPrimitiveDrawInterface* PDI) { using namespace UE::Sequencer; for (TWeakPtr WeakSequencer : Sequencers) { TSharedPtr Sequencer = WeakSequencer.Pin(); if (!Sequencer.IsValid()) { continue; } UMovieSceneSequence* Sequence = Sequencer->GetFocusedMovieSceneSequence(); if (!Sequence) { continue; } FQualifiedFrameTime CurrentTime = Sequencer->GetLocalTime(); for (TViewModelPtr TrackModel : Sequencer->GetViewModel()->GetSelection()->Outliner.Filter()) { UMovieSceneAudioTrack* AudioTrack = Cast(TrackModel->GetTrack()); if (!AudioTrack) { continue; } for (UMovieSceneSection* Section : AudioTrack->GetAudioSections()) { UMovieSceneAudioSection* AudioSection = Cast(Section); const FMovieSceneActorReferenceData& AttachActorData = AudioSection->GetAttachActorData(); TMovieSceneChannelData ChannelData = AttachActorData.GetData(); TArrayView Values = ChannelData.GetValues().Num() != 0 ? ChannelData.GetValues() : MakeArrayView(&AttachActorData.GetDefault(), 1); FMovieSceneActorReferenceKey CurrentValue; AttachActorData.Evaluate(CurrentTime.Time, CurrentValue); for (int32 Index = 0; Index < Values.Num(); ++Index) { FMovieSceneObjectBindingID AttachBindingID = Values[Index].Object; FName AttachSocketName = Values[Index].SocketName; for (TWeakObjectPtr<> WeakObject : AttachBindingID.ResolveBoundObjects(Sequencer->GetFocusedTemplateID(), *Sequencer)) { AActor* AttachActor = Cast(WeakObject.Get()); if (AttachActor) { USceneComponent* AttachComponent = AudioSection->GetAttachComponent(AttachActor, Values[Index]); if (AttachComponent) { FVector Location = AttachComponent->GetSocketLocation(AttachSocketName); bool bIsActive = CurrentValue == Values[Index]; FColor Color = bIsActive ? FColor::Green : FColor::White; float Scale = PDI->View->WorldToScreen(Location).W * (4.0f / PDI->View->UnscaledViewRect.Width() / PDI->View->ViewMatrices.GetProjectionMatrix().M[0][0]); Scale *= bIsActive ? 15.f : 10.f; PDI->DrawSprite(Location, Scale, Scale, AudioTexture->GetResource(), Color, SDPG_Foreground, 0.0, 0.0, 0.0, 0.0, SE_BLEND_Masked); break; } } } } } } } } bool FSequencerEdMode::IsViewportSelectionLimited() const { return SelectabilityTool.IsValid() && SelectabilityTool->IsSelectionLimited(); } void FSequencerEdMode::EnableSelectabilityTool(const bool bInEnabled) { SelectabilityTool->EnableLimitedSelection(bInEnabled); } bool FSequencerEdMode::IsObjectSelectableInViewport(UObject* const InObject) const { if (const TSharedPtr Sequencer = GetFirstActiveSequencer()) { return Sequencer->IsObjectSelectableInViewport(InObject); } return true; } FSequencerEdModeTool::FSequencerEdModeTool(FSequencerEdMode* InSequencerEdMode) : SequencerEdMode(InSequencerEdMode) { } FSequencerEdModeTool::~FSequencerEdModeTool() { } bool FSequencerEdModeTool::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) { if( Key == EKeys::LeftMouseButton ) { if( Event == IE_Pressed) { int32 HitX = ViewportClient->Viewport->GetMouseX(); int32 HitY = ViewportClient->Viewport->GetMouseY(); HHitProxy*HitResult = ViewportClient->Viewport->GetHitProxy(HitX, HitY); if(HitResult) { if( HitResult->IsA(HMovieSceneKeyProxy::StaticGetType()) ) { HMovieSceneKeyProxy* KeyProxy = (HMovieSceneKeyProxy*)HitResult; SequencerEdMode->OnKeySelected(ViewportClient->Viewport, KeyProxy); } } } } return FModeTool::InputKey(ViewportClient, Viewport, Key, Event); } /* * * Drag Tool Handler * */ FMarqueeDragTool::FMarqueeDragTool() : bIsDeletingDragTool(false) { } bool FMarqueeDragTool::StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport) { return (DragTool.IsValid() && InViewportClient->GetCurrentWidgetAxis() == EAxisList::None); } bool FMarqueeDragTool::EndTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport) { if (!bIsDeletingDragTool) { // Ending the drag tool may pop up a modal dialog which can cause unwanted reentrancy - protect against this. TGuardValue RecursionGuard(bIsDeletingDragTool, true); // Delete the drag tool if one exists. if (DragTool.IsValid()) { if (DragTool->IsDragging()) { DragTool->EndDrag(); } DragTool.Reset(); return true; } } return false; } bool FMarqueeDragTool::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) { if (DragTool.IsValid() == false || InViewportClient->GetCurrentWidgetAxis() != EAxisList::None) { return false; } if (DragTool->IsDragging() == false) { int32 InX = InViewport->GetMouseX(); int32 InY = InViewport->GetMouseY(); FVector2D Start(InX, InY); DragTool->StartDrag(InViewportClient, GEditor->ClickLocation,Start); } const bool bUsingDragTool = UsingDragTool(); if (bUsingDragTool == false) { return false; } DragTool->AddDelta(InDrag); return true; } /** * @return true if a drag tool is being used by the tracker, false otherwise. */ bool FMarqueeDragTool::UsingDragTool() const { return DragTool.IsValid() && DragTool->IsDragging(); } /** * Renders the drag tool. Does nothing if no drag tool exists. */ void FMarqueeDragTool::Render3DDragTool(const FSceneView* View, FPrimitiveDrawInterface* PDI) { if (DragTool.IsValid()) { DragTool->Render3D(View, PDI); } } /** * Renders the drag tool. Does nothing if no drag tool exists. */ void FMarqueeDragTool::RenderDragTool(const FSceneView* View, FCanvas* Canvas) { if (DragTool.IsValid()) { DragTool->Render(View, Canvas); } } void FMarqueeDragTool::MakeDragTool(FEditorViewportClient* InViewportClient) { DragTool.Reset(); if (InViewportClient->IsOrtho()) { DragTool = InViewportClient->MakeDragTool(EDragTool::BoxSelect); } else { DragTool = InViewportClient->MakeDragTool(EDragTool::FrustumSelect); } }