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

216 lines
8.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNodes/AnimNode_AimOffsetLookAt.h"
#include "Animation/AnimInstanceProxy.h"
#include "Animation/AnimInstance.h"
#include "Animation/AnimStats.h"
#include "AnimationRuntime.h"
#include "Animation/BlendSpace.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/SkeletalMesh.h"
#include "Engine/SkeletalMeshSocket.h"
#include "DrawDebugHelpers.h"
#include "Animation/AnimTrace.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_AimOffsetLookAt)
TAutoConsoleVariable<int32> CVarAimOffsetLookAtEnable(TEXT("a.AnimNode.AimOffsetLookAt.Enable"), 1, TEXT("Enable/Disable LookAt AimOffset"));
TAutoConsoleVariable<int32> CVarAimOffsetLookAtDebug(TEXT("a.AnimNode.AimOffsetLookAt.Debug"), 0, TEXT("Toggle LookAt AimOffset debug"));
/////////////////////////////////////////////////////
// FAnimNode_AimOffsetLookAt
void FAnimNode_AimOffsetLookAt::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Initialize_AnyThread)
FAnimNode_BlendSpacePlayer::Initialize_AnyThread(Context);
BasePose.Initialize(Context);
}
void FAnimNode_AimOffsetLookAt::OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance)
{
FAnimNode_BlendSpacePlayer::OnInitializeAnimInstance(InProxy, InAnimInstance);
SocketBoneReference.BoneName = NAME_None;
if (USkeletalMeshComponent* SkelMeshComp = InAnimInstance->GetSkelMeshComponent())
{
if (USkeletalMesh* SkelMesh = SkelMeshComp->GetSkeletalMeshAsset())
{
if (const USkeletalMeshSocket* Socket = SkelMesh->FindSocket(SourceSocketName))
{
SocketLocalTransform = Socket->GetSocketLocalTransform();
SocketBoneReference.BoneName = Socket->BoneName;
}
else if (SkelMeshComp->GetBoneIndex(SourceSocketName) != INDEX_NONE)
{
SocketLocalTransform.SetIdentity();
SocketBoneReference.BoneName = SourceSocketName;
}
if (const USkeletalMeshSocket* Socket = SkelMesh->FindSocket(PivotSocketName))
{
PivotSocketLocalTransform = Socket->GetSocketLocalTransform();
PivotSocketBoneReference.BoneName = Socket->BoneName;
}
else if (SkelMeshComp->GetBoneIndex(PivotSocketName) != INDEX_NONE)
{
PivotSocketLocalTransform.SetIdentity();
PivotSocketBoneReference.BoneName = PivotSocketName;
}
}
}
}
void FAnimNode_AimOffsetLookAt::UpdateAssetPlayer(const FAnimationUpdateContext& Context)
{
bIsLODEnabled = IsLODEnabled(Context.AnimInstanceProxy);
if (bIsLODEnabled)
{
FAnimNode_BlendSpacePlayer::UpdateAssetPlayer(Context);
}
BasePose.Update(Context);
TRACE_ANIM_NODE_VALUE(Context, TEXT("Playback Time"), InternalTimeAccumulator);
}
void FAnimNode_AimOffsetLookAt::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(CacheBones_AnyThread)
FAnimNode_BlendSpacePlayer::CacheBones_AnyThread(Context);
BasePose.CacheBones(Context);
SocketBoneReference.Initialize(Context.AnimInstanceProxy->GetRequiredBones());
PivotSocketBoneReference.Initialize(Context.AnimInstanceProxy->GetRequiredBones());
}
void FAnimNode_AimOffsetLookAt::Evaluate_AnyThread(FPoseContext& Context)
{
ANIM_MT_SCOPE_CYCLE_COUNTER_VERBOSE(AimOffsetLookAt, !IsInGameThread());
// Evaluate base pose
BasePose.Evaluate(Context);
if (bIsLODEnabled && FAnimWeight::IsRelevant(Alpha) && (CVarAimOffsetLookAtEnable.GetValueOnAnyThread() == 1))
{
UpdateFromLookAtTarget(Context);
// Evaluate MeshSpaceRotation additive blendspace
FPoseContext MeshSpaceRotationAdditivePoseContext(Context);
FAnimNode_BlendSpacePlayer::Evaluate_AnyThread(MeshSpaceRotationAdditivePoseContext);
// Accumulate poses together
FAnimationPoseData BaseAnimationPoseData(Context);
const FAnimationPoseData AdditiveAnimationPoseData(MeshSpaceRotationAdditivePoseContext);
FAnimationRuntime::AccumulateMeshSpaceRotationAdditiveToLocalPose(BaseAnimationPoseData, AdditiveAnimationPoseData, Alpha);
// Resulting rotations are not normalized, so normalize here.
Context.Pose.NormalizeRotations();
}
}
void FAnimNode_AimOffsetLookAt::UpdateFromLookAtTarget(FPoseContext& LocalPoseContext)
{
UBlendSpace* CurrentBlendSpace = GetBlendSpace();
const FBoneContainer& RequiredBones = LocalPoseContext.Pose.GetBoneContainer();
if (CurrentBlendSpace && SocketBoneReference.IsValidToEvaluate(RequiredBones))
{
FCSPose<FCompactPose> GlobalPose;
GlobalPose.InitPose(LocalPoseContext.Pose);
const FCompactPoseBoneIndex SocketBoneIndex = SocketBoneReference.GetCompactPoseIndex(RequiredBones);
const FTransform BoneTransform = GlobalPose.GetComponentSpaceTransform(SocketBoneIndex);
FTransform SourceComponentTransform = SocketLocalTransform * BoneTransform;
if (PivotSocketBoneReference.IsValidToEvaluate(RequiredBones))
{
const FCompactPoseBoneIndex PivotSocketBoneIndex = PivotSocketBoneReference.GetCompactPoseIndex(RequiredBones);
const FTransform PivotBoneComponentTransform = PivotSocketLocalTransform * GlobalPose.GetComponentSpaceTransform(PivotSocketBoneIndex);
SourceComponentTransform.SetTranslation(PivotBoneComponentTransform.GetTranslation());
}
FAnimInstanceProxy* AnimProxy = LocalPoseContext.AnimInstanceProxy;
check(AnimProxy);
const FTransform SourceWorldTransform = SourceComponentTransform * AnimProxy->GetComponentTransform();
const FTransform ActorTransform = AnimProxy->GetActorTransform();
// Convert Target to Actor Space
const FTransform TargetWorldTransform(LookAtLocation);
const FVector DirectionToTarget = ActorTransform.InverseTransformVectorNoScale(TargetWorldTransform.GetLocation() - SourceWorldTransform.GetLocation()).GetSafeNormal();
const FVector CurrentDirection = ActorTransform.InverseTransformVectorNoScale(SourceWorldTransform.TransformVector(SocketAxis).GetSafeNormal());
const FVector AxisX = FVector::ForwardVector;
const FVector AxisY = FVector::RightVector;
const FVector AxisZ = FVector::UpVector;
const FVector2D CurrentCoords = FMath::GetAzimuthAndElevation(CurrentDirection, AxisX, AxisY, AxisZ);
const FVector2D TargetCoords = FMath::GetAzimuthAndElevation(DirectionToTarget, AxisX, AxisY, AxisZ);
CurrentBlendInput.X = FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.X - CurrentCoords.X));
CurrentBlendInput.Y = FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.Y - CurrentCoords.Y));
#if ENABLE_DRAW_DEBUG
if (CVarAimOffsetLookAtDebug.GetValueOnAnyThread() == 1)
{
AnimProxy->AnimDrawDebugLine(SourceWorldTransform.GetLocation(), TargetWorldTransform.GetLocation(), FColor::Green);
AnimProxy->AnimDrawDebugLine(SourceWorldTransform.GetLocation(), SourceWorldTransform.GetLocation() + SourceWorldTransform.TransformVector(SocketAxis).GetSafeNormal() * (TargetWorldTransform.GetLocation() - SourceWorldTransform.GetLocation()).Size(), FColor::Red);
AnimProxy->AnimDrawDebugCoordinateSystem(ActorTransform.GetLocation(), ActorTransform.GetRotation().Rotator(), 100.f);
FString DebugString = FString::Printf(TEXT("Socket (X:%f, Y:%f), Target (X:%f, Y:%f), Result (X:%f, Y:%f)")
, FMath::RadiansToDegrees(CurrentCoords.X)
, FMath::RadiansToDegrees(CurrentCoords.Y)
, FMath::RadiansToDegrees(TargetCoords.X)
, FMath::RadiansToDegrees(TargetCoords.Y)
, CurrentBlendInput.X
, CurrentBlendInput.Y);
AnimProxy->AnimDrawDebugOnScreenMessage(DebugString, FColor::Red);
}
#endif // ENABLE_DRAW_DEBUG
}
// Update Blend Space, including the smoothing/filtering, and put the result into BlendSampleDataCache.
if (CurrentBlendSpace)
{
const FVector BlendSpacePosition(CurrentBlendInput.X, CurrentBlendInput.Y, 0.f);
const FVector FilteredBlendInput = CurrentBlendSpace->FilterInput(
&BlendFilter, BlendSpacePosition, DeltaTimeRecord.Delta);
CurrentBlendSpace->UpdateBlendSamples(
FilteredBlendInput, DeltaTimeRecord.Delta, BlendSampleDataCache, CachedTriangulationIndex);
}
}
void FAnimNode_AimOffsetLookAt::GatherDebugData(FNodeDebugData& DebugData)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(GatherDebugData)
FString DebugLine = DebugData.GetNodeName(this);
DebugLine += FString::Printf(TEXT("(Play Time: %.3f)"), InternalTimeAccumulator);
DebugData.AddDebugItem(DebugLine);
BasePose.GatherDebugData(DebugData);
}
FVector FAnimNode_AimOffsetLookAt::GetPosition() const
{
// Use our calculated coordinates rather than the folded values
return CurrentBlendInput;
}
FAnimNode_AimOffsetLookAt::FAnimNode_AimOffsetLookAt()
: SocketLocalTransform(FTransform::Identity)
, PivotSocketLocalTransform(FTransform::Identity)
, LODThreshold(INDEX_NONE)
, SourceSocketName(NAME_None)
, PivotSocketName(NAME_None)
, LookAtLocation(ForceInitToZero)
, SocketAxis(1.0f, 0.0f, 0.0f)
, Alpha(1.f)
, CurrentBlendInput(FVector::ZeroVector)
, bIsLODEnabled(false)
{
}