Files
UnrealEngineUWP/Engine/Source/Runtime/AnimGraphRuntime/Private/BoneControllers/AnimNode_LookAt.cpp
Matthew Griffin bb70b349ce Merging CL 2804086 from //UE4/Release-4.11 to Dev-Main (//UE4/Dev-Main) to isolate copyright update
#lockdown Nick.Penwarden

[CL 2819020 by Matthew Griffin in Main branch]
2016-01-07 08:17:16 -05:00

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);;
}