You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
206 lines
7.1 KiB
C++
206 lines
7.1 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimGraphRuntimePrivatePCH.h"
|
|
#include "BoneControllers/AnimNode_LookAt.h"
|
|
#include "AnimationRuntime.h"
|
|
|
|
FVector FAnimNode_LookAt::GetAlignVector(const FTransform& Transform, EAxisOption::Type AxisOption)
|
|
{
|
|
switch(AxisOption)
|
|
{
|
|
case EAxisOption::X:
|
|
case EAxisOption::X_Neg:
|
|
return Transform.GetUnitAxis(EAxis::X);
|
|
case EAxisOption::Y:
|
|
case EAxisOption::Y_Neg:
|
|
return Transform.GetUnitAxis(EAxis::Y);
|
|
case EAxisOption::Z:
|
|
case EAxisOption::Z_Neg:
|
|
return Transform.GetUnitAxis(EAxis::Z);
|
|
}
|
|
|
|
return FVector(1.f, 0.f, 0.f);
|
|
}
|
|
/////////////////////////////////////////////////////
|
|
// FAnimNode_LookAt
|
|
|
|
FAnimNode_LookAt::FAnimNode_LookAt()
|
|
: LookAtLocation(FVector::ZeroVector)
|
|
, LookAtAxis(EAxis::X)
|
|
, LookAtClamp(0.f)
|
|
, InterpolationTime(0.f)
|
|
, InterpolationTriggerThreashold(0.f)
|
|
, CurrentLookAtLocation(FVector::ZeroVector)
|
|
, AccumulatedInterpoolationTime(0.f)
|
|
{
|
|
}
|
|
|
|
void FAnimNode_LookAt::GatherDebugData(FNodeDebugData& DebugData)
|
|
{
|
|
FString DebugLine = DebugData.GetNodeName(this);
|
|
|
|
DebugLine += "(";
|
|
AddDebugNodeData(DebugLine);
|
|
if (LookAtBone.BoneIndex != INDEX_NONE)
|
|
{
|
|
DebugLine += FString::Printf(TEXT(" Target: %s, Look At Bone: %s, Location : %s)"), *BoneToModify.BoneName.ToString(), *LookAtBone.BoneName.ToString(), *CurrentLookAtLocation.ToString());
|
|
}
|
|
else
|
|
{
|
|
DebugLine += FString::Printf(TEXT(" Target: %s, Look At Location : %s, Location : %s)"), *BoneToModify.BoneName.ToString(), *LookAtLocation.ToString(), *CurrentLookAtLocation.ToString());
|
|
}
|
|
|
|
DebugData.AddDebugItem(DebugLine);
|
|
|
|
ComponentPose.GatherDebugData(DebugData);
|
|
}
|
|
|
|
void DrawDebugData(UWorld* World, const FTransform& TransformVector, const FVector& StartLoc, const FVector& TargetLoc, FColor Color)
|
|
{
|
|
FVector Start = TransformVector.TransformPosition(StartLoc);
|
|
FVector End = TransformVector.TransformPosition(TargetLoc);
|
|
|
|
DrawDebugLine(World, Start, End, Color);
|
|
}
|
|
|
|
void FAnimNode_LookAt::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)
|
|
{
|
|
check(OutBoneTransforms.Num() == 0);
|
|
|
|
const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();
|
|
const FCompactPoseBoneIndex ModifyBoneIndex = BoneToModify.GetCompactPoseIndex(BoneContainer);
|
|
FTransform ComponentBoneTransform = MeshBases.GetComponentSpaceTransform(ModifyBoneIndex);
|
|
|
|
// get target location
|
|
FVector TargetLocationInComponentSpace;
|
|
if (LookAtBone.IsValid(BoneContainer))
|
|
{
|
|
const FTransform& LookAtTransform = MeshBases.GetComponentSpaceTransform(LookAtBone.GetCompactPoseIndex(BoneContainer));
|
|
TargetLocationInComponentSpace = LookAtTransform.GetLocation();
|
|
}
|
|
else
|
|
{
|
|
TargetLocationInComponentSpace = SkelComp->ComponentToWorld.InverseTransformPosition(LookAtLocation);
|
|
}
|
|
|
|
FVector OldCurrentTargetLocation = CurrentTargetLocation;
|
|
FVector NewCurrentTargetLocation = TargetLocationInComponentSpace;
|
|
|
|
if ((NewCurrentTargetLocation - OldCurrentTargetLocation).SizeSquared() > InterpolationTriggerThreashold*InterpolationTriggerThreashold)
|
|
{
|
|
if (AccumulatedInterpoolationTime >= InterpolationTime)
|
|
{
|
|
// reset current Alpha, we're starting to move
|
|
AccumulatedInterpoolationTime = 0.f;
|
|
}
|
|
|
|
PreviousTargetLocation = OldCurrentTargetLocation;
|
|
CurrentTargetLocation = NewCurrentTargetLocation;
|
|
}
|
|
else if (InterpolationTriggerThreashold == 0.f)
|
|
{
|
|
CurrentTargetLocation = NewCurrentTargetLocation;
|
|
}
|
|
|
|
if (InterpolationTime > 0.f)
|
|
{
|
|
float CurrentAlpha = AccumulatedInterpoolationTime/InterpolationTime;
|
|
|
|
if (CurrentAlpha < 1.f)
|
|
{
|
|
float BlendAlpha = AlphaToBlendType(CurrentAlpha, GetInterpolationType());
|
|
|
|
CurrentLookAtLocation = FMath::Lerp(PreviousTargetLocation, CurrentTargetLocation, BlendAlpha);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CurrentLookAtLocation = CurrentTargetLocation;
|
|
}
|
|
|
|
if (bEnableDebug)
|
|
{
|
|
UWorld* World = SkelComp->GetWorld();
|
|
|
|
DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), PreviousTargetLocation, FColor(0, 255, 0));
|
|
DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), CurrentTargetLocation, FColor(255, 0, 0));
|
|
DrawDebugData(World, SkelComp->GetComponentToWorld(), ComponentBoneTransform.GetLocation(), CurrentLookAtLocation, FColor(0, 0, 255));
|
|
}
|
|
|
|
// lookat vector
|
|
FVector LookAtVector = GetAlignVector(ComponentBoneTransform, LookAtAxis);
|
|
// flip to target vector if it wasnt negative
|
|
bool bShouldFlip = LookAtAxis == EAxisOption::X_Neg || LookAtAxis == EAxisOption::Y_Neg || LookAtAxis == EAxisOption::Z_Neg;
|
|
FVector ToTarget = CurrentLookAtLocation - ComponentBoneTransform.GetLocation();
|
|
ToTarget.Normalize();
|
|
if (bShouldFlip)
|
|
{
|
|
ToTarget *= -1.f;
|
|
}
|
|
|
|
if ( LookAtClamp > ZERO_ANIMWEIGHT_THRESH )
|
|
{
|
|
float LookAtClampInRadians = FMath::DegreesToRadians(LookAtClamp);
|
|
float DiffAngle = FMath::Acos(FVector::DotProduct(LookAtVector, ToTarget));
|
|
if (LookAtClampInRadians > 0.f && DiffAngle > LookAtClampInRadians)
|
|
{
|
|
FVector OldToTarget = ToTarget;
|
|
FVector DeltaTarget = ToTarget-LookAtVector;
|
|
|
|
float Ratio = LookAtClampInRadians/DiffAngle;
|
|
DeltaTarget *= Ratio;
|
|
|
|
ToTarget = LookAtVector + DeltaTarget;
|
|
ToTarget.Normalize();
|
|
// UE_LOG(LogAnimation, Warning, TEXT("Recalculation required - old target %f, new target %f"),
|
|
// FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(LookAtVector, OldToTarget))), FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(LookAtVector, ToTarget))));
|
|
}
|
|
}
|
|
|
|
FQuat DeltaRot;
|
|
// if want to use look up,
|
|
if (bUseLookUpAxis)
|
|
{
|
|
// find look up vector in local space
|
|
FVector LookUpVector = GetAlignVector(ComponentBoneTransform, LookUpAxis);
|
|
// project target to the plane
|
|
FVector NewTarget = FVector::VectorPlaneProject(ToTarget, LookUpVector);
|
|
NewTarget.Normalize();
|
|
DeltaRot = FQuat::FindBetweenNormals(LookAtVector, NewTarget);
|
|
}
|
|
else
|
|
{
|
|
DeltaRot = FQuat::FindBetweenNormals(LookAtVector, ToTarget);
|
|
}
|
|
|
|
// transform current rotation to delta rotation
|
|
FQuat CurrentRot = ComponentBoneTransform.GetRotation();
|
|
FQuat NewRotation = DeltaRot * CurrentRot;
|
|
ComponentBoneTransform.SetRotation(NewRotation);
|
|
|
|
OutBoneTransforms.Add(FBoneTransform(ModifyBoneIndex, ComponentBoneTransform));
|
|
}
|
|
|
|
bool FAnimNode_LookAt::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
|
|
{
|
|
// if both bones are valid
|
|
return (BoneToModify.IsValid(RequiredBones) &&
|
|
// or if name isn't set (use Look At Location) or Look at bone is valid
|
|
// do not call isValid since that means if look at bone isn't in LOD, we won't evaluate
|
|
// we still should evaluate as long as the BoneToModify is valid even LookAtBone isn't included in required bones
|
|
(LookAtBone.BoneName == NAME_None || LookAtBone.BoneIndex != INDEX_NONE) );
|
|
}
|
|
|
|
void FAnimNode_LookAt::InitializeBoneReferences(const FBoneContainer& RequiredBones)
|
|
{
|
|
BoneToModify.Initialize(RequiredBones);
|
|
LookAtBone.Initialize(RequiredBones);
|
|
}
|
|
|
|
void FAnimNode_LookAt::UpdateInternal(const FAnimationUpdateContext& Context)
|
|
{
|
|
FAnimNode_SkeletalControlBase::UpdateInternal(Context);
|
|
|
|
AccumulatedInterpoolationTime = FMath::Clamp(AccumulatedInterpoolationTime+Context.GetDeltaTime(), 0.f, InterpolationTime);;
|
|
}
|