Fix Multi Blend 1D/2D root motion extraction with blending

#678
This commit is contained in:
Wojciech Figat
2021-12-10 12:48:37 +01:00
parent cb575ffe04
commit c23ec9935f
2 changed files with 68 additions and 115 deletions

View File

@@ -38,24 +38,17 @@ int32 AnimGraphExecutor::GetRootNodeIndex(Animation* anim)
return rootNodeIndex;
}
void AnimGraphExecutor::UpdateRootMotion(const Animation::NodeToChannel* mapping, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion)
void AnimGraphExecutor::ExtractRootMotion(const Animation::NodeToChannel* mapping, int32 rootNodeIndex, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion)
{
// Pick node
const int32 rootNodeIndex = GetRootNodeIndex(anim);
// Extract it's current motion
const Transform rootNow = rootNode;
const Transform refPose = GetEmptyNodes()->Nodes[rootNodeIndex];
rootNode = refPose;
// Apply
const int32 nodeToChannel = mapping->At(rootNodeIndex);
if (_rootMotionMode == RootMotionMode::Enable && nodeToChannel != -1)
if (_rootMotionMode == RootMotionMode::Enable)
{
const NodeAnimationData& rootChannel = anim->Data.Channels[nodeToChannel];
const int32 nodeToChannel = mapping->At(rootNodeIndex);
ASSERT_LOW_LAYER(nodeToChannel != -1);
// Get the root bone transformation in the previous update
// Get the root bone transformation
Transform rootBefore = refPose;
const NodeAnimationData& rootChannel = anim->Data.Channels[nodeToChannel];
rootChannel.Evaluate(prevPos, &rootBefore, false);
// Check if animation looped
@@ -76,18 +69,21 @@ void AnimGraphExecutor::UpdateRootMotion(const Animation::NodeToChannel* mapping
// Complex motion calculation to preserve the looped movement
// (end - before + now - begin)
// It sums the motion since the last update to anim end and since the start to now
rootMotion.Translation = rootEnd.Translation - rootBefore.Translation + rootNow.Translation - rootBegin.Translation;
rootMotion.Rotation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNow.Orientation * rootBegin.Orientation.Conjugated());
rootMotion.Translation = rootEnd.Translation - rootBefore.Translation + rootNode.Translation - rootBegin.Translation;
rootMotion.Rotation = rootEnd.Orientation * rootBefore.Orientation.Conjugated() * (rootNode.Orientation * rootBegin.Orientation.Conjugated());
//rootMotion.Rotation = Quaternion::Identity;
}
else
{
// Simple motion delta
// (now - before)
rootMotion.Translation = rootNow.Translation - rootBefore.Translation;
rootMotion.Rotation = rootBefore.Orientation.Conjugated() * rootNow.Orientation;
rootMotion.Translation = rootNode.Translation - rootBefore.Translation;
rootMotion.Rotation = rootBefore.Orientation.Conjugated() * rootNode.Orientation;
}
}
// Remove root node motion after extraction
rootNode = refPose;
}
float GetAnimPos(float& timePos, float startTimePos, bool loop, float length)
@@ -178,10 +174,10 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float
}
// Handle root motion
if (anim->Data.EnableRootMotion && _rootMotionMode != RootMotionMode::NoExtraction)
if (_rootMotionMode != RootMotionMode::NoExtraction && anim->Data.EnableRootMotion)
{
const int32 rootNodeIndex = GetRootNodeIndex(anim);
UpdateRootMotion(mapping, anim, animPos, animPrevPos, nodes->Nodes[rootNodeIndex], nodes->RootMotion);
ExtractRootMotion(mapping, rootNodeIndex, anim, animPos, animPrevPos, nodes->Nodes[rootNodeIndex], nodes->RootMotion);
}
return nodes;
@@ -211,21 +207,36 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
nodes->Length = length;
const auto mappingA = animA->GetMapping(_graph.BaseModel);
const auto mappingB = animB->GetMapping(_graph.BaseModel);
const auto emptyNodes = GetEmptyNodes();
RootMotionData rootMotionA, rootMotionB;
int32 rootNodeIndexA = -1, rootNodeIndexB = -1;
if (_rootMotionMode != RootMotionMode::NoExtraction)
{
rootMotionA = rootMotionB = RootMotionData::Identity;
if (animA->Data.EnableRootMotion)
rootNodeIndexA = GetRootNodeIndex(animA);
if (animB->Data.EnableRootMotion)
rootNodeIndexB = GetRootNodeIndex(animB);
}
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
const int32 nodeToChannelA = mappingA->At(i);
const int32 nodeToChannelB = mappingB->At(i);
Transform nodeA = GetEmptyNodes()->Nodes[i];
Transform nodeA = emptyNodes->Nodes[i];
Transform nodeB = nodeA;
// Calculate the animated node transformations
if (nodeToChannelA != -1)
{
animA->Data.Channels[nodeToChannelA].Evaluate(animPosA, &nodeA, false);
if (rootNodeIndexA == i)
ExtractRootMotion(mappingA, rootNodeIndexA, animA, animPosA, animPrevPosA, nodeA, rootMotionA);
}
if (nodeToChannelB != -1)
{
animB->Data.Channels[nodeToChannelB].Evaluate(animPosB, &nodeB, false);
if (rootNodeIndexB == i)
ExtractRootMotion(mappingB, rootNodeIndexB, animB, animPosB, animPrevPosB, nodeB, rootMotionB);
}
// Blend
@@ -235,37 +246,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
// Handle root motion
if (_rootMotionMode != RootMotionMode::NoExtraction)
{
// Extract root motion from animation A
if (animA->Data.EnableRootMotion)
{
RootMotionData rootMotion = RootMotionData::Identity;
const int32 rootNodeIndex = GetRootNodeIndex(animA);
const int32 nodeToChannel = mappingA->At(rootNodeIndex);
Transform rootNode = Transform::Identity;
if (nodeToChannel != -1)
{
animA->Data.Channels[nodeToChannel].Evaluate(animPosA, &rootNode, false);
}
UpdateRootMotion(mappingA, animA, animPosA, animPrevPosA, rootNode, rootMotion);
RootMotionData::Lerp(nodes->RootMotion, rootMotion, 1.0f - alpha, nodes->RootMotion);
Transform::Lerp(nodes->Nodes[rootNodeIndex], rootNode, 1.0f - alpha, nodes->Nodes[rootNodeIndex]);
}
// Extract root motion from animation B
if (animB->Data.EnableRootMotion)
{
RootMotionData rootMotion = RootMotionData::Identity;
const int32 rootNodeIndex = GetRootNodeIndex(animA);
const int32 nodeToChannel = mappingB->At(rootNodeIndex);
Transform rootNode = Transform::Identity;
if (nodeToChannel != -1)
{
animB->Data.Channels[nodeToChannel].Evaluate(animPosB, &rootNode, false);
}
UpdateRootMotion(mappingB, animB, animPosB, animPrevPosB, rootNode, rootMotion);
RootMotionData::Lerp(nodes->RootMotion, rootMotion, alpha, nodes->RootMotion);
Transform::Lerp(nodes->Nodes[rootNodeIndex], rootNode, alpha, nodes->Nodes[rootNodeIndex]);
}
RootMotionData::Lerp(rootMotionA, rootMotionB, alpha, nodes->RootMotion);
}
return nodes;
@@ -301,6 +282,18 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
const auto mappingC = animC->GetMapping(_graph.BaseModel);
Transform tmp, t;
const auto emptyNodes = GetEmptyNodes();
RootMotionData rootMotionA, rootMotionB, rootMotionC;
int32 rootNodeIndexA = -1, rootNodeIndexB = -1, rootNodeIndexC = -1;
if (_rootMotionMode != RootMotionMode::NoExtraction)
{
rootMotionA = rootMotionB = rootMotionC = RootMotionData::Identity;
if (animA->Data.EnableRootMotion)
rootNodeIndexA = GetRootNodeIndex(animA);
if (animB->Data.EnableRootMotion)
rootNodeIndexB = GetRootNodeIndex(animB);
if (animC->Data.EnableRootMotion)
rootNodeIndexC = GetRootNodeIndex(animC);
}
ASSERT(Math::Abs(alphaA + alphaB + alphaC - 1.0f) <= ANIM_GRAPH_BLEND_THRESHOLD); // Assumes weights are normalized
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
{
@@ -311,6 +304,8 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
// Override
tmp = t;
animA->Data.Channels[nodeToChannelA].Evaluate(animPosA, &tmp, false);
if (rootNodeIndexA == i)
ExtractRootMotion(mappingA, rootNodeIndexA, animA, animPosA, animPrevPosA, tmp, rootMotionA);
t.Translation = tmp.Translation * alphaA;
t.Orientation = tmp.Orientation * alphaA;
t.Scale = tmp.Scale * alphaA;
@@ -327,6 +322,8 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
// Additive
tmp = emptyNodes->Nodes[i];
animB->Data.Channels[nodeToChannelB].Evaluate(animPosB, &tmp, false);
if (rootNodeIndexB == i)
ExtractRootMotion(mappingB, rootNodeIndexB, animB, animPosB, animPrevPosB, tmp, rootMotionB);
t.Translation += tmp.Translation * alphaB;
t.Scale += tmp.Scale * alphaB;
BlendAdditiveWeightedRotation(t.Orientation, tmp.Orientation, alphaB);
@@ -336,6 +333,8 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
// Additive
tmp = emptyNodes->Nodes[i];
animC->Data.Channels[nodeToChannelC].Evaluate(animPosC, &tmp, false);
if (rootNodeIndexC == i)
ExtractRootMotion(mappingC, rootNodeIndexC, animC, animPosC, animPrevPosC, tmp, rootMotionC);
t.Translation += tmp.Translation * alphaC;
t.Scale += tmp.Scale * alphaC;
BlendAdditiveWeightedRotation(t.Orientation, tmp.Orientation, alphaC);
@@ -347,53 +346,16 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
// Handle root motion
if (_rootMotionMode != RootMotionMode::NoExtraction)
{
// Extract root motion from animation A
if (animA->Data.EnableRootMotion)
{
RootMotionData rootMotion = RootMotionData::Identity;
const int32 rootNodeIndex = GetRootNodeIndex(animA);
const int32 nodeToChannel = mappingA->At(rootNodeIndex);
Transform rootNode = Transform::Identity;
if (nodeToChannel != -1)
{
animA->Data.Channels[nodeToChannel].Evaluate(animPosA, &rootNode, false);
}
UpdateRootMotion(mappingA, animA, animPosA, animPrevPosA, rootNode, rootMotion);
RootMotionData::Lerp(nodes->RootMotion, rootMotion, alphaA, nodes->RootMotion);
Transform::Lerp(nodes->Nodes[rootNodeIndex], rootNode, alphaA, nodes->Nodes[rootNodeIndex]);
}
nodes->RootMotion.Translation = rootMotionA.Translation * alphaA;
nodes->RootMotion.Rotation = rootMotionA.Rotation * alphaA;
// Extract root motion from animation B
if (animB->Data.EnableRootMotion)
{
RootMotionData rootMotion = RootMotionData::Identity;
const int32 rootNodeIndex = GetRootNodeIndex(animA);
const int32 nodeToChannel = mappingB->At(rootNodeIndex);
Transform rootNode = Transform::Identity;
if (nodeToChannel != -1)
{
animB->Data.Channels[nodeToChannel].Evaluate(animPosB, &rootNode, false);
}
UpdateRootMotion(mappingB, animB, animPosB, animPrevPosB, rootNode, rootMotion);
RootMotionData::Lerp(nodes->RootMotion, rootMotion, alphaB, nodes->RootMotion);
Transform::Lerp(nodes->Nodes[rootNodeIndex], rootNode, alphaB, nodes->Nodes[rootNodeIndex]);
}
nodes->RootMotion.Translation += rootMotionB.Translation * alphaB;
nodes->RootMotion.Rotation += rootMotionB.Rotation * alphaB;
BlendAdditiveWeightedRotation(nodes->RootMotion.Rotation, rootMotionB.Rotation, alphaB);
// Extract root motion from animation C
if (animC->Data.EnableRootMotion)
{
RootMotionData rootMotion = RootMotionData::Identity;
const int32 rootNodeIndex = GetRootNodeIndex(animA);
const int32 nodeToChannel = mappingC->At(rootNodeIndex);
Transform rootNode = Transform::Identity;
if (nodeToChannel != -1)
{
animC->Data.Channels[nodeToChannel].Evaluate(animPosC, &rootNode, false);
}
UpdateRootMotion(mappingC, animC, animPosC, animPrevPosC, rootNode, rootMotion);
RootMotionData::Lerp(nodes->RootMotion, rootMotion, alphaC, nodes->RootMotion);
Transform::Lerp(nodes->Nodes[rootNodeIndex], rootNode, alphaC, nodes->Nodes[rootNodeIndex]);
}
nodes->RootMotion.Translation = rootMotionC.Translation * alphaC;
nodes->RootMotion.Rotation = rootMotionC.Rotation * alphaC;
BlendAdditiveWeightedRotation(nodes->RootMotion.Rotation, rootMotionB.Rotation, alphaC);
}
return nodes;
@@ -967,6 +929,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
case 12:
{
ASSERT(box->ID == 0);
value = Value::Null;
// Note data layout:
// [0]: Vector4 Range (minX, maxX, 0, 0)
@@ -987,11 +950,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// Check if not valid animation binded
if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
{
// Nothing to sample
value = Value::Null;
break;
}
// Get axis X
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
@@ -999,15 +958,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// Check if need to evaluate multi blend length
if (data.Length < 0)
{
ComputeMultiBlendLength(data.Length, node);
}
if (data.Length <= ZeroTolerance)
{
// Nothing to sample
value = Value::Null;
break;
}
// Calculate new time position
if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1)
@@ -1086,10 +1039,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// Check if not valid animation binded
if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS)
{
// Nothing to sample
break;
}
// Get axis X
float x = (float)tryGetValue(node->GetBox(4), Value::Zero);
@@ -1101,14 +1051,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// Check if need to evaluate multi blend length
if (data.Length < 0)
{
ComputeMultiBlendLength(data.Length, node);
}
if (data.Length <= ZeroTolerance)
{
// Nothing to sample
break;
}
// Calculate new time position
if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1)
@@ -1287,6 +1232,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
value = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
break;
}
ASSERT(bucket.PreviousBlendPoseIndex >= 0 && bucket.PreviousBlendPoseIndex < poseCount);
// Blend two animations
{