Files
UnrealEngine/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_Mirror.cpp
2025-05-18 13:04:45 +08:00

466 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNodes/AnimNode_Mirror.h"
#include "Animation/AnimInstanceProxy.h"
#include "Animation/AnimNode_Inertialization.h"
#include "Animation/AnimTrace.h"
#include "AnimationRuntime.h"
#include "Animation/AnimInertializationSyncScope.h"
#include "Animation/AttributesRuntime.h"
#include "Animation/MirrorSyncScope.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_Mirror)
#define LOCTEXT_NAMESPACE "AnimNode_Mirror"
FAnimNode_MirrorBase::FAnimNode_MirrorBase()
: bMirrorState(false)
, bMirrorStateIsValid(false)
{
}
FAnimNode_MirrorBase::FAnimNode_MirrorBase(const FAnimNode_MirrorBase&) = default;
FAnimNode_MirrorBase::~FAnimNode_MirrorBase() = default;
UMirrorDataTable* FAnimNode_MirrorBase::GetMirrorDataTable() const
{
return nullptr;
}
bool FAnimNode_MirrorBase::SetMirrorDataTable(UMirrorDataTable* MirrorTable)
{
return false;
}
bool FAnimNode_MirrorBase::GetMirror() const
{
return false;
}
float FAnimNode_MirrorBase::GetBlendTimeOnMirrorStateChange() const
{
return 0.0;
}
bool FAnimNode_MirrorBase::GetBoneMirroring() const
{
return false;
}
bool FAnimNode_MirrorBase::GetCurveMirroring() const
{
return false;
}
bool FAnimNode_MirrorBase::GetAttributeMirroring() const
{
return false;
}
bool FAnimNode_MirrorBase::GetResetChildOnMirrorStateChange() const
{
return false;
}
bool FAnimNode_MirrorBase::SetMirror(bool bInMirror)
{
return false;
}
bool FAnimNode_MirrorBase::SetBlendTimeOnMirrorStateChange(float InBlendTime)
{
return false;
}
bool FAnimNode_MirrorBase::SetBoneMirroring(bool bInBoneMirroring)
{
return false;
}
bool FAnimNode_MirrorBase::SetCurveMirroring(bool bInCurveMirroring)
{
return false;
}
bool FAnimNode_MirrorBase::SetAttributeMirroring(bool bInAttributeMirroring)
{
return false;
}
bool FAnimNode_MirrorBase::SetResetChildOnMirrorStateChange(bool bInResetChildOnMirrorStateChange)
{
return false;
}
void FAnimNode_MirrorBase::SetSourceLinkNode(FAnimNode_Base* NewLinkNode)
{
Source.SetLinkNode(NewLinkNode);
}
FAnimNode_Base* FAnimNode_MirrorBase::GetSourceLinkNode()
{
return Source.GetLinkNode();
}
void FAnimNode_MirrorBase::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
FAnimNode_Base::Initialize_AnyThread(Context);
Source.Initialize(Context);
}
void FAnimNode_MirrorBase::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
Source.CacheBones(Context);
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(CacheBones_AnyThread)
Super::CacheBones_AnyThread(Context);
const FBoneContainer& BoneContainer = Context.AnimInstanceProxy->GetRequiredBones();
FillCompactPoseAndComponentRefRotations(BoneContainer);
}
void FAnimNode_MirrorBase::FillCompactPoseAndComponentRefRotations(const FBoneContainer& BoneContainer)
{
UMirrorDataTable* MirrorDataTable = GetMirrorDataTable();
if (MirrorDataTable)
{
MirrorDataTable->FillCompactPoseAndComponentRefRotations(
BoneContainer,
CompactPoseMirrorBones,
ComponentSpaceRefRotations);
}
else
{
CompactPoseMirrorBones.Reset();
ComponentSpaceRefRotations.Reset();
}
}
void FAnimNode_MirrorBase::Update_AnyThread(const FAnimationUpdateContext& Context)
{
GetEvaluateGraphExposedInputs().Execute(Context);
bool bMirror = GetMirror();
bool bRequestedInertialization = false;
if (bMirrorStateIsValid && bMirrorState != bMirror)
{
if (GetBlendTimeOnMirrorStateChange() > SMALL_NUMBER)
{
// Inertialize when switching between mirrored and unmirrored states to smooth out the pose discontinuity
UE::Anim::IInertializationRequester* InertializationRequester = Context.GetMessage<UE::Anim::IInertializationRequester>();
if (InertializationRequester)
{
FInertializationRequest Request;
Request.Duration = GetBlendTimeOnMirrorStateChange();
#if ANIM_TRACE_ENABLED
Request.NodeId = Context.GetCurrentNodeId();
Request.AnimInstance = Context.AnimInstanceProxy->GetAnimInstanceObject();
#endif
InertializationRequester->RequestInertialization(Request);
InertializationRequester->AddDebugRecord(*Context.AnimInstanceProxy, Context.GetCurrentNodeId());
bRequestedInertialization = true;
}
else
{
FAnimNode_Inertialization::LogRequestError(Context, Source);
}
}
// Optionally reinitialize the source when the mirror state changes
if (GetResetChildOnMirrorStateChange())
{
FAnimationInitializeContext ReinitializeContext(Context.AnimInstanceProxy, Context.SharedContext);
Source.Initialize(ReinitializeContext);
}
}
UMirrorDataTable* MirrorDataTable = GetMirrorDataTable();
UE::Anim::TOptionalScopedGraphMessage<UE::Anim::FMirrorSyncScope> Message(bMirror, Context, Context, MirrorDataTable);
UE::Anim::TOptionalScopedGraphMessage<UE::Anim::FAnimInertializationSyncScope> InertializationSync(bRequestedInertialization, Context);
bMirrorState = bMirror;
bMirrorStateIsValid = true;
Source.Update(Context);
TRACE_ANIM_NODE_VALUE(Context, TEXT("Mirrored"), bMirrorState);
}
void FAnimNode_MirrorBase::Evaluate_AnyThread(FPoseContext& Output)
{
Source.Evaluate(Output);
UMirrorDataTable* MirrorDataTable = GetMirrorDataTable();
if (bMirrorState && MirrorDataTable)
{
if (GetBoneMirroring())
{
if (Output.ExpectsAdditivePose())
{
FText Message = FText::Format(LOCTEXT("AdditiveMirrorWarning", "Trying to mirror an additive animation pose is not supported in anim instance '{0}'"), FText::FromString(Output.AnimInstanceProxy->GetAnimInstanceName()));
Output.LogMessage(EMessageSeverity::Warning, Message);
// Force a bind pose to make it obvious
Output.Pose.ResetToAdditiveIdentity();
}
else
{
const FBoneContainer& BoneContainer = Output.Pose.GetBoneContainer();
const TArray<FBoneIndexType>& RequiredBoneIndices = BoneContainer.GetBoneIndicesArray();
int32 NumReqBones = RequiredBoneIndices.Num();
if (CompactPoseMirrorBones.Num() != NumReqBones)
{
FillCompactPoseAndComponentRefRotations(BoneContainer);
}
FAnimationRuntime::MirrorPose(Output.Pose, MirrorDataTable->MirrorAxis, CompactPoseMirrorBones, ComponentSpaceRefRotations);
}
}
if (GetCurveMirroring())
{
FAnimationRuntime::MirrorCurves(Output.Curve, *MirrorDataTable);
}
if (GetAttributeMirroring())
{
UE::Anim::Attributes::MirrorAttributes(Output.CustomAttributes, *MirrorDataTable, CompactPoseMirrorBones);
}
}
}
void FAnimNode_MirrorBase::GatherDebugData(FNodeDebugData& DebugData)
{
FString DebugLine = DebugData.GetNodeName(this);
DebugLine += FString::Printf(TEXT("(Mirrored: %s)"), (bMirrorState) ? TEXT("true") : TEXT("false"));
DebugData.AddDebugItem(DebugLine);
Source.GatherDebugData(DebugData);
}
FAnimNode_Mirror::FAnimNode_Mirror() = default;
FAnimNode_Mirror::~FAnimNode_Mirror() = default;
UMirrorDataTable* FAnimNode_Mirror::GetMirrorDataTable() const
{
return GET_ANIM_NODE_DATA(TObjectPtr<UMirrorDataTable>, MirrorDataTable);
}
bool FAnimNode_Mirror::SetMirrorDataTable(UMirrorDataTable* InMirrorTable)
{
#if WITH_EDITORONLY_DATA
MirrorDataTable = InMirrorTable;
GET_MUTABLE_ANIM_NODE_DATA(TObjectPtr<UMirrorDataTable>, MirrorDataTable) = InMirrorTable;
#endif
if (TObjectPtr<UMirrorDataTable>* InMirrorTablePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(TObjectPtr<UMirrorDataTable>, MirrorDataTable))
{
*InMirrorTablePtr = InMirrorTable;
return true;
}
return false;
}
bool FAnimNode_Mirror::GetMirror() const
{
return GET_ANIM_NODE_DATA(bool, bMirror);
}
float FAnimNode_Mirror::GetBlendTimeOnMirrorStateChange() const
{
return GET_ANIM_NODE_DATA(float, BlendTime);
}
bool FAnimNode_Mirror::GetBoneMirroring() const
{
return GET_ANIM_NODE_DATA(bool, bBoneMirroring);
}
bool FAnimNode_Mirror::GetCurveMirroring() const
{
return GET_ANIM_NODE_DATA(bool, bCurveMirroring);
}
bool FAnimNode_Mirror::GetAttributeMirroring() const
{
return GET_ANIM_NODE_DATA(bool, bAttributeMirroring);
}
bool FAnimNode_Mirror::GetResetChildOnMirrorStateChange() const
{
return GET_ANIM_NODE_DATA(bool, bResetChild);
}
bool FAnimNode_Mirror::SetMirror(bool bInMirror)
{
#if WITH_EDITORONLY_DATA
bMirror = bInMirror;
#endif
if (bool* bMirrorPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bMirror))
{
*bMirrorPtr = bInMirror;
return true;
}
return false;
}
bool FAnimNode_Mirror::SetBlendTimeOnMirrorStateChange(float InBlendTime)
{
#if WITH_EDITORONLY_DATA
BlendTime = InBlendTime;
#endif
if (float* BlendTimePtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(float, BlendTime))
{
*BlendTimePtr = InBlendTime;
return true;
}
return false;
}
bool FAnimNode_Mirror::SetBoneMirroring(bool bInBoneMirroring)
{
#if WITH_EDITORONLY_DATA
bBoneMirroring = bInBoneMirroring;
#endif
if (bool* bBoneMirroringPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bBoneMirroring))
{
*bBoneMirroringPtr = bInBoneMirroring;
return true;
}
return false;
}
bool FAnimNode_Mirror::SetCurveMirroring(bool bInCurveMirroring)
{
#if WITH_EDITORONLY_DATA
bCurveMirroring = bInCurveMirroring;
#endif
if (bool* bCurveMirroringPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bCurveMirroring))
{
*bCurveMirroringPtr = bInCurveMirroring;
return true;
}
return false;
}
bool FAnimNode_Mirror::SetAttributeMirroring(bool bInAttributeMirroring)
{
#if WITH_EDITORONLY_DATA
bAttributeMirroring = bInAttributeMirroring;
#endif
if (bool* bAttributeMirroringPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bAttributeMirroring))
{
*bAttributeMirroringPtr = bInAttributeMirroring;
return true;
}
return false;
}
bool FAnimNode_Mirror::SetResetChildOnMirrorStateChange(bool bInResetChildOnMirrorStateChange)
{
#if WITH_EDITORONLY_DATA
bResetChild = bInResetChildOnMirrorStateChange;
#endif
if (bool* bResetChildPtr = GET_INSTANCE_ANIM_NODE_DATA_PTR(bool, bResetChild))
{
*bResetChildPtr = bInResetChildOnMirrorStateChange;
return true;
}
return false;
}
FAnimNode_Mirror_Standalone::FAnimNode_Mirror_Standalone() = default;
FAnimNode_Mirror_Standalone::FAnimNode_Mirror_Standalone(const FAnimNode_Mirror_Standalone&) = default;
FAnimNode_Mirror_Standalone::~FAnimNode_Mirror_Standalone() = default;
UMirrorDataTable* FAnimNode_Mirror_Standalone::GetMirrorDataTable() const
{
return MirrorDataTable.Get();
}
bool FAnimNode_Mirror_Standalone::SetMirrorDataTable(UMirrorDataTable* MirrorTable)
{
MirrorDataTable = MirrorTable;
return true;
}
bool FAnimNode_Mirror_Standalone::GetMirror() const
{
return bMirror;
}
float FAnimNode_Mirror_Standalone::GetBlendTimeOnMirrorStateChange() const
{
return BlendTime;
}
bool FAnimNode_Mirror_Standalone::GetBoneMirroring() const
{
return bBoneMirroring;
}
bool FAnimNode_Mirror_Standalone::GetCurveMirroring() const
{
return bCurveMirroring;
}
bool FAnimNode_Mirror_Standalone::GetAttributeMirroring() const
{
return bAttributeMirroring;
}
bool FAnimNode_Mirror_Standalone::GetResetChildOnMirrorStateChange() const
{
return bResetChild;
}
bool FAnimNode_Mirror_Standalone::SetMirror(bool bInMirror)
{
bMirror = bInMirror;
return true;
}
bool FAnimNode_Mirror_Standalone::SetBlendTimeOnMirrorStateChange(float InBlendTime)
{
BlendTime = InBlendTime;
return true;
}
bool FAnimNode_Mirror_Standalone::SetBoneMirroring(bool bInBoneMirroring)
{
bBoneMirroring = bInBoneMirroring;
return true;
}
bool FAnimNode_Mirror_Standalone::SetCurveMirroring(bool bInCurveMirroring)
{
bCurveMirroring = bInCurveMirroring;
return true;
}
bool FAnimNode_Mirror_Standalone::SetAttributeMirroring(bool bInAttributeMirroring)
{
bAttributeMirroring = bInAttributeMirroring;
return true;
}
bool FAnimNode_Mirror_Standalone::SetResetChildOnMirrorStateChange(bool bInResetChildOnMirrorStateChange)
{
bResetChild = bInResetChildOnMirrorStateChange;
return true;
}
#undef LOCTEXT_NAMESPACE