You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
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]
401 lines
11 KiB
C++
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);
|
|
} |