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

198 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SequencerAddKeyOperation.h"
#include "Containers/Array.h"
#include "HAL/PlatformCrt.h"
#include "IKeyArea.h"
#include "ISequencer.h"
#include "ISequencerSection.h"
#include "ISequencerTrackEditor.h"
#include "MVVM/Extensions/IOutlinerExtension.h"
#include "MVVM/Extensions/ITrackExtension.h"
#include "MVVM/ViewModelTypeID.h"
#include "MVVM/ViewModels/ChannelModel.h"
#include "MVVM/ViewModels/ViewModel.h"
#include "MVVM/ViewModels/ViewModelIterators.h"
#include "Misc/AssertionMacros.h"
#include "Misc/FrameNumber.h"
#include "MovieSceneSection.h"
#include "MovieSceneTrack.h"
#include "SequencerKeyParams.h"
#include "Templates/Tuple.h"
#include "Templates/TypeHash.h"
#include "Templates/UnrealTemplate.h"
namespace UE::Sequencer { class FSequenceModel; }
namespace UE
{
namespace Sequencer
{
FAddKeyOperation FAddKeyOperation::FromNodes(const TSet<TWeakViewModelPtr<IOutlinerExtension>>& InNodes)
{
FAddKeyOperation Operation;
TArray<TWeakPtr<FViewModel>> FilteredNodes;
// Remove any child nodes that have a parent also included in the set
for (const TWeakViewModelPtr<IOutlinerExtension>& ProspectiveNode : InNodes)
{
TSharedPtr<FViewModel> Parent = ProspectiveNode.Pin().AsModel()->GetParent();
while (Parent)
{
if (InNodes.Contains(CastViewModel<IOutlinerExtension>(Parent)))
{
goto Continue;
}
Parent = Parent->GetParent();
}
FilteredNodes.Add(ProspectiveNode);
Continue:
continue;
}
Operation.AddPreFilteredNodes(FilteredNodes);
return Operation;
}
FAddKeyOperation FAddKeyOperation::FromNode(TWeakPtr<FViewModel> InNode)
{
FAddKeyOperation Operation;
Operation.AddPreFilteredNodes(MakeArrayView(&InNode, 1));
return Operation;
}
FAddKeyOperation FAddKeyOperation::FromKeyAreas(ISequencerTrackEditor* TrackEditor, const TArrayView<TSharedRef<IKeyArea>> InKeyAreas)
{
FAddKeyOperation Operation;
if (ensure(TrackEditor))
{
for (const TSharedRef<IKeyArea>& KeyArea : InKeyAreas)
{
Operation.ProcessKeyArea(TrackEditor, KeyArea);
}
}
return Operation;
}
void FAddKeyOperation::AddPreFilteredNodes(TArrayView<const TWeakPtr<FViewModel>> FilteredNodes)
{
TSharedPtr<FSequenceModel> SequenceModel;
const TArray<FViewModelTypeID> TypeIDs({ ITrackExtension::ID, IOutlinerExtension::ID });
for (const TWeakPtr<FViewModel>& FilteredNode : FilteredNodes)
{
TSharedPtr<FViewModel> Node = FilteredNode.Pin();
TSharedPtr<FViewModel> ParentModel = Node->FindAncestorOfTypes(MakeArrayView(TypeIDs));
if (ParentModel)
{
ConsiderKeyableAreas(ParentModel->CastThisShared<ITrackExtension>(), Node);
}
else
{
constexpr bool bIncludeThis = true;
for (FParentFirstChildIterator Child(FilteredNode.Pin(), bIncludeThis); Child; ++Child)
{
if (Child->IsA<ITrackExtension>() && Child->IsA<IOutlinerExtension>())
{
TSharedPtr<ITrackExtension> TrackExtension = Child->CastThisShared<ITrackExtension>();
ConsiderKeyableAreas(TrackExtension, *Child);
}
}
}
}
}
bool FAddKeyOperation::ConsiderKeyableAreas(TSharedPtr<ITrackExtension> InTrackModel, FViewModelPtr KeyAnythingBeneath)
{
bool bKeyedAnything = false;
constexpr bool bIncludeThis = true;
// Prefer the section that is marked SectionToKey
for (const TViewModelPtr<FSectionModel>& SectionModel : KeyAnythingBeneath->GetDescendantsOfType<FSectionModel>(bIncludeThis))
{
UMovieSceneSection* Section = SectionModel->GetSection();
UMovieSceneTrack* Track = Cast<UMovieSceneTrack>(Section->GetOuter());
if (Track && Track->GetSectionToKey() == Section)
{
for (TParentFirstChildIterator<FChannelGroupModel> ChannelGroupModelIt(SectionModel->AsShared(), bIncludeThis); ChannelGroupModelIt; ++ChannelGroupModelIt)
{
bKeyedAnything |= ProcessKeyArea(InTrackModel, *ChannelGroupModelIt);
}
}
}
// Otherwise if nothing was found, key all
if (!bKeyedAnything)
{
for (TParentFirstChildIterator<FChannelGroupModel> ChannelGroupModelIt(KeyAnythingBeneath->AsShared(), bIncludeThis); ChannelGroupModelIt; ++ChannelGroupModelIt)
{
bKeyedAnything |= ProcessKeyArea(InTrackModel, *ChannelGroupModelIt);
}
}
return bKeyedAnything;
}
bool FAddKeyOperation::ProcessKeyArea(TSharedPtr<ITrackExtension> InTrackModel, TViewModelPtr<FChannelGroupModel> InChannelGroupModel)
{
bool bKeyedAnything = false;
ISequencerTrackEditor* TrackEditor = InTrackModel->GetTrackEditor().Get();
IOutlinerExtension* OutlinerExtension = InChannelGroupModel->CastThis<IOutlinerExtension>();
if (!OutlinerExtension || OutlinerExtension->IsFilteredOut() == false)
{
constexpr bool bIncludeThis = true;
for (const TWeakViewModelPtr<FChannelModel>& WeakChannel : InChannelGroupModel->GetChannels())
{
if (TSharedPtr<FChannelModel> Channel = WeakChannel.Pin())
{
bKeyedAnything |= ProcessKeyArea(TrackEditor, Channel->GetKeyArea());
}
}
}
return bKeyedAnything;
}
bool FAddKeyOperation::ProcessKeyArea(ISequencerTrackEditor* InTrackEditor, TSharedPtr<IKeyArea> InKeyArea)
{
TSharedPtr<ISequencerSection> Section = InKeyArea->GetSectionInterface();
UMovieSceneSection* SectionObject = Section ? Section->GetSectionObject() : nullptr;
UMovieSceneTrack* TrackObject = SectionObject ? SectionObject->GetTypedOuter<UMovieSceneTrack>() : nullptr;
if (TrackObject)
{
GetTrackOperation(InTrackEditor).Populate(TrackObject, Section, InKeyArea);
return true;
}
return false;
}
void FAddKeyOperation::Commit(FFrameNumber KeyTime, ISequencer& InSequencer, TArray<FAddKeyResult>* OutResults)
{
for (TTuple<ISequencerTrackEditor*, FKeyOperation>& Pair : OperationsByTrackEditor)
{
Pair.Value.InitializeOperation(KeyTime);
Pair.Key->ProcessKeyOperation(KeyTime, Pair.Value, InSequencer, OutResults);
}
InSequencer.UpdatePlaybackRange();
InSequencer.NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChanged);
}
FKeyOperation& FAddKeyOperation::GetTrackOperation(ISequencerTrackEditor* TrackEditor)
{
return OperationsByTrackEditor.FindOrAdd(TrackEditor);
}
} // namespace Sequencer
} // namespace UE