Files
UnrealEngineUWP/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_BlendSpacePlayer.cpp
Marc Audy de2c3cb8e0 [Backout] - CL20593017, 20594056, 20625634, 20645670, 20649179, 20649223, 20649380, 20658535
Add new custom version to specify a certain window of AnimSequences are not loadable
#fyi Jurre.deBaare
#robomerge EngineMerge
Original CL Desc
-----------------------------------------------------------------
**New**
- Engine
	- IAnimationDataModel interface
- AnimationData plugin
	- AnimSequencerDataModel, sequencer based implementation of IAnimationDataModel
	- AnimSequencerDataController, controller for above (implementation of IAnimationDataController)
- Added FCompressedRichCurve::PopulateCurve, allowing users to convert back to a FRichCurve for validation/debugging
- Added DefaultFrameRate to AnimationSettings, this replaces the hardcoded 30FPS in code
- Added ::GetKeyIndicesFromTime which takes FFrameRate
- Added AnimSequenceBase::OnAnimModelLoaded, this is required for correct postloading behaviour of data model (specifically AnimSequencerDataModel, as it depends on data from its outer ? anim sequence)
- Added IAnimationDataModel ::Evaluate which now takes responsibility for evaluating raw animation bone, curve and attribute data. And moved all trackbased evaluation code into AnimDataModel.cpp
- Added a.ForceEvalRawData allowing to force evaluation of source data
- Added a.SkipDDC to force compressing animation data locally

**Changed**
- Reparent UAnimDataModel to IAnimationDataModel interface, and rejig behaviour
- AnimSequenceBase now reference AnimDataModel as IAnimationDataModel
- Upgrade path for legacy to IAnimationDataModel and existing UAnimDataModel to IAnimationDataModel through IAnimationDataController::PopulateWithExisting
- IAnimationDataModel data is now frame(number/rate/time) based rather than seconds/keyindices. This enforces frameborder aligned data from now on.
- Moved RichCurve evaluation code from RichCurve.cpp to separate file and consolidated all instances of copy-pasta behaviour
- Updated/removed deprecated EngineTests around AnimSequences
- Changed many instances to use a FFrameTime together with a known FFrameRate versus seconds and sequencelength in float involving frame \/ time calculations (including instances of FAnimKeyHelper)
- Switched anim sequence evaluation to use double in Extraction context for time value and patched up places with static\_cast\<double\>
- FRichCurve::Eval
	- Make sure we always evaluate keys when T >= Key0.Time and T <= KeyN.Time to deal with crazy userweighted tangents
- Fixed WeightedKeyDataAdapter::GetKeyInterpMode/GetKeyTangentWeightMode retrieving invalid values, as it was indexing according to KeyIndex rather than KeyIndex\*2. This made it so that values were misinterpreted.
- Fixed WeightedEvalForTwoKeys for compressed data retrieving GetKeyInterpMode and GetKeyTangentWeightMode using the wrong index value. As they are not indexed using KeyDataHandle type.
- Fixed issue where compression could crash when containing zero-key scale additivebase track (tries to retrieve)
- Replaced instances of NULL with nullptr
- Moved required decompression information into FAnimSequenceDecompressionContext and reimplemented decompression within UE::Anim::Decompression namespace and new file
- Deprecated ::GetRawDataGuid and replaced with GetDataModel()->GenerateGuid()
- Fixed UAnimStreamable evaluation/compression (previously broken with MVC refactor)
- Updated Animation exporting pipeline to be frame-based rather than seconds
- Deprecated RetargetPose, replaced with FRetargetingScope (used within AnimModel::Evaluate)
- Fixed BaseAdditiveAnimation array become invalid/incorrect when removing zero-additve tracks during compression
- Updated EngineTest animation sequence related tests to new APIs (while maintaining deprecated path testing as well for now)
- AnimDataController / Animation Sequence tests now generate a USkeleton for the transient animation sequence (used to perform the test on)

**Removed**
- Unused file/class AnimData/AnimDataNotifyCollector.h

#rb Thomas.Sarkanen, Martin.Wilson, Alexis.Matte, Mike.Zyracki
#jira UE-131296
#preflight 62a308a8b0150a87f9d6891b

[CL 20677979 by Marc Audy in ue5-main branch]
2022-06-15 18:24:46 -04:00

401 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNodes/AnimNode_BlendSpacePlayer.h"
#include "Animation/BlendSpace.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimInstanceProxy.h"
#include "AnimGraphRuntimeTrace.h"
#include "Animation/AnimSync.h"
#include "Animation/AnimSyncScope.h"
#if WITH_EDITORONLY_DATA
#include "Animation/AnimBlueprintGeneratedClass.h"
#endif
/////////////////////////////////////////////////////
// FAnimNode_BlendSpacePlayerBase
float FAnimNode_BlendSpacePlayerBase::GetCurrentAssetTime() const
{
if(const FBlendSampleData* HighestWeightedSample = GetHighestWeightedSample())
{
return HighestWeightedSample->Time;
}
// No sample
return 0.0f;
}
float FAnimNode_BlendSpacePlayerBase::GetCurrentAssetTimePlayRateAdjusted() const
{
float Length = GetCurrentAssetLength();
return GetPlayRate() < 0.0f ? Length - InternalTimeAccumulator * Length : Length * InternalTimeAccumulator;
}
float FAnimNode_BlendSpacePlayerBase::GetCurrentAssetLength() const
{
if(const FBlendSampleData* HighestWeightedSample = GetHighestWeightedSample())
{
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
if (CurrentBlendSpace != nullptr)
{
const FBlendSample& Sample = CurrentBlendSpace->GetBlendSample(HighestWeightedSample->SampleDataIndex);
return Sample.Animation->GetPlayLength();
}
}
// No sample
return 0.0f;
}
void FAnimNode_BlendSpacePlayerBase::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Initialize_AnyThread)
FAnimNode_AssetPlayerBase::Initialize_AnyThread(Context);
GetEvaluateGraphExposedInputs().Execute(Context);
Reinitialize();
PreviousBlendSpace = GetBlendSpace();
}
void FAnimNode_BlendSpacePlayerBase::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(CacheBones_AnyThread)
}
void FAnimNode_BlendSpacePlayerBase::UpdateAssetPlayer(const FAnimationUpdateContext& Context)
{
GetEvaluateGraphExposedInputs().Execute(Context);
UpdateInternal(Context);
}
void FAnimNode_BlendSpacePlayerBase::UpdateInternal(const FAnimationUpdateContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(UpdateInternal)
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
if ((CurrentBlendSpace != nullptr) && (Context.AnimInstanceProxy->IsSkeletonCompatible(CurrentBlendSpace->GetSkeleton())))
{
if (PreviousBlendSpace != CurrentBlendSpace)
{
Reinitialize(ShouldResetPlayTimeWhenBlendSpaceChanges());
}
const FVector Position = GetPosition();
// Create a tick record and push into the closest scope
UE::Anim::FAnimSyncGroupScope& SyncScope = Context.GetMessageChecked<UE::Anim::FAnimSyncGroupScope>();
FAnimTickRecord TickRecord(
CurrentBlendSpace, Position, BlendSampleDataCache, BlendFilter, GetLoop(), GetPlayRate(), ShouldTeleportToTime(),
IsEvaluator(), Context.GetFinalBlendWeight(), /*inout*/ InternalTimeAccumulator, MarkerTickRecord);
TickRecord.RootMotionWeightModifier = Context.GetRootMotionWeightModifier();
TickRecord.DeltaTimeRecord = &DeltaTimeRecord;
UE::Anim::FAnimSyncParams SyncParams(GetGroupName(), GetGroupRole(), GetGroupMethod());
TickRecord.GatherContextData(Context);
SyncScope.AddTickRecord(TickRecord, SyncParams, UE::Anim::FAnimSyncDebugInfo(Context));
TRACE_ANIM_TICK_RECORD(Context, TickRecord);
#if WITH_EDITORONLY_DATA
if (FAnimBlueprintDebugData* DebugData = Context.AnimInstanceProxy->GetAnimBlueprintDebugData())
{
DebugData->RecordBlendSpacePlayer(Context.GetCurrentNodeId(), CurrentBlendSpace, Position, BlendFilter.GetFilterLastOutput());
}
#endif
PreviousBlendSpace = CurrentBlendSpace;
}
TRACE_BLENDSPACE_PLAYER(Context, *this);
TRACE_ANIM_NODE_VALUE(Context, TEXT("Name"), CurrentBlendSpace ? *CurrentBlendSpace->GetName() : TEXT("None"));
TRACE_ANIM_NODE_VALUE(Context, TEXT("Blend Space"), CurrentBlendSpace);
TRACE_ANIM_NODE_VALUE(Context, TEXT("Playback Time"), InternalTimeAccumulator);
}
void FAnimNode_BlendSpacePlayerBase::Evaluate_AnyThread(FPoseContext& Output)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Evaluate_AnyThread)
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
if ((CurrentBlendSpace != nullptr) && (Output.AnimInstanceProxy->IsSkeletonCompatible(CurrentBlendSpace->GetSkeleton())))
{
FAnimationPoseData AnimationPoseData(Output);
CurrentBlendSpace->GetAnimationPose(BlendSampleDataCache, FAnimExtractContext(InternalTimeAccumulator, Output.AnimInstanceProxy->ShouldExtractRootMotion(), DeltaTimeRecord, GetLoop()), AnimationPoseData);
}
else
{
Output.ResetToRefPose();
}
}
void FAnimNode_BlendSpacePlayerBase::GatherDebugData(FNodeDebugData& DebugData)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(GatherDebugData)
FString DebugLine = DebugData.GetNodeName(this);
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
if (CurrentBlendSpace)
{
DebugLine += FString::Printf(TEXT("('%s' Play Time: %.3f)"), *CurrentBlendSpace->GetName(), InternalTimeAccumulator);
DebugData.AddDebugItem(DebugLine, true);
}
}
float FAnimNode_BlendSpacePlayerBase::GetTimeFromEnd(float CurrentTime) const
{
// Blend-spaces use normalized time value
const float PlayLength = 1.0f;
return GetBlendSpace() != nullptr ? PlayLength - CurrentTime : 0.0f;
}
UAnimationAsset* FAnimNode_BlendSpacePlayerBase::GetAnimAsset() const
{
return GetBlendSpace();
}
const FBlendSampleData* FAnimNode_BlendSpacePlayerBase::GetHighestWeightedSample() const
{
if(BlendSampleDataCache.Num() == 0)
{
return nullptr;
}
const FBlendSampleData* HighestSample = &BlendSampleDataCache[0];
for(int32 Idx = 1; Idx < BlendSampleDataCache.Num(); ++Idx)
{
if(BlendSampleDataCache[Idx].TotalWeight > HighestSample->TotalWeight)
{
HighestSample = &BlendSampleDataCache[Idx];
}
}
return HighestSample;
}
void FAnimNode_BlendSpacePlayerBase::Reinitialize(bool bResetTime)
{
BlendSampleDataCache.Empty();
if(bResetTime)
{
float CurrentStartPosition = GetStartPosition();
InternalTimeAccumulator = FMath::Clamp(CurrentStartPosition, 0.f, 1.0f);
if (CurrentStartPosition == 0.f && GetPlayRate() < 0.0f)
{
// Blend spaces run between 0 and 1
InternalTimeAccumulator = 1.0f;
}
}
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
if (CurrentBlendSpace != nullptr)
{
CurrentBlendSpace->InitializeFilter(&BlendFilter);
}
}
/////////////////////////////////////////////////////
// FAnimNode_BlendSpacePlayer
FName FAnimNode_BlendSpacePlayer::GetGroupName() const
{
return GET_ANIM_NODE_DATA(FName, GroupName);
}
EAnimGroupRole::Type FAnimNode_BlendSpacePlayer::GetGroupRole() const
{
return GET_ANIM_NODE_DATA(TEnumAsByte<EAnimGroupRole::Type>, GroupRole);
}
EAnimSyncMethod FAnimNode_BlendSpacePlayer::GetGroupMethod() const
{
return GET_ANIM_NODE_DATA(EAnimSyncMethod, Method);
}
bool FAnimNode_BlendSpacePlayer::GetIgnoreForRelevancyTest() const
{
return GET_ANIM_NODE_DATA(bool, bIgnoreForRelevancyTest);
}
bool FAnimNode_BlendSpacePlayer::SetGroupName(FName InGroupName)
{
#if WITH_EDITORONLY_DATA
GroupName = InGroupName;
#endif
if(FName* GroupNamePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(FName, GroupName))
{
*GroupNamePtr = InGroupName;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::SetGroupRole(EAnimGroupRole::Type InRole)
{
#if WITH_EDITORONLY_DATA
GroupRole = InRole;
#endif
if(TEnumAsByte<EAnimGroupRole::Type>* GroupRolePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(TEnumAsByte<EAnimGroupRole::Type>, GroupRole))
{
*GroupRolePtr = InRole;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::SetGroupMethod(EAnimSyncMethod InMethod)
{
#if WITH_EDITORONLY_DATA
Method = InMethod;
#endif
if(EAnimSyncMethod* MethodPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(EAnimSyncMethod, Method))
{
*MethodPtr = InMethod;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::SetIgnoreForRelevancyTest(bool bInIgnoreForRelevancyTest)
{
#if WITH_EDITORONLY_DATA
bIgnoreForRelevancyTest = bInIgnoreForRelevancyTest;
#endif
if(bool* bIgnoreForRelevancyTestPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bIgnoreForRelevancyTest))
{
*bIgnoreForRelevancyTestPtr = bInIgnoreForRelevancyTest;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::SetResetPlayTimeWhenBlendSpaceChanges(bool bInReset)
{
#if WITH_EDITORONLY_DATA
bResetPlayTimeWhenBlendSpaceChanges = bInReset;
#endif
if (bool* bResetPlayTimeWhenBlendSpaceChangesPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bResetPlayTimeWhenBlendSpaceChanges))
{
*bResetPlayTimeWhenBlendSpaceChangesPtr = bInReset;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::SetBlendSpace(UBlendSpace* InBlendSpace)
{
#if WITH_EDITORONLY_DATA
BlendSpace = InBlendSpace;
GET_MUTABLE_ANIM_NODE_DATA(TObjectPtr<UBlendSpace>, BlendSpace) = InBlendSpace;
#endif
if (TObjectPtr<UBlendSpace>* BlendSpacePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(TObjectPtr<UBlendSpace>, BlendSpace))
{
*BlendSpacePtr = InBlendSpace;
return true;
}
return false;
}
FVector FAnimNode_BlendSpacePlayer::GetPosition() const
{
return FVector(GET_ANIM_NODE_DATA(float, X), GET_ANIM_NODE_DATA(float, Y), 0.0f);
}
bool FAnimNode_BlendSpacePlayer::SetPosition(FVector InPosition)
{
#if WITH_EDITORONLY_DATA
X = InPosition[0];
Y = InPosition[1];
GET_MUTABLE_ANIM_NODE_DATA(float, X) = InPosition[0];
GET_MUTABLE_ANIM_NODE_DATA(float, Y) = InPosition[1];
#endif
float* XPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(float, X);
float* YPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(float, Y);
if (XPtr && YPtr)
{
*XPtr = InPosition[0];
*YPtr = InPosition[1];
return true;
}
return false;
}
float FAnimNode_BlendSpacePlayer::GetPlayRate() const
{
return GET_ANIM_NODE_DATA(float, PlayRate);
}
bool FAnimNode_BlendSpacePlayer::SetPlayRate(float InPlayRate)
{
#if WITH_EDITORONLY_DATA
PlayRate = InPlayRate;
GET_MUTABLE_ANIM_NODE_DATA(float, PlayRate) = InPlayRate;
#endif
if (float* PlayRatePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(float, PlayRate))
{
*PlayRatePtr = InPlayRate;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::GetLoop() const
{
return GET_ANIM_NODE_DATA(bool, bLoop);
}
bool FAnimNode_BlendSpacePlayer::SetLoop(bool bInLoop)
{
#if WITH_EDITORONLY_DATA
bLoop = bInLoop;
GET_MUTABLE_ANIM_NODE_DATA(bool, bLoop) = bInLoop;
#endif
if (bool* bLoopPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bLoop))
{
*bLoopPtr = bInLoop;
return true;
}
return false;
}
bool FAnimNode_BlendSpacePlayer::ShouldResetPlayTimeWhenBlendSpaceChanges() const
{
return GET_ANIM_NODE_DATA(bool, bResetPlayTimeWhenBlendSpaceChanges);
}
float FAnimNode_BlendSpacePlayer::GetStartPosition() const
{
return GET_ANIM_NODE_DATA(float, StartPosition);
}
UBlendSpace* FAnimNode_BlendSpacePlayer::GetBlendSpace() const
{
return GET_ANIM_NODE_DATA(TObjectPtr<UBlendSpace>, BlendSpace);
}