You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
[FYI] Richard.TalbotWatkings Fixed Static analysis in AnimationModifier.cpp the CurrentAnimSequence ptr can't be null since it was deferenced at the line 75. Fixed Static analysis in PackageStoreOptimizer for some reason the static analysis did like the way the check was presented. Fixed Static analysis in SComponentClassCombo.cpp and SDisplayClusterConfiguratorComponentCombo.cpp changed the code so that the static analyser should understand it better. #jira UE-120410 #rb Jean.MichelDignard #ROBOMERGE-SOURCE: CL 17071360 in //UE5/Main/... #ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v853-17066230) [CL 17071396 by julien stjean in ue5-release-engine-test branch]
385 lines
12 KiB
C++
385 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimationModifier.h"
|
|
#include "AssetViewUtils.h"
|
|
#include "Animation/AnimSequence.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "ModifierOutputFilter.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
#include "UObject/ReleaseObjectVersion.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UObject/AnimObjectVersion.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "AnimationModifier"
|
|
|
|
int32 UE::Anim::FApplyModifiersScope::ScopesOpened = 0;
|
|
TMap<FObjectKey, TOptional<EAppReturnType::Type>> UE::Anim::FApplyModifiersScope::PerClassReturnTypeValues;
|
|
|
|
TOptional<EAppReturnType::Type> UE::Anim::FApplyModifiersScope::GetReturnType(const UAnimationModifier* InModifier)
|
|
{
|
|
TOptional<EAppReturnType::Type>* ReturnTypePtr = PerClassReturnTypeValues.Find(FObjectKey(InModifier->GetClass()));
|
|
return ReturnTypePtr ? *ReturnTypePtr : TOptional<EAppReturnType::Type>();
|
|
}
|
|
|
|
void UE::Anim::FApplyModifiersScope::SetReturnType(const UAnimationModifier* InModifier, EAppReturnType::Type InReturnType)
|
|
{
|
|
const FObjectKey Key(InModifier->GetClass());
|
|
ensure(!PerClassReturnTypeValues.Contains(Key));
|
|
PerClassReturnTypeValues.Add(Key, InReturnType);
|
|
}
|
|
|
|
const FName UAnimationModifier::RevertModifierObjectName("REVERT_AnimationModifier");
|
|
|
|
UAnimationModifier::UAnimationModifier()
|
|
: PreviouslyAppliedModifier(nullptr)
|
|
{
|
|
}
|
|
|
|
void UAnimationModifier::ApplyToAnimationSequence(class UAnimSequence* InAnimationSequence)
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
CurrentAnimSequence = InAnimationSequence;
|
|
checkf(CurrentAnimSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
CurrentSkeleton = InAnimationSequence->GetSkeleton();
|
|
|
|
// Filter to check for warnings / errors thrown from animation blueprint library (rudimentary approach for now)
|
|
FCategoryLogOutputFilter OutputLog;
|
|
OutputLog.SetAutoEmitLineTerminator(true);
|
|
OutputLog.AddCategoryName("LogAnimationBlueprintLibrary");
|
|
OutputLog.AddCategoryName("LogAnimation");
|
|
|
|
GLog->AddOutputDevice(&OutputLog);
|
|
|
|
// Transact the modifier to prevent instance variables/data to change during applying
|
|
FTransaction ModifierTransaction;
|
|
ModifierTransaction.SaveObject(this);
|
|
|
|
FTransaction AnimationDataTransaction;
|
|
AnimationDataTransaction.SaveObject(CurrentAnimSequence);
|
|
AnimationDataTransaction.SaveObject(CurrentSkeleton);
|
|
|
|
/** In case this modifier has been previously applied, revert it using the serialised out version at the time */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
PreviouslyAppliedModifier->Modify();
|
|
PreviouslyAppliedModifier->OnRevert(CurrentAnimSequence);
|
|
}
|
|
|
|
IAnimationDataController& Controller = CurrentAnimSequence->GetController();
|
|
|
|
{
|
|
IAnimationDataController::FScopedBracket ScopedBracket(Controller, LOCTEXT("ApplyModifierBracket", "Applying Animation Modifier"));
|
|
/** Reverting and applying, populates the log with possible warnings and or errors to notify the user about */
|
|
OnApply(CurrentAnimSequence);
|
|
}
|
|
|
|
// Apply transaction
|
|
ModifierTransaction.BeginOperation();
|
|
ModifierTransaction.Apply();
|
|
ModifierTransaction.EndOperation();
|
|
|
|
GLog->RemoveOutputDevice(&OutputLog);
|
|
|
|
// Check if warnings or errors have occurred and show dialog to user to inform her about this
|
|
const bool bWarnings = OutputLog.ContainsWarnings();
|
|
const bool bErrors = OutputLog.ContainsErrors();
|
|
bool bShouldRevert = bErrors;
|
|
|
|
// If errors have occured - prompt user with OK and revert
|
|
TOptional<EAppReturnType::Type> ScopeReturnType = UE::Anim::FApplyModifiersScope::GetReturnType(this);
|
|
static const FText MessageTitle = LOCTEXT("ModifierDialogTitle", "Modifier has generated errors during test run.");
|
|
if (bErrors)
|
|
{
|
|
if (UE::Anim::FApplyModifiersScope::ScopesOpened == 0 || !ScopeReturnType.IsSet() || ScopeReturnType.GetValue() != EAppReturnType::Type::Ok)
|
|
{
|
|
const FText ErrorMessageFormat = FText::FormatOrdered(LOCTEXT("ModifierErrorDescription", "Modifier: {0}\nAsset: {1}\n{2}\nResolve the errors before trying to apply again."), FText::FromString(GetClass()->GetPathName()),
|
|
FText::FromString(CurrentAnimSequence->GetPathName()), FText::FromString(OutputLog));
|
|
|
|
EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::Ok, ErrorMessageFormat, &MessageTitle);
|
|
UE::Anim::FApplyModifiersScope::SetReturnType(this, ReturnValue);
|
|
}
|
|
}
|
|
|
|
// If _only_ warnings have occured - check if user has previously said YesAll / NoAll and process the result
|
|
if (bWarnings && !bShouldRevert)
|
|
{
|
|
if (UE::Anim::FApplyModifiersScope::ScopesOpened == 0 || !ScopeReturnType.IsSet())
|
|
{
|
|
const FText WarningMessage = FText::FormatOrdered(LOCTEXT("ModifierWarningDescription", "Modifier: {0}\nAsset: {1}\n{2}\nAre you sure you want to apply it?"), FText::FromString(GetClass()->GetPathName()),
|
|
FText::FromString(CurrentAnimSequence ? CurrentAnimSequence->GetPathName() : CurrentSkeleton->GetPathName()), FText::FromString(OutputLog));
|
|
|
|
EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNoYesAllNoAll, WarningMessage, &MessageTitle);
|
|
bShouldRevert = ReturnValue == EAppReturnType::No || ReturnValue == EAppReturnType::NoAll;
|
|
|
|
// check if user response should be stored for further modifier applications
|
|
if(UE::Anim::FApplyModifiersScope::ScopesOpened > 0)
|
|
{
|
|
if (ReturnValue == EAppReturnType::Type::YesAll || ReturnValue == EAppReturnType::Type::NoAll)
|
|
{
|
|
UE::Anim::FApplyModifiersScope::SetReturnType(this, ReturnValue);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Revert if previous user prompt return NoAll or if any errors occured previously
|
|
bShouldRevert = ScopeReturnType.GetValue() == EAppReturnType::NoAll || ScopeReturnType.GetValue() == EAppReturnType::Ok;
|
|
}
|
|
}
|
|
|
|
// Revert changes if necessary, otherwise post edit and refresh animation data
|
|
if (bShouldRevert)
|
|
{
|
|
AnimationDataTransaction.BeginOperation();
|
|
AnimationDataTransaction.Apply();
|
|
AnimationDataTransaction.EndOperation();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
}
|
|
else
|
|
{
|
|
/** Mark the previous modifier pending kill, as it will be replaced with the current modifier state */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
PreviouslyAppliedModifier->MarkPendingKill();
|
|
}
|
|
|
|
PreviouslyAppliedModifier = DuplicateObject(this, GetOuter(), RevertModifierObjectName);
|
|
|
|
CurrentAnimSequence->PostEditChange();
|
|
CurrentSkeleton->PostEditChange();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
|
|
UpdateStoredRevisions();
|
|
}
|
|
|
|
// Finished
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateCompressedAnimationData()
|
|
{
|
|
if (CurrentAnimSequence->DoesNeedRecompress())
|
|
{
|
|
CurrentAnimSequence->RequestSyncAnimRecompression(false);
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::RevertFromAnimationSequence(class UAnimSequence* InAnimationSequence)
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
/** Can only revert if previously applied, which means there should be a previous modifier */
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
checkf(InAnimationSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
CurrentAnimSequence = InAnimationSequence;
|
|
CurrentSkeleton = InAnimationSequence->GetSkeleton();
|
|
|
|
// Transact the modifier to prevent instance variables/data to change during reverting
|
|
FTransaction Transaction;
|
|
Transaction.SaveObject(this);
|
|
|
|
PreviouslyAppliedModifier->Modify();
|
|
|
|
IAnimationDataController& Controller = CurrentAnimSequence->GetController();
|
|
|
|
{
|
|
IAnimationDataController::FScopedBracket ScopedBracket(Controller, LOCTEXT("RevertModifierBracket", "Reverting Animation Modifier"));
|
|
PreviouslyAppliedModifier->OnRevert(CurrentAnimSequence);
|
|
}
|
|
|
|
// Apply transaction
|
|
Transaction.BeginOperation();
|
|
Transaction.Apply();
|
|
Transaction.EndOperation();
|
|
|
|
CurrentAnimSequence->PostEditChange();
|
|
CurrentSkeleton->PostEditChange();
|
|
CurrentAnimSequence->RefreshCacheData();
|
|
|
|
ResetStoredRevisions();
|
|
|
|
// Finished
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
|
|
PreviouslyAppliedModifier->MarkPendingKill();
|
|
PreviouslyAppliedModifier = nullptr;
|
|
}
|
|
}
|
|
|
|
bool UAnimationModifier::IsLatestRevisionApplied() const
|
|
{
|
|
return (AppliedGuid == RevisionGuid);
|
|
}
|
|
|
|
void UAnimationModifier::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
UpdateNativeRevisionGuid();
|
|
}
|
|
|
|
void UAnimationModifier::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
Ar.UsingCustomVersion(FReleaseObjectVersion::GUID);
|
|
|
|
/** Back-wards compatibility, assume the current modifier as previously applied */
|
|
if (Ar.CustomVer(FReleaseObjectVersion::GUID) < FReleaseObjectVersion::SerializeAnimModifierState)
|
|
{
|
|
PreviouslyAppliedModifier = DuplicateObject(this, GetOuter());
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
UClass* Class = GetClass();
|
|
UObject* DefaultObject = Class->GetDefaultObject();
|
|
|
|
// CDO, set GUID if invalid
|
|
if(DefaultObject == this)
|
|
{
|
|
// Ensure we always have a valid guid
|
|
if (!RevisionGuid.IsValid())
|
|
{
|
|
UpdateRevisionGuid(GetClass());
|
|
MarkPackageDirty();
|
|
}
|
|
}
|
|
// Non CDO, update revision GUID
|
|
else if(UAnimationModifier* TypedDefaultObject = Cast<UAnimationModifier>(DefaultObject))
|
|
{
|
|
RevisionGuid = TypedDefaultObject->RevisionGuid;
|
|
}
|
|
}
|
|
|
|
const USkeleton* UAnimationModifier::GetSkeleton()
|
|
{
|
|
return CurrentSkeleton;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateRevisionGuid(UClass* ModifierClass)
|
|
{
|
|
if (ModifierClass)
|
|
{
|
|
RevisionGuid = FGuid::NewGuid();
|
|
|
|
// Apply to any currently loaded instances of this class
|
|
for (TObjectIterator<UAnimationModifier> It; It; ++It)
|
|
{
|
|
if (*It != this && It->GetClass() == ModifierClass)
|
|
{
|
|
It->SetInstanceRevisionGuid(RevisionGuid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::UpdateNativeRevisionGuid()
|
|
{
|
|
UClass* Class = GetClass();
|
|
// Check if this is the class default object
|
|
if (this == GetDefault<UAnimationModifier>(Class))
|
|
{
|
|
// If so check whether or not the config stored revision matches the natively defined one
|
|
if (StoredNativeRevision != GetNativeClassRevision())
|
|
{
|
|
// If not update the blueprint revision GUID
|
|
UpdateRevisionGuid(Class);
|
|
StoredNativeRevision = GetNativeClassRevision();
|
|
|
|
MarkPackageDirty();
|
|
|
|
// Save the new native revision to config files
|
|
SaveConfig();
|
|
UpdateDefaultConfigFile();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::ApplyToAll(TSubclassOf<UAnimationModifier> ModifierSubClass, bool bForceApply /*= true*/)
|
|
{
|
|
if (UClass* ModifierClass = ModifierSubClass.Get())
|
|
{
|
|
// Make sure all packages (in this case UAnimSequences) are loaded to ensure the TObjectIterator has any instances to iterate over
|
|
LoadModifierReferencers(ModifierSubClass);
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("UndoAction_ApplyModifiers", "Applying Animation Modifier to Animation Sequence(s)"));
|
|
for (TObjectIterator<UAnimationModifier> It; It; ++It)
|
|
{
|
|
// Check if valid, of the required class, not pending kill and not a modifier back-up for reverting
|
|
if (*It && It->GetClass() == ModifierClass && !It->IsPendingKill() && It->GetFName() != RevertModifierObjectName)
|
|
{
|
|
if (bForceApply || !It->IsLatestRevisionApplied())
|
|
{
|
|
// Go through outer chain to find AnimSequence
|
|
UObject* Outer = It->GetOuter();
|
|
|
|
while(Outer && !Outer->IsA<UAnimSequence>())
|
|
{
|
|
Outer = Outer->GetOuter();
|
|
}
|
|
|
|
if (UAnimSequence* AnimSequence = Cast<UAnimSequence>(Outer))
|
|
{
|
|
AnimSequence->Modify();
|
|
It->ApplyToAnimationSequence(AnimSequence);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::LoadModifierReferencers(TSubclassOf<UAnimationModifier> ModifierSubClass)
|
|
{
|
|
if (UClass* ModifierClass = ModifierSubClass.Get())
|
|
{
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
|
TArray<FName> PackageDependencies;
|
|
AssetRegistryModule.GetRegistry().GetReferencers(ModifierClass->GetPackage()->GetFName(), PackageDependencies);
|
|
|
|
TArray<FString> PackageNames;
|
|
Algo::Transform(PackageDependencies, PackageNames, [](FName Name) { return Name.ToString(); });
|
|
TArray<UPackage*> Packages = AssetViewUtils::LoadPackages(PackageNames);
|
|
}
|
|
}
|
|
|
|
int32 UAnimationModifier::GetNativeClassRevision() const
|
|
{
|
|
// Overriden in derrived classes to perform native revisioning
|
|
return 0;
|
|
}
|
|
|
|
const UAnimSequence* UAnimationModifier::GetAnimationSequence()
|
|
{
|
|
return CurrentAnimSequence;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateStoredRevisions()
|
|
{
|
|
AppliedGuid = RevisionGuid;
|
|
}
|
|
|
|
void UAnimationModifier::ResetStoredRevisions()
|
|
{
|
|
AppliedGuid.Invalidate();
|
|
}
|
|
|
|
void UAnimationModifier::SetInstanceRevisionGuid(FGuid Guid)
|
|
{
|
|
RevisionGuid = Guid;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|