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