You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Upgrade the overrides and callsites in all of Epic's code. Every class that can be subclassed by licensees that has its override change needs to keep the old version of the function as deprecated. Otherwise subclasses that call Super::GetAssetRegistryTags will have a compile error instead of a deprecation warning. Several classes had their own extension method for GetAssetRegistryTags being passed on to user data classes. Updated all of those to take FAssetRegistryTagsContext just like GetAssetRegistryTags does. #rnx #rb Francis.Hurteau [CL 30224166 by matt peters in ue5-main branch]
641 lines
22 KiB
C++
641 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimationModifier.h"
|
|
#include "AnimationModifiersAssetUserData.h"
|
|
#include "AnimationModifierHelpers.h"
|
|
|
|
#include "Algo/Transform.h"
|
|
#include "Animation/AnimData/IAnimationDataController.h"
|
|
#include "Animation/AnimSequence.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "AssetRegistry/IAssetRegistry.h"
|
|
#include "AssetViewUtils.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "CoreGlobals.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Misc/OutputDeviceRedirector.h"
|
|
#include "ModifierOutputFilter.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Serialization/Archive.h"
|
|
#include "Templates/Casts.h"
|
|
#include "UObject/AssetRegistryTagsContext.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/ObjectKey.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/ReleaseObjectVersion.h"
|
|
#include "UObject/Script.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "AnimationModifier"
|
|
|
|
const FName UAnimationModifier::AnimationModifiersTag = TEXT("AnimationModifierList");
|
|
|
|
UAnimationModifier::UAnimationModifier()
|
|
{
|
|
}
|
|
|
|
void UAnimationModifier::ApplyToAnimationSequence(class UAnimSequence* AnimSequence) const
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
checkf(AnimSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
USkeleton* Skeleton = AnimSequence->GetSkeleton();
|
|
checkf(Skeleton, TEXT("Invalid Skeleton for supplied Animation Sequence"));
|
|
|
|
// 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);
|
|
|
|
IAnimationDataModel::FEvaluationAndModificationLock EvaluationAndModificationLock(*AnimSequence->GetDataModelInterface());
|
|
|
|
bool AppliedModifierOuterWasCreated = false;
|
|
UObject* AppliedModifierOuter = AnimSequence->GetAssetUserData<UAnimationModifiersAssetUserData>();
|
|
if (!AppliedModifierOuter)
|
|
{
|
|
AppliedModifierOuterWasCreated = true;
|
|
AppliedModifierOuter = FAnimationModifierHelpers::RetrieveOrCreateModifierUserData(AnimSequence);
|
|
}
|
|
// Using explicit name may cause duplicate path name
|
|
UAnimationModifier* AppliedModifier = DuplicateObject(this, AppliedModifierOuter);
|
|
// Set the revision guid on applied modifier to latest
|
|
AppliedModifier->RevisionGuid = GetLatestRevisionGuid();
|
|
|
|
AnimSequence->Modify();
|
|
|
|
FTransaction AnimationDataTransaction;
|
|
AnimationDataTransaction.SaveObject(AnimSequence);
|
|
AnimationDataTransaction.SaveObject(Skeleton);
|
|
|
|
{
|
|
// Group the OnRevert & OnApply operation into one Bracket to reduce compression request
|
|
IAnimationDataController::FScopedBracket ScopedBracket(AnimSequence->GetController(), LOCTEXT("ApplyModifierBracket", "Applying Animation Modifier"));
|
|
|
|
/** In case this modifier has been previously applied, revert it using the serialised out version at the time */
|
|
if (UAnimationModifier* PreviouslyAppliedModifier = GetAppliedModifier(AnimSequence))
|
|
{
|
|
// Save the applied modifier as well
|
|
AnimationDataTransaction.SaveObject(PreviouslyAppliedModifier);
|
|
PreviouslyAppliedModifier->ExecuteOnRevert(AnimSequence);
|
|
}
|
|
|
|
{
|
|
/** Reverting and applying, populates the log with possible warnings and or errors to notify the user about */
|
|
AppliedModifier->ExecuteOnApply(AnimSequence);
|
|
}
|
|
}
|
|
|
|
GLog->RemoveOutputDevice(&OutputLog);
|
|
|
|
// Check if warnings or errors have occurred and show dialog to user to inform them 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
|
|
static const FText MessageTitle = LOCTEXT("ModifierDialogTitle", "Modifier has generated errors.");
|
|
if (bErrors)
|
|
{
|
|
const FText ErrorMessage = 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));
|
|
UE::Anim::FApplyModifiersScope::HandleError(this, ErrorMessage, MessageTitle);
|
|
}
|
|
|
|
// If _only_ warnings have occured - check if user has previously said YesAll / NoAll and process the result
|
|
if (bWarnings && !bShouldRevert)
|
|
{
|
|
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->GetPathName()),
|
|
FText::FromString(OutputLog));
|
|
bShouldRevert = !UE::Anim::FApplyModifiersScope::HandleWarning(this, WarningMessage, MessageTitle);
|
|
}
|
|
|
|
// Revert changes if necessary, otherwise post edit and refresh animation data
|
|
if (bShouldRevert)
|
|
{
|
|
AnimationDataTransaction.BeginOperation();
|
|
AnimationDataTransaction.Apply();
|
|
AnimationDataTransaction.EndOperation();
|
|
AnimSequence->RefreshCacheData();
|
|
AppliedModifier->MarkAsGarbage();
|
|
if (AppliedModifierOuterWasCreated)
|
|
{
|
|
// The animation sequence's asset user data array should have been reverted by the transaction
|
|
AppliedModifierOuter->MarkAsGarbage();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetAppliedModifier(AnimSequence, AppliedModifier);
|
|
|
|
if (Skeleton->GetPackage()->IsDirty())
|
|
{
|
|
Skeleton->PostEditChange();
|
|
}
|
|
if (AnimSequence->GetPackage()->IsDirty())
|
|
{
|
|
AnimSequence->PostEditChange();
|
|
AnimSequence->RefreshCacheData();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::UpdateCompressedAnimationData()
|
|
{
|
|
if (CurrentAnimSequence->DoesNeedRecompress())
|
|
{
|
|
CurrentAnimSequence->CacheDerivedDataForCurrentPlatform();
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::RevertFromAnimationSequence(class UAnimSequence* AnimSequence) const
|
|
{
|
|
FEditorScriptExecutionGuard ScriptGuard;
|
|
|
|
checkf(AnimSequence, TEXT("Invalid Animation Sequence supplied"));
|
|
USkeleton* Skeleton = AnimSequence->GetSkeleton();
|
|
checkf(Skeleton, TEXT("Invalid Skeleton for supplied Animation Sequence"));
|
|
/** Can only revert if previously applied, which means there should be a previous modifier */
|
|
UAnimationModifier* PreviouslyAppliedModifier = FindAndRemoveAppliedModifier(AnimSequence);
|
|
|
|
// Backward compatibility
|
|
// Is PreviouslyAppliedModifier owned by InAnimationSequence
|
|
// Modifier on Skeleton read from legacy version may _share_ the PreviouslyAppliedModifier
|
|
bool AppliedModifierOwnedByAnimation = true;
|
|
if (!PreviouslyAppliedModifier)
|
|
{
|
|
PreviouslyAppliedModifier = GetLegacyPreviouslyAppliedModifierForModifierOnSkeleton();
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
AppliedModifierOwnedByAnimation = false;
|
|
}
|
|
}
|
|
|
|
if (PreviouslyAppliedModifier)
|
|
{
|
|
// Transact the modifier to prevent instance variables/data to change during reverting
|
|
{
|
|
IAnimationDataController::FScopedBracket ScopedBracket(AnimSequence->GetController(), LOCTEXT("ApplyModifierBracket", "Applying Animation Modifier"));
|
|
PreviouslyAppliedModifier->ExecuteOnRevert(AnimSequence);
|
|
}
|
|
|
|
if (AppliedModifierOwnedByAnimation)
|
|
{
|
|
PreviouslyAppliedModifier->MarkAsGarbage();
|
|
}
|
|
|
|
if (Skeleton->GetPackage()->IsDirty())
|
|
{
|
|
Skeleton->PostEditChange();
|
|
}
|
|
if (AnimSequence->GetPackage()->IsDirty())
|
|
{
|
|
AnimSequence->PostEditChange();
|
|
AnimSequence->RefreshCacheData();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UAnimationModifier::CanRevert(IInterface_AssetUserData* AssetUserDataInterface) const
|
|
{
|
|
return GetAppliedModifier(AssetUserDataInterface) != nullptr;
|
|
}
|
|
|
|
UAnimationModifier* UAnimationModifier::GetAppliedModifier(IInterface_AssetUserData* AssetUserDataInterface) const
|
|
{
|
|
if (UAnimationModifiersAssetUserData* AssetData = AssetUserDataInterface->GetAssetUserData<UAnimationModifiersAssetUserData>())
|
|
{
|
|
TObjectPtr<UAnimationModifier>* AppliedModifier = AssetData->AppliedModifiers.Find(this);
|
|
if (AppliedModifier)
|
|
{
|
|
return AppliedModifier->Get();
|
|
}
|
|
}
|
|
|
|
// Backward compatibility
|
|
// Read the shared applied instance for modifier on skeleton ready from legacy version
|
|
if (UAnimationModifier* LegacyAppliedModifierOnSkeleton = GetLegacyPreviouslyAppliedModifierForModifierOnSkeleton())
|
|
{
|
|
return LegacyAppliedModifierOnSkeleton;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UAnimationModifier* UAnimationModifier::FindAndRemoveAppliedModifier(TScriptInterface<IInterface_AssetUserData> AssetUserDataInterface) const
|
|
{
|
|
if (UAnimationModifiersAssetUserData* AssetData = AssetUserDataInterface->GetAssetUserData<UAnimationModifiersAssetUserData>())
|
|
{
|
|
TObjectPtr<UAnimationModifier> AppliedModifier = nullptr;
|
|
AssetData->AppliedModifiers.RemoveAndCopyValue(this, AppliedModifier);
|
|
if (AppliedModifier)
|
|
{
|
|
AssetData->Modify();
|
|
AssetUserDataInterface.GetObject()->Modify();
|
|
}
|
|
return AppliedModifier.Get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UAnimationModifier::SetAppliedModifier(TScriptInterface<IInterface_AssetUserData> AssetUserDataInterface, UAnimationModifier* AppliedModifier) const
|
|
{
|
|
if (UAnimationModifiersAssetUserData* AssetData = FAnimationModifierHelpers::RetrieveOrCreateModifierUserData(AssetUserDataInterface))
|
|
{
|
|
TObjectPtr<UAnimationModifier>& Modifier = AssetData->AppliedModifiers.FindOrAdd(this);
|
|
|
|
if (Modifier)
|
|
{
|
|
Modifier->MarkAsGarbage();
|
|
}
|
|
Modifier = AppliedModifier;
|
|
AssetData->Modify();
|
|
AssetUserDataInterface.GetObject()->Modify();
|
|
}
|
|
}
|
|
|
|
bool UAnimationModifier::IsLatestRevisionApplied(IInterface_AssetUserData* AssetUserDataInterface) const
|
|
{
|
|
if (UAnimationModifier* AppliedModifier = GetAppliedModifier(AssetUserDataInterface))
|
|
{
|
|
return AppliedModifier->RevisionGuid == GetLatestRevisionGuid();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UAnimationModifier* UAnimationModifier::GetLegacyPreviouslyAppliedModifierForModifierOnSkeleton() const
|
|
{
|
|
// Read PreviouslyAppliedModifier_DEPRECATED from previous version
|
|
// Check PostLoad() when we moved the deprecated value into this place
|
|
if (UAnimationModifiersAssetUserData* AssetData = Cast<UAnimationModifiersAssetUserData>(GetOuter()))
|
|
{
|
|
if (USkeleton* Skeleton = Cast<USkeleton>(AssetData->GetOuter()))
|
|
{
|
|
if (TObjectPtr<UAnimationModifier>* PtrToModifier = AssetData->AppliedModifiers.Find(this))
|
|
{
|
|
// The new system uses a _placeholder_ object with _exact_ UAnimationAsset class to store RevisionGuid
|
|
// Any object with concrete modifier class must come from PreviouslyAppliedModifier_DEPRECATED
|
|
if (PtrToModifier->GetClass() != UAnimationAsset::StaticClass())
|
|
{
|
|
return PtrToModifier->Get();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FGuid UAnimationModifier::GetLatestRevisionGuid() const
|
|
{
|
|
return GetDefault<UAnimationModifier>(GetClass())->RevisionGuid;
|
|
}
|
|
|
|
void UAnimationModifier::GetAssetRegistryTagsForAppliedModifiersFromSkeleton(FAssetRegistryTagsContext Context)
|
|
{
|
|
const UObject* Object = Context.GetObject();
|
|
FString ModifiersList;
|
|
if (UAnimSequence* AnimSequence = const_cast<UAnimSequence*>(Cast<UAnimSequence>(Object)))
|
|
{
|
|
if (USkeleton* Skeleton = AnimSequence->GetSkeleton())
|
|
{
|
|
if (UAnimationModifiersAssetUserData* SkeletonAssetData = Skeleton->GetAssetUserData<UAnimationModifiersAssetUserData>())
|
|
{
|
|
for (const UAnimationModifier* Modifier : SkeletonAssetData->GetAnimationModifierInstances())
|
|
{
|
|
if (const UAnimationModifier* AppliedModifier = Modifier->GetAppliedModifier(AnimSequence))
|
|
{
|
|
// "{Name}={Revision};"
|
|
FString Revision;
|
|
AppliedModifier->RevisionGuid.AppendString(Revision, EGuidFormats::Short);
|
|
ModifiersList += FString::Printf(TEXT("%s%c%s%c"), *Modifier->GetName(), AnimationModifiersAssignment, *Revision, AnimationModifiersDelimiter);
|
|
}
|
|
}
|
|
if (!ModifiersList.IsEmpty())
|
|
{
|
|
Context.AddTag(FAssetRegistryTag(AnimationModifiersTag, ModifiersList, UObject::FAssetRegistryTag::TT_Hidden));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! @brief Mark this modifier on skeleton as reverted (affect CanRevert, IsLatestRevisionApplied)
|
|
void UAnimationModifier::RemoveLegacyPreviousAppliedModifierOnSkeleton(USkeleton* Skeleton)
|
|
{
|
|
if (UAnimationModifier* AppliedModifier = FindAndRemoveAppliedModifier(Skeleton))
|
|
{
|
|
AppliedModifier->MarkAsGarbage();
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
UObject* AssetData = GetOuter();
|
|
UObject* AnimationOrSkeleton = AssetData ? AssetData->GetOuter() : nullptr;
|
|
if (AnimationOrSkeleton)
|
|
{
|
|
SetAppliedModifier(AnimationOrSkeleton, DuplicateObject(this, GetOuter()));
|
|
}
|
|
Modify();
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
if (PreviouslyAppliedModifier_DEPRECATED)
|
|
{
|
|
// The applied guid is now stored at the revision guid of applied modifier
|
|
PreviouslyAppliedModifier_DEPRECATED->RevisionGuid = this->AppliedGuid_DEPRECATED;
|
|
if (UAnimationModifiersAssetUserData* AssetData = Cast<UAnimationModifiersAssetUserData>(GetOuter()))
|
|
{
|
|
if (UAnimSequence* AnimSequence = Cast<UAnimSequence>(AssetData->GetOuter()))
|
|
{
|
|
UE_LOG(LogAnimation, Log, TEXT("Upgrading Applied Animation Modifier on Sequence %s."), *AssetData->GetOuter()->GetName());
|
|
UAnimationModifier* AppliedModifier = PreviouslyAppliedModifier_DEPRECATED;
|
|
PreviouslyAppliedModifier_DEPRECATED = nullptr;
|
|
SetAppliedModifier(AnimSequence, AppliedModifier);
|
|
}
|
|
else if (USkeleton* Skeleton = Cast<USkeleton>(AssetData->GetOuter()))
|
|
{
|
|
UE_LOG(LogAnimation, Warning, TEXT("Upgrading Applied Animation Modifier %s on Skeleton %s, please reapply the modifier."), *GetName(), *AssetData->GetOuter()->GetName());
|
|
UAnimationModifier* AppliedModifier = PreviouslyAppliedModifier_DEPRECATED;
|
|
PreviouslyAppliedModifier_DEPRECATED = nullptr;
|
|
SetAppliedModifier(Skeleton, AppliedModifier);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimation, Error, TEXT("Upgrading Applied Animation Modifier on Unknown type of asset %s, Ignored."), *AssetData->GetOuter()->GetName());
|
|
PreviouslyAppliedModifier_DEPRECATED->MarkAsGarbage();
|
|
PreviouslyAppliedModifier_DEPRECATED = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAnimation, Error, TEXT("Upgrading Applied Animation Modifier on Unknown asset, Ignored."));
|
|
PreviouslyAppliedModifier_DEPRECATED->MarkAsGarbage();
|
|
PreviouslyAppliedModifier_DEPRECATED = nullptr;
|
|
}
|
|
this->Modify();
|
|
}
|
|
}
|
|
|
|
const USkeleton* UAnimationModifier::GetSkeleton()
|
|
{
|
|
return CurrentSkeleton;
|
|
}
|
|
|
|
void UAnimationModifier::UpdateRevisionGuid(UClass* ModifierClass)
|
|
{
|
|
if (ModifierClass)
|
|
{
|
|
RevisionGuid = FGuid::NewGuid();
|
|
|
|
}
|
|
}
|
|
|
|
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();
|
|
TryUpdateDefaultConfigFile();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimationModifier::ExecuteOnRevert(UAnimSequence* InAnimSequence)
|
|
{
|
|
CurrentAnimSequence = InAnimSequence;
|
|
CurrentSkeleton = InAnimSequence->GetSkeleton();
|
|
OnRevert(InAnimSequence);
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
}
|
|
|
|
void UAnimationModifier::ExecuteOnApply(UAnimSequence* InAnimSequence)
|
|
{
|
|
CurrentAnimSequence = InAnimSequence;
|
|
CurrentSkeleton = InAnimSequence->GetSkeleton();
|
|
OnApply(InAnimSequence);
|
|
CurrentAnimSequence = nullptr;
|
|
CurrentSkeleton = nullptr;
|
|
}
|
|
|
|
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 (UAnimationModifiersAssetUserData* AssetUserData : TObjectRange<UAnimationModifiersAssetUserData>{})
|
|
{
|
|
UAnimSequence* AnimSequence = Cast<UAnimSequence>(AssetUserData->GetOuter());
|
|
USkeleton* Skeleton = Cast<USkeleton>(AssetUserData->GetOuter());
|
|
ensure(AnimSequence || Skeleton);
|
|
|
|
for (UAnimationModifier* Modifier : AssetUserData->AnimationModifierInstances)
|
|
{
|
|
if (Modifier->GetClass() == ModifierClass && IsValidChecked(Modifier))
|
|
{
|
|
if (AnimSequence)
|
|
{
|
|
if (bForceApply || !Modifier->IsLatestRevisionApplied(AnimSequence))
|
|
{
|
|
Modifier->ApplyToAnimationSequence(AnimSequence);
|
|
}
|
|
}
|
|
else if (Skeleton)
|
|
{
|
|
// TODO : Modifier on Skeleton
|
|
// We can apply the modifier to all animation sequences referencing this skeleton
|
|
// Save this behavior change for another CL
|
|
UE_LOG(LogAnimation, Warning, TEXT("Animation Modifier %s on Skeleton %s was skipped, please manually apply it from Skeleton"), *ModifierClass->GetName(), *Skeleton->GetName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
namespace UE::Anim
|
|
{
|
|
TArray<FApplyModifiersScope::ESuppressionMode, TInlineAllocator<4>> FApplyModifiersScope::ScopeModeStack;
|
|
TMap<FObjectKey, EAppReturnType::Type> FApplyModifiersScope::WarningResponse;
|
|
TSet<FObjectKey> FApplyModifiersScope::ErrorResponse;
|
|
|
|
FApplyModifiersScope::ESuppressionMode FApplyModifiersScope::CurrentMode()
|
|
{
|
|
// Force Dialog is the legacy behavior when no scope is open
|
|
return ScopeModeStack.IsEmpty() ? ForceDialog : ScopeModeStack.Top();
|
|
}
|
|
|
|
FApplyModifiersScope::ESuppressionMode FApplyModifiersScope::Open(ESuppressionMode Mode)
|
|
{
|
|
if (Mode == NoChange)
|
|
{
|
|
// When no scope was open, we should be transit to ShowDialog mode (not ForceDialog mode)
|
|
Mode = ScopeModeStack.IsEmpty() ? ShowDialog : ScopeModeStack.Top();
|
|
}
|
|
if (ScopeModeStack.IsEmpty())
|
|
{
|
|
// Clear the response at the out most scope open
|
|
WarningResponse.Empty();
|
|
ErrorResponse.Empty();
|
|
}
|
|
ScopeModeStack.Push(Mode);
|
|
return Mode;
|
|
}
|
|
|
|
void FApplyModifiersScope::Close()
|
|
{
|
|
if (!ensureMsgf(!ScopeModeStack.IsEmpty(), TEXT("Unpaired FApplyModifiersScope::Open/Close call.")))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ScopeModeStack.Pop();
|
|
if (ScopeModeStack.IsEmpty())
|
|
{
|
|
// Clear the response at the out most scope close
|
|
WarningResponse.Empty();
|
|
ErrorResponse.Empty();
|
|
}
|
|
}
|
|
|
|
void FApplyModifiersScope::HandleError(const UAnimationModifier* Modifier, const FText& Message, const FText& Title)
|
|
{
|
|
ESuppressionMode Mode = CurrentMode();
|
|
if (Mode > SuppressWarningAndError && Mode < RevertAtWarning)
|
|
{
|
|
// Show the dialog if this error was not shown before, or when forced
|
|
if (Mode == ForceDialog || !ErrorResponse.Contains(Modifier->GetClass()))
|
|
{
|
|
FMessageDialog::Open(EAppMsgType::Ok, Message, Title);
|
|
}
|
|
}
|
|
// Record the error to avoid same error dialog
|
|
ErrorResponse.Add(Modifier->GetClass());
|
|
}
|
|
|
|
bool FApplyModifiersScope::HandleWarning(const UAnimationModifier* Modifier, const FText& Message, const FText& Title)
|
|
{
|
|
auto IsYesAllNoAll = [](EAppReturnType::Type Value) { return Value == EAppReturnType::YesAll || Value == EAppReturnType::NoAll; };
|
|
auto PtrValueOr = [](auto* Ptr, auto Default) { return Ptr ? *Ptr : Default; };
|
|
|
|
EAppReturnType::Type Response = EAppReturnType::No;
|
|
switch (ESuppressionMode Mode = CurrentMode())
|
|
{
|
|
case SuppressWarningAndError:
|
|
case SuppressWarning:
|
|
Response = EAppReturnType::Yes;
|
|
break;
|
|
case RevertAtWarning:
|
|
Response = EAppReturnType::No;
|
|
break;
|
|
case ForceDialog:
|
|
Response = EAppReturnType::Retry;
|
|
break;
|
|
case ShowDialog:
|
|
default:
|
|
Response = PtrValueOr(WarningResponse.Find(Modifier->GetClass()), EAppReturnType::Retry);
|
|
break;
|
|
}
|
|
|
|
// Don't show the dialog if the previous response is YesToAll or NoToAll
|
|
if (Response == EAppReturnType::Retry)
|
|
{
|
|
Response = FMessageDialog::Open(EAppMsgType::YesNoYesAllNoAll, Message, Title);
|
|
// Record the YesAll NoAll response to avoid same the warning dialog
|
|
if (IsYesAllNoAll(Response))
|
|
{
|
|
WarningResponse.Add(Modifier->GetClass(), Response);
|
|
}
|
|
}
|
|
|
|
return (Response == EAppReturnType::Yes) || (Response == EAppReturnType::YesAll);
|
|
}
|
|
} // namespace UE::Anim
|
|
|
|
#undef LOCTEXT_NAMESPACE
|