You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- at the end, no change on the data because this guarantees frame independent. - Added help and renamed variable to speed, so that it knows it is speed. Merging //depot/UE4-Orion/Engine/... to //depot/UE4-Orion/Engine/... [CL 2683714 by Lina Halper in Main branch]
271 lines
9.2 KiB
C++
271 lines
9.2 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimGraphRuntimePrivatePCH.h"
|
|
#include "BoneControllers/AnimNode_Trail.h"
|
|
|
|
/////////////////////////////////////////////////////
|
|
// FAnimNode_Trail
|
|
|
|
FAnimNode_Trail::FAnimNode_Trail()
|
|
: ChainLength(2)
|
|
, ChainBoneAxis(EAxis::X)
|
|
, bInvertChainBoneAxis(false)
|
|
, bLimitStretch(false)
|
|
, TrailRelaxation_DEPRECATED(10.f)
|
|
, StretchLimit(0)
|
|
, FakeVelocity(FVector::ZeroVector)
|
|
, bActorSpaceFakeVel(false)
|
|
, bHadValidStrength(false)
|
|
{
|
|
FRichCurve* TrailRelaxRichCurve = TrailRelaxationSpeed.GetRichCurve();
|
|
TrailRelaxRichCurve->AddKey(0.f, 10.f);
|
|
TrailRelaxRichCurve->AddKey(1.f, 5.f);
|
|
}
|
|
|
|
void FAnimNode_Trail::Update(const FAnimationUpdateContext& Context)
|
|
{
|
|
FAnimNode_SkeletalControlBase::Update(Context);
|
|
|
|
ThisTimstep = Context.GetDeltaTime();
|
|
}
|
|
|
|
void FAnimNode_Trail::GatherDebugData(FNodeDebugData& DebugData)
|
|
{
|
|
FString DebugLine = DebugData.GetNodeName(this);
|
|
|
|
DebugLine += "(";
|
|
AddDebugNodeData(DebugLine);
|
|
DebugLine += FString::Printf(TEXT(" Active: %s)"), *TrailBone.BoneName.ToString());
|
|
DebugData.AddDebugItem(DebugLine);
|
|
|
|
ComponentPose.GatherDebugData(DebugData);
|
|
}
|
|
|
|
void FAnimNode_Trail::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)
|
|
{
|
|
check(OutBoneTransforms.Num() == 0);
|
|
|
|
if( ChainBoneIndices.Num() <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
checkSlow (ChainBoneIndices.Num() == ChainLength);
|
|
checkSlow (PerJointTrailData.Num() == ChainLength);
|
|
|
|
// The incoming BoneIndex is the 'end' of the spline chain. We need to find the 'start' by walking SplineLength bones up hierarchy.
|
|
// Fail if we walk past the root bone.
|
|
const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();
|
|
FCompactPoseBoneIndex WalkBoneIndex = TrailBone.GetCompactPoseIndex(BoneContainer);
|
|
|
|
ChainBoneIndices[ChainLength - 1] = WalkBoneIndex;
|
|
|
|
for (int32 i = 1; i < ChainLength; i++)
|
|
{
|
|
// returns to avoid a crash
|
|
// @TODO : shows an error message why failed
|
|
if (WalkBoneIndex == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get parent bone.
|
|
WalkBoneIndex = BoneContainer.GetParentBoneIndex(WalkBoneIndex);
|
|
|
|
//Insert indices at the start of array, so that parents are before children in the array.
|
|
int32 TransformIndex = ChainLength - (i + 1);
|
|
ChainBoneIndices[TransformIndex] = WalkBoneIndex;
|
|
}
|
|
|
|
OutBoneTransforms.AddZeroed(ChainLength);
|
|
|
|
// If we have >0 this frame, but didn't last time, record positions of all the bones.
|
|
// Also do this if number has changed or array is zero.
|
|
bool bHasValidStrength = (Alpha > 0.f);
|
|
if(bHasValidStrength && !bHadValidStrength)
|
|
{
|
|
for(int32 i=0; i<ChainBoneIndices.Num(); i++)
|
|
{
|
|
FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i];
|
|
const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(ChildIndex);
|
|
TrailBoneLocations[i] = ChainTransform.GetTranslation();
|
|
}
|
|
OldLocalToWorld = SkelComp->GetTransformMatrix();
|
|
}
|
|
bHadValidStrength = bHasValidStrength;
|
|
|
|
// transform between last frame and now.
|
|
FMatrix OldToNewTM = OldLocalToWorld * SkelComp->GetTransformMatrix().InverseFast();
|
|
|
|
// Add fake velocity if present to all but root bone
|
|
if(!FakeVelocity.IsZero())
|
|
{
|
|
FVector FakeMovement = -FakeVelocity * ThisTimstep;
|
|
|
|
if (bActorSpaceFakeVel && SkelComp->GetOwner())
|
|
{
|
|
const FTransform BoneToWorld(SkelComp->GetOwner()->GetActorQuat(), SkelComp->GetOwner()->GetActorLocation());
|
|
FakeMovement = BoneToWorld.TransformVector(FakeMovement);
|
|
}
|
|
|
|
FakeMovement = SkelComp->GetTransformMatrix().InverseTransformVector(FakeMovement);
|
|
// Then add to each bone
|
|
for(int32 i=1; i<TrailBoneLocations.Num(); i++)
|
|
{
|
|
TrailBoneLocations[i] += FakeMovement;
|
|
}
|
|
}
|
|
|
|
// Root bone of trail is not modified.
|
|
FCompactPoseBoneIndex RootIndex = ChainBoneIndices[0];
|
|
const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(RootIndex);
|
|
OutBoneTransforms[0] = FBoneTransform(RootIndex, ChainTransform);
|
|
TrailBoneLocations[0] = ChainTransform.GetTranslation();
|
|
|
|
// Starting one below head of chain, move bones.
|
|
// this Parent/Child relationship is backward. From start joint (from bottom) to end joint(higher parent )
|
|
for(int32 i=1; i<ChainBoneIndices.Num(); i++)
|
|
{
|
|
// Parent bone position in component space.
|
|
FCompactPoseBoneIndex ParentIndex = ChainBoneIndices[i - 1];
|
|
FVector ParentPos = TrailBoneLocations[i-1];
|
|
FVector ParentAnimPos = MeshBases.GetComponentSpaceTransform(ParentIndex).GetTranslation();
|
|
|
|
// Child bone position in component space.
|
|
FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i];
|
|
FVector ChildPos = OldToNewTM.TransformPosition(TrailBoneLocations[i]); // move from 'last frames component' frame to 'this frames component' frame
|
|
FVector ChildAnimPos = MeshBases.GetComponentSpaceTransform(ChildIndex).GetTranslation();
|
|
|
|
// Desired parent->child offset.
|
|
FVector TargetDelta = (ChildAnimPos - ParentAnimPos);
|
|
|
|
// Desired child position.
|
|
FVector ChildTarget = ParentPos + TargetDelta;
|
|
|
|
// Find vector from child to target
|
|
FVector Error = (ChildTarget - ChildPos);
|
|
|
|
// Calculate how much to push the child towards its target
|
|
float Correction = FMath::Clamp<float>(ThisTimstep * PerJointTrailData[i].TrailRelaxationSpeedPerSecond, 0.f, 1.f);
|
|
|
|
// Scale correction vector and apply to get new world-space child position.
|
|
TrailBoneLocations[i] = ChildPos + (Error * Correction);
|
|
|
|
// If desired, prevent bones stretching too far.
|
|
if(bLimitStretch)
|
|
{
|
|
float RefPoseLength = TargetDelta.Size();
|
|
FVector CurrentDelta = TrailBoneLocations[i] - TrailBoneLocations[i-1];
|
|
float CurrentLength = CurrentDelta.Size();
|
|
|
|
// If we are too far - cut it back (just project towards parent particle).
|
|
if( (CurrentLength - RefPoseLength > StretchLimit) && CurrentLength > SMALL_NUMBER )
|
|
{
|
|
FVector CurrentDir = CurrentDelta / CurrentLength;
|
|
TrailBoneLocations[i] = TrailBoneLocations[i-1] + (CurrentDir * (RefPoseLength + StretchLimit));
|
|
}
|
|
}
|
|
|
|
// Modify child matrix
|
|
OutBoneTransforms[i] = FBoneTransform(ChildIndex, MeshBases.GetComponentSpaceTransform(ChildIndex));
|
|
OutBoneTransforms[i].Transform.SetTranslation(TrailBoneLocations[i]);
|
|
|
|
// Modify rotation of parent matrix to point at this one.
|
|
|
|
// Calculate the direction that parent bone is currently pointing.
|
|
FVector CurrentBoneDir = OutBoneTransforms[i-1].Transform.TransformVector( GetAlignVector(ChainBoneAxis, bInvertChainBoneAxis) );
|
|
CurrentBoneDir = CurrentBoneDir.GetSafeNormal(SMALL_NUMBER);
|
|
|
|
// Calculate vector from parent to child.
|
|
FVector NewBoneDir = FVector(OutBoneTransforms[i].Transform.GetTranslation() - OutBoneTransforms[i - 1].Transform.GetTranslation()).GetSafeNormal(SMALL_NUMBER);
|
|
|
|
// Calculate a quaternion that gets us from our current rotation to the desired one.
|
|
FQuat DeltaLookQuat = FQuat::FindBetweenNormals(CurrentBoneDir, NewBoneDir);
|
|
FTransform DeltaTM( DeltaLookQuat, FVector(0.f) );
|
|
|
|
// Apply to the current parent bone transform.
|
|
FTransform TmpMatrix = FTransform::Identity;
|
|
TmpMatrix.CopyRotationPart(OutBoneTransforms[i - 1].Transform);
|
|
TmpMatrix = TmpMatrix * DeltaTM;
|
|
OutBoneTransforms[i - 1].Transform.CopyRotationPart(TmpMatrix);
|
|
}
|
|
|
|
// For the last bone in the chain, use the rotation from the bone above it.
|
|
OutBoneTransforms[ChainLength - 1].Transform.CopyRotationPart(OutBoneTransforms[ChainLength - 2].Transform);
|
|
|
|
// Update OldLocalToWorld
|
|
OldLocalToWorld = SkelComp->GetTransformMatrix();
|
|
}
|
|
|
|
bool FAnimNode_Trail::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
|
|
{
|
|
// if bones are valid
|
|
return (TrailBone.IsValid(RequiredBones));
|
|
}
|
|
|
|
void FAnimNode_Trail::InitializeBoneReferences(const FBoneContainer& RequiredBones)
|
|
{
|
|
TrailBone.Initialize(RequiredBones);
|
|
}
|
|
|
|
FVector FAnimNode_Trail::GetAlignVector(EAxis::Type AxisOption, bool bInvert)
|
|
{
|
|
FVector AxisDir;
|
|
|
|
if (AxisOption == EAxis::X)
|
|
{
|
|
AxisDir = FVector(1, 0, 0);
|
|
}
|
|
else if (AxisOption == EAxis::Y)
|
|
{
|
|
AxisDir = FVector(0, 1, 0);
|
|
}
|
|
else
|
|
{
|
|
AxisDir = FVector(0, 0, 1);
|
|
}
|
|
|
|
if (bInvert)
|
|
{
|
|
AxisDir *= -1.f;
|
|
}
|
|
|
|
return AxisDir;
|
|
}
|
|
|
|
void FAnimNode_Trail::PostLoad()
|
|
{
|
|
if (TrailRelaxation_DEPRECATED != 10.f)
|
|
{
|
|
FRichCurve* TrailRelaxRichCurve = TrailRelaxationSpeed.GetRichCurve();
|
|
TrailRelaxRichCurve->Reset();
|
|
TrailRelaxRichCurve->AddKey(0.f, TrailRelaxation_DEPRECATED);
|
|
TrailRelaxRichCurve->AddKey(1.f, TrailRelaxation_DEPRECATED);
|
|
// since we don't know if it's same as default or not, we have to keep default
|
|
// if default, the default constructor will take care of it. If not, we'll reset
|
|
TrailRelaxation_DEPRECATED = 10.f;
|
|
}
|
|
}
|
|
void FAnimNode_Trail::Initialize(const FAnimationInitializeContext& Context)
|
|
{
|
|
FAnimNode_SkeletalControlBase::Initialize(Context);
|
|
|
|
// allocated all memory here in initialize
|
|
PerJointTrailData.Reset();
|
|
ChainBoneIndices.Reset();
|
|
TrailBoneLocations.Reset();
|
|
if(ChainLength > 2)
|
|
{
|
|
PerJointTrailData.AddZeroed(ChainLength);
|
|
ChainBoneIndices.AddZeroed(ChainLength);
|
|
TrailBoneLocations.AddZeroed(ChainLength);
|
|
|
|
float Interval = (ChainLength > 1)? (1.f/(ChainLength-1)) : 0.f;
|
|
const FRichCurve* TrailRelaxRichCurve = TrailRelaxationSpeed.GetRichCurveConst();
|
|
ensure(TrailRelaxRichCurve);
|
|
for(int32 Idx=0; Idx<ChainLength; ++Idx)
|
|
{
|
|
PerJointTrailData[Idx].TrailRelaxationSpeedPerSecond = TrailRelaxRichCurve->Eval(Interval * Idx);
|
|
}
|
|
}
|
|
} |