Add support for using animated model with anim graph using different skinned model

This commit is contained in:
Wojtek Figat
2021-05-13 11:59:47 +02:00
parent 9fcac44faf
commit 389eee22e3
5 changed files with 42 additions and 40 deletions

View File

@@ -6,7 +6,8 @@
#include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h"
Array<AnimatedModel*> UpdateList(256);
Array<AnimatedModel*> UpdateList;
Array<Matrix> UpdateBones;
class AnimationManagerService : public EngineService
{
@@ -67,9 +68,24 @@ void AnimationManagerService::Update()
}
animatedModel->GraphInstance.LastUpdateTime = t;
const auto bones = graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
// Evaluate animated nodes pose
graph->GraphExecutor.Update(animatedModel->GraphInstance, dt);
// Calculate the final bones transformations
{
ANIM_GRAPH_PROFILE_EVENT("Final Pose");
auto& skeleton = animatedModel->SkinnedModel->Skeleton;
UpdateBones.Resize(skeleton.Bones.Count(), false);
for (int32 boneIndex = 0; boneIndex < skeleton.Bones.Count(); boneIndex++)
{
auto& bone = skeleton.Bones[boneIndex];
UpdateBones[boneIndex] = bone.OffsetMatrix * animatedModel->GraphInstance.NodesPose[bone.NodeIndex];
}
}
// Update gameplay
const bool usePrevFrameBones = animatedModel->PerBoneMotionBlur;
animatedModel->_skinningData.SetData(bones, !usePrevFrameBones);
animatedModel->_skinningData.SetData(UpdateBones.Get(), !usePrevFrameBones);
animatedModel->OnAnimUpdate();
}
}
@@ -79,6 +95,7 @@ void AnimationManagerService::Update()
void AnimationManagerService::Dispose()
{
UpdateList.Resize(0);
UpdateBones.Resize(0);
}
void AnimationManager::AddToUpdate(AnimatedModel* obj)

View File

@@ -152,8 +152,8 @@ bool AnimGraph::IsReady() const
bool AnimGraph::CanUseWithSkeleton(SkinnedModel* other) const
{
// All data loaded and bones count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Bones.Count() == BaseModel->Skeleton.Bones.Count();
// All data loaded and nodes count the same
return IsReady() && other && other->IsLoaded() && other->Skeleton.Nodes.Count() == BaseModel->Skeleton.Nodes.Count();
}
void AnimGraph::ClearCustomNode(Node* node)

View File

@@ -175,7 +175,7 @@ AnimGraphExecutor::AnimGraphExecutor(AnimGraph& graph)
_perGroupProcessCall[16] = (ProcessBoxHandler)&AnimGraphExecutor::ProcessGroupFunction;
}
const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
{
ASSERT(data.Parameters.Count() == _graph.Parameters.Count());
@@ -185,7 +185,6 @@ const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
ANIM_GRAPH_PROFILE_EVENT("Init");
// Prepare graph data for the evaluation
_skeletonBonesCount = skeleton.Bones.Count();
_skeletonNodesCount = skeleton.Nodes.Count();
_graphStack.Clear();
_graphStack.Push((Graph*)&_graph);
@@ -265,23 +264,19 @@ const Matrix* AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
_data->RootMotion = animResult->RootMotion;
}
// Calculate the final bones transformations
{
ANIM_GRAPH_PROFILE_EVENT("Final Pose");
_bonesTransformations.Resize(_skeletonBonesCount, false);
for (int32 boneIndex = 0; boneIndex < _skeletonBonesCount; boneIndex++)
{
auto& bone = skeleton.Bones[boneIndex];
_bonesTransformations[boneIndex] = bone.OffsetMatrix * _data->NodesPose[bone.NodeIndex];
}
}
// Cleanup
_data = nullptr;
}
return _bonesTransformations.Get();
void AnimGraphExecutor::GetInputValue(Box* box, Value& result)
{
result = eatBox(box->GetParent<Node>(), box->FirstConnection());
}
void AnimGraphExecutor::ResetBucket(int32 bucketIndex)
{
auto& stateBucket = _data->State[bucketIndex];
_graph._bucketInitializerList[bucketIndex](stateBucket);
}
void AnimGraphExecutor::ResetBuckets(AnimGraphBase* graph)

View File

@@ -817,12 +817,10 @@ private:
AnimGraph& _graph;
float _deltaTime = 0.0f;
uint64 _currentFrameIndex = 0;
int32 _skeletonBonesCount = 0;
int32 _skeletonNodesCount = 0;
RootMotionMode _rootMotionMode = RootMotionMode::NoExtraction;
AnimGraphInstanceData* _data = nullptr;
AnimGraphImpulse _emptyNodes;
Array<Matrix> _bonesTransformations;
AnimGraphTransitionData _transitionData;
Array<Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> _callStack;
Array<Graph*, FixedAllocation<32>> _graphStack;
@@ -853,18 +851,13 @@ public:
/// </summary>
/// <param name="data">The instance data.</param>
/// <param name="dt">The delta time (in seconds).</param>
/// <returns>The pointer to the final bones structure as a result of the animation evaluation.</returns>
const Matrix* Update(AnimGraphInstanceData& data, float dt);
void Update(AnimGraphInstanceData& data, float dt);
void GetInputValue(Box* box, Value& result)
{
result = eatBox(box->GetParent<Node>(), box->FirstConnection());
}
void GetInputValue(Box* box, Value& result);
/// <summary>
/// Gets the skeleton nodes transformations structure containing identity matrices.
/// </summary>
/// <returns>The data.</returns>
FORCE_INLINE const AnimGraphImpulse* GetEmptyNodes() const
{
return &_emptyNodes;
@@ -914,11 +907,7 @@ public:
/// Resets the state bucket.
/// </summary>
/// <param name="bucketIndex">The zero-based index of the bucket.</param>
FORCE_INLINE void ResetBucket(int32 bucketIndex)
{
auto& stateBucket = _data->State[bucketIndex];
_graph._bucketInitializerList[bucketIndex](stateBucket);
}
void ResetBucket(int32 bucketIndex);
/// <summary>
/// Resets all the state bucket used by the given graph including sub-graphs (total). Can eb used to reset the animation state of the nested graph (including children).

View File

@@ -636,7 +636,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
transform.Scale = (Vector3)tryGetValue(node->GetBox(4), Vector3::One);
// Skip if no change will be performed
if (boneIndex < 0 || boneIndex >= _skeletonBonesCount || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
auto& skeleton = _graph.BaseModel->Skeleton;
if (boneIndex < 0 || boneIndex >= skeleton.Bones.Count() || transformMode == BoneTransformMode::None || (transformMode == BoneTransformMode::Add && transform.IsIdentity()))
{
// Pass through the input
value = Value::Null;
@@ -767,8 +768,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const auto copyScale = (bool)node->Values[4];
// Skip if no change will be performed
if (srcBoneIndex < 0 || srcBoneIndex >= _skeletonBonesCount ||
dstBoneIndex < 0 || dstBoneIndex >= _skeletonBonesCount ||
const auto& skeleton = _graph.BaseModel->Skeleton;
if (srcBoneIndex < 0 || srcBoneIndex >= skeleton.Bones.Count() ||
dstBoneIndex < 0 || dstBoneIndex >= skeleton.Bones.Count() ||
!(copyTranslation || copyRotation || copyScale))
{
// Pass through the input
@@ -776,7 +778,6 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
box->Cache = value;
return;
}
const auto& skeleton = _graph.BaseModel->Skeleton;
// Copy bone data
Transform srcTransform = nodes->Nodes[skeleton.Bones[srcBoneIndex].NodeIndex];
@@ -800,7 +801,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
const auto boneIndex = (int32)node->Values[0];
const auto& skeleton = _graph.BaseModel->Skeleton;
const auto input = tryGetValue(node->GetBox(0), Value::Null);
if (ANIM_GRAPH_IS_VALID_PTR(input) && boneIndex >= 0 && boneIndex < _skeletonBonesCount)
if (ANIM_GRAPH_IS_VALID_PTR(input) && boneIndex >= 0 && boneIndex < skeleton.Bones.Count())
value = Variant(((AnimGraphImpulse*)input.AsPointer)->Nodes[skeleton.Bones[boneIndex].NodeIndex]);
else
value = Variant(Transform::Identity);