You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Animation Modifier : Fix Modifier on Skeleton Transfered the ownership of 'PreviouslyAppliedModifier' from an animation modifier instance to the animation sequence being applied. This solved the following issue: - Modifier on Skeleton cause USkeleton dirtied everytime the modifier is applied to an animation sequence. - Modifier on Skeleton cannot be re-apply or reverted correctly. - CanRevert & OutOfDate status for Modifier on Skeleton was not reflect the true status of all animation sequences referencing that skeleton. - CurrentAnimSequence/CurrentSkeleton was not set on OnRevert() - IAnimationDataController::FScopedBracket was not open on OnRevert() before re-apply modifier - Stateful animation modifier can now be reverted correctly (Applied modifier instance is nolonger reverted after OnApply call) #preflight 63775e0ff514e1ded99ef095 [CL 23191977 by Jurre deBaare in ue5-main branch]
575 lines
20 KiB
C++
575 lines
20 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/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"
|
|
|
|
int32 UE::Anim::FApplyModifiersScope::ScopesOpened = 0;
|
|
TMap<FObjectKey, TOptional<EAppReturnType::Type>> UE::Anim::FApplyModifiersScope::PerClassReturnTypeValues;
|
|
|
|
const FName UAnimationModifier::AnimationModifiersTag = TEXT("AnimationModifierList");
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
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();
|
|
|
|
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
|
|
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(AnimSequence->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(AnimSequence->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();
|
|
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->RequestSyncAnimRecompression(false);
|
|
}
|
|
}
|
|
|
|
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(const UObject* Object, TArray<UObject::FAssetRegistryTag>& OutTags)
|
|
{
|
|
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())
|
|
{
|
|
OutTags.Emplace(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;
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|