diff --git a/src/AnimGraphResource.cc b/src/AnimGraphResource.cc index fecf245..b7d56a9 100644 --- a/src/AnimGraphResource.cc +++ b/src/AnimGraphResource.cc @@ -355,10 +355,11 @@ void AnimGraph::UpdateOrderedNodes() { std::vector node_index_stack; node_index_stack.push_back(0); - m_ordered_nodes.clear(); + m_eval_ordered_nodes.clear(); while (node_index_stack.size() > 0) { - std::vector& node_inputs = m_node_inputs[node_index_stack.back()]; + std::vector& node_inputs = + m_node_inputs[node_index_stack.back()]; node_index_stack.pop_back(); for (size_t i = 0, n = node_inputs.size(); i < n; i++) { @@ -369,8 +370,8 @@ void AnimGraph::UpdateOrderedNodes() { int input_node_index = getAnimNodeIndex(input_node); bool is_node_processed = false; - for (size_t j = 0, m = m_ordered_nodes.size(); j < m; j++) { - if (m_ordered_nodes[j] == input_node) { + for (size_t j = 0, m = m_eval_ordered_nodes.size(); j < m; j++) { + if (m_eval_ordered_nodes[j] == input_node) { is_node_processed = true; break; } @@ -380,31 +381,61 @@ void AnimGraph::UpdateOrderedNodes() { continue; } - m_ordered_nodes.push_back(input_node); + m_eval_ordered_nodes.push_back(input_node); node_index_stack.push_back(input_node_index); } } } void AnimGraph::MarkActiveNodes() { - m_frame_counter++; + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; + } const std::vector graph_output_inputs = m_node_inputs[0]; for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { - AnimNode* node = m_ordered_nodes[i]; + AnimNode* node = m_eval_ordered_nodes[i]; if (node != nullptr) { - node->m_frame_counter = m_frame_counter; + node->m_state = AnimNodeEvalState::Activated; } } - for (size_t i = 0, n = m_ordered_nodes.size(); i < n; i++) { - AnimNode* node = m_ordered_nodes[i]; + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; if (CheckIsNodeActive(node)) { int node_index = getAnimNodeIndex(node); - node->UpdateActiveInputFrameCounters(m_node_inputs[node_index]); - } else { - // Avoid active frame counter overrun. - node->m_frame_counter = m_frame_counter - 1; + node->MarkActiveInputs(m_node_inputs[node_index]); + } + } +} + +void AnimGraph::UpdateTime(float dt) { + const std::vector graph_output_inputs = m_node_inputs[0]; + for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (node != nullptr) { + node->UpdateTime(node->m_time_now, node->m_time_now + dt); + } + } + + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (node->m_state != AnimNodeEvalState::TimeUpdated) { + continue; + } + + int node_index = getAnimNodeIndex(node); + const std::vector node_inputs = m_node_inputs[node_index]; + float node_time_now = node->m_time_now; + float node_time_last = node->m_time_last; + + for (size_t i = 0, n = node_inputs.size(); i < n; i++) { + AnimNode* input_node = node_inputs[i].m_node; + if (input_node != nullptr + && input_node->m_state == AnimNodeEvalState::Activated + && node_inputs[i].m_type == SocketType::SocketTypeAnimation) { + input_node->UpdateTime(node_time_last, node_time_now); + } } } } @@ -427,4 +458,4 @@ void* AnimGraph::GetInput(const std::string& name) const { return *(socket->m_value.ptr_ptr); } -} \ No newline at end of file +} // namespace AnimGraphCode \ No newline at end of file diff --git a/src/AnimGraphResource.h b/src/AnimGraphResource.h index 8280b9c..6cdf6de 100644 --- a/src/AnimGraphResource.h +++ b/src/AnimGraphResource.h @@ -84,26 +84,42 @@ struct AnimNodeResource { struct AnimNodeInput { AnimNode* m_node; + SocketType m_type = SocketType::SocketTypeUndefined; std::string m_input_name; }; +enum class AnimNodeEvalState { + Undefined, + Deactivated, + Activated, + SyncTrackUpdated, + TimeUpdated, + Evaluated +}; + struct AnimNode { virtual ~AnimNode(){}; - virtual void UpdateActiveInputFrameCounters (const std::vector& inputs) { + virtual void MarkActiveInputs(const std::vector& inputs) { for (size_t i = 0, n = inputs.size(); i < n; i++) { - if (inputs[i].m_node != nullptr) { - inputs[i].m_node->m_frame_counter = m_frame_counter; + AnimNode* input_node = inputs[i].m_node; + if (input_node != nullptr) { + input_node->m_state = AnimNodeEvalState::Activated; } } } + virtual void UpdateTime(float time_last, float time_now) { + m_time_last = time_last; + m_time_now = time_now; + m_state = AnimNodeEvalState::TimeUpdated; + } + std::string m_name; std::string m_node_type_name; - bool m_is_time_synced; - float m_time_now; - float m_time_last; - int m_frame_counter = 0; + float m_time_now = 0.f; + float m_time_last = 0.f; + AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; SyncTrack m_sync_track; }; @@ -321,7 +337,7 @@ struct Blend2Node : public AnimNode { bool m_sync_blend = false; - virtual void UpdateActiveInputFrameCounters (const std::vector& inputs) override { + virtual void MarkActiveInputs(const std::vector& inputs) override { for (size_t i = 0, n = inputs.size(); i < n; i++) { AnimNode* input_node = inputs[i].m_node; if (input_node == nullptr) { @@ -329,12 +345,33 @@ struct Blend2Node : public AnimNode { } if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) { - input_node->m_frame_counter = m_frame_counter; + input_node->m_state = AnimNodeEvalState::Activated; continue; } if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) { - input_node->m_frame_counter = m_frame_counter; + input_node->m_state = AnimNodeEvalState::Activated; + continue; + } + } + } + + virtual void UpdateTime(float dt, std::vector& inputs) { + if (!m_sync_blend) { + m_time_now = m_time_now + dt; + } + + for (size_t i = 0, n = inputs.size(); i < n; i++) { + AnimNode* input_node = inputs[i].m_node; + if (input_node == nullptr) { + continue; + } + + if (input_node->m_state != AnimNodeEvalState::Deactivated) { + if (!m_sync_blend) { + input_node->m_time_now = m_time_now; + } + input_node->m_state = AnimNodeEvalState::TimeUpdated; continue; } } @@ -373,6 +410,12 @@ struct SpeedScaleNode : public AnimNode { AnimData m_input; AnimData* m_output = nullptr; float m_speed_scale = 0.f; + + virtual void UpdateTime(float time_last, float time_now) { + m_time_last = time_last; + m_time_now = time_last + (time_now - time_last) * m_speed_scale; + m_state = AnimNodeEvalState::TimeUpdated; + } }; template <> @@ -553,16 +596,22 @@ struct AnimGraph { void UpdateOrderedNodes(); void MarkActiveNodes(); bool CheckIsNodeActive(AnimNode* node) { - return node->m_frame_counter == m_frame_counter; + return node->m_state != AnimNodeEvalState::Deactivated; } void UpdateTime(float dt); void Evaluate(); + void Reset() { + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + m_nodes[i]->m_time_now = 0.f; + m_nodes[i]->m_time_last = 0.f; + m_nodes[i]->m_state = AnimNodeEvalState::Undefined; + } + } - int m_frame_counter = 0; AnimData m_local_transforms; std::vector m_nodes; - std::vector m_ordered_nodes; + std::vector m_eval_ordered_nodes; std::vector > m_node_inputs; NodeSocketAccessorBase* m_socket_accessor; char* m_input_buffer = nullptr; @@ -574,9 +623,9 @@ struct AnimGraph { void* GetOutput(const std::string& name) const; void* GetInput(const std::string& name) const; - int getAnimNodeOrderIndex(const AnimNode* node) { - for (size_t i = 0, n = m_ordered_nodes.size(); i < n; i++) { - if (m_ordered_nodes[i] == node) { + int getNodeEvalOrderIndex(const AnimNode* node) { + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + if (m_eval_ordered_nodes[i] == node) { return i; } } @@ -641,6 +690,7 @@ struct AnimGraph { node_resource.m_socket_accessor->m_inputs[j]; AnimNodeInput input; input.m_node = nullptr; + input.m_type = input_socket.m_type; input.m_input_name = input_socket.m_name; node_inputs.push_back(input); @@ -789,6 +839,7 @@ struct AnimGraph { } result.UpdateOrderedNodes(); + result.Reset(); return result; } diff --git a/tests/AnimGraphResourceTests.cc b/tests/AnimGraphResourceTests.cc index 570d9f9..cdb8df6 100644 --- a/tests/AnimGraphResourceTests.cc +++ b/tests/AnimGraphResourceTests.cc @@ -247,6 +247,9 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") { graph_input_node.m_socket_accessor->RegisterOutput( "GraphFloatInput", nullptr); + graph_input_node.m_socket_accessor->RegisterOutput( + "SpeedScaleInput", + nullptr); graph_input_node.m_socket_accessor->RegisterOutput( "GraphAnimInput0", nullptr); @@ -372,6 +375,12 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") { blend2_node_resource, "Weight")); + REQUIRE(graph_resource.connectSockets( + graph_resource.getGraphInputNode(), + "SpeedScaleInput", + speed_scale_node_resource, + "SpeedScale")); + REQUIRE(graph_resource.connectSockets( graph_resource.getGraphInputNode(), "GraphAnimInput0", @@ -440,14 +449,14 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") { // check ordering // REQUIRE( - anim_graph.getAnimNodeOrderIndex(blend2_node) - < anim_graph.getAnimNodeOrderIndex(sampler_node)); + anim_graph.getNodeEvalOrderIndex(blend2_node) + < anim_graph.getNodeEvalOrderIndex(sampler_node)); REQUIRE( - anim_graph.getAnimNodeOrderIndex(blend2_node) - < anim_graph.getAnimNodeOrderIndex(speed_scale_node)); + anim_graph.getNodeEvalOrderIndex(blend2_node) + < anim_graph.getNodeEvalOrderIndex(speed_scale_node)); REQUIRE( - anim_graph.getAnimNodeOrderIndex(speed_scale_node) - < anim_graph.getAnimNodeOrderIndex(sampler_node)); + anim_graph.getNodeEvalOrderIndex(speed_scale_node) + < anim_graph.getNodeEvalOrderIndex(sampler_node)); } WHEN("Instantiating graph") { @@ -491,6 +500,36 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") { REQUIRE(anim_graph.CheckIsNodeActive(sampler_node) == true); } } + + WHEN("Updating time with dt = 0.3f and speed scale = 1.0f") { + float* speed_scale_input = + reinterpret_cast(anim_graph.GetInput("SpeedScaleInput")); + + *blend_weight_input = 0.1; + *speed_scale_input = 1.0f; + + anim_graph.MarkActiveNodes(); + anim_graph.UpdateTime(0.3f); + + THEN ("Anim sampler node time now must be 0.3f") { + REQUIRE(sampler_node->m_time_now == Approx(0.3f)); + } + } + + WHEN("Updating time with dt = 0.3f and speed scale = 1.3f") { + float* speed_scale_input = + reinterpret_cast(anim_graph.GetInput("SpeedScaleInput")); + + *blend_weight_input = 0.1; + *speed_scale_input = 1.3f; + + anim_graph.MarkActiveNodes(); + anim_graph.UpdateTime(0.3f); + + THEN ("Anim sampler node time now must be 0.39f") { + REQUIRE(sampler_node->m_time_now == Approx(0.39f)); + } + } } } } \ No newline at end of file