diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 353422ddd..0136e35df 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -57,6 +57,13 @@ struct RootMotionData Rotation = other.Orientation; } + RootMotionData& operator=(const Transform& other) + { + Translation = other.Translation; + Rotation = other.Orientation; + return *this; + } + RootMotionData& operator+=(const RootMotionData& b); RootMotionData& operator+=(const Transform& b); RootMotionData& operator-=(const Transform& b); @@ -851,7 +858,7 @@ private: void ProcessGroupFunction(Box* boxBase, Node* node, Value& value); int32 GetRootNodeIndex(Animation* anim); - void UpdateRootMotion(const Animation::NodeToChannel* mapping, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion); + void ExtractRootMotion(const Animation::NodeToChannel* mapping, int32 rootNodeIndex, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion); Variant SampleAnimation(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* anim, float speed); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 4b99e2026..e02b3eb4a 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -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 {