diff --git a/src/AnimGraph/AnimGraphBlendTree.cc b/src/AnimGraph/AnimGraphBlendTree.cc index d582779..7ccef9e 100644 --- a/src/AnimGraph/AnimGraphBlendTree.cc +++ b/src/AnimGraph/AnimGraphBlendTree.cc @@ -32,8 +32,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) { const std::vector& node_input_connections = m_node_input_connections[node_index]; 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 = - GetAnimNodeIndex(node_input_connections.at(i).m_source_node); + GetAnimNodeIndex(node_input_connections[i].m_source_node); if (input_node_index == 1) { continue; @@ -58,9 +62,11 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) { } } -void AnimGraphBlendTree::MarkActiveInputs() { +void AnimGraphBlendTree::MarkActiveInputs(const std::vector& input_connections) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) { - m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; + if (m_nodes[i]->m_tick_number != m_tick_number) { + m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; + } } const std::vector& graph_output_inputs = @@ -69,15 +75,16 @@ void AnimGraphBlendTree::MarkActiveInputs() { const AnimGraphConnection& graph_input = graph_output_inputs[i]; AnimNode* node = graph_input.m_source_node; if (node != nullptr) { + node->m_tick_number = m_tick_number; 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]; - if (checkIsNodeActive(node)) { - node->MarkActiveInputs(); + if (CheckIsNodeActive(node)) { size_t node_index = GetAnimNodeIndex(node); + node->MarkActiveInputs(m_node_input_connections[node_index]); // Non-animation data inputs are always active. 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& input_connections) { for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { AnimNode* node = m_eval_ordered_nodes[i]; if (node->m_state == AnimNodeEvalState::Deactivated) { continue; } - node->CalcSyncTrack(); + node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]); } } @@ -113,35 +120,21 @@ void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) { m_node_input_connections[0]; for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { AnimNode* node = graph_output_inputs[i].m_source_node; - if (node != nullptr) { - node->UpdateTime(node->m_time_now, node->m_time_now + dt); + if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) { + 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]; if (node->m_state != AnimNodeEvalState::TimeUpdated) { continue; } - size_t node_index = GetAnimNodeIndex(node); - float node_time_now = node->m_time_now; - float node_time_last = node->m_time_last; - - const std::vector& node_input_connections = - m_node_input_connections[node_index]; - for (size_t i = 0, n = node_input_connections.size(); i < n; i++) { - AnimNode* input_node = node_input_connections[i].m_source_node; - - // Only propagate time updates via animation sockets. - if (input_node != nullptr - && node_input_connections[i].m_target_socket.m_type - == SocketType::SocketTypeAnimation - && input_node->m_state == AnimNodeEvalState::Activated) { - input_node->UpdateTime(node_time_last, node_time_now); - } - } + PropagateTimeToNodeInputs(node); } + + m_state = AnimNodeEvalState::TimeUpdated; } void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) { @@ -156,6 +149,27 @@ void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) { } } +void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) { + size_t node_index = GetAnimNodeIndex(node); + + float node_time_now = node->m_time_now; + float node_time_last = node->m_time_last; + + const std::vector& node_input_connections = + m_node_input_connections[node_index]; + for (size_t i = 0, n = node_input_connections.size(); i < n; i++) { + AnimNode* input_node = node_input_connections[i].m_source_node; + + // Only propagate time updates via animation sockets. + if (input_node != nullptr + && node_input_connections[i].m_target_socket.m_type + == SocketType::SocketTypeAnimation + && input_node->m_state == AnimNodeEvalState::Activated) { + input_node->UpdateTime(node_time_last, node_time_now); + } + } +} + Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) { for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) { AnimGraphConnection& connection = m_node_output_connections[1][i]; diff --git a/src/AnimGraph/AnimGraphBlendTree.h b/src/AnimGraph/AnimGraphBlendTree.h index 5f83828..a964d66 100644 --- a/src/AnimGraph/AnimGraphBlendTree.h +++ b/src/AnimGraph/AnimGraphBlendTree.h @@ -31,13 +31,19 @@ struct AnimGraphBlendTree : public AnimNode { ~AnimGraphBlendTree() override { dealloc(); } + void StartUpdateTick() { m_tick_number++; } + // AnimNode overrides bool Init(AnimGraphContext& context) override; - void MarkActiveInputs() override; - void CalcSyncTrack() override; + void MarkActiveInputs( + const std::vector& input_connections) override; + void CalcSyncTrack( + const std::vector& input_connections) override; void UpdateTime(float time_last, float time_now) override; void Evaluate(AnimGraphContext& context) override; + void PropagateTimeToNodeInputs(const AnimNode* node); + void dealloc() { for (size_t i = 0; i < m_animdata_blocks.size(); i++) { m_animdata_blocks[i]->m_local_matrices.vector::~vector(); @@ -62,8 +68,8 @@ struct AnimGraphBlendTree : public AnimNode { void UpdateOrderedNodes(); void UpdateOrderedNodesRecursive(int node_index); - bool checkIsNodeActive(AnimNode* node) { - return node->m_state != AnimNodeEvalState::Deactivated; + bool CheckIsNodeActive(AnimNode* node) { + return node->m_tick_number == m_tick_number; } void ResetNodeStates() { @@ -117,9 +123,10 @@ struct AnimGraphBlendTree : public AnimNode { if (graph_output_connection.m_target_socket.m_name == name) { if (graph_output_connection.m_source_node == m_nodes[1] && graph_output_connection.m_target_node == m_nodes[0]) { - std::cerr << "Error: cannot set output for direct graph input to graph " - "output connections. Use GetOutptPtr for output instead!" - << std::endl; + std::cerr + << "Error: cannot set output for direct graph input to graph " + "output connections. Use GetOutptPtr for output instead!" + << std::endl; return; } @@ -130,14 +137,18 @@ struct AnimGraphBlendTree : public AnimNode { // Make sure all other output connections of this pin use the same output pointer int source_node_index = GetAnimNodeIndex(graph_output_connection.m_source_node); - for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) { - const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j]; + for (int j = 0; j < m_node_output_connections[source_node_index].size(); + j++) { + const AnimGraphConnection& source_output_connection = + m_node_output_connections[source_node_index][j]; if (source_output_connection.m_target_node == m_nodes[0]) { continue; } - if (source_output_connection.m_source_socket.m_name == graph_output_connection.m_source_socket.m_name) { - *source_output_connection.m_target_socket.m_reference.ptr_ptr = value_ptr; + if (source_output_connection.m_source_socket.m_name + == 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 = m_node_input_connections[0][i]; if (graph_output_connection.m_target_socket.m_name == name) { - return static_cast(*graph_output_connection.m_source_socket.m_reference.ptr_ptr); + return static_cast( + *graph_output_connection.m_source_socket.m_reference.ptr_ptr); } } @@ -219,7 +231,7 @@ struct AnimGraphBlendTree : public AnimNode { return nullptr; } - size_t GetAnimNodeIndex(AnimNode* node) { + size_t GetAnimNodeIndex(const AnimNode* node) const { for (size_t i = 0; i < m_nodes.size(); i++) { if (m_nodes[i] == node) { return i; @@ -230,5 +242,14 @@ struct AnimGraphBlendTree : public AnimNode { } }; +// +// BlendTreeSocketNode +// +struct BlendTreeSocketNode : public AnimNode {}; + +template <> +struct NodeDescriptor : public NodeDescriptorBase { + NodeDescriptor(BlendTreeSocketNode* node_) {} +}; #endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H diff --git a/src/AnimGraph/AnimGraphData.h b/src/AnimGraph/AnimGraphData.h index d9ad84e..6584893 100644 --- a/src/AnimGraph/AnimGraphData.h +++ b/src/AnimGraph/AnimGraphData.h @@ -265,6 +265,7 @@ struct AnimGraphConnection { Socket m_source_socket; AnimNode* m_target_node = nullptr; Socket m_target_socket; + bool m_crosses_hierarchy = false; }; diff --git a/src/AnimGraph/AnimGraphEditor.cc b/src/AnimGraph/AnimGraphEditor.cc index 982408c..d3b98f9 100644 --- a/src/AnimGraph/AnimGraphEditor.cc +++ b/src/AnimGraph/AnimGraphEditor.cc @@ -7,14 +7,14 @@ #include #include "3rdparty/imgui-node-editor/imgui_node_editor.h" -#include "AnimGraphBlendTreeResource.h" +#include "AnimGraphResource.h" #include "SkinnedMesh.h" #include "imgui.h" #include "imnodes.h" #include "misc/cpp/imgui_stdlib.h" -static AnimGraphBlendTreeResource sGraphGresource = - AnimGraphBlendTreeResource(); +static AnimGraphResource sGraphGresource = + AnimGraphResource(); static bool sGraphLoadedThisFrame = false; ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { @@ -59,13 +59,13 @@ void NodeSocketEditor(Socket& socket) { } void RemoveConnectionsForSocket( - AnimGraphBlendTreeResource& graph_resource, + AnimGraphResource& graph_resource, AnimNodeResource& node_resource, Socket& socket) { - std::vector::iterator iter = - graph_resource.m_connections.begin(); + std::vector::iterator iter = + 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 assert(false); diff --git a/src/AnimGraph/AnimGraphNodes.cc b/src/AnimGraph/AnimGraphNodes.cc index bc9bfb5..4828d0b 100644 --- a/src/AnimGraph/AnimGraphNodes.cc +++ b/src/AnimGraph/AnimGraphNodes.cc @@ -3,13 +3,13 @@ // #include "AnimGraphNodes.h" -#include "AnimGraphBlendTree.h" -#include "ozz/base/log.h" -#include "ozz/animation/runtime/blending_job.h" +#include "AnimGraphBlendTree.h" #include "ozz/animation/runtime/animation.h" +#include "ozz/animation/runtime/blending_job.h" #include "ozz/base/io/archive.h" #include "ozz/base/io/stream.h" +#include "ozz/base/log.h" AnimNode* AnimNodeFactory(const std::string& name) { AnimNode* result; @@ -24,7 +24,7 @@ AnimNode* AnimNodeFactory(const std::string& name) { } else if (name == "BlendTree") { result = new AnimGraphBlendTree; } else if (name == "BlendTreeSockets") { - result = new BlendTreeNode; + result = new BlendTreeSocketNode; } else if (name == "MathAddNode") { result = new MathAddNode; } else if (name == "MathFloatToVec3Node") { @@ -42,12 +42,39 @@ AnimNode* AnimNodeFactory(const std::string& name) { return nullptr; } +NodeDescriptorBase* AnimNodeDescriptorFactory( + const std::string& node_type_name, + AnimNode* node) { + if (node_type_name == "Blend2") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "SpeedScale") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "AnimSampler") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "LockTranslationNode") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "BlendTree") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "BlendTreeSockets") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "MathAddNode") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "MathFloatToVec3Node") { + return CreateNodeDescriptor(node); + } else if (node_type_name == "ConstScalarNode") { + return CreateNodeDescriptor(node); + } else { + std::cerr << "Invalid node type name " << node_type_name << "." + << std::endl; + } + return nullptr; +} void Blend2Node::Evaluate(AnimGraphContext& context) { - assert (i_input0 != nullptr); - assert (i_input1 != nullptr); - assert (i_blend_weight != nullptr); - assert (o_output != nullptr); + assert(i_input0 != nullptr); + assert(i_input1 != nullptr); + assert(i_blend_weight != nullptr); + assert(o_output != nullptr); // perform blend ozz::animation::BlendingJob::Layer layers[2]; @@ -71,12 +98,10 @@ void Blend2Node::Evaluate(AnimGraphContext& context) { // // AnimSamplerNode // -AnimSamplerNode::~AnimSamplerNode() noexcept { - m_animation = nullptr; -} +AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; } bool AnimSamplerNode::Init(AnimGraphContext& context) { - assert (m_animation == nullptr); + assert(m_animation == nullptr); assert(!m_filename.empty()); AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter; @@ -103,14 +128,14 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) { context.m_animation_map[m_filename] = m_animation; } - assert (context.m_skeleton != nullptr); + assert(context.m_skeleton != nullptr); m_sampling_context.Resize(context.m_skeleton->num_joints()); return true; } void AnimSamplerNode::Evaluate(AnimGraphContext& context) { - assert (o_output != nullptr); + assert(o_output != nullptr); ozz::animation::SamplingJob sampling_job; sampling_job.animation = m_animation; diff --git a/src/AnimGraph/AnimGraphNodes.h b/src/AnimGraph/AnimGraphNodes.h index d4b2d77..63ee36c 100644 --- a/src/AnimGraph/AnimGraphNodes.h +++ b/src/AnimGraph/AnimGraphNodes.h @@ -7,23 +7,13 @@ #include -#include "AnimNode.h" #include "AnimGraphData.h" +#include "AnimNode.h" #include "SyncTrack.h" #include "ozz/animation/runtime/sampling_job.h" struct AnimNode; -// -// BlendTreeNode -// -struct BlendTreeNode : public AnimNode {}; - -template <> -struct NodeDescriptor : public NodeDescriptorBase { - NodeDescriptor(BlendTreeNode* node_) {} -}; - // // Blend2Node // @@ -34,8 +24,9 @@ struct Blend2Node : public AnimNode { float* i_blend_weight = nullptr; bool m_sync_blend = false; - virtual void MarkActiveInputs() override { - for (const auto & input : m_inputs) { + void MarkActiveInputs( + const std::vector& input_connections) override { + for (const auto& input : input_connections) { AnimNode* input_node = input.m_source_node; if (input_node == nullptr) { continue; @@ -53,7 +44,7 @@ struct Blend2Node : public AnimNode { } } - virtual void Evaluate(AnimGraphContext& context) override; + void Evaluate(AnimGraphContext& context) override; }; template <> @@ -80,7 +71,6 @@ struct NodeDescriptor : public NodeDescriptorBase { } }; - // // SpeedScaleNode // @@ -96,8 +86,8 @@ struct SpeedScaleNode : public AnimNode { } void Evaluate(AnimGraphContext& context) override { - assert (i_input != nullptr); - assert (o_output != nullptr); + assert(i_input != nullptr); + assert(o_output != nullptr); o_output->m_local_matrices = i_input->m_local_matrices; }; @@ -116,7 +106,6 @@ struct NodeDescriptor : public NodeDescriptorBase { } }; - // // AnimSamplerNode // @@ -172,7 +161,6 @@ struct NodeDescriptor : public NodeDescriptorBase { } }; - // // ConstScalarNode // @@ -180,9 +168,7 @@ struct ConstScalarNode : public AnimNode { float* o_value = nullptr; float value = 0.f; - virtual void Evaluate(AnimGraphContext& context){ - *o_value = value; - }; + virtual void Evaluate(AnimGraphContext& context) { *o_value = value; }; }; template <> @@ -193,7 +179,6 @@ struct NodeDescriptor : public NodeDescriptorBase { } }; - // // MathAddNode // @@ -203,8 +188,8 @@ struct MathAddNode : public AnimNode { float* o_output = nullptr; void Evaluate(AnimGraphContext& context) override { - assert (i_input0 != nullptr); - assert (i_input1 != nullptr); + assert(i_input0 != nullptr); + assert(i_input1 != nullptr); *o_output = *i_input0 + *i_input1; } @@ -229,9 +214,9 @@ struct MathFloatToVec3Node : public AnimNode { Vec3* o_output = nullptr; void Evaluate(AnimGraphContext& context) override { - assert (i_input0 != nullptr); - assert (i_input1 != nullptr); - assert (i_input2 != nullptr); + assert(i_input0 != nullptr); + assert(i_input1 != nullptr); + assert(i_input2 != nullptr); o_output->v[0] = *i_input0; o_output->v[1] = *i_input1; @@ -249,35 +234,10 @@ struct NodeDescriptor : public NodeDescriptorBase { } }; - AnimNode* AnimNodeFactory(const std::string& name); -static inline NodeDescriptorBase* AnimNodeDescriptorFactory( +NodeDescriptorBase* AnimNodeDescriptorFactory( const std::string& node_type_name, - AnimNode* node) { - if (node_type_name == "Blend2") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "SpeedScale") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "AnimSampler") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "LockTranslationNode") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "BlendTree") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "BlendTreeSockets") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "MathAddNode") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "MathFloatToVec3Node") { - return CreateNodeDescriptor(node); - } else if (node_type_name == "ConstScalarNode") { - return CreateNodeDescriptor(node); - } else { - std::cerr << "Invalid node type name " << node_type_name << "." - << std::endl; - } - return nullptr; -} + AnimNode* node); #endif //ANIMTESTBED_ANIMGRAPHNODES_H diff --git a/src/AnimGraph/AnimGraphResource.cc b/src/AnimGraph/AnimGraphResource.cc index 4687086..c0651d7 100644 --- a/src/AnimGraph/AnimGraphResource.cc +++ b/src/AnimGraph/AnimGraphResource.cc @@ -8,15 +8,17 @@ #include #include "3rdparty/json/json.hpp" - #include "AnimGraphBlendTree.h" #include "AnimGraphNodes.h" using json = nlohmann::json; // forward declarations -static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource); -static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource); +static json sAnimGraphResourceBlendTreeToJson( + const AnimGraphResource& anim_graph_resource); +static bool sAnimGraphResourceBlendTreeFromJson( + const json& json_data, + AnimGraphResource* result_graph_resource); // // Socket <-> json @@ -143,7 +145,6 @@ json sAnimGraphNodeToJson( } if (node->m_node_type_name == "BlendTree") { - } for (const auto& socket : node->m_socket_accessor->m_inputs) { @@ -175,7 +176,6 @@ json sAnimGraphNodeToJson( AnimNodeResource* sAnimGraphNodeFromJson( const json& json_node, size_t node_index) { - std::string node_type = json_node["node_type"]; if (node_type == "BlendTree") { @@ -247,20 +247,23 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson( return connection; } -static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource) { +static json sAnimGraphResourceBlendTreeToJson( + const AnimGraphResource& anim_graph_resource) { json result; result["name"] = anim_graph_resource.m_name; result["type"] = "AnimNodeResource"; 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++) { const AnimNodeResource* node = blend_tree_resource.m_nodes[i]; if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = dynamic_cast(node); + const AnimGraphResource* graph_resource = + dynamic_cast(node); result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); } else { result["nodes"][i] = @@ -276,8 +279,7 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap // Graph inputs and outputs { - const AnimNodeResource* graph_output_node = - blend_tree_resource.m_nodes[0]; + const AnimNodeResource* graph_output_node = blend_tree_resource.m_nodes[0]; const std::vector graph_inputs = graph_output_node->m_socket_accessor->m_inputs; for (size_t i = 0; i < graph_inputs.size(); i++) { @@ -294,8 +296,11 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap return result; } -static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource) { - BlendTreeResource& blend_tree_resource = result_graph_resource->m_blend_tree_resource; +static bool sAnimGraphResourceBlendTreeFromJson( + 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_node_type_name = "BlendTree"; @@ -351,14 +356,14 @@ static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraph return true; } -bool BlendTreeResource::ConnectSockets ( +bool BlendTreeResource::ConnectSockets( const AnimNodeResource* source_node, const std::string& source_socket_name, const AnimNodeResource* target_node, const std::string& target_socket_name) { size_t source_node_index = GetNodeIndex(source_node); size_t target_node_index = GetNodeIndex(target_node); - + if (source_node_index >= m_nodes.size() || target_node_index >= m_nodes.size()) { std::cerr << "Cannot connect nodes: could not find nodes." << std::endl; @@ -369,23 +374,29 @@ bool BlendTreeResource::ConnectSockets ( Socket* target_socket; if (target_node->m_node_type_name == "BlendTree") { - const AnimGraphResource* target_graph_resource = dynamic_cast(target_node); - 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()); + const AnimGraphResource* target_graph_resource = + dynamic_cast(target_node); + 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 { - target_socket = - target_node->m_socket_accessor->GetInputSocket(target_socket_name.c_str()); + target_socket = target_node->m_socket_accessor->GetInputSocket( + target_socket_name.c_str()); } if (source_node->m_node_type_name == "BlendTree") { - const AnimGraphResource* source_graph_resource = dynamic_cast(source_node); - 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()); + const AnimGraphResource* source_graph_resource = + dynamic_cast(source_node); + 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 { - source_socket = - source_node->m_socket_accessor->GetOutputSocket(source_socket_name.c_str()); + source_socket = source_node->m_socket_accessor->GetOutputSocket( + source_socket_name.c_str()); } - + if (source_socket == nullptr || target_socket == nullptr) { std::cerr << "Cannot connect nodes: could not find sockets." << std::endl; return false; @@ -443,7 +454,8 @@ bool AnimGraphResource::SaveToFile(const char* filename) const { 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; } @@ -452,8 +464,7 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile( const char* filename) const { json result; - result = sAnimGraphResourceBlendTreeToJson( - *this); + result = sAnimGraphResourceBlendTreeToJson(*this); std::ofstream output_file; output_file.open(filename); @@ -466,14 +477,18 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile( void AnimGraphResource::CreateBlendTreeInstance( AnimGraphBlendTree& result) const { if (m_node_type_name != "BlendTree") { - std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" - << m_graph_type_name << "'." << std::endl; + std::cerr + << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" + << m_graph_type_name << "'." << std::endl; return; } + result.m_name = m_name; + CreateBlendTreeRuntimeNodeInstances(result); PrepareBlendTreeIOData(result); SetRuntimeNodeProperties(result); + CreateBlendTreeConnectionInstances(result); result.UpdateOrderedNodes(); result.ResetNodeStates(); @@ -485,14 +500,19 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); if (node_resource->m_node_type_name == "BlendTree") { - AnimGraphResource* embedded_blend_tree_resource = dynamic_cast(node_resource); + AnimGraphResource* embedded_blend_tree_resource = + dynamic_cast(node_resource); assert(embedded_blend_tree_resource != nullptr); - AnimGraphBlendTree* embedded_blend_tree = dynamic_cast(node); + AnimGraphBlendTree* embedded_blend_tree = + dynamic_cast(node); assert(embedded_blend_tree != nullptr); - embedded_blend_tree_resource->CreateBlendTreeInstance(*embedded_blend_tree); - 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; + embedded_blend_tree_resource->CreateBlendTreeInstance( + *embedded_blend_tree); + 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; @@ -574,16 +594,20 @@ void AnimGraphResource::PrepareBlendTreeIOData( instance.m_connection_data_storage = new char[connection_data_storage_size]; memset(instance.m_connection_data_storage, 0, connection_data_storage_size); } +} +void AnimGraphResource::CreateBlendTreeConnectionInstances( + AnimGraphBlendTree& instance) const { std::vector instance_node_descriptors( m_blend_tree_resource.m_nodes.size(), nullptr); for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { - instance_node_descriptors[i] = AnimNodeDescriptorFactory( - m_blend_tree_resource.m_nodes[i]->m_node_type_name, - instance.m_nodes[i]); + instance_node_descriptors[i] = AnimNodeDescriptorFactory( + m_blend_tree_resource.m_nodes[i]->m_node_type_name, + 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 = m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs; instance_node_descriptors[i]->m_outputs = @@ -611,6 +635,72 @@ void AnimGraphResource::PrepareBlendTreeIOData( connection.target_socket_name.c_str()); 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( + m_blend_tree_resource.m_nodes[connection.source_node_index]); + AnimGraphBlendTree* source_blend_tree = + dynamic_cast(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( + m_blend_tree_resource.m_nodes[connection.target_node_index]); + AnimGraphBlendTree* target_blend_tree = + dynamic_cast(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& 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_socket = *source_socket; 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++) { const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i]; - NodeDescriptorBase* node_instance_accessor = - AnimNodeDescriptorFactory(node_resource->m_node_type_name, result.m_nodes[i]); + NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory( + node_resource->m_node_type_name, + result.m_nodes[i]); std::vector& resource_properties = node_resource->m_socket_accessor->m_properties; @@ -741,7 +832,8 @@ bool AnimGraphResource::SaveStateMachineResourceToFile( return false; } -bool AnimGraphResource::LoadStateMachineResourceFromJson(nlohmann::json const& json_data) { +bool AnimGraphResource::LoadStateMachineResourceFromJson( + nlohmann::json const& json_data) { assert(false && "Not yet implemented"); return false; diff --git a/src/AnimGraph/AnimGraphResource.h b/src/AnimGraph/AnimGraphResource.h index e94fbb6..09eb6ff 100644 --- a/src/AnimGraph/AnimGraphResource.h +++ b/src/AnimGraph/AnimGraphResource.h @@ -100,6 +100,32 @@ struct BlendTreeResource { 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 { @@ -131,6 +157,7 @@ struct AnimGraphResource: AnimNodeResource { bool SaveBlendTreeResourceToFile(const char* filename) const; void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const; + void CreateBlendTreeConnectionInstances(AnimGraphBlendTree& instance) const; void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; bool SaveStateMachineResourceToFile(const char* filename) const; diff --git a/src/AnimGraph/AnimGraphStateMachine.cc b/src/AnimGraph/AnimGraphStateMachine.cc index eefb271..a910f46 100644 --- a/src/AnimGraph/AnimGraphStateMachine.cc +++ b/src/AnimGraph/AnimGraphStateMachine.cc @@ -8,11 +8,11 @@ bool AnimGraphStateMachine::Init(AnimGraphContext& context) { } -void AnimGraphStateMachine::MarkActiveInputs() { +void AnimGraphStateMachine::MarkActiveInputs(const std::vector& input_connections) { } -void AnimGraphStateMachine::CalcSyncTrack() { +void AnimGraphStateMachine::CalcSyncTrack(const std::vector& input_connections) { } diff --git a/src/AnimGraph/AnimGraphStateMachine.h b/src/AnimGraph/AnimGraphStateMachine.h index dbea8b1..c7273f0 100644 --- a/src/AnimGraph/AnimGraphStateMachine.h +++ b/src/AnimGraph/AnimGraphStateMachine.h @@ -25,8 +25,8 @@ struct AnimGraphStateMachine : public AnimNode { Transition* m_active_transition = nullptr; bool Init(AnimGraphContext& context); - void MarkActiveInputs() override; - void CalcSyncTrack() override; + void MarkActiveInputs(const std::vector& input_connections) override; + void CalcSyncTrack(const std::vector& input_connections) override; void UpdateTime(float time_last, float time_now) override; void Evaluate(AnimGraphContext& context) override; }; diff --git a/src/AnimGraph/AnimNode.h b/src/AnimGraph/AnimNode.h index 91c3e3e..d9ecea8 100644 --- a/src/AnimGraph/AnimNode.h +++ b/src/AnimGraph/AnimNode.h @@ -8,8 +8,8 @@ #include #include -#include "SyncTrack.h" #include "AnimGraphData.h" +#include "SyncTrack.h" struct AnimNode; @@ -25,29 +25,35 @@ enum class AnimNodeEvalState { struct AnimNode { std::string m_name; std::string m_node_type_name; + int m_tick_number = 0; AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; float m_time_now = 0.f; float m_time_last = 0.f; SyncTrack m_sync_track; - std::vector m_inputs; - 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() { - for (const auto & input : m_inputs) { + virtual void MarkActiveInputs( + const std::vector& input_connections) { + for (const auto& input : input_connections) { AnimNode* input_node = input.m_source_node; if (input_node != nullptr) { + input_node->m_tick_number = m_tick_number; input_node->m_state = AnimNodeEvalState::Activated; } } } - virtual void CalcSyncTrack() { - for (const auto & input : m_inputs) { + virtual void CalcSyncTrack( + const std::vector& input_connections) { + for (const auto& input : input_connections) { AnimNode* input_node = input.m_source_node; if (input_node != nullptr && input.m_source_socket.m_type == SocketType::SocketTypeAnimation diff --git a/tests/AnimGraphResourceTests.cc b/tests/AnimGraphResourceTests.cc index 6c4b2d4..63c9bb3 100644 --- a/tests/AnimGraphResourceTests.cc +++ b/tests/AnimGraphResourceTests.cc @@ -140,7 +140,9 @@ class EmbeddedBlendTreeGraphResource { BlendTreeResource* embedded_blend_tree_resource = nullptr; 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: EmbeddedBlendTreeGraphResource() { @@ -163,10 +165,11 @@ class EmbeddedBlendTreeGraphResource { // Parent AnimSampler parent_blend_tree_resource->m_nodes.push_back( AnimNodeResourceFactory("AnimSampler")); + walk_node_index = parent_blend_tree_resource->m_nodes.size() - 1; - walk_node = parent_blend_tree_resource->m_nodes.back(); - walk_node->m_name = "WalkAnim"; - walk_node->m_socket_accessor->SetPropertyValue( + walk_node_resource = parent_blend_tree_resource->m_nodes[walk_node_index]; + walk_node_resource->m_name = "WalkAnim"; + walk_node_resource->m_socket_accessor->SetPropertyValue( "Filename", std::string("media/Walking-loop.ozz")); @@ -175,7 +178,7 @@ class EmbeddedBlendTreeGraphResource { // parent_blend_tree_resource->m_nodes.push_back( AnimNodeResourceFactory("BlendTree")); - size_t embedded_blend_tree_node_index = + embedded_blend_tree_node_index = parent_blend_tree_resource->m_nodes.size() - 1; embedded_graph = dynamic_cast( parent_blend_tree_resource->m_nodes.back()); @@ -201,9 +204,13 @@ class EmbeddedBlendTreeGraphResource { // Embedded: SpeedScale node embedded_blend_tree_resource->m_nodes.push_back( AnimNodeResourceFactory("SpeedScale")); + embedded_speed_scale_index = + embedded_blend_tree_resource->m_nodes.size() - 1; AnimNodeResource* embedded_speed_scale_resource = - embedded_blend_tree_resource->m_nodes.back(); - embedded_speed_scale_resource->m_socket_accessor->SetInputValue("SpeedScale", 0.1f); + embedded_blend_tree_resource->m_nodes[embedded_speed_scale_index]; + embedded_speed_scale_resource->m_socket_accessor->SetInputValue( + "SpeedScale", + 0.1f); // Embedded: setup connections embedded_blend_tree_resource->ConnectSockets( @@ -219,7 +226,7 @@ class EmbeddedBlendTreeGraphResource { // Parent: setup connections REQUIRE(parent_blend_tree_resource->ConnectSockets( - walk_node, + walk_node_resource, "Output", embedded_graph, "AnimInput")); @@ -346,8 +353,8 @@ TEST_CASE_METHOD( AnimSamplerNode* anim_sampler_walk = dynamic_cast(anim_graph_blend_tree.m_nodes[2]); - BlendTreeNode* graph_output_node = - dynamic_cast(anim_graph_blend_tree.m_nodes[0]); + BlendTreeSocketNode* graph_output_node = + dynamic_cast(anim_graph_blend_tree.m_nodes[0]); // check node input dependencies size_t anim_sampler_index = @@ -937,7 +944,8 @@ TEST_CASE_METHOD( parent_blend_tree_resource->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++) { - 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 = parent_blend_tree_resource_loaded.m_nodes[i]; @@ -949,7 +957,8 @@ TEST_CASE_METHOD( CHECK( parent_blend_tree_resource->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 = parent_blend_tree_resource->m_connections[i]; const BlendTreeConnectionResource& parent_connection_loaded = @@ -1039,22 +1048,61 @@ TEST_CASE_METHOD( graph_context.m_skeleton = &skeleton; blend_tree.Init(graph_context); - // Marking of active inputs is not properly working as we do not properly - // populate AnimNode::m_inputs. - // - // Here comes an iffy problem: How to mark nodes in a parent blend tree - // as active? - // - // - Simplest would be if the AnimNodes don't distinguish between nodes within - // the tree they are contained or in a possible parent (or nested) tree. - // - But then how to propagate active connections across multiple layers? - // - Therefore probably better to have a blend tree store which sockets are - // active and use that within the graph. - // - But then: AnimGraphConnection already stores raw AnimNode pointers of the - // connected nodes. Still... populating them accross layers could be messy. - blend_tree.MarkActiveInputs(); - blend_tree.UpdateTime(0.f, 0.1f); + + const AnimSamplerNode* walk_node = + dynamic_cast(blend_tree.m_nodes[walk_node_index]); + const AnimGraphBlendTree* embedded_blend_tree_node = + dynamic_cast( + blend_tree.m_nodes[embedded_blend_tree_node_index]); + const SpeedScaleNode* speed_scale_node = dynamic_cast( + embedded_blend_tree_node->m_nodes[walk_node_index]); + + blend_tree.StartUpdateTick(); + blend_tree.MarkActiveInputs(std::vector()); + REQUIRE(embedded_blend_tree_node->m_state == AnimNodeEvalState::Activated); + REQUIRE(speed_scale_node->m_state == AnimNodeEvalState::Activated); + REQUIRE(walk_node->m_state == AnimNodeEvalState::Activated); + + 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); + 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()); + 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(); } \ No newline at end of file