From 95f973595b4a3eecc3ae22d63de6920e7401aaa0 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Sun, 16 Mar 2025 00:14:40 +0100 Subject: [PATCH] WIP Refactoring of BlendTreeResources being a subclass of AnimGraphResource. The latter is used to abstract saving/loading for both BlendTrees and StateMachines. --- src/AnimGraph/AnimGraphResource.cc | 250 +++++++++---------------- src/AnimGraph/AnimGraphResource.h | 236 ++++++++++++----------- src/AnimGraphEditor/AnimGraphEditor.cc | 142 +++++++------- src/AnimGraphEditor/AnimGraphEditor.h | 2 +- src/main.cc | 2 +- 5 files changed, 289 insertions(+), 343 deletions(-) diff --git a/src/AnimGraph/AnimGraphResource.cc b/src/AnimGraph/AnimGraphResource.cc index 0df9a5a..13e6a42 100644 --- a/src/AnimGraph/AnimGraphResource.cc +++ b/src/AnimGraph/AnimGraphResource.cc @@ -16,11 +16,11 @@ using json = nlohmann::json; // forward declarations -static json sAnimGraphResourceBlendTreeToJson( - const AnimGraphResource& anim_graph_resource); -static bool sAnimGraphResourceBlendTreeFromJson( +static json sBlendTreeToJson(const BlendTreeResource& anim_graph_resource); + +static bool sBlendTreeFromJson( const json& json_data, - AnimGraphResource* result_graph_resource); + BlendTreeResource* result_graph_resource); // // Socket <-> json @@ -134,7 +134,7 @@ Socket sJsonToSocket(const json& json_data) { // json sAnimGraphNodeToJson( const AnimNodeResource* node, - size_t node_index, + const size_t node_index, const std::vector& connections) { json result; @@ -181,9 +181,9 @@ AnimNodeResource* sAnimGraphNodeFromJson( std::string node_type = json_node["node_type"]; if (node_type == "BlendTree") { - AnimGraphResource* result = - dynamic_cast(AnimNodeResourceFactory("BlendTree")); - sAnimGraphResourceBlendTreeFromJson(json_node, result); + BlendTreeResource* result = + dynamic_cast(AnimNodeResourceFactory("BlendTree")); + sBlendTreeFromJson(json_node, result); return result; } @@ -244,26 +244,22 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson( return connection; } -static json sAnimGraphResourceBlendTreeToJson( - const AnimGraphResource& anim_graph_resource) { +static json sBlendTreeToJson(const BlendTreeResource& blend_tree_resource) { json result; - result["name"] = anim_graph_resource.m_name; + result["name"] = blend_tree_resource.m_name; result["type"] = "AnimNodeResource"; result["node_type"] = "BlendTree"; - result["position"][0] = anim_graph_resource.m_position[0]; - result["position"][1] = anim_graph_resource.m_position[1]; - - const BlendTreeResource& blend_tree_resource = - anim_graph_resource.m_blend_tree_resource; + result["position"][0] = blend_tree_resource.m_position[0]; + result["position"][1] = blend_tree_resource.m_position[1]; for (size_t i = 0; i < blend_tree_resource.GetNumNodes(); i++) { const AnimNodeResource* node = blend_tree_resource.GetNode(i); if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); + result["nodes"][i] = sBlendTreeToJson(*blend_tree_resource); } else { result["nodes"][i] = sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections()); @@ -295,20 +291,16 @@ static json sAnimGraphResourceBlendTreeToJson( return result; } -static bool sAnimGraphResourceBlendTreeFromJson( +static bool sBlendTreeFromJson( 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"; - result_graph_resource->m_name = json_data["name"]; - result_graph_resource->m_position[0] = json_data["position"][0]; - result_graph_resource->m_position[1] = json_data["position"][1]; + BlendTreeResource* result_blend_tree_resource) { + result_blend_tree_resource->m_node_type_name = "BlendTree"; + result_blend_tree_resource->m_name = json_data["name"]; + result_blend_tree_resource->m_position[0] = json_data["position"][0]; + result_blend_tree_resource->m_position[1] = json_data["position"][1]; // Clear all nodes as we overwrite them here anyway. - blend_tree_resource.ClearAllNodes(); + result_blend_tree_resource->ClearAllNodes(); // Load nodes for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { @@ -321,14 +313,14 @@ static bool sAnimGraphResourceBlendTreeFromJson( } AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i); - blend_tree_resource.AddNode(node); + result_blend_tree_resource->AddNode(node); } // Graph outputs if (json_data["nodes"][0].contains("inputs")) { const json& graph_outputs = json_data["nodes"][0]["inputs"]; for (const auto& graph_output : graph_outputs) { - result_graph_resource->RegisterBlendTreeOutputSocket( + result_blend_tree_resource->RegisterBlendTreeOutputSocket( sJsonToSocket(graph_output)); } } @@ -337,7 +329,7 @@ static bool sAnimGraphResourceBlendTreeFromJson( if (json_data["nodes"][1].contains("outputs")) { const json& graph_inputs = json_data["nodes"][1]["outputs"]; for (const auto& graph_input : graph_inputs) { - result_graph_resource->RegisterBlendTreeInputSocket( + result_blend_tree_resource->RegisterBlendTreeInputSocket( sJsonToSocket(graph_input)); } } @@ -356,10 +348,10 @@ static bool sAnimGraphResourceBlendTreeFromJson( BlendTreeConnectionResource connection = sAnimGraphConnectionFromJson(json_connection); - blend_tree_resource.ConnectSockets( - blend_tree_resource.GetNode(connection.source_node_index), + result_blend_tree_resource->ConnectSockets( + result_blend_tree_resource->GetNode(connection.source_node_index), connection.source_socket_name, - blend_tree_resource.GetNode(connection.target_node_index), + result_blend_tree_resource->GetNode(connection.target_node_index), connection.target_socket_name); } } @@ -563,12 +555,10 @@ Socket* BlendTreeResource::GetNodeOutputSocket( } if (output_socket == nullptr && node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); output_socket = - blend_tree_resource.GetGraphOutputSocket(output_socket_name.c_str()); + blend_tree_resource->GetGraphOutputSocket(output_socket_name.c_str()); } return output_socket; @@ -582,12 +572,10 @@ const Socket* BlendTreeResource::GetNodeOutputSocketByIndex( if (node->m_virtual_socket_accessor) { output_sockets = &node->m_virtual_socket_accessor->m_outputs; } else if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); - output_sockets = &blend_tree_resource.GetGraphOutputNode() + output_sockets = &blend_tree_resource->GetGraphOutputNode() ->m_virtual_socket_accessor->m_outputs; } @@ -610,12 +598,10 @@ Socket* BlendTreeResource::GetNodeInputSocket( } if (input_socket == nullptr && node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); input_socket = - blend_tree_resource.GetGraphInputSocket(input_socket_name.c_str()); + blend_tree_resource->GetGraphInputSocket(input_socket_name.c_str()); } return input_socket; @@ -629,12 +615,10 @@ const Socket* BlendTreeResource::GetNodeInputSocketByIndex( if (node->m_virtual_socket_accessor) { output_sockets = &node->m_virtual_socket_accessor->m_inputs; } else if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); - output_sockets = &blend_tree_resource.GetGraphOutputNode() + output_sockets = &blend_tree_resource->GetGraphOutputNode() ->m_virtual_socket_accessor->m_outputs; } @@ -653,11 +637,10 @@ std::vector BlendTreeResource::GetNodeOutputSockets( } if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; - return blend_tree_resource.GetGraphOutputNode() + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); + + return blend_tree_resource->GetGraphOutputNode() ->m_virtual_socket_accessor->m_inputs; } @@ -671,11 +654,10 @@ std::vector BlendTreeResource::GetNodeInputSockets( } if (node->m_node_type_name == "BlendTree") { - const AnimGraphResource* graph_resource = - dynamic_cast(node); - const BlendTreeResource& blend_tree_resource = - graph_resource->m_blend_tree_resource; - return blend_tree_resource.GetGraphInputNode() + const BlendTreeResource* blend_tree_resource = + dynamic_cast(node); + + return blend_tree_resource->GetGraphInputNode() ->m_virtual_socket_accessor->m_outputs; } @@ -824,21 +806,6 @@ void BlendTreeResource::UpdateNodeSubtrees() { } } -AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) { - if (graph_type == AnimGraphType::GraphTypeBlendTree) { - m_graph_type_name = "BlendTree"; - m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree"); - - m_blend_tree_resource.InitGraphConnectors(); - RegisterBlendTreeOutputSocket( - AnimGraphResource::DefaultAnimOutput); - } else { - std::cerr - << "Warning: construction of state machine graphs not yet implemented!" - << std::endl; - } -} - AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) { std::ifstream input_file; input_file.open(filename); @@ -861,11 +828,11 @@ AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) { return nullptr; } - AnimGraphResource* result = nullptr; + BlendTreeResource* result = nullptr; if (json_data["node_type"] == "BlendTree") { result = - dynamic_cast(AnimNodeResourceFactory("BlendTree")); - sAnimGraphResourceBlendTreeFromJson(json_data, result); + dynamic_cast(AnimNodeResourceFactory("BlendTree")); + sBlendTreeFromJson(json_data, result); } else if (json_data["node_type"] == "StateMachine") { sAnimGraphResourceStateMachineFromJson(json_data, result); } else { @@ -877,24 +844,10 @@ AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) { return result; } -bool AnimGraphResource::SaveToFile(const char* filename) const { - if (m_graph_type_name == "BlendTree") { - return SaveBlendTreeResourceToFile(filename); - } else if (m_graph_type_name == "StateMachine") { - return SaveStateMachineResourceToFile(filename); - } - - std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "." - << std::endl; - - return false; -} - -bool AnimGraphResource::SaveBlendTreeResourceToFile( - const char* filename) const { +bool BlendTreeResource::SaveToFile(const char* filename) const { json result; - result = sAnimGraphResourceBlendTreeToJson(*this); + result = sBlendTreeToJson(*this); std::ofstream output_file; output_file.open(filename); @@ -904,12 +857,12 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile( return true; } -void AnimGraphResource::CreateBlendTreeInstance( +void BlendTreeResource::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; + << m_node_type_name << "'." << std::endl; return; } @@ -926,15 +879,14 @@ void AnimGraphResource::CreateBlendTreeInstance( result.ResetNodeStates(); } -void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( +void BlendTreeResource::CreateBlendTreeRuntimeNodeInstances( AnimGraphBlendTree& result) const { - for (const AnimNodeResource* node_resource : - m_blend_tree_resource.GetNodes()) { + for (const AnimNodeResource* node_resource : GetNodes()) { AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); if (node_resource->m_node_type_name == "BlendTree") { - const AnimGraphResource* embedded_blend_tree_resource = - dynamic_cast(node_resource); + const BlendTreeResource* embedded_blend_tree_resource = + dynamic_cast(node_resource); assert(embedded_blend_tree_resource != nullptr); AnimGraphBlendTree* embedded_blend_tree = dynamic_cast(node); @@ -958,18 +910,16 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( } } -void AnimGraphResource::PrepareBlendTreeIOData( +void BlendTreeResource::PrepareBlendTreeIOData( AnimGraphBlendTree& instance, NodeSocketDataOffsetMap& node_offset_map) const { instance.m_node_descriptor = AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]); instance.m_node_descriptor->m_outputs = - m_blend_tree_resource.GetGraphInputNode() - ->m_virtual_socket_accessor->m_outputs; + GetGraphInputNode()->m_virtual_socket_accessor->m_outputs; instance.m_node_descriptor->m_inputs = - m_blend_tree_resource.GetGraphOutputNode() - ->m_virtual_socket_accessor->m_inputs; + GetGraphOutputNode()->m_virtual_socket_accessor->m_inputs; // // graph inputs @@ -1019,15 +969,13 @@ void AnimGraphResource::PrepareBlendTreeIOData( // connecton data storage // size_t connection_data_storage_size = 0; - for (const BlendTreeConnectionResource& connection : - m_blend_tree_resource.GetConnections()) { - const AnimNodeResource* source_node = - m_blend_tree_resource.GetNode(connection.source_node_index); + for (const BlendTreeConnectionResource& connection : GetConnections()) { + const AnimNodeResource* source_node = GetNode(connection.source_node_index); Socket* source_socket = source_node->m_virtual_socket_accessor->GetOutputSocket( connection.source_socket_name.c_str()); - NodeSocketPair source_socket_pair{source_node, source_socket->m_name}; + NodeSocketNamePair source_socket_pair{source_node, source_socket->m_name}; if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) { node_offset_map.insert( {source_socket_pair, connection_data_storage_size}); @@ -1042,24 +990,22 @@ void AnimGraphResource::PrepareBlendTreeIOData( } } -void AnimGraphResource::CreateBlendTreeConnectionInstances( +void BlendTreeResource::CreateBlendTreeConnectionInstances( AnimGraphBlendTree& instance, NodeSocketDataOffsetMap& node_offset_map) const { std::vector instance_node_descriptors( - m_blend_tree_resource.GetNumNodes(), + GetNumNodes(), nullptr); - for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) { + for (int i = 0; i < GetNumNodes(); i++) { instance_node_descriptors[i] = AnimNodeDescriptorFactory( - m_blend_tree_resource.GetNode(i)->m_node_type_name, + GetNode(i)->m_node_type_name, instance.m_nodes[i]); - if (i > 1 - && m_blend_tree_resource.GetNode(i)->m_node_type_name == "BlendTree") { + if (i > 1 && GetNode(i)->m_node_type_name == "BlendTree") { instance_node_descriptors[i]->m_inputs = - m_blend_tree_resource.GetNode(i)->m_virtual_socket_accessor->m_inputs; + GetNode(i)->m_virtual_socket_accessor->m_inputs; instance_node_descriptors[i]->m_outputs = - m_blend_tree_resource.GetNode(i) - ->m_virtual_socket_accessor->m_outputs; + GetNode(i)->m_virtual_socket_accessor->m_outputs; } } @@ -1067,8 +1013,7 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( instance_node_descriptors[1]->m_outputs = instance.m_node_descriptor->m_outputs; - for (const BlendTreeConnectionResource& connection : - m_blend_tree_resource.GetConnections()) { + for (const BlendTreeConnectionResource& connection : GetConnections()) { NodeDescriptorBase* source_node_descriptor = instance_node_descriptors[connection.source_node_index]; NodeDescriptorBase* target_node_descriptor = @@ -1110,15 +1055,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( instance.m_node_output_connections[connection.source_node_index] .push_back(embedded_graph_activation_connection); - const AnimGraphResource* source_blend_tree_resource = - dynamic_cast( - m_blend_tree_resource.GetNode(connection.source_node_index)); + const BlendTreeResource* source_blend_tree_resource = + dynamic_cast( + GetNode(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_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; @@ -1127,15 +1072,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( // 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. - const AnimGraphResource* target_blend_tree_resource = - dynamic_cast( - m_blend_tree_resource.GetNode(connection.target_node_index)); + const BlendTreeResource* target_blend_tree_resource = + dynamic_cast( + GetNode(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_blend_tree_resource->GetNodeIndexForInputSocket( + connection.target_socket_name); target_node = target_blend_tree->m_nodes[target_blend_tree_output_node_index]; @@ -1175,8 +1120,8 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( instance_connection.m_socket = *source_socket; } - NodeSocketPair node_socket_pair{ - m_blend_tree_resource.GetNode(connection.source_node_index), + NodeSocketNamePair node_socket_pair{ + GetNode(connection.source_node_index), source_socket->m_name}; NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter = @@ -1214,7 +1159,7 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( // const node inputs // std::vector const_inputs = - m_blend_tree_resource.GetConstantNodeInputs(instance_node_descriptors); + GetConstantNodeInputs(instance_node_descriptors); size_t const_node_inputs_buffer_size = 0; for (auto& const_input : const_inputs) { if (const_input->m_type == SocketType::SocketTypeString) { @@ -1248,15 +1193,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( const_input_buffer_offset += i->m_type_size; } - for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) { + for (int i = 0; i < GetNumNodes(); i++) { delete instance_node_descriptors[i]; } } -void AnimGraphResource::SetRuntimeNodeProperties( +void BlendTreeResource::SetRuntimeNodeProperties( AnimGraphBlendTree& result) const { - for (int i = 2; i < m_blend_tree_resource.GetNumNodes(); i++) { - const AnimNodeResource* node_resource = m_blend_tree_resource.GetNode(i); + for (int i = 2; i < GetNumNodes(); i++) { + const AnimNodeResource* node_resource = GetNode(i); NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory( node_resource->m_node_type_name, @@ -1308,17 +1253,4 @@ void AnimGraphResource::SetRuntimeNodeProperties( } } -bool AnimGraphResource::SaveStateMachineResourceToFile( - const char* filename) const { - assert(false && "Not yet implemented"); - - return false; -} - -bool AnimGraphResource::LoadStateMachineResourceFromJson( - nlohmann::json const& json_data) { - assert(false && "Not yet implemented"); - - return false; -} -#pragma clang diagnostic pop \ No newline at end of file +#pragma clang diagnostic pop diff --git a/src/AnimGraph/AnimGraphResource.h b/src/AnimGraph/AnimGraphResource.h index be02716..c69432a 100644 --- a/src/AnimGraph/AnimGraphResource.h +++ b/src/AnimGraph/AnimGraphResource.h @@ -10,9 +10,10 @@ struct AnimGraphBlendTree; struct AnimGraphStateMachine; +struct BlendTreeResource; struct AnimNodeResource { - virtual ~AnimNodeResource() { delete m_virtual_socket_accessor; }; + virtual ~AnimNodeResource() { delete m_virtual_socket_accessor; } std::string m_name; std::string m_node_type_name; @@ -23,6 +24,30 @@ struct AnimNodeResource { static inline AnimNodeResource* AnimNodeResourceFactory( const std::string& node_type_name); +struct StateMachineTransitionResources { + size_t source_state_index = -1; + size_t target_state_index = -1; + float blend_time = 0.f; + bool sync_blend = false; +}; + +struct StateMachineResource { + std::vector m_states; + std::vector m_transitions; +}; + +struct AnimGraphResource : AnimNodeResource { + ~AnimGraphResource() override = default; + + static constexpr char DefaultAnimOutput[] = "Output"; + + virtual [[maybe_unused]] bool SaveToFile(const char* filename) const = 0; + + static AnimGraphResource* CreateFromFile(const char* filename); +}; + +typedef std::unique_ptr AnimGraphResourcePtr; + struct BlendTreeConnectionResource { int source_node_index = -1; std::string source_socket_name; @@ -39,12 +64,26 @@ struct BlendTreeConnectionResource { } }; -struct BlendTreeResource { +struct BlendTreeResource : AnimGraphResource { + typedef std::pair NodeSocketNamePair; + typedef std::map NodeSocketDataOffsetMap; + std::vector > m_node_input_connection_indices; std::vector > m_node_inputs_subtree; + BlendTreeResource() { + m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree"); + + InitGraphConnectors(); + RegisterBlendTreeOutputSocket( + AnimGraphResource::DefaultAnimOutput); + } ~BlendTreeResource() { ClearAllNodes(); } + [[maybe_unused]] bool SaveToFile(const char* filename) const override; + + void CreateBlendTreeInstance(AnimGraphBlendTree& result) const; + void Reset() { ClearAllNodes(); @@ -65,18 +104,79 @@ struct BlendTreeResource { [[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { return m_nodes[0]; } + [[nodiscard]] AnimNodeResource* GetGraphInputNode() const { return m_nodes[1]; } + Socket* GetGraphOutputSocket(const char* socket_name) const { return GetGraphOutputNode()->m_virtual_socket_accessor->GetInputSocket( socket_name); } + Socket* GetGraphInputSocket(const char* socket_name) const { return GetGraphInputNode()->m_virtual_socket_accessor->GetOutputSocket( socket_name); } + static constexpr char DefaultAnimOutput[] = "Output"; + + template + bool RegisterBlendTreeInputSocket(const std::string& socket_name) { + Socket socket; + socket.m_name = socket_name; + socket.m_type = GetSocketType(); + socket.m_type_size = sizeof(T); + + return RegisterBlendTreeInputSocket(socket); + } + + bool RegisterBlendTreeInputSocket(const Socket& socket) { + AnimNodeResource* input_node = GetGraphInputNode(); + + Socket* input_socket = GetGraphInputSocket(socket.m_name.c_str()); + + if (input_socket != nullptr) { + std::cerr << "Error: cannot register output socket as socket with name '" + << socket.m_name << "' already exists!" << std::endl; + return false; + } + + input_node->m_virtual_socket_accessor->m_outputs.push_back(socket); + m_virtual_socket_accessor->m_inputs = + input_node->m_virtual_socket_accessor->m_outputs; + + return true; + } + + template + bool RegisterBlendTreeOutputSocket(const std::string& socket_name) { + Socket socket; + socket.m_name = socket_name; + socket.m_type = GetSocketType(); + socket.m_type_size = sizeof(T); + + return RegisterBlendTreeOutputSocket(socket); + } + + bool RegisterBlendTreeOutputSocket(const Socket& socket) { + AnimNodeResource* output_node = GetGraphOutputNode(); + + Socket* output_socket = GetGraphOutputSocket(socket.m_name.c_str()); + + if (output_socket != nullptr) { + std::cerr << "Error: cannot register output socket as socket with name '" + << socket.m_name << "' already exists!" << std::endl; + return false; + } + + output_node->m_virtual_socket_accessor->m_inputs.push_back(socket); + m_virtual_socket_accessor->m_outputs = + output_node->m_virtual_socket_accessor->m_inputs; + + return true; + } + int GetNodeIndex(const AnimNodeResource* node_resource) const { for (size_t i = 0, n = m_nodes.size(); i < n; i++) { if (m_nodes[i] == node_resource) { @@ -125,14 +225,18 @@ struct BlendTreeResource { void RemoveConnectionsForSocket( const AnimNodeResource* node_resource, const Socket& socket); + void RemoveNodeConnections(AnimNodeResource* node_resource); + [[maybe_unused]] bool RemoveNode(AnimNodeResource* node_resource); [[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); } [[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; } + [[nodiscard]] const AnimNodeResource* GetNode(size_t i) const { return m_nodes[i]; } + [[nodiscard]] const std::vector& GetNodes() const { return m_nodes; } @@ -172,6 +276,7 @@ struct BlendTreeResource { const size_t socket_input_index) const; std::vector GetNodeOutputSockets(const AnimNodeResource* node) const; + std::vector GetNodeInputSockets(const AnimNodeResource* node) const; bool ConnectSockets( @@ -265,6 +370,18 @@ struct BlendTreeResource { } private: + void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; + + void PrepareBlendTreeIOData( + AnimGraphBlendTree& instance, + NodeSocketDataOffsetMap& node_offset_map) const; + + void CreateBlendTreeConnectionInstances( + AnimGraphBlendTree& instance, + NodeSocketDataOffsetMap& node_offset_map) const; + + void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; + void InitGraphConnectors() { AddNode(AnimNodeResourceFactory("BlendTreeSockets")); AnimNodeResource* output_node = GetGraphOutputNode(); @@ -281,7 +398,9 @@ struct BlendTreeResource { m_node_eval_order.clear(); UpdateNodeEvalOrderRecursive(0); } + void UpdateNodeEvalOrderRecursive(size_t node_index); + void UpdateNodeSubtrees(); std::vector m_nodes; @@ -291,123 +410,12 @@ struct BlendTreeResource { friend class AnimGraphResource; }; -struct StateMachineTransitionResources { - size_t source_state_index = -1; - size_t target_state_index = -1; - float blend_time = 0.f; - bool sync_blend = false; -}; - -struct StateMachineResource { - std::vector m_states; - std::vector m_transitions; -}; - -struct AnimGraphResource : AnimNodeResource { - explicit AnimGraphResource(AnimGraphType graph_type); - virtual ~AnimGraphResource() { Clear(); }; - - static constexpr char DefaultAnimOutput[] = "Output"; - - std::string m_graph_type_name; - - BlendTreeResource m_blend_tree_resource; - typedef std::pair NodeSocketPair; - typedef std::map NodeSocketDataOffsetMap; - - StateMachineResource m_state_machine_resource; - - void Clear() { m_blend_tree_resource.Reset(); } - [[maybe_unused]] bool SaveToFile(const char* filename) const; - static AnimGraphResource* CreateFromFile(const char* filename); - - void CreateBlendTreeInstance(AnimGraphBlendTree& result) const; - - template - bool RegisterBlendTreeInputSocket(const std::string& socket_name) { - Socket socket; - socket.m_name = socket_name; - socket.m_type = GetSocketType(); - socket.m_type_size = sizeof(T); - - return RegisterBlendTreeInputSocket(socket); - } - - bool RegisterBlendTreeInputSocket(const Socket& socket) { - AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode(); - - Socket* input_socket = - m_blend_tree_resource.GetGraphInputSocket(socket.m_name.c_str()); - - if (input_socket != nullptr) { - std::cerr << "Error: cannot register output socket as socket with name '" - << socket.m_name << "' already exists!" << std::endl; - return false; - } - - input_node->m_virtual_socket_accessor->m_outputs.push_back(socket); - m_virtual_socket_accessor->m_inputs = - input_node->m_virtual_socket_accessor->m_outputs; - - return true; - } - - template - bool RegisterBlendTreeOutputSocket(const std::string& socket_name) { - Socket socket; - socket.m_name = socket_name; - socket.m_type = GetSocketType(); - socket.m_type_size = sizeof(T); - - return RegisterBlendTreeOutputSocket(socket); - } - - bool RegisterBlendTreeOutputSocket(const Socket& socket) { - AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode(); - - Socket* output_socket = - m_blend_tree_resource.GetGraphOutputSocket(socket.m_name.c_str()); - - if (output_socket != nullptr) { - std::cerr << "Error: cannot register output socket as socket with name '" - << socket.m_name << "' already exists!" << std::endl; - return false; - } - - output_node->m_virtual_socket_accessor->m_inputs.push_back(socket); - m_virtual_socket_accessor->m_outputs = - output_node->m_virtual_socket_accessor->m_inputs; - - return true; - } - - void CreateStateMachineInstance(AnimGraphStateMachine& result) const; - - private: - // BlendTree - bool SaveBlendTreeResourceToFile(const char* filename) const; - void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; - void PrepareBlendTreeIOData( - AnimGraphBlendTree& instance, - NodeSocketDataOffsetMap& node_offset_map) const; - void CreateBlendTreeConnectionInstances( - AnimGraphBlendTree& instance, - NodeSocketDataOffsetMap& node_offset_map) const; - void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; - - bool SaveStateMachineResourceToFile(const char* filename) const; - bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data); -}; - -typedef std::unique_ptr AnimGraphResourcePtr; - inline AnimNodeResource* AnimNodeResourceFactory( const std::string& node_type_name) { AnimNodeResource* result; if (node_type_name == "BlendTree") { - AnimGraphResource* blend_tree_resource = - new AnimGraphResource(AnimGraphType::GraphTypeBlendTree); + AnimGraphResource* blend_tree_resource = new BlendTreeResource(); result = blend_tree_resource; } else { result = new AnimNodeResource(); diff --git a/src/AnimGraphEditor/AnimGraphEditor.cc b/src/AnimGraphEditor/AnimGraphEditor.cc index 1ce6c82..35674d3 100644 --- a/src/AnimGraphEditor/AnimGraphEditor.cc +++ b/src/AnimGraphEditor/AnimGraphEditor.cc @@ -214,9 +214,15 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) { } } -void AnimGraphEditorRenderSidebar( - BlendTreeResource& blend_tree_resource, +void BlendTreeEditorRenderSidebar( + BlendTreeResource* blend_tree_resource, AnimNodeResource* node_resource) { + BlendTreeResource* current_blend_tree_resource = + dynamic_cast( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]); + + assert(current_blend_tree_resource != nullptr); + ImGui::Text( "[%s (%2.2f, %2.2f)]", node_resource->m_node_type_name.c_str(), @@ -276,7 +282,7 @@ void AnimGraphEditorRenderSidebar( } } - if (node_resource == blend_tree_resource.GetGraphOutputNode()) { + if (node_resource == blend_tree_resource->GetGraphOutputNode()) { ImGui::Text("Outputs"); // Graph outputs are the inputs of the output node! @@ -319,17 +325,14 @@ void AnimGraphEditorRenderSidebar( ImGui::PopStyleVar(); if (ImGui::Button("+")) { - AnimGraphResource* current_graph_resource = - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; - current_graph_resource->RegisterBlendTreeOutputSocket( + current_blend_tree_resource->RegisterBlendTreeOutputSocket( "GraphFloatOutput" - + std::to_string(current_graph_resource->m_blend_tree_resource - .GetGraphOutputNode() + + std::to_string(current_blend_tree_resource->GetGraphOutputNode() ->m_virtual_socket_accessor->m_inputs.size())); } } - if (node_resource == blend_tree_resource.GetGraphInputNode()) { + if (node_resource == blend_tree_resource->GetGraphInputNode()) { ImGui::Text("Inputs"); // Graph inputs are the outputs of the input node! @@ -346,7 +349,7 @@ void AnimGraphEditorRenderSidebar( current_graph_resource->m_virtual_socket_accessor->m_inputs = inputs; } if (ImGui::Button("X")) { - blend_tree_resource.RemoveConnectionsForSocket(node_resource, input); + blend_tree_resource->RemoveConnectionsForSocket(node_resource, input); iter = inputs.erase(iter); } else { iter++; @@ -355,13 +358,10 @@ void AnimGraphEditorRenderSidebar( } if (ImGui::Button("+")) { - AnimGraphResource* current_graph_resource = - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; - current_graph_resource->RegisterBlendTreeInputSocket( + current_blend_tree_resource->RegisterBlendTreeInputSocket( "GraphFloatInput" - + std::to_string( - current_graph_resource->m_blend_tree_resource.GetGraphInputNode() - ->m_virtual_socket_accessor->m_outputs.size())); + + std::to_string(current_blend_tree_resource->GetGraphInputNode() + ->m_virtual_socket_accessor->m_outputs.size())); } } } @@ -439,12 +439,15 @@ void BlendTreeEditorNodePopup() { } if (!node_type_name.empty()) { + BlendTreeResource* current_blend_tree_resource = + dynamic_cast( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]); + AnimNodeResource* node_resource = AnimNodeResourceFactory(node_type_name); ax::NodeEditor::SetNodePosition( ax::NodeEditor::NodeId(node_resource), sEditorState.mousePopupStart); - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] - ->m_blend_tree_resource.AddNode(node_resource); + current_blend_tree_resource->AddNode(node_resource); } ImGui::EndPopup(); @@ -559,7 +562,7 @@ void AnimGraphEditorBreadcrumbNavigation() { } } -void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { +void BlendTreeHandleConnectionCreation(BlendTreeResource* current_blend_tree) { if (ax::NodeEditor::BeginCreate()) { ax::NodeEditor::PinId input_pin_id, output_pin_id; if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) { @@ -603,14 +606,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { &source_node_index, &source_node_socket_index); - source_node = current_blend_tree.GetNode(source_node_index); + source_node = current_blend_tree->GetNode(source_node_index); if (source_node != nullptr) { if (source_node->m_virtual_socket_accessor->m_outputs.size() < source_node_socket_index) { source_node_socket_index = -1; } else { - source_socket = current_blend_tree.GetNodeOutputSocketByIndex( + source_socket = current_blend_tree->GetNodeOutputSocketByIndex( source_node, source_node_socket_index); } @@ -628,14 +631,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { &target_node_index, &target_node_socket_index); - target_node = current_blend_tree.GetNode(target_node_index); + target_node = current_blend_tree->GetNode(target_node_index); if (target_node != nullptr) { if (target_node->m_virtual_socket_accessor->m_inputs.size() < target_node_socket_index) { target_node_socket_index = -1; } else { - target_socket = current_blend_tree.GetNodeInputSocketByIndex( + target_socket = current_blend_tree->GetNodeInputSocketByIndex( target_node, target_node_socket_index); } @@ -658,14 +661,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { if (!source_pin.Invalid && !target_pin.Invalid) { if (source_socket == nullptr || target_socket == nullptr - || !current_blend_tree.IsConnectionValid( + || !current_blend_tree->IsConnectionValid( source_node, source_socket->m_name, target_node, target_socket->m_name)) { ax::NodeEditor::RejectNewItem(); } else if (ax::NodeEditor::AcceptNewItem()) { - current_blend_tree.ConnectSockets( + current_blend_tree->ConnectSockets( source_node, source_socket->m_name, target_node, @@ -678,12 +681,12 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { } void BlendTreeRenderNodes( - BlendTreeResource& current_blend_tree, + BlendTreeResource* current_blend_tree, ax::NodeEditor::Utilities::BlueprintNodeBuilder& builder) { - for (size_t node_index = 0, n = current_blend_tree.GetNumNodes(); + for (size_t node_index = 0, n = current_blend_tree->GetNumNodes(); node_index < n; node_index++) { - AnimNodeResource* node_resource = current_blend_tree.GetNode(node_index); + AnimNodeResource* node_resource = current_blend_tree->GetNode(node_index); ax::NodeEditor::NodeId node_id(node_resource); @@ -706,7 +709,7 @@ void BlendTreeRenderNodes( // Inputs std::vector node_inputs = - current_blend_tree.GetNodeInputSockets(node_resource); + current_blend_tree->GetNodeInputSockets(node_resource); for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) { Socket& socket = node_inputs[j]; ax::NodeEditor::PinId input_pin = NodeIndexAndSocketIndexToInputPinId( @@ -718,7 +721,7 @@ void BlendTreeRenderNodes( DrawSocketIcon( socket.m_type, - current_blend_tree.IsSocketConnected(node_resource, socket.m_name)); + current_blend_tree->IsSocketConnected(node_resource, socket.m_name)); ImGui::Spring(0); //ImGui::PushItemWidth(100.0f); @@ -729,7 +732,7 @@ void BlendTreeRenderNodes( // Outputs std::vector node_outputs = - current_blend_tree.GetNodeOutputSockets(node_resource); + current_blend_tree->GetNodeOutputSockets(node_resource); for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) { Socket& socket = node_outputs[j]; builder.Output(NodeIndexAndSocketIndexToOutputPinId( @@ -741,7 +744,7 @@ void BlendTreeRenderNodes( ImGui::Spring(0); DrawSocketIcon( socket.m_type, - current_blend_tree.IsSocketConnected(node_resource, socket.m_name)); + current_blend_tree->IsSocketConnected(node_resource, socket.m_name)); builder.EndOutput(); } @@ -754,21 +757,21 @@ void BlendTreeRenderNodes( } } -void BlendTreeRenderConnections(BlendTreeResource& current_blend_tree) { - for (size_t connection_id = 0, n = current_blend_tree.GetNumConnections(); +void BlendTreeRenderConnections(BlendTreeResource* current_blend_tree) { + for (size_t connection_id = 0, n = current_blend_tree->GetNumConnections(); connection_id < n; connection_id++) { const BlendTreeConnectionResource* connection_resource = - current_blend_tree.GetConnection(connection_id); + current_blend_tree->GetConnection(connection_id); const AnimNodeResource* source_node_resource = - current_blend_tree.GetNode(connection_resource->source_node_index); + current_blend_tree->GetNode(connection_resource->source_node_index); int source_socket_index = source_node_resource->m_virtual_socket_accessor->GetOutputIndex( connection_resource->source_socket_name.c_str()); const AnimNodeResource* target_node_resource = - current_blend_tree.GetNode(connection_resource->target_node_index); + current_blend_tree->GetNode(connection_resource->target_node_index); int target_socket_index = target_node_resource->m_virtual_socket_accessor->GetInputIndex( connection_resource->target_socket_name.c_str()); @@ -786,7 +789,8 @@ void BlendTreeRenderConnections(BlendTreeResource& current_blend_tree) { target_socket_pin_id); } } -void AnimGraphEditorDebugWidget() { + +void BlendTreeEditorDebugWidget() { ImGui::Begin("Connection Debug Panel"); ImGui::BeginTable("Connection", 3); ImGui::TableNextRow(); @@ -882,17 +886,19 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { graph_size.y -= 20; ax::NodeEditor::Begin("Graph Editor", graph_size); - AnimGraphResource* current_graph = - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; - BlendTreeResource& current_blend_tree = current_graph->m_blend_tree_resource; + BlendTreeResource* current_blend_tree_resource = + dynamic_cast( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]); - ax::NodeEditor::Utilities::BlueprintNodeBuilder builder; + if (current_blend_tree_resource) { + ax::NodeEditor::Utilities::BlueprintNodeBuilder builder; - BlendTreeRenderNodes(current_blend_tree, builder); - BlendTreeRenderConnections(current_blend_tree); + BlendTreeRenderNodes(current_blend_tree_resource, builder); + BlendTreeRenderConnections(current_blend_tree_resource); + BlendTreeHandleConnectionCreation(current_blend_tree_resource); - HandleConnectionCreation(current_blend_tree); - BlendTreeEditorNodePopup(); + BlendTreeEditorNodePopup(); + } ax::NodeEditor::End(); @@ -903,22 +909,22 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { // ImGui::TableSetColumnIndex(1); - if (ax::NodeEditor::GetSelectedObjectCount() > 0) { + if (current_blend_tree_resource + && ax::NodeEditor::GetSelectedObjectCount() > 0) { ax::NodeEditor::NodeId selected_node_id = 0; ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1); if (selected_node_id.Get() != 0) { - AnimGraphEditorRenderSidebar( - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] - ->m_blend_tree_resource, + BlendTreeEditorRenderSidebar( + current_blend_tree_resource, selected_node_id.AsPointer()); } } ImGui::EndTable(); - AnimGraphEditorDebugWidget(); + BlendTreeEditorDebugWidget(); // Clear flag, however it may be re-set further down when handling double // clicking into subgraphs. @@ -962,15 +968,14 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { BlendTreeConnectionResource* connection_resource = hovered_link.AsPointer(); - if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) { - BlendTreeResource* blend_tree_resource = - &sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] - ->m_blend_tree_resource; - - blend_tree_resource->DisconnectSockets( - blend_tree_resource->GetNode(connection_resource->source_node_index), + if (connection_resource && current_blend_tree_resource + && ImGui::IsKeyPressed(ImGuiKey_Delete)) { + current_blend_tree_resource->DisconnectSockets( + current_blend_tree_resource->GetNode( + connection_resource->source_node_index), connection_resource->source_socket_name, - blend_tree_resource->GetNode(connection_resource->target_node_index), + current_blend_tree_resource->GetNode( + connection_resource->target_node_index), connection_resource->target_socket_name); ax::NodeEditor::DeleteLink(hovered_link); @@ -982,19 +987,20 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { AnimNodeResource* node_resource = hovered_node.AsPointer(); - if (node_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) { - AnimGraphResource* current_graph_resource = - sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; - - current_graph_resource->m_blend_tree_resource.RemoveNodeConnections( - node_resource); - current_graph_resource->m_blend_tree_resource.RemoveNode(node_resource); + if (node_resource && current_blend_tree_resource + && ImGui::IsKeyPressed(ImGuiKey_Delete)) { + current_blend_tree_resource->RemoveNodeConnections(node_resource); + current_blend_tree_resource->RemoveNode(node_resource); } } ax::NodeEditor::SetCurrentEditor(nullptr); } -void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) { - sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree); +void AnimGraphEditorGetRuntimeBlendTree(AnimGraphBlendTree& blend_tree) { + BlendTreeResource* root_blend_tree_resource = + dynamic_cast(sEditorState.rootGraphResource); + assert(root_blend_tree_resource); + + root_blend_tree_resource->CreateBlendTreeInstance(blend_tree); } \ No newline at end of file diff --git a/src/AnimGraphEditor/AnimGraphEditor.h b/src/AnimGraphEditor/AnimGraphEditor.h index 11e29c2..8b01f11 100644 --- a/src/AnimGraphEditor/AnimGraphEditor.h +++ b/src/AnimGraphEditor/AnimGraphEditor.h @@ -83,6 +83,6 @@ void AnimGraphEditorClear(); void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context); -void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph); +void AnimGraphEditorGetRuntimeBlendTree(AnimGraphBlendTree& anim_graph); #endif //ANIMTESTBED_ANIMGRAPHEDITOR_H diff --git a/src/main.cc b/src/main.cc index 59735fe..5f804d8 100644 --- a/src/main.cc +++ b/src/main.cc @@ -745,7 +745,7 @@ int main() { if (ImGui::Button("Update Runtime Graph")) { anim_graph.dealloc(); - AnimGraphEditorGetRuntimeGraph(anim_graph); + AnimGraphEditorGetRuntimeBlendTree(anim_graph); anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton; anim_graph.Init(anim_graph_context);