Files
UnrealEngineUWP/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimNodes/AnimNode_AimOffsetLookAt.cpp
Thomas Sarkanen 1dee2903c0 Added schematic view of animation graphs
Initial basic implementation, more features to follow.
Accessed via the context menu of a graph track.
Shows anim graph nodes in a linearized tree similar to ShowDebug ANIMATION.
Select a node to 'pin' all the properties that it traced (multi select supported) to the bottom of the window.
Scrub the timeline to see those properties change over time.
Asset references use clickable hyperlinks to go to the specified asset editor.

#rb Jurre.deBaare

[CL 11041140 by Thomas Sarkanen in Dev-Core branch]
2020-01-17 03:32:34 -05:00

197 lines
7.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNodes/AnimNode_AimOffsetLookAt.h"
#include "Animation/AnimInstanceProxy.h"
#include "AnimationRuntime.h"
#include "Animation/BlendSpaceBase.h"
#include "Animation/BlendSpace1D.h"
#include "Engine/SkeletalMeshSocket.h"
#include "DrawDebugHelpers.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "Animation/AnimTrace.h"
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->SkeletalMesh)
{
if (const USkeletalMeshSocket* Socket = SkelMesh->FindSocket(SourceSocketName))
{
SocketLocalTransform = Socket->GetSocketLocalTransform();
SocketBoneReference.BoneName = Socket->BoneName;
}
if (const USkeletalMeshSocket* Socket = SkelMesh->FindSocket(PivotSocketName))
{
PivotSocketLocalTransform = Socket->GetSocketLocalTransform();
PivotSocketBoneReference.BoneName = Socket->BoneName;
}
}
}
}
void FAnimNode_AimOffsetLookAt::UpdateAssetPlayer(const FAnimationUpdateContext& Context)
{
GetEvaluateGraphExposedInputs().Execute(Context);
bIsLODEnabled = IsLODEnabled(Context.AnimInstanceProxy);
// We don't support ticking and advancing time, because Inputs are determined during Evaluate.
// it may be possible to advance time there (is it a problem with notifies?)
// But typically AimOffsets contain single frame poses, so time doesn't matter.
// 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)
{
// 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
FAnimationRuntime::AccumulateMeshSpaceRotationAdditiveToLocalPose(Context.Pose, MeshSpaceRotationAdditivePoseContext.Pose, Context.Curve, MeshSpaceRotationAdditivePoseContext.Curve, Alpha);
// Resulting rotations are not normalized, so normalize here.
Context.Pose.NormalizeRotations();
}
}
void FAnimNode_AimOffsetLookAt::UpdateFromLookAtTarget(FPoseContext& LocalPoseContext)
{
FVector BlendInput(X, Y, Z);
const FBoneContainer& RequiredBones = LocalPoseContext.Pose.GetBoneContainer();
if (BlendSpace && 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 = GlobalPose.GetComponentSpaceTransform(PivotSocketBoneIndex);
SourceComponentTransform.SetTranslation(PivotBoneComponentTransform.GetTranslation());
}
FAnimInstanceProxy* AnimProxy = LocalPoseContext.AnimInstanceProxy;
check(AnimProxy);
const FTransform SourceWorldTransform = SourceComponentTransform * AnimProxy->GetSkelMeshCompLocalToWorld();
const FTransform ActorTransform = AnimProxy->GetSkelMeshCompOwnerTransform();
// 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);
BlendInput.X = FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.X - CurrentCoords.X));
BlendInput.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)
, BlendInput.X
, BlendInput.Y);
AnimProxy->AnimDrawDebugOnScreenMessage(DebugString, FColor::Red);
}
#endif // ENABLE_DRAW_DEBUG
}
// Set X and Y, so ticking next frame is based on correct weights.
X = BlendInput.X;
Y = BlendInput.Y;
// Generate BlendSampleDataCache from inputs.
if (BlendSpace)
{
BlendSpace->GetSamplesFromBlendInput(BlendInput, BlendSampleDataCache);
}
}
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);
}
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)
, bIsLODEnabled(false)
{
}