Refactored BlendTreeResources to be a subclass of AnimGraphResource.
The latter is used to abstract saving/loading for both BlendTrees and StateMachines.
This commit is contained in:
		
							parent
							
								
									f351939d61
								
							
						
					
					
						commit
						2ea2c56bbb
					
				| @ -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<BlendTreeConnectionResource>& 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<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|     sAnimGraphResourceBlendTreeFromJson(json_node, result); | ||||
|     BlendTreeResource* result = | ||||
|         dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|     sBlendTreeFromJson(json_node, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
| @ -244,26 +244,25 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson( | ||||
|   return connection; | ||||
| } | ||||
| 
 | ||||
| static json sAnimGraphResourceBlendTreeToJson( | ||||
|     const AnimGraphResource& anim_graph_resource) { | ||||
| //
 | ||||
| // BlendTreeResource <-> Json
 | ||||
| //
 | ||||
| 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<const AnimGraphResource*>(node); | ||||
|       result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); | ||||
|       const BlendTreeResource* blend_tree_resource = | ||||
|           dynamic_cast<const BlendTreeResource*>(node); | ||||
|       result["nodes"][i] = sBlendTreeToJson(*blend_tree_resource); | ||||
|     } else { | ||||
|       result["nodes"][i] = | ||||
|           sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections()); | ||||
| @ -295,20 +294,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 +316,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 +332,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 +351,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); | ||||
|     } | ||||
|   } | ||||
| @ -375,6 +370,92 @@ static bool sAnimGraphResourceStateMachineFromJson( | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) { | ||||
|   std::ifstream input_file; | ||||
|   input_file.open(filename); | ||||
|   std::stringstream buffer; | ||||
|   buffer << input_file.rdbuf(); | ||||
| 
 | ||||
|   json json_data = json::parse(buffer.str(), nullptr, false); | ||||
|   if (json_data.is_discarded()) { | ||||
|     std::cerr << "Error parsing json of file '" << filename << "'." | ||||
|               << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   if (json_data["type"] != "AnimNodeResource") { | ||||
|     std::cerr | ||||
|         << "Invalid json object. Expected type 'AnimNodeResource' but got '" | ||||
|         << json_data["type"] << "'." << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   AnimGraphResource* result = nullptr; | ||||
|   if (json_data["node_type"] == "BlendTree") { | ||||
|     result = BlendTreeResource::CreateFromFile(filename); | ||||
|   } else if (json_data["node_type"] == "StateMachine") { | ||||
|     sAnimGraphResourceStateMachineFromJson(json_data, result); | ||||
|   } else { | ||||
|     std::cerr << "Invalid node_type. Expected type 'BlendTree' or " | ||||
|                  "'StateMachine' but got '" | ||||
|               << json_data["node_type"] << "'." << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| bool BlendTreeResource::SaveToFile(const char* filename) const { | ||||
|   json result; | ||||
| 
 | ||||
|   result = sBlendTreeToJson(*this); | ||||
| 
 | ||||
|   std::ofstream output_file; | ||||
|   output_file.open(filename); | ||||
|   output_file << result.dump(4, ' ') << std::endl; | ||||
|   output_file.close(); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| BlendTreeResource* BlendTreeResource::CreateFromFile(const char* filename) { | ||||
|   std::ifstream input_file; | ||||
|   input_file.open(filename); | ||||
|   std::stringstream buffer; | ||||
|   buffer << input_file.rdbuf(); | ||||
| 
 | ||||
|   json json_data = json::parse(buffer.str(), nullptr, false); | ||||
|   if (json_data.is_discarded()) { | ||||
|     std::cerr << "Error parsing json of file '" << filename << "'." | ||||
|               << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   if (json_data["type"] != "AnimNodeResource") { | ||||
|     std::cerr | ||||
|         << "Invalid json object. Expected type 'AnimNodeResource' but got '" | ||||
|         << json_data["type"] << "'." << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   if (json_data["node_type"] != "BlendTree") { | ||||
|     std::cerr << "Invalid node type. Expected type 'BlendTree' but got '" | ||||
|               << json_data["node_type"] << "'." << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   BlendTreeResource* result = nullptr; | ||||
|   result = | ||||
|       dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|   sBlendTreeFromJson(json_data, result); | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| void BlendTreeResource::RemoveConnectionsForSocket( | ||||
|     const AnimNodeResource* node_resource, | ||||
|     const Socket& socket) { | ||||
| @ -563,12 +644,10 @@ Socket* BlendTreeResource::GetNodeOutputSocket( | ||||
|   } | ||||
| 
 | ||||
|   if (output_socket == nullptr && node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* graph_resource = | ||||
|         dynamic_cast<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(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 +661,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<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(node); | ||||
| 
 | ||||
|     output_sockets = &blend_tree_resource.GetGraphOutputNode() | ||||
|     output_sockets = &blend_tree_resource->GetGraphOutputNode() | ||||
|                           ->m_virtual_socket_accessor->m_outputs; | ||||
|   } | ||||
| 
 | ||||
| @ -610,12 +687,10 @@ Socket* BlendTreeResource::GetNodeInputSocket( | ||||
|   } | ||||
| 
 | ||||
|   if (input_socket == nullptr && node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* graph_resource = | ||||
|         dynamic_cast<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(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 +704,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<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(node); | ||||
| 
 | ||||
|     output_sockets = &blend_tree_resource.GetGraphOutputNode() | ||||
|     output_sockets = &blend_tree_resource->GetGraphOutputNode() | ||||
|                           ->m_virtual_socket_accessor->m_outputs; | ||||
|   } | ||||
| 
 | ||||
| @ -653,11 +726,10 @@ std::vector<Socket> BlendTreeResource::GetNodeOutputSockets( | ||||
|   } | ||||
| 
 | ||||
|   if (node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* graph_resource = | ||||
|         dynamic_cast<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     return blend_tree_resource.GetGraphOutputNode() | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(node); | ||||
| 
 | ||||
|     return blend_tree_resource->GetGraphOutputNode() | ||||
|         ->m_virtual_socket_accessor->m_inputs; | ||||
|   } | ||||
| 
 | ||||
| @ -671,11 +743,10 @@ std::vector<Socket> BlendTreeResource::GetNodeInputSockets( | ||||
|   } | ||||
| 
 | ||||
|   if (node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* graph_resource = | ||||
|         dynamic_cast<const AnimGraphResource*>(node); | ||||
|     const BlendTreeResource& blend_tree_resource = | ||||
|         graph_resource->m_blend_tree_resource; | ||||
|     return blend_tree_resource.GetGraphInputNode() | ||||
|     const BlendTreeResource* blend_tree_resource = | ||||
|         dynamic_cast<const BlendTreeResource*>(node); | ||||
| 
 | ||||
|     return blend_tree_resource->GetGraphInputNode() | ||||
|         ->m_virtual_socket_accessor->m_outputs; | ||||
|   } | ||||
| 
 | ||||
| @ -824,92 +895,12 @@ 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<AnimData>( | ||||
|         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); | ||||
|   std::stringstream buffer; | ||||
|   buffer << input_file.rdbuf(); | ||||
| 
 | ||||
|   json json_data = json::parse(buffer.str(), nullptr, false); | ||||
|   if (json_data.is_discarded()) { | ||||
|     std::cerr << "Error parsing json of file '" << filename << "'." | ||||
|               << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   if (json_data["type"] != "AnimNodeResource") { | ||||
|     std::cerr | ||||
|         << "Invalid json object. Expected type 'AnimNodeResource' but got '" | ||||
|         << json_data["type"] << "'." << std::endl; | ||||
| 
 | ||||
|     return nullptr; | ||||
|   } | ||||
| 
 | ||||
|   AnimGraphResource* result = nullptr; | ||||
|   if (json_data["node_type"] == "BlendTree") { | ||||
|     result = | ||||
|         dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|     sAnimGraphResourceBlendTreeFromJson(json_data, result); | ||||
|   } else if (json_data["node_type"] == "StateMachine") { | ||||
|     sAnimGraphResourceStateMachineFromJson(json_data, result); | ||||
|   } else { | ||||
|     std::cerr << "Invalid node_type. Expected type 'BlendTree' or " | ||||
|                  "'StateMachine' but got '" | ||||
|               << json_data["node_type"] << "'." << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   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 { | ||||
|   json result; | ||||
| 
 | ||||
|   result = sAnimGraphResourceBlendTreeToJson(*this); | ||||
| 
 | ||||
|   std::ofstream output_file; | ||||
|   output_file.open(filename); | ||||
|   output_file << result.dump(4, ' ') << std::endl; | ||||
|   output_file.close(); | ||||
| 
 | ||||
|   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 +917,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<const AnimGraphResource*>(node_resource); | ||||
|       const BlendTreeResource* embedded_blend_tree_resource = | ||||
|           dynamic_cast<const BlendTreeResource*>(node_resource); | ||||
|       assert(embedded_blend_tree_resource != nullptr); | ||||
|       AnimGraphBlendTree* embedded_blend_tree = | ||||
|           dynamic_cast<AnimGraphBlendTree*>(node); | ||||
| @ -958,18 +948,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 +1007,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 +1028,22 @@ void AnimGraphResource::PrepareBlendTreeIOData( | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void AnimGraphResource::CreateBlendTreeConnectionInstances( | ||||
| void BlendTreeResource::CreateBlendTreeConnectionInstances( | ||||
|     AnimGraphBlendTree& instance, | ||||
|     NodeSocketDataOffsetMap& node_offset_map) const { | ||||
|   std::vector<NodeDescriptorBase*> 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 +1051,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 +1093,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<const AnimGraphResource*>( | ||||
|               m_blend_tree_resource.GetNode(connection.source_node_index)); | ||||
|       const BlendTreeResource* source_blend_tree_resource = | ||||
|           dynamic_cast<const BlendTreeResource*>( | ||||
|               GetNode(connection.source_node_index)); | ||||
|       AnimGraphBlendTree* source_blend_tree = | ||||
|           dynamic_cast<AnimGraphBlendTree*>(source_node); | ||||
| 
 | ||||
|       size_t source_blend_tree_output_node_index = | ||||
|           source_blend_tree_resource->m_blend_tree_resource | ||||
|               .GetNodeIndexForOutputSocket(connection.source_socket_name); | ||||
|           source_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 +1110,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<const AnimGraphResource*>( | ||||
|               m_blend_tree_resource.GetNode(connection.target_node_index)); | ||||
|       const BlendTreeResource* target_blend_tree_resource = | ||||
|           dynamic_cast<const BlendTreeResource*>( | ||||
|               GetNode(connection.target_node_index)); | ||||
|       AnimGraphBlendTree* target_blend_tree = | ||||
|           dynamic_cast<AnimGraphBlendTree*>(target_node); | ||||
| 
 | ||||
|       size_t target_blend_tree_output_node_index = | ||||
|           target_blend_tree_resource->m_blend_tree_resource | ||||
|               .GetNodeIndexForInputSocket(connection.target_socket_name); | ||||
|           target_blend_tree_resource->GetNodeIndexForInputSocket( | ||||
|               connection.target_socket_name); | ||||
|       target_node = | ||||
|           target_blend_tree->m_nodes[target_blend_tree_output_node_index]; | ||||
| 
 | ||||
| @ -1175,8 +1158,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 +1197,7 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances( | ||||
|   // const node inputs
 | ||||
|   //
 | ||||
|   std::vector<Socket*> 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 +1231,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 +1291,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 | ||||
| #pragma clang diagnostic pop | ||||
|  | ||||
| @ -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<AnimNodeResource> m_states; | ||||
|   std::vector<StateMachineTransitionResources> m_transitions; | ||||
| }; | ||||
| 
 | ||||
| struct AnimGraphResource : AnimNodeResource { | ||||
|   ~AnimGraphResource() override = default; | ||||
| 
 | ||||
|   static constexpr char DefaultAnimOutput[] = "Output"; | ||||
| 
 | ||||
|   [[maybe_unused]] virtual bool SaveToFile(const char* filename) const = 0; | ||||
| 
 | ||||
|   static AnimGraphResource* CreateFromFile(const char* filename); | ||||
| }; | ||||
| 
 | ||||
| typedef std::unique_ptr<AnimGraphResource> AnimGraphResourcePtr; | ||||
| 
 | ||||
| struct BlendTreeConnectionResource { | ||||
|   int source_node_index = -1; | ||||
|   std::string source_socket_name; | ||||
| @ -39,12 +64,27 @@ struct BlendTreeConnectionResource { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| struct BlendTreeResource { | ||||
| struct BlendTreeResource : AnimGraphResource { | ||||
|   typedef std::pair<const AnimNodeResource*, std::string> NodeSocketNamePair; | ||||
|   typedef std::map<NodeSocketNamePair, int> NodeSocketDataOffsetMap; | ||||
| 
 | ||||
|   std::vector<std::vector<size_t> > m_node_input_connection_indices; | ||||
|   std::vector<std::vector<size_t> > m_node_inputs_subtree; | ||||
| 
 | ||||
|   BlendTreeResource() { | ||||
|     m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree"); | ||||
| 
 | ||||
|     InitGraphConnectors(); | ||||
|     RegisterBlendTreeOutputSocket<AnimData>( | ||||
|         AnimGraphResource::DefaultAnimOutput); | ||||
|   } | ||||
|   ~BlendTreeResource() { ClearAllNodes(); } | ||||
| 
 | ||||
|   [[maybe_unused]] bool SaveToFile(const char* filename) const override; | ||||
|   static BlendTreeResource* CreateFromFile(const char* filename); | ||||
| 
 | ||||
|   void CreateBlendTreeInstance(AnimGraphBlendTree& result) const; | ||||
| 
 | ||||
|   void Reset() { | ||||
|     ClearAllNodes(); | ||||
| 
 | ||||
| @ -65,18 +105,77 @@ 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); | ||||
|   } | ||||
| 
 | ||||
|   template <typename T> | ||||
|   bool RegisterBlendTreeInputSocket(const std::string& socket_name) { | ||||
|     Socket socket; | ||||
|     socket.m_name = socket_name; | ||||
|     socket.m_type = GetSocketType<T>(); | ||||
|     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 <typename T> | ||||
|   bool RegisterBlendTreeOutputSocket(const std::string& socket_name) { | ||||
|     Socket socket; | ||||
|     socket.m_name = socket_name; | ||||
|     socket.m_type = GetSocketType<T>(); | ||||
|     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 +224,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<AnimNodeResource*>& GetNodes() const { | ||||
|     return m_nodes; | ||||
|   } | ||||
| @ -172,6 +275,7 @@ struct BlendTreeResource { | ||||
|       const size_t socket_input_index) const; | ||||
| 
 | ||||
|   std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const; | ||||
| 
 | ||||
|   std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const; | ||||
| 
 | ||||
|   bool ConnectSockets( | ||||
| @ -265,6 +369,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 +397,9 @@ struct BlendTreeResource { | ||||
|     m_node_eval_order.clear(); | ||||
|     UpdateNodeEvalOrderRecursive(0); | ||||
|   } | ||||
| 
 | ||||
|   void UpdateNodeEvalOrderRecursive(size_t node_index); | ||||
| 
 | ||||
|   void UpdateNodeSubtrees(); | ||||
| 
 | ||||
|   std::vector<AnimNodeResource*> m_nodes; | ||||
| @ -291,123 +409,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<AnimNodeResource> m_states; | ||||
|   std::vector<StateMachineTransitionResources> 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<const AnimNodeResource*, std::string> NodeSocketPair; | ||||
|   typedef std::map<NodeSocketPair, int> 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 <typename T> | ||||
|   bool RegisterBlendTreeInputSocket(const std::string& socket_name) { | ||||
|     Socket socket; | ||||
|     socket.m_name = socket_name; | ||||
|     socket.m_type = GetSocketType<T>(); | ||||
|     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 <typename T> | ||||
|   bool RegisterBlendTreeOutputSocket(const std::string& socket_name) { | ||||
|     Socket socket; | ||||
|     socket.m_name = socket_name; | ||||
|     socket.m_type = GetSocketType<T>(); | ||||
|     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<AnimGraphResource> 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(); | ||||
|  | ||||
| @ -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<BlendTreeResource*>( | ||||
|           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<float>( | ||||
|       current_blend_tree_resource->RegisterBlendTreeOutputSocket<float>( | ||||
|           "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<float>( | ||||
|       current_blend_tree_resource->RegisterBlendTreeInputSocket<float>( | ||||
|           "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<BlendTreeResource*>( | ||||
|               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<Socket> 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<Socket> 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<BlendTreeResource*>( | ||||
|           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<AnimNodeResource>()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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<BlendTreeConnectionResource>(); | ||||
| 
 | ||||
|     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<AnimNodeResource>(); | ||||
| 
 | ||||
|     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<BlendTreeResource*>(sEditorState.rootGraphResource); | ||||
|   assert(root_blend_tree_resource); | ||||
| 
 | ||||
|   root_blend_tree_resource->CreateBlendTreeInstance(blend_tree); | ||||
| } | ||||
| @ -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
 | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -11,22 +11,26 @@ | ||||
| #include "ozz/base/io/stream.h" | ||||
| #include "ozz/base/log.h" | ||||
| 
 | ||||
| class SimpleAnimSamplerGraphResource { | ||||
|  protected: | ||||
|   AnimGraphResource graph_resource; | ||||
| class BlendTreeResourceFixture { | ||||
|  public: | ||||
|   BlendTreeResourceFixture() { | ||||
|     blend_tree_resource = | ||||
|         dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|   } | ||||
| 
 | ||||
|   virtual ~BlendTreeResourceFixture() { delete blend_tree_resource; } | ||||
| 
 | ||||
|   BlendTreeResource* blend_tree_resource = nullptr; | ||||
| }; | ||||
| 
 | ||||
| class SimpleAnimSamplerBlendTreeResourceFixture | ||||
|     : public BlendTreeResourceFixture { | ||||
|  protected: | ||||
|   size_t walk_node_index = -1; | ||||
|   AnimNodeResource* walk_node = nullptr; | ||||
| 
 | ||||
|  public: | ||||
|   SimpleAnimSamplerGraphResource() | ||||
|       : graph_resource(AnimGraphType::GraphTypeBlendTree) { | ||||
|     graph_resource.m_name = "AnimSamplerBlendTree"; | ||||
|     graph_resource.m_node_type_name = "BlendTree"; | ||||
|     graph_resource.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|     blend_tree_resource = &graph_resource.m_blend_tree_resource; | ||||
| 
 | ||||
|   SimpleAnimSamplerBlendTreeResourceFixture() { | ||||
|     // Prepare graph inputs and outputs
 | ||||
|     walk_node_index = | ||||
|         blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler")); | ||||
| @ -49,10 +53,8 @@ class SimpleAnimSamplerGraphResource { | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class Blend2GraphResource { | ||||
| class Blend2BlendTreeResource : public BlendTreeResourceFixture { | ||||
|  protected: | ||||
|   AnimGraphResourcePtr graph_resource; | ||||
|   BlendTreeResource* blend_tree_resource = nullptr; | ||||
|   size_t walk_node_index = -1; | ||||
|   size_t run_node_index = -1; | ||||
|   size_t blend_node_index = -1; | ||||
| @ -61,13 +63,8 @@ class Blend2GraphResource { | ||||
|   AnimNodeResource* blend_node = nullptr; | ||||
| 
 | ||||
|  public: | ||||
|   Blend2GraphResource() | ||||
|       : graph_resource( | ||||
|             dynamic_cast<AnimGraphResource*>( | ||||
|                 AnimNodeResourceFactory("BlendTree"))) { | ||||
|     graph_resource->m_name = "WalkRunBlendGraph"; | ||||
| 
 | ||||
|     blend_tree_resource = &graph_resource->m_blend_tree_resource; | ||||
|   Blend2BlendTreeResource() { | ||||
|     blend_tree_resource->m_name = "WalkRunBlendGraph"; | ||||
| 
 | ||||
|     // Prepare graph inputs and outputs
 | ||||
|     walk_node_index = | ||||
| @ -129,12 +126,8 @@ class Blend2GraphResource { | ||||
| //   |                                        |
 | ||||
| //   +----------------------------------------+
 | ||||
| //
 | ||||
| class EmbeddedBlendTreeGraphResource { | ||||
| class EmbeddedBlendTreeGraphResource : public BlendTreeResourceFixture { | ||||
|  protected: | ||||
|   AnimGraphResource parent_graph_resource; | ||||
|   BlendTreeResource* parent_blend_tree_resource = nullptr; | ||||
| 
 | ||||
|   AnimGraphResource* embedded_graph = nullptr; | ||||
|   BlendTreeResource* embedded_blend_tree_resource = nullptr; | ||||
| 
 | ||||
|   size_t walk_node_index = -1; | ||||
| @ -143,19 +136,14 @@ class EmbeddedBlendTreeGraphResource { | ||||
|   size_t embedded_speed_scale_index = -1; | ||||
| 
 | ||||
|  public: | ||||
|   EmbeddedBlendTreeGraphResource() | ||||
|       : parent_graph_resource(AnimGraphType::GraphTypeBlendTree) { | ||||
|     parent_graph_resource.m_name = "ParentBlendTree"; | ||||
|     parent_graph_resource.m_graph_type_name = "BlendTree"; | ||||
|     parent_graph_resource.m_node_type_name = "BlendTree"; | ||||
| 
 | ||||
|     parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource; | ||||
|   EmbeddedBlendTreeGraphResource() { | ||||
|     blend_tree_resource->m_name = "ParentBlendTree"; | ||||
| 
 | ||||
|     // Parent AnimSampler
 | ||||
|     walk_node_index = parent_blend_tree_resource->AddNode( | ||||
|         AnimNodeResourceFactory("AnimSampler")); | ||||
|     walk_node_index = | ||||
|         blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler")); | ||||
| 
 | ||||
|     walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index); | ||||
|     walk_node_resource = blend_tree_resource->GetNode(walk_node_index); | ||||
|     walk_node_resource->m_name = "WalkAnim"; | ||||
|     walk_node_resource->m_virtual_socket_accessor->SetPropertyValue( | ||||
|         "Filename", | ||||
| @ -164,14 +152,11 @@ class EmbeddedBlendTreeGraphResource { | ||||
|     //
 | ||||
|     // Embedded Tree
 | ||||
|     //
 | ||||
|     embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode( | ||||
|         AnimNodeResourceFactory("BlendTree")); | ||||
|     embedded_graph = dynamic_cast<AnimGraphResource*>( | ||||
|         parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index)); | ||||
|     embedded_graph->m_name = "EmbeddedBlendTree"; | ||||
|     embedded_graph->m_node_type_name = "BlendTree"; | ||||
|     embedded_graph->m_graph_type_name = "BlendTree"; | ||||
|     embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource; | ||||
|     embedded_blend_tree_node_index = | ||||
|         blend_tree_resource->AddNode(AnimNodeResourceFactory("BlendTree")); | ||||
|     embedded_blend_tree_resource = dynamic_cast<BlendTreeResource*>( | ||||
|         blend_tree_resource->GetNode(embedded_blend_tree_node_index)); | ||||
|     embedded_blend_tree_resource->m_name = "EmbeddedBlendTree"; | ||||
| 
 | ||||
|     // Embedded: outputs
 | ||||
|     AnimNodeResource* embedded_outputs = | ||||
| @ -210,15 +195,15 @@ class EmbeddedBlendTreeGraphResource { | ||||
| 
 | ||||
|     // Parent: setup connections
 | ||||
|     const AnimNodeResource* parent_blend_tree_outputs = | ||||
|         parent_blend_tree_resource->GetGraphOutputNode(); | ||||
|         blend_tree_resource->GetGraphOutputNode(); | ||||
| 
 | ||||
|     REQUIRE(parent_blend_tree_resource->ConnectSockets( | ||||
|     REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|         walk_node_resource, | ||||
|         "Output", | ||||
|         embedded_graph, | ||||
|         embedded_blend_tree_resource, | ||||
|         "AnimInput")); | ||||
|     REQUIRE(parent_blend_tree_resource->ConnectSockets( | ||||
|         embedded_graph, | ||||
|     REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|         embedded_blend_tree_resource, | ||||
|         "AnimOutput", | ||||
|         parent_blend_tree_outputs, | ||||
|         "Output")); | ||||
| @ -238,12 +223,8 @@ class EmbeddedBlendTreeGraphResource { | ||||
| //   |                                        |
 | ||||
| //   +----------------------------------------+
 | ||||
| //
 | ||||
| class EmbeddedTreeBlend2GraphResource { | ||||
| class EmbeddedTreeBlend2GraphResource : public BlendTreeResourceFixture { | ||||
|  protected: | ||||
|   AnimGraphResource parent_graph_resource; | ||||
|   BlendTreeResource* parent_blend_tree_resource = nullptr; | ||||
| 
 | ||||
|   AnimGraphResource* embedded_graph = nullptr; | ||||
|   BlendTreeResource* embedded_blend_tree_resource = nullptr; | ||||
| 
 | ||||
|   size_t walk_node_index = -1; | ||||
| @ -256,26 +237,21 @@ class EmbeddedTreeBlend2GraphResource { | ||||
|   AnimNodeResource* embedded_run_node_resource = nullptr; | ||||
| 
 | ||||
|  public: | ||||
|   EmbeddedTreeBlend2GraphResource() | ||||
|       : parent_graph_resource(AnimGraphType::GraphTypeBlendTree) { | ||||
|     parent_graph_resource.m_name = "ParentBlendTree"; | ||||
|     parent_graph_resource.m_graph_type_name = "BlendTree"; | ||||
|     parent_graph_resource.m_node_type_name = "BlendTree"; | ||||
| 
 | ||||
|     parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource; | ||||
|   EmbeddedTreeBlend2GraphResource() { | ||||
|     blend_tree_resource->m_name = "ParentBlendTree"; | ||||
| 
 | ||||
|     // Setup parent inputs
 | ||||
|     AnimNodeResource* parent_blend_tree_inputs = | ||||
|         parent_blend_tree_resource->GetGraphInputNode(); | ||||
|         blend_tree_resource->GetGraphInputNode(); | ||||
|     parent_blend_tree_inputs->m_virtual_socket_accessor->RegisterOutput<float>( | ||||
|         "EmbeddedBlend2Weight", | ||||
|         nullptr); | ||||
| 
 | ||||
|     // Parent AnimSampler
 | ||||
|     walk_node_index = parent_blend_tree_resource->AddNode( | ||||
|         AnimNodeResourceFactory("AnimSampler")); | ||||
|     walk_node_index = | ||||
|         blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler")); | ||||
| 
 | ||||
|     walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index); | ||||
|     walk_node_resource = blend_tree_resource->GetNode(walk_node_index); | ||||
|     walk_node_resource->m_name = "WalkAnim"; | ||||
|     walk_node_resource->m_virtual_socket_accessor->SetPropertyValue( | ||||
|         "Filename", | ||||
| @ -284,18 +260,17 @@ class EmbeddedTreeBlend2GraphResource { | ||||
|     //
 | ||||
|     // Embedded Tree
 | ||||
|     //
 | ||||
|     embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode( | ||||
|         AnimNodeResourceFactory("BlendTree")); | ||||
|     embedded_graph = dynamic_cast<AnimGraphResource*>( | ||||
|         parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index)); | ||||
|     embedded_graph->m_name = "EmbeddedTreeBlend2GraphResource"; | ||||
|     embedded_graph->m_node_type_name = "BlendTree"; | ||||
|     embedded_graph->m_graph_type_name = "BlendTree"; | ||||
|     embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource; | ||||
|     embedded_blend_tree_node_index = | ||||
|         blend_tree_resource->AddNode(AnimNodeResourceFactory("BlendTree")); | ||||
|     embedded_blend_tree_resource = dynamic_cast<BlendTreeResource*>( | ||||
|         blend_tree_resource->GetNode(embedded_blend_tree_node_index)); | ||||
|     embedded_blend_tree_resource->m_name = "EmbeddedTreeBlend2GraphResource"; | ||||
| 
 | ||||
|     // Embedded: inputs
 | ||||
|     embedded_graph->RegisterBlendTreeInputSocket<AnimData>("AnimInput"); | ||||
|     embedded_graph->RegisterBlendTreeInputSocket<float>("BlendWeight"); | ||||
|     embedded_blend_tree_resource->RegisterBlendTreeInputSocket<AnimData>( | ||||
|         "AnimInput"); | ||||
|     embedded_blend_tree_resource->RegisterBlendTreeInputSocket<float>( | ||||
|         "BlendWeight"); | ||||
| 
 | ||||
|     // Embedded nodes
 | ||||
|     embedded_blend2_node_index = embedded_blend_tree_resource->AddNode( | ||||
| @ -342,22 +317,22 @@ class EmbeddedTreeBlend2GraphResource { | ||||
| 
 | ||||
|     // Parent: setup connections
 | ||||
|     AnimNodeResource* parent_blend_tree_outputs = | ||||
|         parent_blend_tree_resource->GetGraphOutputNode(); | ||||
|         blend_tree_resource->GetGraphOutputNode(); | ||||
| 
 | ||||
|     REQUIRE(parent_blend_tree_resource->ConnectSockets( | ||||
|     REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|         walk_node_resource, | ||||
|         "Output", | ||||
|         embedded_graph, | ||||
|         embedded_blend_tree_resource, | ||||
|         "AnimInput")); | ||||
|     REQUIRE(parent_blend_tree_resource->ConnectSockets( | ||||
|         embedded_graph, | ||||
|     REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|         embedded_blend_tree_resource, | ||||
|         AnimGraphResource::DefaultAnimOutput, | ||||
|         parent_blend_tree_outputs, | ||||
|         AnimGraphResource::DefaultAnimOutput)); | ||||
|     REQUIRE(parent_blend_tree_resource->ConnectSockets( | ||||
|     REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|         parent_blend_tree_inputs, | ||||
|         "EmbeddedBlend2Weight", | ||||
|         embedded_graph, | ||||
|         embedded_blend_tree_resource, | ||||
|         "BlendWeight")); | ||||
|   } | ||||
| }; | ||||
| @ -386,6 +361,14 @@ bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) { | ||||
| void CheckBlendTreeResourcesEqual( | ||||
|     const BlendTreeResource* blend_tree_resource_reference, | ||||
|     const BlendTreeResource* blend_tree_resource_rhs) { | ||||
|   REQUIRE( | ||||
|       blend_tree_resource_reference->m_node_type_name | ||||
|       == blend_tree_resource_rhs->m_node_type_name); | ||||
|   REQUIRE(blend_tree_resource_reference->m_node_type_name == "BlendTree"); | ||||
| 
 | ||||
|   REQUIRE( | ||||
|       blend_tree_resource_reference->m_name == blend_tree_resource_rhs->m_name); | ||||
| 
 | ||||
|   REQUIRE( | ||||
|       blend_tree_resource_reference->GetNumNodes() | ||||
|       == blend_tree_resource_rhs->GetNumNodes()); | ||||
| @ -420,27 +403,6 @@ void CheckBlendTreeResourcesEqual( | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void CheckAnimGraphResourceEqual( | ||||
|     const AnimGraphResource& graph_resource_reference, | ||||
|     const AnimGraphResource& graph_resource_rhs) { | ||||
|   REQUIRE( | ||||
|       graph_resource_reference.m_graph_type_name | ||||
|       == graph_resource_rhs.m_graph_type_name); | ||||
|   REQUIRE(graph_resource_reference.m_name == graph_resource_rhs.m_name); | ||||
| 
 | ||||
|   REQUIRE(graph_resource_reference.m_graph_type_name == "BlendTree"); | ||||
| 
 | ||||
|   const BlendTreeResource* blend_tree_resource_reference = | ||||
|       &graph_resource_reference.m_blend_tree_resource; | ||||
| 
 | ||||
|   const BlendTreeResource* blend_tree_resource_rhs = | ||||
|       &graph_resource_rhs.m_blend_tree_resource; | ||||
| 
 | ||||
|   CheckBlendTreeResourcesEqual( | ||||
|       blend_tree_resource_reference, | ||||
|       blend_tree_resource_rhs); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { | ||||
|   int node_id = 3321; | ||||
|   int input_index = 221; | ||||
| @ -461,24 +423,26 @@ TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { | ||||
| } | ||||
| 
 | ||||
| TEST_CASE_METHOD( | ||||
|     SimpleAnimSamplerGraphResource, | ||||
|     SimpleAnimSamplerBlendTreeResourceFixture, | ||||
|     "SimpleAnimSamplerGraphResource saving and loading results in same " | ||||
|     "resource", | ||||
|     "[SimpleAnimSamplerGraphResource]") { | ||||
|   graph_resource.SaveToFile("TestGraphAnimSamplerBlendTree.json"); | ||||
|   blend_tree_resource->SaveToFile("TestGraphAnimSamplerBlendTree.json"); | ||||
| 
 | ||||
|   std::unique_ptr<AnimGraphResource> graph_resource_loaded( | ||||
|       AnimGraphResource::CreateFromFile("TestGraphAnimSamplerBlendTree.json")); | ||||
|   std::unique_ptr<BlendTreeResource> blend_tree_resource_loaded( | ||||
|       BlendTreeResource::CreateFromFile("TestGraphAnimSamplerBlendTree.json")); | ||||
| 
 | ||||
|   CheckAnimGraphResourceEqual(graph_resource, *graph_resource_loaded); | ||||
|   CheckBlendTreeResourcesEqual( | ||||
|       blend_tree_resource, | ||||
|       blend_tree_resource_loaded.get()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE_METHOD( | ||||
|     SimpleAnimSamplerGraphResource, | ||||
|     SimpleAnimSamplerBlendTreeResourceFixture, | ||||
|     "SimpleAnimSamplerGraphResource emulated evaluation", | ||||
|     "[SimpleAnimSamplerGraphResource]") { | ||||
|   AnimGraphBlendTree anim_graph_blend_tree; | ||||
|   graph_resource.CreateBlendTreeInstance(anim_graph_blend_tree); | ||||
|   blend_tree_resource->CreateBlendTreeInstance(anim_graph_blend_tree); | ||||
|   AnimGraphContext graph_context; | ||||
| 
 | ||||
|   ozz::animation::Skeleton skeleton; | ||||
| @ -537,29 +501,25 @@ TEST_CASE_METHOD( | ||||
| // Checks that node const inputs are properly set.
 | ||||
| //
 | ||||
| TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|   AnimGraphResourcePtr graph_resource( | ||||
|       dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree"))); | ||||
|   graph_resource->m_name = "AnimSamplerSpeedScaleGraph"; | ||||
|   graph_resource->m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = | ||||
|       graph_resource->m_blend_tree_resource; | ||||
|   BlendTreeResource* blend_tree_resource = | ||||
|       dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|   blend_tree_resource->m_name = "AnimSamplerSpeedScaleBlendTree"; | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   size_t walk_node_index = | ||||
|       blend_tree_resource.AddNode(AnimNodeResourceFactory("AnimSampler")); | ||||
|       blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler")); | ||||
| 
 | ||||
|   size_t speed_scale_node_index = | ||||
|       blend_tree_resource.AddNode(AnimNodeResourceFactory("SpeedScale")); | ||||
|       blend_tree_resource->AddNode(AnimNodeResourceFactory("SpeedScale")); | ||||
| 
 | ||||
|   AnimNodeResource* walk_node = blend_tree_resource.GetNode(walk_node_index); | ||||
|   AnimNodeResource* walk_node = blend_tree_resource->GetNode(walk_node_index); | ||||
|   walk_node->m_name = "WalkAnim"; | ||||
|   walk_node->m_virtual_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Walking-loop.ozz")); | ||||
| 
 | ||||
|   AnimNodeResource* speed_scale_node = | ||||
|       blend_tree_resource.GetNode(speed_scale_node_index); | ||||
|       blend_tree_resource->GetNode(speed_scale_node_index); | ||||
|   speed_scale_node->m_name = "SpeedScale"; | ||||
|   float speed_scale_value = 1.35f; | ||||
|   speed_scale_node->m_virtual_socket_accessor->SetInputValue( | ||||
| @ -567,25 +527,23 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|       speed_scale_value); | ||||
| 
 | ||||
|   blend_tree_resource | ||||
|       .ConnectSockets(walk_node, "Output", speed_scale_node, "Input"); | ||||
|       ->ConnectSockets(walk_node, "Output", speed_scale_node, "Input"); | ||||
| 
 | ||||
|   blend_tree_resource.ConnectSockets( | ||||
|   blend_tree_resource->ConnectSockets( | ||||
|       speed_scale_node, | ||||
|       "Output", | ||||
|       blend_tree_resource.GetGraphOutputNode(), | ||||
|       blend_tree_resource->GetGraphOutputNode(), | ||||
|       AnimGraphResource::DefaultAnimOutput); | ||||
| 
 | ||||
|   graph_resource->SaveToFile( | ||||
|       "TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"); | ||||
|   AnimGraphResourcePtr graph_resource_loaded( | ||||
|       AnimGraphResource::CreateFromFile( | ||||
|           "TestGraphAnimSamplerSpeedScaleGraph.animgraph.json")); | ||||
|   constexpr char filename[] = | ||||
|       "TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource_loaded = | ||||
|       graph_resource_loaded->m_blend_tree_resource; | ||||
|   REQUIRE(blend_tree_resource->SaveToFile(filename)); | ||||
|   BlendTreeResource* blend_tree_resource_loaded = | ||||
|       BlendTreeResource::CreateFromFile(filename); | ||||
| 
 | ||||
|   Socket* speed_scale_resource_loaded_input = | ||||
|       blend_tree_resource_loaded.GetNode(speed_scale_node_index) | ||||
|       blend_tree_resource_loaded->GetNode(speed_scale_node_index) | ||||
|           ->m_virtual_socket_accessor->GetInputSocket("SpeedScale"); | ||||
|   REQUIRE(speed_scale_resource_loaded_input != nullptr); | ||||
| 
 | ||||
| @ -594,7 +552,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|       Catch::Matchers::WithinAbs(speed_scale_value, 0.1)); | ||||
| 
 | ||||
|   AnimGraphBlendTree blend_tree; | ||||
|   graph_resource_loaded->CreateBlendTreeInstance(blend_tree); | ||||
|   blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree); | ||||
| 
 | ||||
|   REQUIRE_THAT( | ||||
|       *dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index]) | ||||
| @ -603,7 +561,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
| 
 | ||||
|   WHEN("Checking node eval order and node subtrees") { | ||||
|     const std::vector<size_t>& eval_order = | ||||
|         graph_resource_loaded->m_blend_tree_resource.GetNodeEvalOrder(); | ||||
|         blend_tree_resource_loaded->GetNodeEvalOrder(); | ||||
| 
 | ||||
|     THEN("Walk node gets evaluated before speed scale node") { | ||||
|       CHECK(eval_order.size() == 2); | ||||
| @ -613,23 +571,26 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
| 
 | ||||
|     THEN("Subtree of the speed scale node contains only the walk node") { | ||||
|       CHECK( | ||||
|           graph_resource_loaded->m_blend_tree_resource | ||||
|               .m_node_inputs_subtree[speed_scale_node_index] | ||||
|           blend_tree_resource_loaded | ||||
|               ->m_node_inputs_subtree[speed_scale_node_index] | ||||
|               .size() | ||||
|           == 1); | ||||
|       CHECK( | ||||
|           graph_resource_loaded->m_blend_tree_resource | ||||
|               .m_node_inputs_subtree[speed_scale_node_index][0] | ||||
|           blend_tree_resource_loaded | ||||
|               ->m_node_inputs_subtree[speed_scale_node_index][0] | ||||
|           == walk_node_index); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   delete blend_tree_resource_loaded; | ||||
|   delete blend_tree_resource; | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| // Checks that connections additions and removals are properly validated.
 | ||||
| //
 | ||||
| TEST_CASE_METHOD( | ||||
|     Blend2GraphResource, | ||||
|     Blend2BlendTreeResource, | ||||
|     "Connectivity Tests", | ||||
|     "[AnimGraphResource][Blend2GraphResource]") { | ||||
|   INFO("Removing Blend2 -> Output Connection") | ||||
| @ -704,18 +665,16 @@ TEST_CASE("FreeAnimGraphResource", "[Test]") { | ||||
| } | ||||
| 
 | ||||
| TEST_CASE_METHOD( | ||||
|     Blend2GraphResource, | ||||
|     Blend2BlendTreeResource, | ||||
|     "Blend2GraphResource saving and loading results in same resource", | ||||
|     "[Blend2GraphResource]") { | ||||
|   graph_resource->SaveToFile("TestGraphBlend2Graph.animgraph.json"); | ||||
| 
 | ||||
|   AnimGraphResourcePtr graph_resource_loaded( | ||||
|       AnimGraphResource::CreateFromFile("TestGraphBlend2Graph.animgraph.json")); | ||||
| 
 | ||||
|   CheckAnimGraphResourceEqual(*graph_resource, *graph_resource_loaded); | ||||
|   constexpr char filename[] = "TestGraphBlend2Graph.animgraph.json"; | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource->SaveToFile(filename)); | ||||
|   BlendTreeResource* blend_tree_resource_loaded = | ||||
|       &graph_resource_loaded->m_blend_tree_resource; | ||||
|       BlendTreeResource::CreateFromFile(filename); | ||||
| 
 | ||||
|   CheckBlendTreeResourcesEqual(blend_tree_resource, blend_tree_resource_loaded); | ||||
| 
 | ||||
|   // Check that the constant weight of the Blend2 node was properly applied when
 | ||||
|   // loading the resource.
 | ||||
| @ -729,14 +688,16 @@ TEST_CASE_METHOD( | ||||
|       Catch::Matchers::WithinAbs( | ||||
|           blend2_node_descriptor_loaded->GetInputValue<float>("Weight"), | ||||
|           0.01)); | ||||
| 
 | ||||
|   delete blend_tree_resource_loaded; | ||||
| } | ||||
| 
 | ||||
| TEST_CASE_METHOD( | ||||
|     Blend2GraphResource, | ||||
|     Blend2BlendTreeResource, | ||||
|     "Blend2GraphResource graph unsynced evaluation", | ||||
|     "[Blend2GraphResource]") { | ||||
|   AnimGraphBlendTree blend_tree_graph; | ||||
|   graph_resource->CreateBlendTreeInstance(blend_tree_graph); | ||||
|   blend_tree_resource->CreateBlendTreeInstance(blend_tree_graph); | ||||
|   AnimGraphContext graph_context; | ||||
| 
 | ||||
|   ozz::animation::Skeleton skeleton; | ||||
| @ -835,19 +796,16 @@ TEST_CASE_METHOD( | ||||
| //
 | ||||
| //
 | ||||
| TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree); | ||||
|   graph_resource_origin.m_name = "TestInputOutputGraph"; | ||||
|   graph_resource_origin.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = | ||||
|       graph_resource_origin.m_blend_tree_resource; | ||||
|   BlendTreeResource* blend_tree_resource = | ||||
|       dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|   blend_tree_resource->m_name = "TestInputOutputGraph"; | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   size_t float_to_vec3_node_index = blend_tree_resource.AddNode( | ||||
|   size_t float_to_vec3_node_index = blend_tree_resource->AddNode( | ||||
|       AnimNodeResourceFactory("MathFloatToVec3Node")); | ||||
| 
 | ||||
|   AnimNodeResource* graph_output_node = | ||||
|       blend_tree_resource.GetGraphOutputNode(); | ||||
|       blend_tree_resource->GetGraphOutputNode(); | ||||
|   graph_output_node->m_virtual_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloatOutput", | ||||
|       nullptr); | ||||
| @ -856,38 +814,38 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|       nullptr); | ||||
| 
 | ||||
|   AnimNodeResource* graph_input_node_resource = | ||||
|       blend_tree_resource.GetGraphInputNode(); | ||||
|       blend_tree_resource->GetGraphInputNode(); | ||||
|   graph_input_node_resource->m_virtual_socket_accessor->RegisterOutput<float>( | ||||
|       "GraphFloatInput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   AnimNodeResource* float_to_vec3_node_resource = | ||||
|       blend_tree_resource.GetNode(float_to_vec3_node_index); | ||||
|       blend_tree_resource->GetNode(float_to_vec3_node_index); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node_resource, | ||||
|       "GraphFloatInput", | ||||
|       graph_output_node, | ||||
|       "GraphFloatOutput")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node_resource, | ||||
|       "GraphFloatInput", | ||||
|       float_to_vec3_node_resource, | ||||
|       "Input0")); | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node_resource, | ||||
|       "GraphFloatInput", | ||||
|       float_to_vec3_node_resource, | ||||
|       "Input1")); | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node_resource, | ||||
|       "GraphFloatInput", | ||||
|       float_to_vec3_node_resource, | ||||
|       "Input2")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       float_to_vec3_node_resource, | ||||
|       "Output", | ||||
|       graph_output_node, | ||||
| @ -895,18 +853,15 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
| 
 | ||||
|   WHEN("Saving and loading graph resource") { | ||||
|     const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; | ||||
|     graph_resource_origin.SaveToFile(filename); | ||||
|     REQUIRE(blend_tree_resource->SaveToFile(filename)); | ||||
| 
 | ||||
|     AnimGraphResourcePtr graph_resource_loaded( | ||||
|         AnimGraphResource::CreateFromFile(filename)); | ||||
| 
 | ||||
|     BlendTreeResource& graph_blend_tree_loaded = | ||||
|         graph_resource_loaded->m_blend_tree_resource; | ||||
|     BlendTreeResource* blend_tree_resource_loaded = | ||||
|         BlendTreeResource::CreateFromFile(filename); | ||||
| 
 | ||||
|     const AnimNodeResource* graph_loaded_output_node = | ||||
|         graph_blend_tree_loaded.GetGraphOutputNode(); | ||||
|         blend_tree_resource_loaded->GetGraphOutputNode(); | ||||
|     const AnimNodeResource* graph_loaded_input_node = | ||||
|         graph_blend_tree_loaded.GetGraphInputNode(); | ||||
|         blend_tree_resource_loaded->GetGraphInputNode(); | ||||
| 
 | ||||
|     THEN("Graph inputs and outputs must be in loaded resource as well.") { | ||||
|       REQUIRE( | ||||
| @ -935,7 +890,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
| 
 | ||||
|       WHEN("Instantiating an AnimGraph") { | ||||
|         AnimGraphBlendTree blend_tree_node; | ||||
|         graph_resource_loaded->CreateBlendTreeInstance(blend_tree_node); | ||||
|         blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree_node); | ||||
| 
 | ||||
|         float graph_float_input = 123.456f; | ||||
|         blend_tree_node.SetInput("GraphFloatInput", &graph_float_input); | ||||
| @ -975,7 +930,11 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     delete blend_tree_resource_loaded; | ||||
|   } | ||||
| 
 | ||||
|   delete blend_tree_resource; | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| @ -992,21 +951,18 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
| //              GraphFloat1Output -> GraphFLoatInputSingle * 3
 | ||||
| //
 | ||||
| TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree); | ||||
|   graph_resource_origin.m_name = "TestSimpleMathGraph"; | ||||
|   graph_resource_origin.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = | ||||
|       graph_resource_origin.m_blend_tree_resource; | ||||
|   BlendTreeResource* blend_tree_resource = | ||||
|       dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
|   blend_tree_resource->m_name = "TestSimpleMathGraph"; | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   size_t math_add0_node_index = | ||||
|       blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode")); | ||||
|       blend_tree_resource->AddNode(AnimNodeResourceFactory("MathAddNode")); | ||||
|   size_t math_add1_node_index = | ||||
|       blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode")); | ||||
|       blend_tree_resource->AddNode(AnimNodeResourceFactory("MathAddNode")); | ||||
| 
 | ||||
|   AnimNodeResource* graph_output_node = | ||||
|       blend_tree_resource.GetGraphOutputNode(); | ||||
|       blend_tree_resource->GetGraphOutputNode(); | ||||
| 
 | ||||
|   graph_output_node->m_virtual_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloat0Output", | ||||
| @ -1018,57 +974,57 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|       "GraphFloat2Output", | ||||
|       nullptr); | ||||
| 
 | ||||
|   AnimNodeResource* graph_input_node = blend_tree_resource.GetGraphInputNode(); | ||||
|   AnimNodeResource* graph_input_node = blend_tree_resource->GetGraphInputNode(); | ||||
|   graph_input_node->m_virtual_socket_accessor->RegisterOutput<float>( | ||||
|       "GraphFloatInput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   AnimNodeResource* math_add0_node = | ||||
|       blend_tree_resource.GetNode(math_add0_node_index); | ||||
|       blend_tree_resource->GetNode(math_add0_node_index); | ||||
|   AnimNodeResource* math_add1_node = | ||||
|       blend_tree_resource.GetNode(math_add1_node_index); | ||||
|       blend_tree_resource->GetNode(math_add1_node_index); | ||||
| 
 | ||||
|   // direct output
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node, | ||||
|       "GraphFloatInput", | ||||
|       graph_output_node, | ||||
|       "GraphFloat0Output")); | ||||
| 
 | ||||
|   // add0 node
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node, | ||||
|       "GraphFloatInput", | ||||
|       math_add0_node, | ||||
|       "Input0")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node, | ||||
|       "GraphFloatInput", | ||||
|       math_add0_node, | ||||
|       "Input1")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       math_add0_node, | ||||
|       "Output", | ||||
|       graph_output_node, | ||||
|       "GraphFloat1Output")); | ||||
| 
 | ||||
|   // add1 node
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       math_add0_node, | ||||
|       "Output", | ||||
|       math_add1_node, | ||||
|       "Input0")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       graph_input_node, | ||||
|       "GraphFloatInput", | ||||
|       math_add1_node, | ||||
|       "Input1")); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
|   REQUIRE(blend_tree_resource->ConnectSockets( | ||||
|       math_add1_node, | ||||
|       "Output", | ||||
|       graph_output_node, | ||||
| @ -1076,14 +1032,14 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
| 
 | ||||
|   WHEN("Saving and loading graph resource") { | ||||
|     const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; | ||||
|     graph_resource_origin.SaveToFile(filename); | ||||
|     REQUIRE(blend_tree_resource->SaveToFile(filename)); | ||||
| 
 | ||||
|     AnimGraphResourcePtr graph_resource_loaded( | ||||
|         AnimGraphResource::CreateFromFile(filename)); | ||||
|     BlendTreeResource* blend_tree_resource_loaded = | ||||
|         BlendTreeResource::CreateFromFile(filename); | ||||
| 
 | ||||
|     WHEN("Instantiating an AnimGraph") { | ||||
|       AnimGraphBlendTree blend_tree; | ||||
|       graph_resource_loaded->CreateBlendTreeInstance(blend_tree); | ||||
|       blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree); | ||||
| 
 | ||||
|       float graph_float_input = 123.456f; | ||||
|       blend_tree.SetInput("GraphFloatInput", &graph_float_input); | ||||
| @ -1117,7 +1073,11 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|         context.freeAnimations(); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     delete blend_tree_resource_loaded; | ||||
|   } | ||||
| 
 | ||||
|   delete blend_tree_resource; | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| @ -1137,33 +1097,28 @@ TEST_CASE_METHOD( | ||||
|     "EmbeddedBlendTreeGraphResource saving and loading results in same " | ||||
|     "resource", | ||||
|     "[EmbeddedBlendTreeGraphResource]") { | ||||
|   parent_graph_resource.SaveToFile("TestGraphEmbeddedBlendTree.json"); | ||||
|   constexpr char filename[] = "TestGraphEmbeddedBlendTree.json"; | ||||
|   REQUIRE(blend_tree_resource->SaveToFile(filename)); | ||||
| 
 | ||||
|   AnimGraphResourcePtr parent_graph_resource_loaded( | ||||
|       AnimGraphResource::CreateFromFile("TestGraphEmbeddedBlendTree.json")); | ||||
|   BlendTreeResource* blend_tree_resource_loaded = | ||||
|       BlendTreeResource::CreateFromFile(filename); | ||||
| 
 | ||||
|   // Check the loaded parent graph
 | ||||
|   CheckAnimGraphResourceEqual( | ||||
|       parent_graph_resource, | ||||
|       *parent_graph_resource_loaded); | ||||
| 
 | ||||
|   const BlendTreeResource& parent_blend_tree_resource_loaded = | ||||
|       parent_graph_resource_loaded->m_blend_tree_resource; | ||||
|   CheckBlendTreeResourcesEqual(blend_tree_resource, blend_tree_resource_loaded); | ||||
| 
 | ||||
|   // Check the loaded embedded graph
 | ||||
|   REQUIRE( | ||||
|       parent_blend_tree_resource_loaded.GetNode(3)->m_node_type_name | ||||
|       == "BlendTree"); | ||||
|       blend_tree_resource_loaded->GetNode(3)->m_node_type_name == "BlendTree"); | ||||
| 
 | ||||
|   const AnimGraphResource* embedded_graph_loaded = | ||||
|       dynamic_cast<const AnimGraphResource*>( | ||||
|           parent_blend_tree_resource_loaded.GetNode(3)); | ||||
|   const BlendTreeResource* embedded_blend_tree_resource_loaded = | ||||
|       &embedded_graph_loaded->m_blend_tree_resource; | ||||
|       dynamic_cast<const BlendTreeResource*>( | ||||
|           blend_tree_resource_loaded->GetNode(3)); | ||||
| 
 | ||||
|   CheckBlendTreeResourcesEqual( | ||||
|       embedded_blend_tree_resource, | ||||
|       embedded_blend_tree_resource_loaded); | ||||
| 
 | ||||
|   delete blend_tree_resource_loaded; | ||||
| } | ||||
| 
 | ||||
| TEST_CASE_METHOD( | ||||
| @ -1172,7 +1127,7 @@ TEST_CASE_METHOD( | ||||
|     "[EmbeddedBlendTreeGraphResource]") { | ||||
|   AnimGraphBlendTree blend_tree; | ||||
| 
 | ||||
|   parent_graph_resource.CreateBlendTreeInstance(blend_tree); | ||||
|   blend_tree_resource->CreateBlendTreeInstance(blend_tree); | ||||
|   AnimGraphContext graph_context; | ||||
| 
 | ||||
|   ozz::animation::Skeleton skeleton; | ||||
| @ -1249,7 +1204,7 @@ TEST_CASE_METHOD( | ||||
|     "[EmbeddedTreeBlend2GraphResource]") { | ||||
|   AnimGraphBlendTree blend_tree; | ||||
| 
 | ||||
|   parent_graph_resource.CreateBlendTreeInstance(blend_tree); | ||||
|   blend_tree_resource->CreateBlendTreeInstance(blend_tree); | ||||
|   AnimGraphContext graph_context; | ||||
| 
 | ||||
|   ozz::animation::Skeleton skeleton; | ||||
| @ -1332,21 +1287,19 @@ TEST_CASE_METHOD( | ||||
| TEST_CASE( | ||||
|     "Register AnimGraphResource Blendtree Sockets", | ||||
|     "[AnimGraphResource]") { | ||||
|   AnimNodeResource* blend_tree_anim_node_resource = | ||||
|       AnimNodeResourceFactory("BlendTree"); | ||||
|   AnimGraphResource* blend_tree_graph_resource = | ||||
|       dynamic_cast<AnimGraphResource*>(blend_tree_anim_node_resource); | ||||
|   BlendTreeResource* blend_tree_resource = | ||||
|       dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree")); | ||||
| 
 | ||||
|   Socket socket; | ||||
|   socket.m_name = "FloatSocket"; | ||||
|   socket.m_type = SocketType::SocketTypeFloat; | ||||
|   socket.m_reference.ptr = nullptr; | ||||
| 
 | ||||
|   CHECK(blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket)); | ||||
|   CHECK(!blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket)); | ||||
|   CHECK(blend_tree_resource->RegisterBlendTreeInputSocket(socket)); | ||||
|   CHECK(!blend_tree_resource->RegisterBlendTreeInputSocket(socket)); | ||||
| 
 | ||||
|   CHECK(blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket)); | ||||
|   CHECK(!blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket)); | ||||
|   CHECK(blend_tree_resource->RegisterBlendTreeOutputSocket(socket)); | ||||
|   CHECK(!blend_tree_resource->RegisterBlendTreeOutputSocket(socket)); | ||||
| 
 | ||||
|   delete blend_tree_anim_node_resource; | ||||
|   delete blend_tree_resource; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Martin Felis
						Martin Felis