//Tasks/Fortnite/Dev-UEA-ControlRig/Engine/...

to //Fortnite/Main/Engine/...

Original CL: 6081637

#jira: UEA-490
#rb lina.halper

#ROBOMERGE-OWNER: ben.marsh
#ROBOMERGE-AUTHOR: helge.mathee
#ROBOMERGE-SOURCE: CL 6098916 via CL 6100686 via CL 6100754
#ROBOMERGE-BOT: BUILD (Main -> Dev-Build)

[CL 6124553 by helge mathee in Dev-Build branch]
This commit is contained in:
helge mathee
2019-04-26 21:57:05 -04:00
parent 7c14abbcd3
commit ad3210e086
6 changed files with 160 additions and 72 deletions

View File

@@ -85,7 +85,7 @@ struct FRigUnit_AimBone_DebugSettings
* Aligns the rotation of a primary and secondary axis of a bone to a world target.
* Note: This node operates in world space!
*/
USTRUCT(meta=(DisplayName="Aim", Keywords="Lookat"))
USTRUCT(meta=(DisplayName="Aim", Category="Hierarchy", Keywords="Lookat"))
struct FRigUnit_AimBone : public FRigUnit_HighlevelBaseMutable
{
GENERATED_BODY()

View File

@@ -1,57 +1,123 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "RigUnit_FABRIK.h"
#include "Units/RigUnitContext.h"
void FRigUnit_FABRIK::Execute(const FRigUnitContext& Context)
{
FRigHierarchyRef& HierarchyRef = ExecuteContext.HierarchyReference;
FRigHierarchy* Hierarchy = (FRigHierarchy*)(Context.HierarchyReference.Get());
if (Hierarchy == nullptr)
{
return;
}
if (Context.State == EControlRigState::Init)
{
const FRigHierarchy* Hierarchy = HierarchyRef.Get();
if (Hierarchy)
{
FullLimbLength = 0.f;
BoneIndices.Reset();
// verify the chain
const int32 RootIndex = Hierarchy->GetIndex(StartJoint);
if (RootIndex != INDEX_NONE)
// verify the chain
const int32 RootIndex = Hierarchy->GetIndex(StartBone);
if (RootIndex != INDEX_NONE)
{
int32 CurrentIndex = EffectorIndex = Hierarchy->GetIndex(EffectorBone);
while (CurrentIndex != INDEX_NONE)
{
int32 CurrentIndex = Hierarchy->GetIndex(EndJoint);
while (CurrentIndex != INDEX_NONE)
// ensure the chain
int32 ParentIndex = Hierarchy->GetParentIndex(CurrentIndex);
if (ParentIndex != INDEX_NONE)
{
// ensure the chain
int32 ParentIndex = Hierarchy->GetParentIndex(CurrentIndex);
if (ParentIndex != INDEX_NONE)
{
// set length for upper/lower length
FTransform ParentTransform = Hierarchy->GetGlobalTransform(ParentIndex);
FTransform CurrentTransform = Hierarchy->GetGlobalTransform(CurrentIndex);
FVector Length = ParentTransform.GetLocation() - CurrentTransform.GetLocation();
FullLimbLength += Length.Size();
}
if (ParentIndex == RootIndex)
{
break;
}
CurrentIndex = ParentIndex;
BoneIndices.Add(CurrentIndex);
}
if (ParentIndex == RootIndex)
{
BoneIndices.Add(RootIndex);
break;
}
CurrentIndex = ParentIndex;
}
}
else
{
UnitLogHelpers::PrintMissingHierarchy(RigUnitName);
Chain.Reserve(BoneIndices.Num());
}
}
else if (Context.State == EControlRigState::Update)
{
if (FullLimbLength > 0.f)
if (BoneIndices.Num() > 0)
{
UnitLogHelpers::PrintUnimplemented(RigUnitName);
// Gather chain links. These are non zero length bones.
Chain.Reset();
TArray<FTransform> Transforms;
Transforms.AddDefaulted(BoneIndices.Num());
float MaximumReach = 0.f;
int32 const NumChainLinks = BoneIndices.Num();
const int32 RootIndex = BoneIndices.Last();
Chain.Add(FABRIKChainLink(Hierarchy->GetGlobalTransform(RootIndex).GetLocation(), 0.f, RootIndex, 0));
Transforms[0] = Hierarchy->GetGlobalTransform(RootIndex);
// start from child to up
for (int32 ChainIndex = BoneIndices.Num() - 2; ChainIndex >= 0 ; --ChainIndex)
{
const FTransform& BoneTransform = Hierarchy->GetGlobalTransform(BoneIndices[ChainIndex]);
const FTransform& ParentTransform = Hierarchy->GetGlobalTransform(BoneIndices[ChainIndex + 1]);
// Calculate the combined length of this segment of skeleton
float const BoneLength = FVector::Dist(BoneTransform.GetLocation(), ParentTransform.GetLocation());
const int32 TransformIndex = Chain.Num();
Chain.Add(FABRIKChainLink(BoneTransform.GetLocation(), BoneLength, BoneIndices[ChainIndex], TransformIndex));
MaximumReach += BoneLength;
Transforms[TransformIndex] = BoneTransform;
}
bool bBoneLocationUpdated = AnimationCore::SolveFabrik(Chain, EffectorTransform.GetLocation(), MaximumReach, Precision, MaxIterations);
// If we moved some bones, update bone transforms.
if (bBoneLocationUpdated)
{
// FABRIK algorithm - re-orientation of bone local axes after translation calculation
for (int32 LinkIndex = 0; LinkIndex < NumChainLinks - 1; LinkIndex++)
{
const FABRIKChainLink& CurrentLink = Chain[LinkIndex];
const FABRIKChainLink& ChildLink = Chain[LinkIndex + 1];
// Calculate pre-translation vector between this bone and child
FVector const OldDir = (Hierarchy->GetGlobalTransform(ChildLink.BoneIndex).GetLocation() - Hierarchy->GetGlobalTransform(CurrentLink.BoneIndex).GetLocation()).GetUnsafeNormal();
// Get vector from the post-translation bone to it's child
FVector const NewDir = (ChildLink.Position - CurrentLink.Position).GetUnsafeNormal();
// Calculate axis of rotation from pre-translation vector to post-translation vector
FVector const RotationAxis = FVector::CrossProduct(OldDir, NewDir).GetSafeNormal();
float const RotationAngle = FMath::Acos(FVector::DotProduct(OldDir, NewDir));
FQuat const DeltaRotation = FQuat(RotationAxis, RotationAngle);
// We're going to multiply it, in order to not have to re-normalize the final quaternion, it has to be a unit quaternion.
checkSlow(DeltaRotation.IsNormalized());
// Calculate absolute rotation and set it
FTransform& CurrentBoneTransform = Transforms[CurrentLink.TransformIndex];
CurrentBoneTransform.SetRotation(DeltaRotation * CurrentBoneTransform.GetRotation());
CurrentBoneTransform.NormalizeRotation();
CurrentBoneTransform.SetTranslation(CurrentLink.Position);
}
// fill up the last data transform
const FABRIKChainLink & CurrentLink = Chain[NumChainLinks - 1];
FTransform& CurrentBoneTransform = Transforms[CurrentLink.TransformIndex];
CurrentBoneTransform.SetTranslation(CurrentLink.Position);
CurrentBoneTransform.SetRotation(Hierarchy->GetGlobalTransform(CurrentLink.BoneIndex).GetRotation());
for (int32 LinkIndex = 0; LinkIndex < NumChainLinks; LinkIndex++)
{
FABRIKChainLink const & LocalLink = Chain[LinkIndex];
Hierarchy->SetGlobalTransform(LocalLink.BoneIndex, Transforms[LocalLink.TransformIndex]);
}
Hierarchy->SetGlobalTransform(EffectorIndex, EffectorTransform);
}
}
}
}

View File

@@ -2,48 +2,61 @@
#pragma once
#include "Units/RigUnit.h"
#include "Hierarchy.h"
#include "ControlRigDefines.h"
#include "Units/Highlevel/RigUnit_HighlevelBase.h"
#include "FABRIK.h"
#include "RigUnit_FABRIK.generated.h"
/**
* Spec define: TBD
/**
* The FABRIK solver can solve N-Bone chains using
* the Forward and Backward Reaching Inverse Kinematics algorithm.
* For now this node supports single effector chains only.
*/
// make it abstract since it's not working yet
USTRUCT(meta=(DisplayName="FABRIK", Category="Transforms", Abstract))
struct CONTROLRIG_API FRigUnit_FABRIK : public FRigUnitMutable
USTRUCT(meta=(DisplayName="Basic FABRIK", Category="Hierarchy", Keywords="N-Bone,IK"))
struct FRigUnit_FABRIK : public FRigUnit_HighlevelBaseMutable
{
GENERATED_BODY()
virtual void Execute(const FRigUnitContext& Context) override;
FRigUnit_FABRIK()
: Precision(1.f)
, MaxIterations(10)
, FullLimbLength(0.f)
{}
{
EffectorIndex = INDEX_NONE;
Precision = 1.f;
MaxIterations = 10;
EffectorTransform = FTransform::Identity;
}
UPROPERTY(EditAnywhere, Category = "FABRIK", meta = (Input))
FName StartJoint;
/**
* The first bone in the chain to solve
*/
UPROPERTY(meta = (Input, Constant, BoneName))
FName StartBone;
UPROPERTY(EditAnywhere, Category = "FABRIK", meta = (Input))
FName EndJoint;
/**
* The last bone in the chain to solve - the effector
*/
UPROPERTY(meta = (Input, Constant, BoneName))
FName EffectorBone;
/** Tolerance for final tip location delta from EffectorLocation*/
UPROPERTY(EditAnywhere, Category = Solver)
/**
* The transform of the effector in global space
*/
UPROPERTY(meta = (Input))
FTransform EffectorTransform;
/**
* The precision to use for the fabrik solver
*/
UPROPERTY(meta = (Input, Constant))
float Precision;
/** Maximum number of iterations allowed, to control performance. */
UPROPERTY(EditAnywhere, Category = Solver)
/**
* The maximum number of iterations. Values between 4 and 16 are common.
*/
UPROPERTY(meta = (Input))
int32 MaxIterations;
private:
TArray<FABRIKChainLink> ChainLink;
// by default, it is full skeleton length
// we can support stretch option
float FullLimbLength;
TArray<FABRIKChainLink> Chain;
TArray<int32> BoneIndices;
int32 EffectorIndex;
};

View File

@@ -40,7 +40,7 @@ struct FRigUnit_TwoBoneIKSimple_DebugSettings
* Aligns the rotation of a primary and secondary axis of a bone to a world target.
* Note: This node operates in world space!
*/
USTRUCT(meta=(DisplayName="Basic IK", Keywords="TwoBone,IK"))
USTRUCT(meta=(DisplayName="Basic IK", Category="Hierarchy", Keywords="TwoBone,IK"))
struct FRigUnit_TwoBoneIKSimple : public FRigUnit_HighlevelBaseMutable
{
GENERATED_BODY()

View File

@@ -148,7 +148,7 @@ void FAnimNode_Fabrik::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseCont
FABRIKChainLink const & ChildLink = Chain[LinkIndex + 1];
// Calculate pre-translation vector between this bone and child
FVector const OldDir = (GetCurrentLocation(Output.Pose, ChildLink.BoneIndex) - GetCurrentLocation(Output.Pose, CurrentLink.BoneIndex)).GetUnsafeNormal();
FVector const OldDir = (GetCurrentLocation(Output.Pose, FCompactPoseBoneIndex(ChildLink.BoneIndex)) - GetCurrentLocation(Output.Pose, FCompactPoseBoneIndex(CurrentLink.BoneIndex))).GetUnsafeNormal();
// Get vector from the post-translation bone to it's child
FVector const NewDir = (ChildLink.Position - CurrentLink.Position).GetUnsafeNormal();

View File

@@ -24,7 +24,7 @@ public:
float Length;
/** Bone Index in SkeletalMesh */
FCompactPoseBoneIndex BoneIndex;
int32 BoneIndex;
/** Transform Index that this control will output */
int32 TransformIndex;
@@ -45,21 +45,30 @@ public:
{
}
FABRIKChainLink(const FVector& InPosition, const float& InLength, const FCompactPoseBoneIndex& InBoneIndex, const int32& InTransformIndex)
FABRIKChainLink(const FVector& InPosition, const float InLength, const FCompactPoseBoneIndex& InBoneIndex, const int32& InTransformIndex)
: Position(InPosition)
, Length(InLength)
, BoneIndex(InBoneIndex)
, BoneIndex(InBoneIndex.GetInt())
, TransformIndex(InTransformIndex)
, DefaultDirToParent(FVector(-1.f, 0.f, 0.f))
{
}
FABRIKChainLink(const FVector& InPosition, const float& InLength, const FCompactPoseBoneIndex& InBoneIndex, const int32& InTransformIndex, const FVector& InDefaultDirToParent)
FABRIKChainLink(const FVector& InPosition, const float InLength, const FCompactPoseBoneIndex& InBoneIndex, const int32& InTransformIndex, const FVector& InDefaultDirToParent)
: Position(InPosition)
, Length(InLength)
, BoneIndex(InBoneIndex.GetInt())
, TransformIndex(InTransformIndex)
, DefaultDirToParent(InDefaultDirToParent)
{
}
FABRIKChainLink(const FVector& InPosition, const float InLength, const int32 InBoneIndex, const int32 InTransformIndex)
: Position(InPosition)
, Length(InLength)
, BoneIndex(InBoneIndex)
, TransformIndex(InTransformIndex)
, DefaultDirToParent(InDefaultDirToParent)
, DefaultDirToParent(FVector(-1.f, 0.f, 0.f))
{
}
@@ -79,7 +88,7 @@ namespace AnimationCore
* @param Precision Precision
* @param MaxIteration Number of Max Iteration
*
* @return true if modified. False if not.
* @return true if modified. False if not.
*/
ANIMATIONCORE_API bool SolveFabrik(TArray<FABRIKChainLink>& InOutChain, const FVector& TargetPosition, float MaximumReach, float Precision, int32 MaxIteration);
};
};