You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This represents UE4/Main @ 16261013 and Dev-PerfTest @ 16259937 [CL 16306996 by aurel cordonnier in ue5-main branch]
330 lines
11 KiB
C++
330 lines
11 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SequencerUtilities.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Fonts/SlateFontInfo.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Input/SComboButton.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Styling/CoreStyle.h"
|
|
#include "EditorStyleSet.h"
|
|
#include "SequencerTrackNode.h"
|
|
#include "MovieSceneTrack.h"
|
|
#include "MovieSceneSection.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "ISequencerTrackEditor.h"
|
|
#include "ISequencer.h"
|
|
#include "Sequencer.h"
|
|
#include "SequencerNodeTree.h"
|
|
#include "DisplayNodes/SequencerObjectBindingNode.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "UObject/Package.h"
|
|
#include "AssetRegistryModule.h"
|
|
#include "FileHelpers.h"
|
|
#include "LevelSequence.h"
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "FSequencerUtilities"
|
|
|
|
static EVisibility GetRolloverVisibility(TAttribute<bool> HoverState, TWeakPtr<SComboButton> WeakComboButton)
|
|
{
|
|
TSharedPtr<SComboButton> ComboButton = WeakComboButton.Pin();
|
|
if (HoverState.Get() || ComboButton->IsOpen())
|
|
{
|
|
return EVisibility::SelfHitTestInvisible;
|
|
}
|
|
else
|
|
{
|
|
return EVisibility::Collapsed;
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FSequencerUtilities::MakeAddButton(FText HoverText, FOnGetContent MenuContent, const TAttribute<bool>& HoverState, TWeakPtr<ISequencer> InSequencer)
|
|
{
|
|
FSlateFontInfo SmallLayoutFont = FCoreStyle::GetDefaultFontStyle("Regular", 8);
|
|
|
|
TSharedRef<STextBlock> ComboButtonText = SNew(STextBlock)
|
|
.Text(HoverText)
|
|
.Font(SmallLayoutFont)
|
|
.ColorAndOpacity( FSlateColor::UseForeground() );
|
|
|
|
TSharedRef<SComboButton> ComboButton =
|
|
|
|
SNew(SComboButton)
|
|
.HasDownArrow(false)
|
|
.IsFocusable(true)
|
|
.ButtonStyle(FEditorStyle::Get(), "HoverHintOnly")
|
|
.ForegroundColor( FSlateColor::UseForeground() )
|
|
.IsEnabled_Lambda([=]() { return InSequencer.IsValid() ? !InSequencer.Pin()->IsReadOnly() : false; })
|
|
.OnGetMenuContent(MenuContent)
|
|
.ContentPadding(FMargin(5, 2))
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.ButtonContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0,0,2,0))
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity( FSlateColor::UseForeground() )
|
|
.Image(FEditorStyle::GetBrush("Plus"))
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
ComboButtonText
|
|
]
|
|
];
|
|
|
|
TAttribute<EVisibility> Visibility = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateStatic(GetRolloverVisibility, HoverState, TWeakPtr<SComboButton>(ComboButton)));
|
|
ComboButtonText->SetVisibility(Visibility);
|
|
|
|
return ComboButton;
|
|
}
|
|
|
|
void FSequencerUtilities::PopulateMenu_CreateNewSection(FMenuBuilder& MenuBuilder, int32 RowIndex, UMovieSceneTrack* Track, TWeakPtr<ISequencer> InSequencer)
|
|
{
|
|
if (!Track)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto CreateNewSection = [Track, InSequencer, RowIndex](EMovieSceneBlendType BlendType)
|
|
{
|
|
TSharedPtr<ISequencer> Sequencer = InSequencer.Pin();
|
|
if (!Sequencer.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
FQualifiedFrameTime CurrentTime = Sequencer->GetLocalTime();
|
|
TRange<double> VisibleRange = Sequencer->GetViewRange();
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("AddSectionTransactionText", "Add Section"));
|
|
if (UMovieSceneSection* NewSection = Track->CreateNewSection())
|
|
{
|
|
int32 OverlapPriority = 0;
|
|
for (UMovieSceneSection* Section : Track->GetAllSections())
|
|
{
|
|
OverlapPriority = FMath::Max(Section->GetOverlapPriority() + 1, OverlapPriority);
|
|
|
|
// Move existing sections on the same row or beyond so that they don't overlap with the new section
|
|
if (Section != NewSection && Section->GetRowIndex() >= RowIndex)
|
|
{
|
|
Section->SetRowIndex(Section->GetRowIndex() + 1);
|
|
}
|
|
}
|
|
|
|
Track->Modify();
|
|
|
|
int32 DurationFrames = ( (VisibleRange.Size<double>() * 0.75) * CurrentTime.Rate ).FloorToFrame().Value;
|
|
NewSection->SetRange(TRange<FFrameNumber>(CurrentTime.Time.FrameNumber, CurrentTime.Time.FrameNumber + DurationFrames));
|
|
NewSection->SetOverlapPriority(OverlapPriority);
|
|
NewSection->SetRowIndex(RowIndex);
|
|
NewSection->SetBlendType(BlendType);
|
|
|
|
Track->AddSection(*NewSection);
|
|
Track->UpdateEasing();
|
|
|
|
Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
|
|
Sequencer->EmptySelection();
|
|
Sequencer->SelectSection(NewSection);
|
|
Sequencer->ThrobSectionSelection();
|
|
}
|
|
else
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
};
|
|
|
|
FText NameOverride = Track->GetSupportedBlendTypes().Num() == 1 ? LOCTEXT("AddSectionText", "Add New Section") : FText();
|
|
FText TooltipOverride = Track->GetSupportedBlendTypes().Num() == 1 ? LOCTEXT("AddSectionToolTip", "Adds a new section at the current time") : FText();
|
|
|
|
const UEnum* MovieSceneBlendType = FindObjectChecked<UEnum>(ANY_PACKAGE, TEXT("EMovieSceneBlendType"));
|
|
for (EMovieSceneBlendType BlendType : Track->GetSupportedBlendTypes())
|
|
{
|
|
FText DisplayName = MovieSceneBlendType->GetDisplayNameTextByValue((int64)BlendType);
|
|
FName EnumValueName = MovieSceneBlendType->GetNameByValue((int64)BlendType);
|
|
MenuBuilder.AddMenuEntry(
|
|
NameOverride.IsEmpty() ? DisplayName : NameOverride,
|
|
TooltipOverride.IsEmpty() ? FText::Format(LOCTEXT("AddSectionFormatToolTip", "Adds a new {0} section at the current time"), DisplayName) : TooltipOverride,
|
|
FSlateIcon("EditorStyle", EnumValueName),
|
|
FUIAction(FExecuteAction::CreateLambda(CreateNewSection, BlendType))
|
|
);
|
|
}
|
|
}
|
|
|
|
void FSequencerUtilities::PopulateMenu_SetBlendType(FMenuBuilder& MenuBuilder, UMovieSceneSection* Section, TWeakPtr<ISequencer> InSequencer)
|
|
{
|
|
PopulateMenu_SetBlendType(MenuBuilder, TArray<TWeakObjectPtr<UMovieSceneSection>>({ Section }), InSequencer);
|
|
}
|
|
|
|
void FSequencerUtilities::PopulateMenu_SetBlendType(FMenuBuilder& MenuBuilder, const TArray<TWeakObjectPtr<UMovieSceneSection>>& InSections, TWeakPtr<ISequencer> InSequencer)
|
|
{
|
|
auto Execute = [InSections, InSequencer](EMovieSceneBlendType BlendType)
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("SetBlendType", "Set Blend Type"));
|
|
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
|
|
{
|
|
if (UMovieSceneSection* Section = WeakSection.Get())
|
|
{
|
|
Section->Modify();
|
|
Section->SetBlendType(BlendType);
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FSequencer> Sequencer = StaticCastSharedPtr<FSequencer>(InSequencer.Pin());
|
|
if (Sequencer.IsValid())
|
|
{
|
|
// If the blend type is changed to additive or relative, restore the state of the objects boud to this section before evaluating again.
|
|
// This allows the additive or relative to evaluate based on the initial values of the object, rather than the current animated values.
|
|
if (BlendType == EMovieSceneBlendType::Additive || BlendType == EMovieSceneBlendType::Relative)
|
|
{
|
|
TSet<UObject*> ObjectsToRestore;
|
|
TSharedRef<FSequencerNodeTree> SequencerNodeTree = Sequencer->GetNodeTree();
|
|
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
|
|
{
|
|
if (UMovieSceneSection* Section = WeakSection.Get())
|
|
{
|
|
TOptional<FSectionHandle> SectionHandle = SequencerNodeTree->GetSectionHandle(Section);
|
|
if (!SectionHandle)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TSharedPtr<FSequencerObjectBindingNode> ParentObjectBindingNode = SectionHandle->GetTrackNode()->FindParentObjectBindingNode();
|
|
if (!ParentObjectBindingNode.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (TWeakObjectPtr<> BoundObject : Sequencer->FindObjectsInCurrentSequence(ParentObjectBindingNode->GetObjectBinding()))
|
|
{
|
|
if (AActor* BoundActor = Cast<AActor>(BoundObject))
|
|
{
|
|
for (UActorComponent* Component : TInlineComponentArray<UActorComponent*>(BoundActor))
|
|
{
|
|
if (Component)
|
|
{
|
|
ObjectsToRestore.Add(Component);
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjectsToRestore.Add(BoundObject.Get());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UObject* ObjectToRestore : ObjectsToRestore)
|
|
{
|
|
Sequencer->PreAnimatedState.RestorePreAnimatedState(*ObjectToRestore);
|
|
}
|
|
}
|
|
|
|
Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChanged);
|
|
}
|
|
};
|
|
|
|
const UEnum* MovieSceneBlendType = FindObjectChecked<UEnum>(ANY_PACKAGE, TEXT("EMovieSceneBlendType"));
|
|
for (int32 NameIndex = 0; NameIndex < MovieSceneBlendType->NumEnums() - 1; ++NameIndex)
|
|
{
|
|
EMovieSceneBlendType BlendType = (EMovieSceneBlendType)MovieSceneBlendType->GetValueByIndex(NameIndex);
|
|
|
|
// Include this if any section supports it
|
|
bool bAnySupported = false;
|
|
for (TWeakObjectPtr<UMovieSceneSection> WeakSection : InSections)
|
|
{
|
|
UMovieSceneSection* Section = WeakSection.Get();
|
|
if (Section && Section->GetSupportedBlendTypes().Contains(BlendType))
|
|
{
|
|
bAnySupported = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bAnySupported)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FName EnumValueName = MovieSceneBlendType->GetNameByIndex(NameIndex);
|
|
MenuBuilder.AddMenuEntry(
|
|
MovieSceneBlendType->GetDisplayNameTextByIndex(NameIndex),
|
|
MovieSceneBlendType->GetToolTipTextByIndex(NameIndex),
|
|
FSlateIcon("EditorStyle", EnumValueName),
|
|
FUIAction(FExecuteAction::CreateLambda(Execute, BlendType))
|
|
);
|
|
}
|
|
}
|
|
|
|
FName FSequencerUtilities::GetUniqueName( FName CandidateName, const TArray<FName>& ExistingNames )
|
|
{
|
|
if (!ExistingNames.Contains(CandidateName))
|
|
{
|
|
return CandidateName;
|
|
}
|
|
|
|
FString CandidateNameString = CandidateName.ToString();
|
|
FString BaseNameString = CandidateNameString;
|
|
if ( CandidateNameString.Len() >= 3 && CandidateNameString.Right(3).IsNumeric() )
|
|
{
|
|
BaseNameString = CandidateNameString.Left( CandidateNameString.Len() - 3 );
|
|
}
|
|
|
|
FName UniqueName = FName(*BaseNameString);
|
|
int32 NameIndex = 1;
|
|
while ( ExistingNames.Contains( UniqueName ) )
|
|
{
|
|
UniqueName = FName( *FString::Printf(TEXT("%s%i"), *BaseNameString, NameIndex ) );
|
|
NameIndex++;
|
|
}
|
|
|
|
return UniqueName;
|
|
}
|
|
|
|
TArray<FString> FSequencerUtilities::GetAssociatedMapPackages(const ULevelSequence* InSequence)
|
|
{
|
|
if (!InSequence)
|
|
{
|
|
return TArray<FString>();
|
|
}
|
|
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
const FName LSMapPathName = *InSequence->GetOutermost()->GetPathName();
|
|
|
|
TArray<FString> AssociatedMaps;
|
|
TArray<FAssetIdentifier> AssociatedAssets;
|
|
|
|
// This makes the assumption these functions will append the array, and not clear it.
|
|
AssetRegistryModule.Get().GetReferencers(LSMapPathName, AssociatedAssets);
|
|
AssetRegistryModule.Get().GetDependencies(LSMapPathName, AssociatedAssets);
|
|
|
|
for (FAssetIdentifier& AssociatedMap : AssociatedAssets)
|
|
{
|
|
FString MapFilePath;
|
|
FString LevelPath = AssociatedMap.PackageName.ToString();
|
|
if (FEditorFileUtils::IsMapPackageAsset(LevelPath, MapFilePath))
|
|
{
|
|
AssociatedMaps.AddUnique(LevelPath);
|
|
}
|
|
}
|
|
|
|
AssociatedMaps.Sort([](const FString& One, const FString& Two) { return FPaths::GetBaseFilename(One) < FPaths::GetBaseFilename(Two); });
|
|
return AssociatedMaps;
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|