Files
UnrealEngine/Engine/Source/Editor/Sequencer/Private/SequencerEdMode.cpp
2025-05-18 13:04:45 +08:00

1221 lines
37 KiB
C++

// 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<bool> CVarDrawMeshTrails(
TEXT("Sequencer.DrawMeshTrails"),
true,
TEXT("Toggle to show or hide Level Sequence VR Editor trails"));
//DEPRECRATED
TAutoConsoleVariable<bool> 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<FFrameTime> Times;
TArray<FTransform> Transforms;
void Initialize(UObject* BoundObject, TArrayView<const FTrajectoryKey> 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<USceneComponent>(BoundObject);
if (!SceneComponent)
{
AActor* Actor = Cast<AActor>(BoundObject);
SceneComponent = Actor ? Actor->GetRootComponent() : nullptr;
}
if (!SceneComponent)
{
return;
}
FFrameRate TickResolution = Sequencer->GetFocusedTickResolution();
TRange<FFrameNumber> ViewRange(TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetLowerBoundValue()), TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetUpperBoundValue()));
Times.Reserve(TrajectoryKeys.Num());
FInterrogationChannel Channel = Interrogator.ImportTransformHierarchy(SceneComponent, Sequencer, Sequencer->GetFocusedTemplateID());
TArray<FMovieSceneSequenceID> SubsequenceHierarchy = Sequencer->GetSubSequenceHierarchy();
const FMovieSceneRootEvaluationTemplateInstance& RootInstance = Sequencer->GetEvaluationTemplate();
const FMovieSceneSequenceHierarchy* Hierarchy = RootInstance.GetCompiledDataManager()->FindHierarchy(RootInstance.GetCompiledDataID());
if (Hierarchy)
{
Interrogator.SetHierarchy(const_cast<FMovieSceneSequenceHierarchy*>(Hierarchy));
FMovieSceneSequenceID OwningSequenceID = MovieSceneSequenceID::Root;
for (const FMovieSceneSequenceID SubSequenceID : SubsequenceHierarchy)
{
if (const UMovieSceneSubSection* SubSection = Sequencer->FindSubSection(SubSequenceID))
{
if (UMovieSceneTrack* SubTrack = Cast<UMovieSceneTrack>(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<FTransform> 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<FSequencerEdModeTool>(this);
SelectabilityTool = MakeShared<FSequencerSelectabilityTool>(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<UTexture2D>(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<ISequencer> 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<uint8>(EMultipleKeyBindingIndex::NumChords); ++i)
{
EMultipleKeyBindingIndex ChordIndex = static_cast<EMultipleKeyBindingIndex>(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<ISequencer> 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<ISequencer> FSequencerEdMode::GetFirstActiveSequencer() const
{
for (const TWeakPtr<ISequencer> SequencerWeak : Sequencers)
{
if (const TSharedPtr<ISequencer> 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<FSequencer> ActiveSequencer;
for (TWeakPtr<FSequencer> 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<FIntPoint>& CapturedMouseMoves)
{
const TSharedPtr<ISequencer> 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<FFrameNumber, FFrameNumber> 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<FSequencer> WeakSequencer : Sequencers)
{
TSharedPtr<FSequencer> 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<FSectionModel> SectionHandle = Sequencer->GetNodeTree()->GetSectionModel(Section);
if (SectionHandle && KeyData.KeyHandle.IsSet())
{
TParentFirstChildIterator<FChannelGroupModel> KeyAreaNodes = SectionHandle->GetParentTrackModel().AsModel()->GetDescendantsOfType<FChannelGroupModel>();
for (const TViewModelPtr<FChannelGroupModel>& KeyAreaNode : KeyAreaNodes)
{
TSharedPtr<FChannelModel> 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<ASequencerMeshTrail>(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<TWeakObjectPtr<UObject>> KeyObjects;
AActor* TrailActor = KeyActor->GetAssociatedActor();
KeyObjects.Add(TrailActor);
FPrimitiveDrawInterface* PDI = nullptr;
for (TWeakPtr<ISequencer> WeakSequencer : Sequencers)
{
TSharedPtr<ISequencer> 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<ISequencer>& Sequencer, FPrimitiveDrawInterface* PDI,
UMovieScene3DTransformTrack* TransformTrack, TArrayView<const TWeakObjectPtr<>> 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<UMovieScene3DTransformSection>(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<UMovieScene3DTransformSection*> AllSectionsScratch;
FLinearColor TrackColor = STrackAreaView::BlendDefaultTrackColor(TransformTrack->GetColorTint());
FColor KeyColor = TrackColor.ToFColor(true);
// Draw one line per-track (should only really ever be one)
TRange<FFrameNumber> ViewRange(TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetLowerBoundValue()), TickResolution.AsFrameNumber(Sequencer->GetViewRange().GetUpperBoundValue()));
TArray<FTrajectoryKey> 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<FSequencer> WeakSequencer : Sequencers)
{
TSharedPtr<FSequencer> 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<const FMovieSceneBinding*, bool> ObjectBindingNodesSelectionMap;
FObjectBindingModelStorageExtension* ObjectStorage = Sequencer->GetViewModel()->GetRootModel()->CastDynamic<FObjectBindingModelStorageExtension>();
check(ObjectStorage);
const FSequencerSelection& Selection = *Sequencer->GetViewModel()->GetSelection();
const TSharedRef<FSequencerNodeTree>& NodeTree = Sequencer->GetNodeTree();
for (const FMovieSceneBinding& Binding : Sequence->GetMovieScene()->GetBindings())
{
TSharedPtr<FObjectBindingModel> ObjectBindingNode = ObjectStorage->FindModelForObjectBinding(Binding.GetObjectGuid());
if (!ObjectBindingNode)
{
continue;
}
bool bSelected = Selection.Outliner.IsSelected(ObjectBindingNode);
if (!bSelected)
{
for (TViewModelPtr<IOutlinerExtension> Child : ObjectBindingNode->GetDescendantsOfType<IOutlinerExtension>())
{
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<IOutlinerExtension> Parent : ObjectBindingNode->GetAncestorsOfType<IOutlinerExtension>())
{
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<const FMovieSceneBinding*, bool>& Pair : ObjectBindingNodesSelectionMap)
{
for (UMovieSceneTrack* Track : Pair.Key->GetTracks())
{
UMovieScene3DTransformTrack* TransformTrack = Cast<UMovieScene3DTransformTrack>(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<UViewportWorldInteraction>( GEditor->GetEditorWorldExtensionsManager()->GetEditorWorldExtensions( GetWorld() )->FindExtension( UViewportWorldInteraction::StaticClass() ) );
if( WorldInteraction != nullptr )
{
ASequencerMeshTrail* TrailActor = WorldInteraction->SpawnTransientSceneActor<ASequencerMeshTrail>(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<ISequencer> WeakSequencer : Sequencers)
{
TSharedPtr<ISequencer> Sequencer = WeakSequencer.Pin();
if (!Sequencer.IsValid())
{
continue;
}
UMovieSceneSequence* Sequence = Sequencer->GetFocusedMovieSceneSequence();
if (!Sequence)
{
continue;
}
FQualifiedFrameTime CurrentTime = Sequencer->GetLocalTime();
for (TViewModelPtr<ITrackExtension> TrackModel : Sequencer->GetViewModel()->GetSelection()->Outliner.Filter<ITrackExtension>())
{
UMovieSceneAudioTrack* AudioTrack = Cast<UMovieSceneAudioTrack>(TrackModel->GetTrack());
if (!AudioTrack)
{
continue;
}
for (UMovieSceneSection* Section : AudioTrack->GetAudioSections())
{
UMovieSceneAudioSection* AudioSection = Cast<UMovieSceneAudioSection>(Section);
const FMovieSceneActorReferenceData& AttachActorData = AudioSection->GetAttachActorData();
TMovieSceneChannelData<const FMovieSceneActorReferenceKey> ChannelData = AttachActorData.GetData();
TArrayView<const FMovieSceneActorReferenceKey> 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<AActor>(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<ISequencer> 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<bool> 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);
}
}