Added support of time updates for simple embedded graphs.

RefactorUnifiedBlendTreeStateMachineHandling
Martin Felis 2024-04-01 12:33:23 +02:00
parent 0aebe44bd5
commit 76ea38f118
12 changed files with 392 additions and 198 deletions

View File

@ -32,8 +32,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
const std::vector<AnimGraphConnection>& node_input_connections = const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index]; m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) { for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
if (node_input_connections[i].m_crosses_hierarchy) {
continue;
}
int input_node_index = int input_node_index =
GetAnimNodeIndex(node_input_connections.at(i).m_source_node); GetAnimNodeIndex(node_input_connections[i].m_source_node);
if (input_node_index == 1) { if (input_node_index == 1) {
continue; continue;
@ -58,10 +62,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
} }
} }
void AnimGraphBlendTree::MarkActiveInputs() { void AnimGraphBlendTree::MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (m_nodes[i]->m_tick_number != m_tick_number) {
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
} }
}
const std::vector<AnimGraphConnection>& graph_output_inputs = const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0]; m_node_input_connections[0];
@ -69,15 +75,16 @@ void AnimGraphBlendTree::MarkActiveInputs() {
const AnimGraphConnection& graph_input = graph_output_inputs[i]; const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node; AnimNode* node = graph_input.m_source_node;
if (node != nullptr) { if (node != nullptr) {
node->m_tick_number = m_tick_number;
node->m_state = AnimNodeEvalState::Activated; node->m_state = AnimNodeEvalState::Activated;
} }
} }
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (checkIsNodeActive(node)) { if (CheckIsNodeActive(node)) {
node->MarkActiveInputs();
size_t node_index = GetAnimNodeIndex(node); size_t node_index = GetAnimNodeIndex(node);
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active. // Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size(); for (size_t j = 0, nj = m_node_input_connections[node_index].size();
@ -95,14 +102,14 @@ void AnimGraphBlendTree::MarkActiveInputs() {
} }
} }
void AnimGraphBlendTree::CalcSyncTrack() { void AnimGraphBlendTree::CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) {
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) { if (node->m_state == AnimNodeEvalState::Deactivated) {
continue; continue;
} }
node->CalcSyncTrack(); node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]);
} }
} }
@ -113,18 +120,38 @@ void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
m_node_input_connections[0]; m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
AnimNode* node = graph_output_inputs[i].m_source_node; AnimNode* node = graph_output_inputs[i].m_source_node;
if (node != nullptr) { if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) {
node->UpdateTime(node->m_time_now, node->m_time_now + dt); node->UpdateTime(time_last, time_now);
} }
} }
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; --i) { for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) { if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue; continue;
} }
PropagateTimeToNodeInputs(node);
}
m_state = AnimNodeEvalState::TimeUpdated;
}
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) {
size_t node_index = GetAnimNodeIndex(node); size_t node_index = GetAnimNodeIndex(node);
float node_time_now = node->m_time_now; float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last; float node_time_last = node->m_time_last;
@ -142,19 +169,6 @@ void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
} }
} }
} }
}
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) { Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) { for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {

View File

@ -31,13 +31,19 @@ struct AnimGraphBlendTree : public AnimNode {
~AnimGraphBlendTree() override { dealloc(); } ~AnimGraphBlendTree() override { dealloc(); }
void StartUpdateTick() { m_tick_number++; }
// AnimNode overrides // AnimNode overrides
bool Init(AnimGraphContext& context) override; bool Init(AnimGraphContext& context) override;
void MarkActiveInputs() override; void MarkActiveInputs(
void CalcSyncTrack() override; const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override; void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
void PropagateTimeToNodeInputs(const AnimNode* node);
void dealloc() { void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) { for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector(); m_animdata_blocks[i]->m_local_matrices.vector::~vector();
@ -62,8 +68,8 @@ struct AnimGraphBlendTree : public AnimNode {
void UpdateOrderedNodes(); void UpdateOrderedNodes();
void UpdateOrderedNodesRecursive(int node_index); void UpdateOrderedNodesRecursive(int node_index);
bool checkIsNodeActive(AnimNode* node) { bool CheckIsNodeActive(AnimNode* node) {
return node->m_state != AnimNodeEvalState::Deactivated; return node->m_tick_number == m_tick_number;
} }
void ResetNodeStates() { void ResetNodeStates() {
@ -117,7 +123,8 @@ struct AnimGraphBlendTree : public AnimNode {
if (graph_output_connection.m_target_socket.m_name == name) { if (graph_output_connection.m_target_socket.m_name == name) {
if (graph_output_connection.m_source_node == m_nodes[1] if (graph_output_connection.m_source_node == m_nodes[1]
&& graph_output_connection.m_target_node == m_nodes[0]) { && graph_output_connection.m_target_node == m_nodes[0]) {
std::cerr << "Error: cannot set output for direct graph input to graph " std::cerr
<< "Error: cannot set output for direct graph input to graph "
"output connections. Use GetOutptPtr for output instead!" "output connections. Use GetOutptPtr for output instead!"
<< std::endl; << std::endl;
@ -130,14 +137,18 @@ struct AnimGraphBlendTree : public AnimNode {
// Make sure all other output connections of this pin use the same output pointer // Make sure all other output connections of this pin use the same output pointer
int source_node_index = int source_node_index =
GetAnimNodeIndex(graph_output_connection.m_source_node); GetAnimNodeIndex(graph_output_connection.m_source_node);
for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) { for (int j = 0; j < m_node_output_connections[source_node_index].size();
const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j]; j++) {
const AnimGraphConnection& source_output_connection =
m_node_output_connections[source_node_index][j];
if (source_output_connection.m_target_node == m_nodes[0]) { if (source_output_connection.m_target_node == m_nodes[0]) {
continue; continue;
} }
if (source_output_connection.m_source_socket.m_name == graph_output_connection.m_source_socket.m_name) { if (source_output_connection.m_source_socket.m_name
*source_output_connection.m_target_socket.m_reference.ptr_ptr = value_ptr; == graph_output_connection.m_source_socket.m_name) {
*source_output_connection.m_target_socket.m_reference.ptr_ptr =
value_ptr;
} }
} }
} }
@ -159,7 +170,8 @@ struct AnimGraphBlendTree : public AnimNode {
const AnimGraphConnection& graph_output_connection = const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i]; m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) { if (graph_output_connection.m_target_socket.m_name == name) {
return static_cast<float*>(*graph_output_connection.m_source_socket.m_reference.ptr_ptr); return static_cast<float*>(
*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
} }
} }
@ -219,7 +231,7 @@ struct AnimGraphBlendTree : public AnimNode {
return nullptr; return nullptr;
} }
size_t GetAnimNodeIndex(AnimNode* node) { size_t GetAnimNodeIndex(const AnimNode* node) const {
for (size_t i = 0; i < m_nodes.size(); i++) { for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) { if (m_nodes[i] == node) {
return i; return i;
@ -230,5 +242,14 @@ struct AnimGraphBlendTree : public AnimNode {
} }
}; };
//
// BlendTreeSocketNode
//
struct BlendTreeSocketNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeSocketNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeSocketNode* node_) {}
};
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H #endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H

View File

@ -265,6 +265,7 @@ struct AnimGraphConnection {
Socket m_source_socket; Socket m_source_socket;
AnimNode* m_target_node = nullptr; AnimNode* m_target_node = nullptr;
Socket m_target_socket; Socket m_target_socket;
bool m_crosses_hierarchy = false;
}; };

View File

@ -7,14 +7,14 @@
#include <sstream> #include <sstream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h" #include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "AnimGraphBlendTreeResource.h" #include "AnimGraphResource.h"
#include "SkinnedMesh.h" #include "SkinnedMesh.h"
#include "imgui.h" #include "imgui.h"
#include "imnodes.h" #include "imnodes.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
static AnimGraphBlendTreeResource sGraphGresource = static AnimGraphResource sGraphGresource =
AnimGraphBlendTreeResource(); AnimGraphResource();
static bool sGraphLoadedThisFrame = false; static bool sGraphLoadedThisFrame = false;
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
@ -59,13 +59,13 @@ void NodeSocketEditor(Socket& socket) {
} }
void RemoveConnectionsForSocket( void RemoveConnectionsForSocket(
AnimGraphBlendTreeResource& graph_resource, AnimGraphResource& graph_resource,
AnimNodeResource& node_resource, AnimNodeResource& node_resource,
Socket& socket) { Socket& socket) {
std::vector<AnimGraphConnectionResource>::iterator iter = std::vector<BlendTreeConnectionResource>::iterator iter =
graph_resource.m_connections.begin(); graph_resource.m_blend_tree_resource.m_connections.begin();
while (iter != graph_resource.m_connections.end()) { while (iter != graph_resource.m_blend_tree_resource.m_connections.end()) {
// TODO adjust for refactor // TODO adjust for refactor
assert(false); assert(false);

View File

@ -3,13 +3,13 @@
// //
#include "AnimGraphNodes.h" #include "AnimGraphNodes.h"
#include "AnimGraphBlendTree.h"
#include "ozz/base/log.h" #include "AnimGraphBlendTree.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/base/io/archive.h" #include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h" #include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
AnimNode* AnimNodeFactory(const std::string& name) { AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result; AnimNode* result;
@ -24,7 +24,7 @@ AnimNode* AnimNodeFactory(const std::string& name) {
} else if (name == "BlendTree") { } else if (name == "BlendTree") {
result = new AnimGraphBlendTree; result = new AnimGraphBlendTree;
} else if (name == "BlendTreeSockets") { } else if (name == "BlendTreeSockets") {
result = new BlendTreeNode; result = new BlendTreeSocketNode;
} else if (name == "MathAddNode") { } else if (name == "MathAddNode") {
result = new MathAddNode; result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") { } else if (name == "MathFloatToVec3Node") {
@ -42,6 +42,33 @@ AnimNode* AnimNodeFactory(const std::string& name) {
return nullptr; return nullptr;
} }
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
void Blend2Node::Evaluate(AnimGraphContext& context) { void Blend2Node::Evaluate(AnimGraphContext& context) {
assert(i_input0 != nullptr); assert(i_input0 != nullptr);
@ -71,9 +98,7 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
// //
// AnimSamplerNode // AnimSamplerNode
// //
AnimSamplerNode::~AnimSamplerNode() noexcept { AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; }
m_animation = nullptr;
}
bool AnimSamplerNode::Init(AnimGraphContext& context) { bool AnimSamplerNode::Init(AnimGraphContext& context) {
assert(m_animation == nullptr); assert(m_animation == nullptr);

View File

@ -7,23 +7,13 @@
#include <vector> #include <vector>
#include "AnimNode.h"
#include "AnimGraphData.h" #include "AnimGraphData.h"
#include "AnimNode.h"
#include "SyncTrack.h" #include "SyncTrack.h"
#include "ozz/animation/runtime/sampling_job.h" #include "ozz/animation/runtime/sampling_job.h"
struct AnimNode; struct AnimNode;
//
// BlendTreeNode
//
struct BlendTreeNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeNode* node_) {}
};
// //
// Blend2Node // Blend2Node
// //
@ -34,8 +24,9 @@ struct Blend2Node : public AnimNode {
float* i_blend_weight = nullptr; float* i_blend_weight = nullptr;
bool m_sync_blend = false; bool m_sync_blend = false;
virtual void MarkActiveInputs() override { void MarkActiveInputs(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) override {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node == nullptr) { if (input_node == nullptr) {
continue; continue;
@ -53,7 +44,7 @@ struct Blend2Node : public AnimNode {
} }
} }
virtual void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
}; };
template <> template <>
@ -80,7 +71,6 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
} }
}; };
// //
// SpeedScaleNode // SpeedScaleNode
// //
@ -116,7 +106,6 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
} }
}; };
// //
// AnimSamplerNode // AnimSamplerNode
// //
@ -172,7 +161,6 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
} }
}; };
// //
// ConstScalarNode // ConstScalarNode
// //
@ -180,9 +168,7 @@ struct ConstScalarNode : public AnimNode {
float* o_value = nullptr; float* o_value = nullptr;
float value = 0.f; float value = 0.f;
virtual void Evaluate(AnimGraphContext& context){ virtual void Evaluate(AnimGraphContext& context) { *o_value = value; };
*o_value = value;
};
}; };
template <> template <>
@ -193,7 +179,6 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
} }
}; };
// //
// MathAddNode // MathAddNode
// //
@ -249,35 +234,10 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
} }
}; };
AnimNode* AnimNodeFactory(const std::string& name); AnimNode* AnimNodeFactory(const std::string& name);
static inline NodeDescriptorBase* AnimNodeDescriptorFactory( NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name, const std::string& node_type_name,
AnimNode* node) { AnimNode* node);
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
#endif //ANIMTESTBED_ANIMGRAPHNODES_H #endif //ANIMTESTBED_ANIMGRAPHNODES_H

View File

@ -8,15 +8,17 @@
#include <fstream> #include <fstream>
#include "3rdparty/json/json.hpp" #include "3rdparty/json/json.hpp"
#include "AnimGraphBlendTree.h" #include "AnimGraphBlendTree.h"
#include "AnimGraphNodes.h" #include "AnimGraphNodes.h"
using json = nlohmann::json; using json = nlohmann::json;
// forward declarations // forward declarations
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource); static json sAnimGraphResourceBlendTreeToJson(
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource); const AnimGraphResource& anim_graph_resource);
static bool sAnimGraphResourceBlendTreeFromJson(
const json& json_data,
AnimGraphResource* result_graph_resource);
// //
// Socket <-> json // Socket <-> json
@ -143,7 +145,6 @@ json sAnimGraphNodeToJson(
} }
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
} }
for (const auto& socket : node->m_socket_accessor->m_inputs) { for (const auto& socket : node->m_socket_accessor->m_inputs) {
@ -175,7 +176,6 @@ json sAnimGraphNodeToJson(
AnimNodeResource* sAnimGraphNodeFromJson( AnimNodeResource* sAnimGraphNodeFromJson(
const json& json_node, const json& json_node,
size_t node_index) { size_t node_index) {
std::string node_type = json_node["node_type"]; std::string node_type = json_node["node_type"];
if (node_type == "BlendTree") { if (node_type == "BlendTree") {
@ -247,20 +247,23 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson(
return connection; return connection;
} }
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource) { static json sAnimGraphResourceBlendTreeToJson(
const AnimGraphResource& anim_graph_resource) {
json result; json result;
result["name"] = anim_graph_resource.m_name; result["name"] = anim_graph_resource.m_name;
result["type"] = "AnimNodeResource"; result["type"] = "AnimNodeResource";
result["node_type"] = "BlendTree"; result["node_type"] = "BlendTree";
const BlendTreeResource& blend_tree_resource = anim_graph_resource.m_blend_tree_resource; const BlendTreeResource& blend_tree_resource =
anim_graph_resource.m_blend_tree_resource;
for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) { for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) {
const AnimNodeResource* node = blend_tree_resource.m_nodes[i]; const AnimNodeResource* node = blend_tree_resource.m_nodes[i];
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = dynamic_cast<const AnimGraphResource*>(node); const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource);
} else { } else {
result["nodes"][i] = result["nodes"][i] =
@ -276,8 +279,7 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap
// Graph inputs and outputs // Graph inputs and outputs
{ {
const AnimNodeResource* graph_output_node = const AnimNodeResource* graph_output_node = blend_tree_resource.m_nodes[0];
blend_tree_resource.m_nodes[0];
const std::vector<Socket> graph_inputs = const std::vector<Socket> graph_inputs =
graph_output_node->m_socket_accessor->m_inputs; graph_output_node->m_socket_accessor->m_inputs;
for (size_t i = 0; i < graph_inputs.size(); i++) { for (size_t i = 0; i < graph_inputs.size(); i++) {
@ -294,8 +296,11 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap
return result; return result;
} }
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource) { static bool sAnimGraphResourceBlendTreeFromJson(
BlendTreeResource& blend_tree_resource = result_graph_resource->m_blend_tree_resource; const json& json_data,
AnimGraphResource* result_graph_resource) {
BlendTreeResource& blend_tree_resource =
result_graph_resource->m_blend_tree_resource;
result_graph_resource->m_graph_type_name = "BlendTree"; result_graph_resource->m_graph_type_name = "BlendTree";
result_graph_resource->m_node_type_name = "BlendTree"; result_graph_resource->m_node_type_name = "BlendTree";
@ -369,21 +374,27 @@ bool BlendTreeResource::ConnectSockets (
Socket* target_socket; Socket* target_socket;
if (target_node->m_node_type_name == "BlendTree") { if (target_node->m_node_type_name == "BlendTree") {
const AnimGraphResource* target_graph_resource = dynamic_cast<const AnimGraphResource*>(target_node); const AnimGraphResource* target_graph_resource =
AnimNodeResource* graph_output_node = target_graph_resource->m_blend_tree_resource.GetGraphInputNode(); dynamic_cast<const AnimGraphResource*>(target_node);
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(target_socket_name.c_str()); AnimNodeResource* graph_output_node =
target_graph_resource->m_blend_tree_resource.GetGraphInputNode();
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(
target_socket_name.c_str());
} else { } else {
target_socket = target_socket = target_node->m_socket_accessor->GetInputSocket(
target_node->m_socket_accessor->GetInputSocket(target_socket_name.c_str()); target_socket_name.c_str());
} }
if (source_node->m_node_type_name == "BlendTree") { if (source_node->m_node_type_name == "BlendTree") {
const AnimGraphResource* source_graph_resource = dynamic_cast<const AnimGraphResource*>(source_node); const AnimGraphResource* source_graph_resource =
AnimNodeResource* graph_output_node = source_graph_resource->m_blend_tree_resource.GetGraphOutputNode(); dynamic_cast<const AnimGraphResource*>(source_node);
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(source_socket_name.c_str()); AnimNodeResource* graph_output_node =
source_graph_resource->m_blend_tree_resource.GetGraphOutputNode();
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(
source_socket_name.c_str());
} else { } else {
source_socket = source_socket = source_node->m_socket_accessor->GetOutputSocket(
source_node->m_socket_accessor->GetOutputSocket(source_socket_name.c_str()); source_socket_name.c_str());
} }
if (source_socket == nullptr || target_socket == nullptr) { if (source_socket == nullptr || target_socket == nullptr) {
@ -443,7 +454,8 @@ bool AnimGraphResource::SaveToFile(const char* filename) const {
return SaveStateMachineResourceToFile(filename); return SaveStateMachineResourceToFile(filename);
} }
std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "." << std::endl; std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "."
<< std::endl;
return false; return false;
} }
@ -452,8 +464,7 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
const char* filename) const { const char* filename) const {
json result; json result;
result = sAnimGraphResourceBlendTreeToJson( result = sAnimGraphResourceBlendTreeToJson(*this);
*this);
std::ofstream output_file; std::ofstream output_file;
output_file.open(filename); output_file.open(filename);
@ -466,14 +477,18 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
void AnimGraphResource::CreateBlendTreeInstance( void AnimGraphResource::CreateBlendTreeInstance(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
if (m_node_type_name != "BlendTree") { if (m_node_type_name != "BlendTree") {
std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" std::cerr
<< "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
<< m_graph_type_name << "'." << std::endl; << m_graph_type_name << "'." << std::endl;
return; return;
} }
result.m_name = m_name;
CreateBlendTreeRuntimeNodeInstances(result); CreateBlendTreeRuntimeNodeInstances(result);
PrepareBlendTreeIOData(result); PrepareBlendTreeIOData(result);
SetRuntimeNodeProperties(result); SetRuntimeNodeProperties(result);
CreateBlendTreeConnectionInstances(result);
result.UpdateOrderedNodes(); result.UpdateOrderedNodes();
result.ResetNodeStates(); result.ResetNodeStates();
@ -485,14 +500,19 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
if (node_resource->m_node_type_name == "BlendTree") { if (node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* embedded_blend_tree_resource = dynamic_cast<AnimGraphResource*>(node_resource); AnimGraphResource* embedded_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(node_resource);
assert(embedded_blend_tree_resource != nullptr); assert(embedded_blend_tree_resource != nullptr);
AnimGraphBlendTree* embedded_blend_tree = dynamic_cast<AnimGraphBlendTree*>(node); AnimGraphBlendTree* embedded_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(node);
assert(embedded_blend_tree != nullptr); assert(embedded_blend_tree != nullptr);
embedded_blend_tree_resource->CreateBlendTreeInstance(*embedded_blend_tree); embedded_blend_tree_resource->CreateBlendTreeInstance(
embedded_blend_tree_resource->m_socket_accessor->m_inputs = embedded_blend_tree->m_node_descriptor->m_outputs; *embedded_blend_tree);
embedded_blend_tree_resource->m_socket_accessor->m_outputs = embedded_blend_tree->m_node_descriptor->m_inputs; embedded_blend_tree_resource->m_socket_accessor->m_inputs =
embedded_blend_tree->m_node_descriptor->m_outputs;
embedded_blend_tree_resource->m_socket_accessor->m_outputs =
embedded_blend_tree->m_node_descriptor->m_inputs;
} }
node->m_name = node_resource->m_name; node->m_name = node_resource->m_name;
@ -574,7 +594,10 @@ void AnimGraphResource::PrepareBlendTreeIOData(
instance.m_connection_data_storage = new char[connection_data_storage_size]; instance.m_connection_data_storage = new char[connection_data_storage_size];
memset(instance.m_connection_data_storage, 0, connection_data_storage_size); memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
} }
}
void AnimGraphResource::CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance) const {
std::vector<NodeDescriptorBase*> instance_node_descriptors( std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_blend_tree_resource.m_nodes.size(), m_blend_tree_resource.m_nodes.size(),
nullptr); nullptr);
@ -583,7 +606,8 @@ void AnimGraphResource::PrepareBlendTreeIOData(
m_blend_tree_resource.m_nodes[i]->m_node_type_name, m_blend_tree_resource.m_nodes[i]->m_node_type_name,
instance.m_nodes[i]); instance.m_nodes[i]);
if (i > 1 && m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") { if (i > 1
&& m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
instance_node_descriptors[i]->m_inputs = instance_node_descriptors[i]->m_inputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs; m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs;
instance_node_descriptors[i]->m_outputs = instance_node_descriptors[i]->m_outputs =
@ -611,6 +635,72 @@ void AnimGraphResource::PrepareBlendTreeIOData(
connection.target_socket_name.c_str()); connection.target_socket_name.c_str());
AnimGraphConnection instance_connection; AnimGraphConnection instance_connection;
if (source_node->m_node_type_name == "BlendTree") {
// For embedded subgraphs we have to ensure two things:
// 1. The parent graph target node must activate the source node within
// the embedded subgraph.
// 2. The parent graph target node must activate the AnimNode of the
// parent graph which contains the embedded subgraph. Otherwise, the
// embedded graph does not get evaluated.
// For each case we have to add a connection here. First we insert the
// default connection within the graph (i.e. for case 2). Then we alter
// the source node such that it points to the node within the embedded
// subgraph.
AnimGraphConnection embedded_graph_activation_connection;
embedded_graph_activation_connection.m_source_node = source_node;
embedded_graph_activation_connection.m_source_socket = *source_socket;
embedded_graph_activation_connection.m_target_node = target_node;
embedded_graph_activation_connection.m_target_socket = *target_socket;
instance.m_node_input_connections[connection.target_node_index].push_back(
embedded_graph_activation_connection);
instance.m_node_output_connections[connection.source_node_index]
.push_back(embedded_graph_activation_connection);
AnimGraphResource* source_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(
m_blend_tree_resource.m_nodes[connection.source_node_index]);
AnimGraphBlendTree* source_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(source_node);
size_t source_blend_tree_output_node_index =
source_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForOutputSocket(connection.source_socket_name);
source_node =
source_blend_tree->m_nodes[source_blend_tree_output_node_index];
instance_connection.m_crosses_hierarchy = true;
} else if (target_node->m_node_type_name == "BlendTree") {
// When a connection points to an embedded blend tree we have to ensure
// that the embedded node knows about its connection partner in the parent
// tree. This allows the embedded node to properly activate the node in
// the parent graph.
AnimGraphResource* target_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(
m_blend_tree_resource.m_nodes[connection.target_node_index]);
AnimGraphBlendTree* target_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(target_node);
size_t target_blend_tree_output_node_index =
target_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForInputSocket(connection.target_socket_name);
target_node =
target_blend_tree->m_nodes[target_blend_tree_output_node_index];
// Make sure that the embedded node input activates the parent graph node.
std::vector<AnimGraphConnection>& embeded_target_node_inputs =
target_blend_tree
->m_node_input_connections[target_blend_tree_output_node_index];
for (size_t j = 0; j < embeded_target_node_inputs.size(); j++) {
AnimGraphConnection& embedded_target_connection =
embeded_target_node_inputs[j];
if (embedded_target_connection.m_source_socket.m_name
== target_socket->m_name) {
embedded_target_connection.m_source_node = source_node;
embedded_target_connection.m_crosses_hierarchy = true;
}
}
}
instance_connection.m_source_node = source_node; instance_connection.m_source_node = source_node;
instance_connection.m_source_socket = *source_socket; instance_connection.m_source_socket = *source_socket;
instance_connection.m_target_node = target_node; instance_connection.m_target_node = target_node;
@ -685,8 +775,9 @@ void AnimGraphResource::SetRuntimeNodeProperties(
for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) { for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) {
const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i]; const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i];
NodeDescriptorBase* node_instance_accessor = NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
AnimNodeDescriptorFactory(node_resource->m_node_type_name, result.m_nodes[i]); node_resource->m_node_type_name,
result.m_nodes[i]);
std::vector<Socket>& resource_properties = std::vector<Socket>& resource_properties =
node_resource->m_socket_accessor->m_properties; node_resource->m_socket_accessor->m_properties;
@ -741,7 +832,8 @@ bool AnimGraphResource::SaveStateMachineResourceToFile(
return false; return false;
} }
bool AnimGraphResource::LoadStateMachineResourceFromJson(nlohmann::json const& json_data) { bool AnimGraphResource::LoadStateMachineResourceFromJson(
nlohmann::json const& json_data) {
assert(false && "Not yet implemented"); assert(false && "Not yet implemented");
return false; return false;

View File

@ -100,6 +100,32 @@ struct BlendTreeResource {
return result; return result;
} }
size_t GetNodeIndexForOutputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.target_node_index == 0 && connection.target_socket_name == socket_name) {
return connection.source_node_index;
}
}
std::cerr << "Error: could not find a node connected to output '" << socket_name << "'." << std::endl;
return -1;
}
size_t GetNodeIndexForInputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.source_node_index == 1 && connection.source_socket_name == socket_name) {
return connection.target_node_index;
}
}
std::cerr << "Error: could not find a node connected to input '" << socket_name << "'." << std::endl;
return -1;
}
}; };
struct StateMachineTransitionResources { struct StateMachineTransitionResources {
@ -131,6 +157,7 @@ struct AnimGraphResource: AnimNodeResource {
bool SaveBlendTreeResourceToFile(const char* filename) const; bool SaveBlendTreeResourceToFile(const char* filename) const;
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const; void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const;
void CreateBlendTreeConnectionInstances(AnimGraphBlendTree& instance) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
bool SaveStateMachineResourceToFile(const char* filename) const; bool SaveStateMachineResourceToFile(const char* filename) const;

View File

@ -8,11 +8,11 @@ bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
} }
void AnimGraphStateMachine::MarkActiveInputs() { void AnimGraphStateMachine::MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) {
} }
void AnimGraphStateMachine::CalcSyncTrack() { void AnimGraphStateMachine::CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) {
} }

View File

@ -25,8 +25,8 @@ struct AnimGraphStateMachine : public AnimNode {
Transition* m_active_transition = nullptr; Transition* m_active_transition = nullptr;
bool Init(AnimGraphContext& context); bool Init(AnimGraphContext& context);
void MarkActiveInputs() override; void MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack() override; void CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override; void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
}; };

View File

@ -8,8 +8,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "SyncTrack.h"
#include "AnimGraphData.h" #include "AnimGraphData.h"
#include "SyncTrack.h"
struct AnimNode; struct AnimNode;
@ -25,29 +25,35 @@ enum class AnimNodeEvalState {
struct AnimNode { struct AnimNode {
std::string m_name; std::string m_name;
std::string m_node_type_name; std::string m_node_type_name;
int m_tick_number = 0;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
float m_time_now = 0.f; float m_time_now = 0.f;
float m_time_last = 0.f; float m_time_last = 0.f;
SyncTrack m_sync_track; SyncTrack m_sync_track;
std::vector<AnimGraphConnection> m_inputs;
virtual ~AnimNode() = default; virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) { return true; }; virtual bool Init(AnimGraphContext& context) {
m_time_now = 0.f;
m_time_last = 0.f;
return true;
};
virtual void MarkActiveInputs() { virtual void MarkActiveInputs(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) { if (input_node != nullptr) {
input_node->m_tick_number = m_tick_number;
input_node->m_state = AnimNodeEvalState::Activated; input_node->m_state = AnimNodeEvalState::Activated;
} }
} }
} }
virtual void CalcSyncTrack() { virtual void CalcSyncTrack(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node != nullptr if (input_node != nullptr
&& input.m_source_socket.m_type == SocketType::SocketTypeAnimation && input.m_source_socket.m_type == SocketType::SocketTypeAnimation

View File

@ -140,7 +140,9 @@ class EmbeddedBlendTreeGraphResource {
BlendTreeResource* embedded_blend_tree_resource = nullptr; BlendTreeResource* embedded_blend_tree_resource = nullptr;
size_t walk_node_index = -1; size_t walk_node_index = -1;
AnimNodeResource* walk_node = nullptr; AnimNodeResource* walk_node_resource = nullptr;
size_t embedded_blend_tree_node_index = -1;
size_t embedded_speed_scale_index = -1;
public: public:
EmbeddedBlendTreeGraphResource() { EmbeddedBlendTreeGraphResource() {
@ -163,10 +165,11 @@ class EmbeddedBlendTreeGraphResource {
// Parent AnimSampler // Parent AnimSampler
parent_blend_tree_resource->m_nodes.push_back( parent_blend_tree_resource->m_nodes.push_back(
AnimNodeResourceFactory("AnimSampler")); AnimNodeResourceFactory("AnimSampler"));
walk_node_index = parent_blend_tree_resource->m_nodes.size() - 1;
walk_node = parent_blend_tree_resource->m_nodes.back(); walk_node_resource = parent_blend_tree_resource->m_nodes[walk_node_index];
walk_node->m_name = "WalkAnim"; walk_node_resource->m_name = "WalkAnim";
walk_node->m_socket_accessor->SetPropertyValue( walk_node_resource->m_socket_accessor->SetPropertyValue(
"Filename", "Filename",
std::string("media/Walking-loop.ozz")); std::string("media/Walking-loop.ozz"));
@ -175,7 +178,7 @@ class EmbeddedBlendTreeGraphResource {
// //
parent_blend_tree_resource->m_nodes.push_back( parent_blend_tree_resource->m_nodes.push_back(
AnimNodeResourceFactory("BlendTree")); AnimNodeResourceFactory("BlendTree"));
size_t embedded_blend_tree_node_index = embedded_blend_tree_node_index =
parent_blend_tree_resource->m_nodes.size() - 1; parent_blend_tree_resource->m_nodes.size() - 1;
embedded_graph = dynamic_cast<AnimGraphResource*>( embedded_graph = dynamic_cast<AnimGraphResource*>(
parent_blend_tree_resource->m_nodes.back()); parent_blend_tree_resource->m_nodes.back());
@ -201,9 +204,13 @@ class EmbeddedBlendTreeGraphResource {
// Embedded: SpeedScale node // Embedded: SpeedScale node
embedded_blend_tree_resource->m_nodes.push_back( embedded_blend_tree_resource->m_nodes.push_back(
AnimNodeResourceFactory("SpeedScale")); AnimNodeResourceFactory("SpeedScale"));
embedded_speed_scale_index =
embedded_blend_tree_resource->m_nodes.size() - 1;
AnimNodeResource* embedded_speed_scale_resource = AnimNodeResource* embedded_speed_scale_resource =
embedded_blend_tree_resource->m_nodes.back(); embedded_blend_tree_resource->m_nodes[embedded_speed_scale_index];
embedded_speed_scale_resource->m_socket_accessor->SetInputValue("SpeedScale", 0.1f); embedded_speed_scale_resource->m_socket_accessor->SetInputValue(
"SpeedScale",
0.1f);
// Embedded: setup connections // Embedded: setup connections
embedded_blend_tree_resource->ConnectSockets( embedded_blend_tree_resource->ConnectSockets(
@ -219,7 +226,7 @@ class EmbeddedBlendTreeGraphResource {
// Parent: setup connections // Parent: setup connections
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(parent_blend_tree_resource->ConnectSockets(
walk_node, walk_node_resource,
"Output", "Output",
embedded_graph, embedded_graph,
"AnimInput")); "AnimInput"));
@ -346,8 +353,8 @@ TEST_CASE_METHOD(
AnimSamplerNode* anim_sampler_walk = AnimSamplerNode* anim_sampler_walk =
dynamic_cast<AnimSamplerNode*>(anim_graph_blend_tree.m_nodes[2]); dynamic_cast<AnimSamplerNode*>(anim_graph_blend_tree.m_nodes[2]);
BlendTreeNode* graph_output_node = BlendTreeSocketNode* graph_output_node =
dynamic_cast<BlendTreeNode*>(anim_graph_blend_tree.m_nodes[0]); dynamic_cast<BlendTreeSocketNode*>(anim_graph_blend_tree.m_nodes[0]);
// check node input dependencies // check node input dependencies
size_t anim_sampler_index = size_t anim_sampler_index =
@ -937,7 +944,8 @@ TEST_CASE_METHOD(
parent_blend_tree_resource->m_nodes.size() parent_blend_tree_resource->m_nodes.size()
== parent_blend_tree_resource_loaded.m_nodes.size()); == parent_blend_tree_resource_loaded.m_nodes.size());
for (size_t i = 0; i < parent_blend_tree_resource->m_nodes.size(); i++) { for (size_t i = 0; i < parent_blend_tree_resource->m_nodes.size(); i++) {
const AnimNodeResource* parent_node = parent_blend_tree_resource->m_nodes[i]; const AnimNodeResource* parent_node =
parent_blend_tree_resource->m_nodes[i];
const AnimNodeResource* parent_node_loaded = const AnimNodeResource* parent_node_loaded =
parent_blend_tree_resource_loaded.m_nodes[i]; parent_blend_tree_resource_loaded.m_nodes[i];
@ -949,7 +957,8 @@ TEST_CASE_METHOD(
CHECK( CHECK(
parent_blend_tree_resource->m_connections.size() parent_blend_tree_resource->m_connections.size()
== parent_blend_tree_resource_loaded.m_connections.size()); == parent_blend_tree_resource_loaded.m_connections.size());
for (size_t i = 0; i < parent_blend_tree_resource->m_connections.size(); i++) { for (size_t i = 0; i < parent_blend_tree_resource->m_connections.size();
i++) {
const BlendTreeConnectionResource& parent_connection = const BlendTreeConnectionResource& parent_connection =
parent_blend_tree_resource->m_connections[i]; parent_blend_tree_resource->m_connections[i];
const BlendTreeConnectionResource& parent_connection_loaded = const BlendTreeConnectionResource& parent_connection_loaded =
@ -1039,22 +1048,61 @@ TEST_CASE_METHOD(
graph_context.m_skeleton = &skeleton; graph_context.m_skeleton = &skeleton;
blend_tree.Init(graph_context); blend_tree.Init(graph_context);
// Marking of active inputs is not properly working as we do not properly
// populate AnimNode::m_inputs. const AnimSamplerNode* walk_node =
// dynamic_cast<AnimSamplerNode*>(blend_tree.m_nodes[walk_node_index]);
// Here comes an iffy problem: How to mark nodes in a parent blend tree const AnimGraphBlendTree* embedded_blend_tree_node =
// as active? dynamic_cast<AnimGraphBlendTree*>(
// blend_tree.m_nodes[embedded_blend_tree_node_index]);
// - Simplest would be if the AnimNodes don't distinguish between nodes within const SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
// the tree they are contained or in a possible parent (or nested) tree. embedded_blend_tree_node->m_nodes[walk_node_index]);
// - But then how to propagate active connections across multiple layers?
// - Therefore probably better to have a blend tree store which sockets are blend_tree.StartUpdateTick();
// active and use that within the graph. blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
// - But then: AnimGraphConnection already stores raw AnimNode pointers of the REQUIRE(embedded_blend_tree_node->m_state == AnimNodeEvalState::Activated);
// connected nodes. Still... populating them accross layers could be messy. REQUIRE(speed_scale_node->m_state == AnimNodeEvalState::Activated);
blend_tree.MarkActiveInputs(); REQUIRE(walk_node->m_state == AnimNodeEvalState::Activated);
blend_tree.UpdateTime(0.f, 0.1f);
float time_last = 0.f;
float dt = 0.1f;
blend_tree.UpdateTime(time_last, time_last + dt);
CHECK(embedded_blend_tree_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK(speed_scale_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK(walk_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK_THAT(
walk_node->m_time_last,
Catch::Matchers::WithinAbs(time_last, 0.001));
CHECK_THAT(
walk_node->m_time_now,
Catch::Matchers::WithinAbs(
time_last + dt * (*speed_scale_node->i_speed_scale),
0.001));
blend_tree.Evaluate(graph_context); blend_tree.Evaluate(graph_context);
WHEN("Updating the time a second time") {
// Perform another update
time_last = time_last + dt;
dt = 0.3f;
blend_tree.StartUpdateTick();
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
blend_tree.UpdateTime(time_last, time_last + dt);
CHECK(embedded_blend_tree_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK(speed_scale_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK(walk_node->m_state == AnimNodeEvalState::TimeUpdated);
CHECK_THAT(
walk_node->m_time_last,
Catch::Matchers::WithinAbs(
time_last * (*speed_scale_node->i_speed_scale),
0.001));
CHECK_THAT(
walk_node->m_time_now,
Catch::Matchers::WithinAbs(
(time_last + dt) * (*speed_scale_node->i_speed_scale),
0.001));
}
graph_context.freeAnimations(); graph_context.freeAnimations();
} }