// // Created by martin on 17.03.24. // #ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H #define ANIMTESTBED_ANIMGRAPHBLENDTREE_H #include #include "AnimNode.h" // // AnimGraph (Runtime) // struct AnimGraphBlendTree : public AnimNode { AnimData m_local_transforms; std::vector m_nodes; std::vector m_eval_ordered_nodes; std::vector > m_node_input_connections; std::vector > m_node_output_connections; [[nodiscard]] const std::vector& GetGraphInputConnections() const { return m_node_output_connections[1]; } [[nodiscard]] const std::vector& GetGraphOutputConnections() const { return m_node_input_connections[0]; } std::vector m_animdata_blocks; NodeDescriptorBase* m_node_descriptor = nullptr; char* m_input_buffer = nullptr; char* m_output_buffer = nullptr; char* m_connection_data_storage = nullptr; char* m_const_node_inputs = nullptr; std::vector& GetGraphOutputs() { return m_node_descriptor->m_inputs; } std::vector& GetGraphInputs() { return m_node_descriptor->m_outputs; } AnimDataAllocator m_anim_data_allocator; ~AnimGraphBlendTree() override { dealloc(); } void StartUpdateTick() { m_tick_number++; } // AnimNode overrides bool Init(AnimGraphContext& context) override; /// Determines which nodes in the BlendTree are active. /// /// Note: this does not use the provided input_connections, instead it marks /// all nodes directly connected to the BlendTree outputs as active and then /// propagates the node state throught the tree. For this each active node's /// AnimNode::MarkActiveInputs() gets called. 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(); } m_animdata_blocks.clear(); m_node_input_connections.clear(); m_node_output_connections.clear(); delete[] m_input_buffer; delete[] m_output_buffer; delete[] m_connection_data_storage; delete[] m_const_node_inputs; for (int i = 0; i < m_nodes.size(); i++) { delete m_nodes[i]; } m_nodes.clear(); delete m_node_descriptor; } void UpdateOrderedNodes(); void UpdateOrderedNodesRecursive(int node_index); bool CheckIsNodeActive(AnimNode* node) { return node->m_tick_number == m_tick_number; } void ResetNodeStates() { 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; } } /** Sets the address that is used for the specified AnimGraph input Socket. * * @tparam T Type of the Socket. * @param name Name of the Socket. * @param value_ptr Pointer where the input is fetched during evaluation. */ template void SetInput(const char* name, T* value_ptr) { m_node_descriptor->SetOutput(name, value_ptr); std::vector connected_node_indices; for (int i = 0; i < m_node_output_connections[1].size(); i++) { const AnimGraphConnection& graph_input_connection = m_node_output_connections[1][i]; if (graph_input_connection.m_source_socket_name == name) { *graph_input_connection.m_socket.m_reference.ptr_ptr = value_ptr; } } } /** Sets the address that is used for the specified AnimGraph output Socket. * * We update the pointer of the outputting node. We also have to ensure that * all usages of that output use the same pointer. * * @tparam T Type of the Socket. * @param name Name of the Socket. * @param value_ptr Pointer where the graph output output is written to at the end of evaluation. */ template void SetOutput(const char* name, T* value_ptr) { const auto& graph_output_connection = std::find_if( GetGraphOutputConnections().begin(), GetGraphOutputConnections().end(), [&name](const AnimGraphConnection& connection) { return connection.m_target_socket_name == name; }); if (graph_output_connection == GetGraphOutputConnections().end()) { std::cerr << "Error: could not find graph connection to output socket " << name << "." << std::endl; return; } 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; return; } size_t output_node_index = GetAnimNodeIndex(graph_output_connection->m_source_node); const std::vector& node_output_connections = m_node_output_connections[output_node_index]; for (const AnimGraphConnection& connection : node_output_connections) { if (connection.m_source_socket_name == graph_output_connection->m_source_socket_name) { *connection.m_socket.m_reference.ptr_ptr = value_ptr; } } *graph_output_connection->m_socket.m_reference.ptr_ptr = value_ptr; } /** Returns the address that is used for the specified AnimGraph output Socket. * * This function is needed for connections that directly connect an AnimGraph * input Socket to an output Socket of the same AnimGraph. * * @tparam T Type of the Socket. * @param name Name of the Socket. * @return Address that is used for the specified AnimGraph output Socket. */ template T* GetOutputPtr(const char* name) { for (int i = 0; i < m_node_input_connections[0].size(); i++) { const AnimGraphConnection& graph_output_connection = m_node_input_connections[0][i]; if (graph_output_connection.m_target_socket_name == name) { return static_cast( *graph_output_connection.m_socket.m_reference.ptr_ptr); } } return nullptr; } int GetAnimNodeIndex(const AnimNode* node) const { for (int i = 0; i < m_nodes.size(); i++) { if (m_nodes[i] == node) { return i; } } return -1; } }; // // BlendTreeSocketNode // struct BlendTreeSocketNode : public AnimNode {}; template <> struct NodeDescriptor : public NodeDescriptorBase { explicit NodeDescriptor(BlendTreeSocketNode* node_) {} }; #endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H