Refactor AnimGraph to support asynchronous execution
This commit is contained in:
@@ -9,25 +9,6 @@
|
||||
#include "Engine/Utilities/Delaunay2D.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
|
||||
void AnimGraphBase::ClearCache()
|
||||
{
|
||||
// Clear sub-graphs
|
||||
for (int32 i = 0; i < SubGraphs.Count(); i++)
|
||||
{
|
||||
SubGraphs[i]->ClearCache();
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
for (int32 i = 0; i < Nodes.Count(); i++)
|
||||
{
|
||||
auto& node = Nodes[i];
|
||||
for (int32 j = 0; j < node.Boxes.Count(); j++)
|
||||
{
|
||||
node.Boxes[j].InvalidateCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimSubGraph* AnimGraphBase::LoadSubGraph(const void* data, int32 dataLength, const Char* name)
|
||||
{
|
||||
if (data == nullptr || dataLength == 0)
|
||||
|
||||
@@ -89,13 +89,10 @@ void AnimGraphExecutor::initRuntime()
|
||||
|
||||
void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& value)
|
||||
{
|
||||
auto box = (AnimGraphBox*)boxBase;
|
||||
if (box->IsCacheValid())
|
||||
{
|
||||
// Return cache
|
||||
value = box->Cache;
|
||||
auto& context = Context.Get();
|
||||
if (context.ValueCache.TryGet(boxBase, value))
|
||||
return;
|
||||
}
|
||||
auto box = (AnimGraphBox*)boxBase;
|
||||
auto node = (AnimGraphNode*)nodeBase;
|
||||
auto& data = node->Data.Custom;
|
||||
value = Value::Null;
|
||||
@@ -105,16 +102,16 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
|
||||
return;
|
||||
|
||||
// Prepare node context
|
||||
InternalContext context;
|
||||
context.Graph = &_graph;
|
||||
context.GraphExecutor = this;
|
||||
context.Node = node;
|
||||
context.NodeId = node->ID;
|
||||
context.BoxId = box->ID;
|
||||
context.DeltaTime = _deltaTime;
|
||||
context.CurrentFrameIndex = _currentFrameIndex;;
|
||||
context.BaseModel = _graph.BaseModel->GetOrCreateManagedInstance();
|
||||
context.Instance = _data->Object ? _data->Object->GetOrCreateManagedInstance() : nullptr;
|
||||
InternalContext internalContext;
|
||||
internalContext.Graph = &_graph;
|
||||
internalContext.GraphExecutor = this;
|
||||
internalContext.Node = node;
|
||||
internalContext.NodeId = node->ID;
|
||||
internalContext.BoxId = box->ID;
|
||||
internalContext.DeltaTime = context.DeltaTime;
|
||||
internalContext.CurrentFrameIndex = context.CurrentFrameIndex;
|
||||
internalContext.BaseModel = _graph.BaseModel->GetOrCreateManagedInstance();
|
||||
internalContext.Instance = context.Data->Object ? context.Data->Object->GetOrCreateManagedInstance() : nullptr;
|
||||
|
||||
// Peek managed object
|
||||
const auto obj = mono_gchandle_get_target(data.Handle);
|
||||
@@ -126,7 +123,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
|
||||
|
||||
// Evaluate node
|
||||
void* params[1];
|
||||
params[0] = &context;
|
||||
params[0] = &internalContext;
|
||||
MonoObject* exception = nullptr;
|
||||
MonoObject* result = data.Evaluate->Invoke(obj, params, &exception);
|
||||
if (exception)
|
||||
@@ -138,7 +135,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
|
||||
|
||||
// Extract result
|
||||
value = MUtils::UnboxVariant(result);
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
}
|
||||
|
||||
bool AnimGraph::IsReady() const
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "AnimGraph.h"
|
||||
#include "Engine/Animations/Animations.h"
|
||||
#include "Engine/Content/Assets/SkinnedModel.h"
|
||||
#include "Engine/Graphics/Models/SkeletonData.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
|
||||
ThreadLocal<AnimGraphContext, 64> AnimGraphExecutor::Context;
|
||||
|
||||
RootMotionData RootMotionData::Identity = { Vector3(0.0f), Quaternion(0.0f, 0.0f, 0.0f, 1.0f) };
|
||||
|
||||
RootMotionData& RootMotionData::operator+=(const RootMotionData& b)
|
||||
@@ -78,16 +81,44 @@ void AnimGraphImpulse::SetNodeModelTransformation(SkeletonData& skeleton, int32
|
||||
parentTransform.WorldToLocal(value, Nodes[nodeIndex]);
|
||||
}
|
||||
|
||||
void AnimGraphInstanceData::Clear()
|
||||
{
|
||||
Version = 0;
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
RootTransform = Transform::Identity;
|
||||
RootMotion = RootMotionData::Identity;
|
||||
Parameters.Resize(0);
|
||||
State.Resize(0);
|
||||
NodesPose.Resize(0);
|
||||
}
|
||||
|
||||
void AnimGraphInstanceData::ClearState()
|
||||
{
|
||||
Version = 0;
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
RootTransform = Transform::Identity;
|
||||
RootMotion = RootMotionData::Identity;
|
||||
State.Resize(0);
|
||||
NodesPose.Resize(0);
|
||||
}
|
||||
|
||||
void AnimGraphInstanceData::Invalidate()
|
||||
{
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
}
|
||||
|
||||
AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor)
|
||||
{
|
||||
// Ensure to have memory
|
||||
auto& context = AnimGraphExecutor::Context.Get();
|
||||
const int32 count = executor->_skeletonNodesCount;
|
||||
if (Nodes.Nodes.Count() != count)
|
||||
{
|
||||
Nodes.Nodes.Resize(count, false);
|
||||
}
|
||||
|
||||
return &Nodes;
|
||||
if (context.PoseCacheSize == context.PoseCache.Count())
|
||||
context.PoseCache.AddOne();
|
||||
auto& nodes = context.PoseCache[context.PoseCacheSize++];
|
||||
nodes.Nodes.Resize(count, false);
|
||||
return &nodes;
|
||||
}
|
||||
|
||||
bool AnimGraph::Load(ReadStream* stream, bool loadMeta)
|
||||
@@ -181,20 +212,24 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
||||
|
||||
// Initialize
|
||||
auto& skeleton = _graph.BaseModel->Skeleton;
|
||||
auto& context = Context.Get();
|
||||
{
|
||||
ANIM_GRAPH_PROFILE_EVENT("Init");
|
||||
|
||||
// Prepare graph data for the evaluation
|
||||
// Init data from base model
|
||||
_skeletonNodesCount = skeleton.Nodes.Count();
|
||||
_graphStack.Clear();
|
||||
_graphStack.Push((Graph*)&_graph);
|
||||
_data = &data;
|
||||
_deltaTime = dt;
|
||||
_rootMotionMode = (RootMotionMode)(int32)_graph._rootNode->Values[0];
|
||||
_currentFrameIndex = ++data.CurrentFrame;
|
||||
_callStack.Clear();
|
||||
_functions.Clear();
|
||||
_graph.ClearCache();
|
||||
|
||||
// Prepare context data for the evaluation
|
||||
context.GraphStack.Clear();
|
||||
context.GraphStack.Push((Graph*)&_graph);
|
||||
context.Data = &data;
|
||||
context.DeltaTime = dt;
|
||||
context.CurrentFrameIndex = ++data.CurrentFrame;
|
||||
context.CallStack.Clear();
|
||||
context.Functions.Clear();
|
||||
context.PoseCacheSize = 0;
|
||||
context.ValueCache.Clear();
|
||||
|
||||
// Prepare instance data
|
||||
if (data.Version != _graph.Version)
|
||||
@@ -208,18 +243,18 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
||||
data.State.Resize(_graph.BucketsCountTotal, false);
|
||||
|
||||
// Initialize buckets
|
||||
ResetBuckets(&_graph);
|
||||
ResetBuckets(context, &_graph);
|
||||
}
|
||||
|
||||
// Init empty nodes data
|
||||
_emptyNodes.RootMotion = RootMotionData::Identity;
|
||||
_emptyNodes.Position = 0.0f;
|
||||
_emptyNodes.Length = 0.0f;
|
||||
_emptyNodes.Nodes.Resize(_skeletonNodesCount, false);
|
||||
context.EmptyNodes.RootMotion = RootMotionData::Identity;
|
||||
context.EmptyNodes.Position = 0.0f;
|
||||
context.EmptyNodes.Length = 0.0f;
|
||||
context.EmptyNodes.Nodes.Resize(_skeletonNodesCount, false);
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
{
|
||||
auto& node = skeleton.Nodes[i];
|
||||
_emptyNodes.Nodes[i] = node.LocalTransform;
|
||||
context.EmptyNodes.Nodes[i] = node.LocalTransform;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +279,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
||||
{
|
||||
ANIM_GRAPH_PROFILE_EVENT("Global Pose");
|
||||
|
||||
_data->NodesPose.Resize(_skeletonNodesCount, false);
|
||||
data.NodesPose.Resize(_skeletonNodesCount, false);
|
||||
|
||||
// Note: this assumes that nodes are sorted (parents first)
|
||||
for (int32 nodeIndex = 0; nodeIndex < _skeletonNodesCount; nodeIndex++)
|
||||
@@ -254,18 +289,16 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
||||
{
|
||||
nodesTransformations[nodeIndex] = nodesTransformations[parentIndex].LocalToWorld(nodesTransformations[nodeIndex]);
|
||||
}
|
||||
nodesTransformations[nodeIndex].GetWorld(_data->NodesPose[nodeIndex]);
|
||||
nodesTransformations[nodeIndex].GetWorld(data.NodesPose[nodeIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the root node transformation and the motion
|
||||
{
|
||||
_data->RootTransform = nodesTransformations[0];
|
||||
_data->RootMotion = animResult->RootMotion;
|
||||
// Process the root node transformation and the motion
|
||||
data.RootTransform = nodesTransformations[0];
|
||||
data.RootMotion = animResult->RootMotion;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
_data = nullptr;
|
||||
context.Data = nullptr;
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::GetInputValue(Box* box, Value& result)
|
||||
@@ -273,29 +306,39 @@ void AnimGraphExecutor::GetInputValue(Box* box, Value& result)
|
||||
result = eatBox(box->GetParent<Node>(), box->FirstConnection());
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::ResetBucket(int32 bucketIndex)
|
||||
AnimGraphImpulse* AnimGraphExecutor::GetEmptyNodes()
|
||||
{
|
||||
auto& stateBucket = _data->State[bucketIndex];
|
||||
_graph._bucketInitializerList[bucketIndex](stateBucket);
|
||||
return &Context.Get().EmptyNodes;
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::ResetBuckets(AnimGraphBase* graph)
|
||||
void AnimGraphExecutor::InitNodes(AnimGraphImpulse* nodes) const
|
||||
{
|
||||
const auto& emptyNodes = Context.Get().EmptyNodes;
|
||||
Platform::MemoryCopy(nodes->Nodes.Get(), emptyNodes.Nodes.Get(), sizeof(Transform) * _skeletonNodesCount);
|
||||
nodes->RootMotion = emptyNodes.RootMotion;
|
||||
nodes->Position = emptyNodes.Position;
|
||||
nodes->Length = emptyNodes.Length;
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph)
|
||||
{
|
||||
if (graph == nullptr)
|
||||
return;
|
||||
|
||||
ASSERT(_data);
|
||||
auto& state = context.Data->State;
|
||||
for (int32 i = 0; i < graph->BucketsCountTotal; i++)
|
||||
{
|
||||
const int32 bucketIndex = graph->BucketsStart + i;
|
||||
_graph._bucketInitializerList[bucketIndex](_data->State[bucketIndex]);
|
||||
_graph._bucketInitializerList[bucketIndex](state[bucketIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
|
||||
{
|
||||
auto& context = Context.Get();
|
||||
|
||||
// Check if graph is looped or is too deep
|
||||
if (_callStack.Count() >= ANIM_GRAPH_MAX_CALL_STACK)
|
||||
if (context.CallStack.Count() >= ANIM_GRAPH_MAX_CALL_STACK)
|
||||
{
|
||||
OnError(caller, box, TEXT("Graph is looped or too deep!"));
|
||||
return Value::Zero;
|
||||
@@ -309,7 +352,7 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
|
||||
#endif
|
||||
|
||||
// Add to the calling stack
|
||||
_callStack.Add(caller);
|
||||
context.CallStack.Add(caller);
|
||||
|
||||
#if USE_EDITOR
|
||||
Animations::DebugFlow(_graph._owner, context.Data->Object, box->GetParent<Node>()->ID, box->ID);
|
||||
@@ -322,12 +365,13 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box)
|
||||
(this->*func)(box, parentNode, value);
|
||||
|
||||
// Remove from the calling stack
|
||||
_callStack.RemoveLast();
|
||||
context.CallStack.RemoveLast();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
VisjectExecutor::Graph* AnimGraphExecutor::GetCurrentGraph() const
|
||||
{
|
||||
return _graphStack.Peek();
|
||||
auto& context = Context.Get();
|
||||
return context.GraphStack.Peek();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Visject/VisjectGraph.h"
|
||||
#include "Engine/Content/Assets/Animation.h"
|
||||
#include "Engine/Core/Collections/ChunkedArray.h"
|
||||
#include "Engine/Animations/AlphaBlend.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "../Config.h"
|
||||
@@ -362,40 +363,17 @@ public:
|
||||
/// <summary>
|
||||
/// Clears this container data.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
Version = 0;
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
RootTransform = Transform::Identity;
|
||||
RootMotion = RootMotionData::Identity;
|
||||
Parameters.Resize(0);
|
||||
State.Resize(0);
|
||||
NodesPose.Resize(0);
|
||||
}
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Clears this container state data.
|
||||
/// </summary>
|
||||
void ClearState()
|
||||
{
|
||||
Version = 0;
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
RootTransform = Transform::Identity;
|
||||
RootMotion = RootMotionData::Identity;
|
||||
State.Resize(0);
|
||||
NodesPose.Resize(0);
|
||||
}
|
||||
void ClearState();
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the update timer.
|
||||
/// </summary>
|
||||
void Invalidate()
|
||||
{
|
||||
LastUpdateTime = -1;
|
||||
CurrentFrame = 0;
|
||||
}
|
||||
void Invalidate();
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -424,18 +402,6 @@ public:
|
||||
: VisjectGraphBox(parent, id, type)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool IsCacheValid() const
|
||||
{
|
||||
return Cache.Type.Type != VariantType::Pointer || Cache.AsPointer != nullptr;
|
||||
}
|
||||
|
||||
void InvalidateCache()
|
||||
{
|
||||
Cache = Variant::Null;
|
||||
}
|
||||
};
|
||||
|
||||
class AnimGraphNode : public VisjectGraphNode<AnimGraphBox>
|
||||
@@ -575,13 +541,6 @@ public:
|
||||
/// </summary>
|
||||
int32 BucketIndex = -1;
|
||||
|
||||
// TODO: use shared allocator per AnimGraph to reduce dynamic memory allocation (also bones data would be closer in memory -> less cache misses)
|
||||
|
||||
/// <summary>
|
||||
/// The node transformations (layout matches the linked to graph skinned model skeleton).
|
||||
/// </summary>
|
||||
AnimGraphImpulse Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// The custom data (depends on node type). Used to cache data for faster usage at runtime.
|
||||
/// </summary>
|
||||
@@ -661,17 +620,11 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the root node of the graph (cache don load).
|
||||
/// </summary>
|
||||
/// <returns>The root node.</returns>
|
||||
FORCE_INLINE Node* GetRootNode() const
|
||||
{
|
||||
return _rootNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all cached values in the graph nodes and the sub-graphs data.
|
||||
/// </summary>
|
||||
void ClearCache();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the sub-graph.
|
||||
/// </summary>
|
||||
@@ -751,9 +704,9 @@ public:
|
||||
AnimGraph(Asset* owner, bool isFunction = false)
|
||||
: AnimGraphBase(this)
|
||||
, _isFunction(isFunction)
|
||||
, _isRegisteredForScriptingEvents(false)
|
||||
, _bucketInitializerList(64)
|
||||
, _owner(owner)
|
||||
, _isRegisteredForScriptingEvents(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -806,6 +759,24 @@ public:
|
||||
bool onParamCreated(Parameter* p) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The Animation Graph evaluation context.
|
||||
/// </summary>
|
||||
struct AnimGraphContext
|
||||
{
|
||||
float DeltaTime;
|
||||
uint64 CurrentFrameIndex;
|
||||
AnimGraphInstanceData* Data;
|
||||
AnimGraphImpulse EmptyNodes;
|
||||
AnimGraphTransitionData TransitionData;
|
||||
Array<VisjectExecutor::Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> CallStack;
|
||||
Array<VisjectExecutor::Graph*, FixedAllocation<32>> GraphStack;
|
||||
Dictionary<VisjectExecutor::Node*, VisjectExecutor::Graph*> Functions;
|
||||
ChunkedArray<AnimGraphImpulse, 256> PoseCache;
|
||||
int32 PoseCacheSize;
|
||||
Dictionary<VisjectExecutor::Box*, Variant> ValueCache;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The Animation Graph executor runtime for animation pose evaluation.
|
||||
/// </summary>
|
||||
@@ -815,20 +786,14 @@ class AnimGraphExecutor : public VisjectExecutor
|
||||
private:
|
||||
|
||||
AnimGraph& _graph;
|
||||
float _deltaTime = 0.0f;
|
||||
uint64 _currentFrameIndex = 0;
|
||||
int32 _skeletonNodesCount = 0;
|
||||
RootMotionMode _rootMotionMode = RootMotionMode::NoExtraction;
|
||||
AnimGraphInstanceData* _data = nullptr;
|
||||
AnimGraphImpulse _emptyNodes;
|
||||
AnimGraphTransitionData _transitionData;
|
||||
Array<Node*, FixedAllocation<ANIM_GRAPH_MAX_CALL_STACK>> _callStack;
|
||||
Array<Graph*, FixedAllocation<32>> _graphStack;
|
||||
Dictionary<Node*, Graph*> _functions;
|
||||
int32 _skeletonNodesCount = 0;
|
||||
|
||||
// Per-thread context to allow async execution
|
||||
static ThreadLocal<AnimGraphContext, 64> Context;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the managed runtime calls.
|
||||
/// </summary>
|
||||
@@ -854,34 +819,10 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the skeleton nodes transformations structure containing identity matrices.
|
||||
/// </summary>
|
||||
FORCE_INLINE const AnimGraphImpulse* GetEmptyNodes() const
|
||||
{
|
||||
return &_emptyNodes;
|
||||
}
|
||||
AnimGraphImpulse* GetEmptyNodes();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the skeleton nodes transformations structure containing identity matrices.
|
||||
/// </summary>
|
||||
/// <returns>The data.</returns>
|
||||
FORCE_INLINE AnimGraphImpulse* GetEmptyNodes()
|
||||
{
|
||||
return &_emptyNodes;
|
||||
}
|
||||
|
||||
FORCE_INLINE void InitNodes(AnimGraphImpulse* nodes) const
|
||||
{
|
||||
// Initialize with cached node transformations
|
||||
Platform::MemoryCopy(nodes->Nodes.Get(), _emptyNodes.Nodes.Get(), sizeof(Transform) * _skeletonNodesCount);
|
||||
nodes->RootMotion = _emptyNodes.RootMotion;
|
||||
nodes->Position = _emptyNodes.Position;
|
||||
nodes->Length = _emptyNodes.Length;
|
||||
}
|
||||
|
||||
FORCE_INLINE void InitNode(AnimGraphImpulse* nodes, int32 index) const
|
||||
{
|
||||
// Initialize with cached node transformation
|
||||
nodes->Nodes[index] = GetEmptyNodes()->Nodes[index];
|
||||
}
|
||||
// Initialize impulse with cached node transformations
|
||||
void InitNodes(AnimGraphImpulse* nodes) const;
|
||||
|
||||
FORCE_INLINE void CopyNodes(AnimGraphImpulse* dstNodes, AnimGraphImpulse* srcNodes) const
|
||||
{
|
||||
@@ -899,16 +840,10 @@ public:
|
||||
CopyNodes(dstNodes, static_cast<AnimGraphImpulse*>(value.AsPointer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the state bucket.
|
||||
/// </summary>
|
||||
/// <param name="bucketIndex">The zero-based index of the bucket.</param>
|
||||
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).
|
||||
/// </summary>
|
||||
void ResetBuckets(AnimGraphBase* graph);
|
||||
void ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ int32 AnimGraphExecutor::GetRootNodeIndex(Animation* anim)
|
||||
if (anim->Data.RootNodeName.HasChars())
|
||||
{
|
||||
auto& skeleton = _graph.BaseModel->Skeleton;
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < skeleton.Nodes.Count(); i++)
|
||||
{
|
||||
if (skeleton.Nodes[i].Name == anim->Data.RootNodeName)
|
||||
{
|
||||
@@ -119,7 +119,7 @@ float GetAnimSamplePos(float length, Animation* anim, float pos, float speed)
|
||||
// Also, scale the animation to fit the total animation node length without cut in a middle
|
||||
const auto animLength = anim->GetLength();
|
||||
const int32 cyclesCount = Math::FloorToInt(length / animLength);
|
||||
const float cycleLength = animLength * cyclesCount;
|
||||
const float cycleLength = animLength * (float)cyclesCount;
|
||||
const float adjustRateScale = length / cycleLength;
|
||||
auto animPos = pos * speed * adjustRateScale;
|
||||
while (animPos > animLength)
|
||||
@@ -152,10 +152,11 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float
|
||||
nodes->Position = pos;
|
||||
nodes->Length = length;
|
||||
const auto mapping = anim->GetMapping(_graph.BaseModel);
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
const auto emptyNodes = GetEmptyNodes();
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
const int32 nodeToChannel = mapping->At(i);
|
||||
InitNode(nodes, i);
|
||||
nodes->Nodes[i] = emptyNodes->Nodes[i];
|
||||
if (nodeToChannel != -1)
|
||||
{
|
||||
// Calculate the animated node transformation
|
||||
@@ -197,7 +198,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
||||
nodes->Length = length;
|
||||
const auto mappingA = animA->GetMapping(_graph.BaseModel);
|
||||
const auto mappingB = animB->GetMapping(_graph.BaseModel);
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
const int32 nodeToChannelA = mappingA->At(i);
|
||||
const int32 nodeToChannelB = mappingB->At(i);
|
||||
@@ -286,12 +287,13 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
||||
const auto mappingB = animB->GetMapping(_graph.BaseModel);
|
||||
const auto mappingC = animC->GetMapping(_graph.BaseModel);
|
||||
Transform tmp, t;
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
const auto emptyNodes = GetEmptyNodes();
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
const int32 nodeToChannelA = mappingA->At(i);
|
||||
const int32 nodeToChannelB = mappingB->At(i);
|
||||
const int32 nodeToChannelC = mappingC->At(i);
|
||||
tmp = t = GetEmptyNodes()->Nodes[i];
|
||||
tmp = t = emptyNodes->Nodes[i];
|
||||
|
||||
// Calculate the animated node transformations
|
||||
if (nodeToChannelA != -1)
|
||||
@@ -384,7 +386,7 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(poseB))
|
||||
nodesB = GetEmptyNodes();
|
||||
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
|
||||
}
|
||||
@@ -443,6 +445,7 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
|
||||
|
||||
void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value)
|
||||
{
|
||||
auto& context = Context.Get();
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Get
|
||||
@@ -453,7 +456,7 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
|
||||
const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex);
|
||||
if (param)
|
||||
{
|
||||
value = _data->Parameters[paramIndex].Value;
|
||||
value = context.Data->Parameters[paramIndex].Value;
|
||||
switch (param->Type.Type)
|
||||
{
|
||||
case VariantType::Vector2:
|
||||
@@ -523,19 +526,20 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu
|
||||
|
||||
void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value)
|
||||
{
|
||||
auto& context = Context.Get();
|
||||
auto node = (AnimGraphNode*)nodeBase;
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Time
|
||||
case 5:
|
||||
{
|
||||
auto& bucket = _data->State[node->BucketIndex].Animation;
|
||||
if (bucket.LastUpdateFrame != _currentFrameIndex)
|
||||
auto& bucket = context.Data->State[node->BucketIndex].Animation;
|
||||
if (bucket.LastUpdateFrame != context.CurrentFrameIndex)
|
||||
{
|
||||
bucket.TimePosition += _deltaTime;
|
||||
bucket.LastUpdateFrame = _currentFrameIndex;
|
||||
bucket.TimePosition += context.DeltaTime;
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
}
|
||||
value = box->ID == 0 ? bucket.TimePosition : _deltaTime;
|
||||
value = box->ID == 0 ? bucket.TimePosition : context.DeltaTime;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -546,13 +550,10 @@ void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value
|
||||
|
||||
void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Value& value)
|
||||
{
|
||||
auto box = (AnimGraphBox*)boxBase;
|
||||
if (box->IsCacheValid())
|
||||
{
|
||||
// Return cache
|
||||
value = box->Cache;
|
||||
auto& context = Context.Get();
|
||||
if (context.ValueCache.TryGet(boxBase, value))
|
||||
return;
|
||||
}
|
||||
auto box = (AnimGraphBox*)boxBase;
|
||||
auto node = (AnimGraphNode*)nodeBase;
|
||||
switch (node->TypeID)
|
||||
{
|
||||
@@ -569,7 +570,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
case 2:
|
||||
{
|
||||
const auto anim = node->Assets[0].As<Animation>();
|
||||
auto& bucket = _data->State[node->BucketIndex].Animation;
|
||||
auto& bucket = context.Data->State[node->BucketIndex].Animation;
|
||||
const float speed = (float)tryGetValue(node->GetBox(5), node->Values[1]);
|
||||
const bool loop = (bool)tryGetValue(node->GetBox(6), node->Values[2]);
|
||||
const float startTimePos = (float)tryGetValue(node->GetBox(7), node->Values[3]);
|
||||
@@ -584,17 +585,17 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
const float length = anim ? anim->GetLength() : 0.0f;
|
||||
|
||||
// Calculate new time position
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1)
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1)
|
||||
{
|
||||
// If speed is negative and it's the first node update then start playing from end
|
||||
bucket.TimePosition = length;
|
||||
}
|
||||
float newTimePos = bucket.TimePosition + _deltaTime * speed;
|
||||
float newTimePos = bucket.TimePosition + context.DeltaTime * speed;
|
||||
|
||||
value = SampleAnimation(node, loop, length, startTimePos, bucket.TimePosition, newTimePos, anim, 1.0f);
|
||||
|
||||
bucket.TimePosition = newTimePos;
|
||||
bucket.LastUpdateFrame = _currentFrameIndex;
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -615,7 +616,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Is Playing
|
||||
case 4:
|
||||
// If anim was updated during this or a previous frame
|
||||
value = bucket.LastUpdateFrame >= _currentFrameIndex - 1;
|
||||
value = bucket.LastUpdateFrame >= context.CurrentFrameIndex - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -643,7 +644,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
value = Value::Null;
|
||||
if (inputBox->HasConnection())
|
||||
value = eatBox(nodeBase, inputBox->FirstConnection());
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
return;
|
||||
}
|
||||
const auto nodeIndex = _graph.BaseModel->Skeleton.Bones[boneIndex].NodeIndex;
|
||||
@@ -690,7 +691,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Transform every node
|
||||
const auto& skeleton = BaseModel->Skeleton;
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
const int32 parentIndex = skeleton.Nodes[i].ParentIndex;
|
||||
if (parentIndex != -1)
|
||||
@@ -729,7 +730,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Inv transform every node
|
||||
const auto& skeleton = BaseModel->Skeleton;
|
||||
for (int32 i = _skeletonNodesCount - 1; i >= 0; i--)
|
||||
for (int32 i = nodes->Nodes.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
const int32 parentIndex = skeleton.Nodes[i].ParentIndex;
|
||||
if (parentIndex != -1)
|
||||
@@ -775,7 +776,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
// Pass through the input
|
||||
value = input;
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -836,7 +837,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(valueB))
|
||||
nodesB = GetEmptyNodes();
|
||||
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]);
|
||||
}
|
||||
@@ -876,7 +877,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
const auto nodesA = static_cast<AnimGraphImpulse*>(valueA.AsPointer);
|
||||
const auto nodesB = static_cast<AnimGraphImpulse*>(valueB.AsPointer);
|
||||
Transform t, tA, tB;
|
||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||
for (int32 i = 0; i < nodes->Nodes.Count(); i++)
|
||||
{
|
||||
tA = nodesA->Nodes[i];
|
||||
tB = nodesB->Nodes[i];
|
||||
@@ -921,7 +922,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Blend all nodes masked by the user
|
||||
Transform tA, tB;
|
||||
auto& nodesMask = mask->GetNodesMask();
|
||||
for (int32 nodeIndex = 0; nodeIndex < _skeletonNodesCount; nodeIndex++)
|
||||
for (int32 nodeIndex = 0; nodeIndex < nodes->Nodes.Count(); nodeIndex++)
|
||||
{
|
||||
tA = nodesA->Nodes[nodeIndex];
|
||||
if (nodesMask[nodeIndex])
|
||||
@@ -956,7 +957,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// [1]: Guid Animation
|
||||
|
||||
// Prepare
|
||||
auto& bucket = _data->State[node->BucketIndex].MultiBlend;
|
||||
auto& bucket = context.Data->State[node->BucketIndex].MultiBlend;
|
||||
const auto range = node->Values[0].AsVector4();
|
||||
const auto speed = (float)tryGetValue(node->GetBox(1), node->Values[1]);
|
||||
const auto loop = (bool)tryGetValue(node->GetBox(2), node->Values[2]);
|
||||
@@ -988,12 +989,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
// Calculate new time position
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1)
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1)
|
||||
{
|
||||
// If speed is negative and it's the first node update then start playing from end
|
||||
bucket.TimePosition = data.Length;
|
||||
}
|
||||
float newTimePos = bucket.TimePosition + _deltaTime * speed;
|
||||
float newTimePos = bucket.TimePosition + context.DeltaTime * speed;
|
||||
|
||||
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D");
|
||||
|
||||
@@ -1035,7 +1036,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
bucket.TimePosition = newTimePos;
|
||||
bucket.LastUpdateFrame = _currentFrameIndex;
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1054,7 +1055,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// [1]: Guid Animation
|
||||
|
||||
// Prepare
|
||||
auto& bucket = _data->State[node->BucketIndex].MultiBlend;
|
||||
auto& bucket = context.Data->State[node->BucketIndex].MultiBlend;
|
||||
const auto range = node->Values[0].AsVector4();
|
||||
const auto speed = (float)tryGetValue(node->GetBox(1), node->Values[1]);
|
||||
const auto loop = (bool)tryGetValue(node->GetBox(2), node->Values[2]);
|
||||
@@ -1090,12 +1091,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
// Calculate new time position
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1)
|
||||
if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1)
|
||||
{
|
||||
// If speed is negative and it's the first node update then start playing from end
|
||||
bucket.TimePosition = data.Length;
|
||||
}
|
||||
float newTimePos = bucket.TimePosition + _deltaTime * speed;
|
||||
float newTimePos = bucket.TimePosition + context.DeltaTime * speed;
|
||||
|
||||
ANIM_GRAPH_PROFILE_EVENT("Multi Blend 2D");
|
||||
|
||||
@@ -1227,7 +1228,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
bucket.TimePosition = newTimePos;
|
||||
bucket.LastUpdateFrame = _currentFrameIndex;
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1246,7 +1247,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// [3]: AlphaBlendMode Mode
|
||||
|
||||
// Prepare
|
||||
auto& bucket = _data->State[node->BucketIndex].BlendPose;
|
||||
auto& bucket = context.Data->State[node->BucketIndex].BlendPose;
|
||||
const int32 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]);
|
||||
const float blendDuration = (float)tryGetValue(node->GetBox(2), node->Values[1]);
|
||||
const int32 poseCount = Math::Clamp(node->Values[2].AsInt, 0, MaxBlendPoses);
|
||||
@@ -1259,7 +1260,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
// Check if transition is not active (first update, pose not changing or transition ended)
|
||||
bucket.TransitionPosition += _deltaTime;
|
||||
bucket.TransitionPosition += context.DeltaTime;
|
||||
if (bucket.PreviousBlendPoseIndex == -1 || bucket.PreviousBlendPoseIndex == poseIndex || bucket.TransitionPosition >= blendDuration || blendDuration <= ANIM_GRAPH_BLEND_THRESHOLD)
|
||||
{
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
@@ -1356,11 +1357,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
ANIM_GRAPH_PROFILE_EVENT("State Machine");
|
||||
|
||||
// Prepare
|
||||
auto& bucket = _data->State[node->BucketIndex].StateMachine;
|
||||
auto& bucket = context.Data->State[node->BucketIndex].StateMachine;
|
||||
auto& data = node->Data.StateMachine;
|
||||
int32 transitionsLeft = maxTransitionsPerUpdate == 0 ? MAX_uint16 : maxTransitionsPerUpdate;
|
||||
bool isFirstUpdate = bucket.LastUpdateFrame == 0 || bucket.CurrentState == nullptr;
|
||||
if (bucket.LastUpdateFrame != _currentFrameIndex - 1 && reinitializeOnBecomingRelevant)
|
||||
if (bucket.LastUpdateFrame != context.CurrentFrameIndex - 1 && reinitializeOnBecomingRelevant)
|
||||
{
|
||||
// Reset on becoming relevant
|
||||
isFirstUpdate = true;
|
||||
@@ -1384,19 +1385,19 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
|
||||
// Reset all state buckets pof the graphs and nodes included inside the state machine
|
||||
ResetBuckets(data.Graph);
|
||||
ResetBuckets(context, data.Graph);
|
||||
}
|
||||
|
||||
// Update the active transition
|
||||
if (bucket.ActiveTransition)
|
||||
{
|
||||
bucket.TransitionPosition += _deltaTime;
|
||||
bucket.TransitionPosition += context.DeltaTime;
|
||||
|
||||
// Check ofr transition end
|
||||
// Check for transition end
|
||||
if (bucket.TransitionPosition >= bucket.ActiveTransition->BlendDuration)
|
||||
{
|
||||
// End transition
|
||||
ResetBuckets(bucket.CurrentState->Data.State.Graph);
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination;
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
@@ -1422,7 +1423,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Evaluate source state transition data (position, length, etc.)
|
||||
const Value sourceStatePtr = SampleState(bucket.CurrentState);
|
||||
auto& transitionData = _transitionData; // Note: this could support nested transitions but who uses state machine inside transition rule?
|
||||
auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule?
|
||||
if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr))
|
||||
{
|
||||
// Use source state as data provider
|
||||
@@ -1475,7 +1476,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
if (bucket.ActiveTransition && bucket.ActiveTransition->BlendDuration <= ZeroTolerance)
|
||||
{
|
||||
// End transition
|
||||
ResetBuckets(bucket.CurrentState->Data.State.Graph);
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination;
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
@@ -1498,7 +1499,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
|
||||
// Update bucket
|
||||
bucket.LastUpdateFrame = _currentFrameIndex;
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1537,7 +1538,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Transition Source State Anim
|
||||
case 23:
|
||||
{
|
||||
const AnimGraphTransitionData& transitionsData = _transitionData;
|
||||
const AnimGraphTransitionData& transitionsData = context.TransitionData;
|
||||
switch (box->ID)
|
||||
{
|
||||
// Length
|
||||
@@ -1587,7 +1588,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
if (callFunc == function)
|
||||
{
|
||||
value = Value::Zero;
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1606,12 +1607,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
Box* functionOutputBox = functionOutputNode->TryGetBox(0);
|
||||
|
||||
// Cache relation between current node in the call stack to the actual function graph
|
||||
_functions[nodeBase] = (Graph*)data.Graph;
|
||||
context.Functions[nodeBase] = (Graph*)data.Graph;
|
||||
|
||||
// Evaluate the function output
|
||||
_graphStack.Push((Graph*)data.Graph);
|
||||
context.GraphStack.Push((Graph*)data.Graph);
|
||||
value = functionOutputBox && functionOutputBox->HasConnection() ? eatBox(nodeBase, functionOutputBox->FirstConnection()) : Value::Zero;
|
||||
_graphStack.Pop();
|
||||
context.GraphStack.Pop();
|
||||
break;
|
||||
}
|
||||
// Transform Bone (local/model space)
|
||||
@@ -1635,7 +1636,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
value = Value::Null;
|
||||
if (inputBox->HasConnection())
|
||||
value = eatBox(nodeBase, inputBox->FirstConnection());
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
return;
|
||||
}
|
||||
const auto nodes = node->GetNodes(this);
|
||||
@@ -1704,7 +1705,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
// Pass through the input
|
||||
value = input;
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1859,18 +1860,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
default:
|
||||
break;
|
||||
}
|
||||
box->Cache = value;
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& value)
|
||||
{
|
||||
auto box = (AnimGraphBox*)boxBase;
|
||||
if (box->IsCacheValid())
|
||||
{
|
||||
// Return cache
|
||||
value = box->Cache;
|
||||
auto& context = Context.Get();
|
||||
if (context.ValueCache.TryGet(boxBase, value))
|
||||
return;
|
||||
}
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Function Input
|
||||
@@ -1878,13 +1875,13 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
{
|
||||
// Find the function call
|
||||
AnimGraphNode* functionCallNode = nullptr;
|
||||
ASSERT(_graphStack.Count() >= 2);
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = _callStack.Count() - 1; i >= 0; i--)
|
||||
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && _functions.TryGet(_callStack[i], graph) && _graphStack[_graphStack.Count() - 1] == (Graph*)graph)
|
||||
if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == (Graph*)graph)
|
||||
{
|
||||
functionCallNode = (AnimGraphNode*)_callStack[i];
|
||||
functionCallNode = (AnimGraphNode*)context.CallStack[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1926,19 +1923,19 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
if (functionCallBox && functionCallBox->HasConnection())
|
||||
{
|
||||
// Use provided input value from the function call
|
||||
_graphStack.Pop();
|
||||
context.GraphStack.Pop();
|
||||
value = eatBox(node, functionCallBox->FirstConnection());
|
||||
_graphStack.Push(graph);
|
||||
context.GraphStack.Push(graph);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the default value from the function graph
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
}
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
box->Cache = value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user