Compare commits
	
		
			2 Commits
		
	
	
		
			e687c9b613
			...
			116bf7699b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 116bf7699b | ||
|   | 3a7f470acf | 
| @ -21,7 +21,7 @@ struct AnimNode; | ||||
| 
 | ||||
| struct AnimNodeResource { | ||||
|   std::string m_name; | ||||
|   std::string m_type_name; | ||||
|   std::string m_node_type_name; | ||||
|   AnimNode* m_anim_node = nullptr; | ||||
|   NodeDescriptorBase* m_socket_accessor = nullptr; | ||||
|   float m_position[2] = {0.f, 0.f}; | ||||
|  | ||||
| @ -162,7 +162,7 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) { | ||||
| void AnimGraphEditorRenderSidebar( | ||||
|     AnimGraphBlendTreeResource& graph_resource, | ||||
|     AnimNodeResource& node_resource) { | ||||
|   ImGui::Text("[%s]", node_resource.m_type_name.c_str()); | ||||
|   ImGui::Text("[%s]", node_resource.m_node_type_name.c_str()); | ||||
| 
 | ||||
|   char node_name_buffer[256]; | ||||
|   memset(node_name_buffer, 0, sizeof(node_name_buffer)); | ||||
| @ -307,7 +307,7 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { | ||||
|           ImVec2(node_resource.m_position[0], node_resource.m_position[1])); | ||||
|     } | ||||
|     ax::NodeEditor::BeginNode(node_id); | ||||
|     ImGui::Text("%s", node_resource.m_type_name.c_str()); | ||||
|     ImGui::Text("%s", node_resource.m_node_type_name.c_str()); | ||||
| 
 | ||||
|     // Inputs
 | ||||
|     std::vector<Socket>& node_inputs = | ||||
| @ -485,7 +485,7 @@ void LegacyAnimGraphEditorUpdate() { | ||||
|     } else if (&node_resource == &sGraphGresource.getGraphInputNode()) { | ||||
|       ImGui::TextUnformatted("Graph Inputs"); | ||||
|     } else { | ||||
|       ImGui::TextUnformatted(node_resource.m_type_name.c_str()); | ||||
|       ImGui::TextUnformatted(node_resource.m_node_type_name.c_str()); | ||||
|     } | ||||
|     ImNodes::EndNodeTitleBar(); | ||||
| 
 | ||||
|  | ||||
| @ -262,6 +262,8 @@ static inline AnimNode* AnimNodeFactory(const std::string& name) { | ||||
|     result = new LockTranslationNode; | ||||
|   } else if (name == "BlendTree") { | ||||
|     result = new BlendTreeNode; | ||||
|   } else if (name == "BlendTreeSockets") { | ||||
|     result = new BlendTreeNode; | ||||
|   } else if (name == "MathAddNode") { | ||||
|     result = new MathAddNode; | ||||
|   } else if (name == "MathFloatToVec3Node") { | ||||
| @ -292,6 +294,8 @@ static inline NodeDescriptorBase* AnimNodeDescriptorFactory( | ||||
|     return CreateNodeDescriptor<LockTranslationNode>(node); | ||||
|   } else if (node_type_name == "BlendTree") { | ||||
|     return CreateNodeDescriptor<BlendTreeNode>(node); | ||||
|   } else if (node_type_name == "BlendTreeSockets") { | ||||
|     return CreateNodeDescriptor<BlendTreeNode>(node); | ||||
|   } else if (node_type_name == "MathAddNode") { | ||||
|     return CreateNodeDescriptor<MathAddNode>(node); | ||||
|   } else if (node_type_name == "MathFloatToVec3Node") { | ||||
|  | ||||
| @ -14,6 +14,10 @@ | ||||
| 
 | ||||
| using json = nlohmann::json; | ||||
| 
 | ||||
| // forward declarations
 | ||||
| static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource); | ||||
| static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource); | ||||
| 
 | ||||
| //
 | ||||
| // Socket <-> json
 | ||||
| //
 | ||||
| @ -125,20 +129,24 @@ Socket sJsonToSocket(const json& json_data) { | ||||
| // AnimGraphNode <-> json
 | ||||
| //
 | ||||
| json sAnimGraphNodeToJson( | ||||
|     const AnimNodeResource& node, | ||||
|     const AnimNodeResource* node, | ||||
|     size_t node_index, | ||||
|     const std::vector<BlendTreeConnectionResource>& connections) { | ||||
|   json result; | ||||
| 
 | ||||
|   result["name"] = node.m_name; | ||||
|   result["name"] = node->m_name; | ||||
|   result["type"] = "AnimNodeResource"; | ||||
|   result["node_type"] = node.m_type_name; | ||||
|   result["node_type"] = node->m_node_type_name; | ||||
| 
 | ||||
|   for (size_t j = 0; j < 2; j++) { | ||||
|     result["position"][j] = node.m_position[j]; | ||||
|     result["position"][j] = node->m_position[j]; | ||||
|   } | ||||
| 
 | ||||
|   for (const auto& socket : node.m_socket_accessor->m_inputs) { | ||||
|   if (node->m_node_type_name == "BlendTree") { | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   for (const auto& socket : node->m_socket_accessor->m_inputs) { | ||||
|     if (socket.m_type == SocketType::SocketTypeAnimation) { | ||||
|       continue; | ||||
|     } | ||||
| @ -157,28 +165,37 @@ json sAnimGraphNodeToJson( | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   for (auto& property : node.m_socket_accessor->m_properties) { | ||||
|   for (auto& property : node->m_socket_accessor->m_properties) { | ||||
|     result["properties"][property.m_name] = sSocketToJson(property); | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| AnimNodeResource sAnimGraphNodeFromJson( | ||||
| AnimNodeResource* sAnimGraphNodeFromJson( | ||||
|     const json& json_node, | ||||
|     size_t node_index) { | ||||
|   AnimNodeResource result; | ||||
| 
 | ||||
|   result.m_name = json_node["name"]; | ||||
|   result.m_type_name = json_node["node_type"]; | ||||
|   result.m_position[0] = json_node["position"][0]; | ||||
|   result.m_position[1] = json_node["position"][1]; | ||||
|   std::string node_type = json_node["node_type"]; | ||||
| 
 | ||||
|   result.m_anim_node = AnimNodeFactory(result.m_type_name); | ||||
|   result.m_socket_accessor = | ||||
|       AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node); | ||||
|   if (node_type == "BlendTree") { | ||||
|     AnimGraphResource* result = new AnimGraphResource(); | ||||
|     sAnimGraphResourceBlendTreeFromJson(json_node, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   for (auto& property : result.m_socket_accessor->m_properties) { | ||||
|   AnimNodeResource* result; | ||||
|   result = new AnimNodeResource(); | ||||
|   result->m_name = json_node["name"]; | ||||
|   result->m_node_type_name = node_type; | ||||
|   result->m_position[0] = json_node["position"][0]; | ||||
|   result->m_position[1] = json_node["position"][1]; | ||||
| 
 | ||||
|   result->m_anim_node = AnimNodeFactory(result->m_node_type_name); | ||||
|   result->m_socket_accessor = | ||||
|       AnimNodeDescriptorFactory(result->m_node_type_name, result->m_anim_node); | ||||
| 
 | ||||
|   for (auto& property : result->m_socket_accessor->m_properties) { | ||||
|     property = sJsonToSocket(json_node["properties"][property.m_name]); | ||||
|   } | ||||
| 
 | ||||
| @ -187,10 +204,10 @@ AnimNodeResource sAnimGraphNodeFromJson( | ||||
|       assert(json_node["inputs"][j].contains("name")); | ||||
|       std::string input_name = json_node["inputs"][j]["name"]; | ||||
|       Socket* input_socket = | ||||
|           result.m_socket_accessor->GetInputSocket(input_name.c_str()); | ||||
|           result->m_socket_accessor->GetInputSocket(input_name.c_str()); | ||||
|       if (input_socket == nullptr) { | ||||
|         std::cerr << "Could not find input socket with name " << input_name | ||||
|                   << " for node type " << result.m_type_name << std::endl; | ||||
|                   << " for node type " << result->m_node_type_name << std::endl; | ||||
|         abort(); | ||||
|       } | ||||
|       *input_socket = sJsonToSocket(json_node["inputs"][j]); | ||||
| @ -230,6 +247,160 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson( | ||||
|   return connection; | ||||
| } | ||||
| 
 | ||||
| static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource) { | ||||
|   json result; | ||||
| 
 | ||||
|   result["name"] = anim_graph_resource.m_name; | ||||
|   result["type"] = "AnimNodeResource"; | ||||
|   result["node_type"] = "BlendTree"; | ||||
| 
 | ||||
|   const BlendTreeResource& blend_tree_resource = anim_graph_resource.m_blend_tree_resource; | ||||
| 
 | ||||
|   for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) { | ||||
|     const AnimNodeResource* node = blend_tree_resource.m_nodes[i]; | ||||
| 
 | ||||
|     if (node->m_node_type_name == "BlendTree") { | ||||
|       const AnimGraphResource* graph_resource = dynamic_cast<const AnimGraphResource*>(node); | ||||
|       result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); | ||||
|     } else { | ||||
|       result["nodes"][i] = | ||||
|           sAnimGraphNodeToJson(node, i, blend_tree_resource.m_connections); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   for (size_t i = 0; i < blend_tree_resource.m_connections.size(); i++) { | ||||
|     const BlendTreeConnectionResource& connection = | ||||
|         blend_tree_resource.m_connections[i]; | ||||
|     result["connections"][i] = sAnimGraphConnectionToJson(connection); | ||||
|   } | ||||
| 
 | ||||
|   // Graph inputs and outputs
 | ||||
|   { | ||||
|     const AnimNodeResource* graph_output_node = | ||||
|         blend_tree_resource.m_nodes[0]; | ||||
|     const std::vector<Socket> graph_inputs = | ||||
|         graph_output_node->m_socket_accessor->m_inputs; | ||||
|     for (size_t i = 0; i < graph_inputs.size(); i++) { | ||||
|       result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]); | ||||
|     } | ||||
| 
 | ||||
|     const AnimNodeResource* graph_input_node = blend_tree_resource.m_nodes[1]; | ||||
|     const std::vector<Socket> graph_outputs = | ||||
|         graph_input_node->m_socket_accessor->m_outputs; | ||||
|     for (size_t i = 0; i < graph_outputs.size(); i++) { | ||||
|       result["nodes"][1]["outputs"][i] = sSocketToJson(graph_outputs[i]); | ||||
|     } | ||||
|   } | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource) { | ||||
|   BlendTreeResource& blend_tree_resource = result_graph_resource->m_blend_tree_resource; | ||||
| 
 | ||||
|   result_graph_resource->m_graph_type_name = "BlendTree"; | ||||
|   result_graph_resource->m_node_type_name = "BlendTree"; | ||||
|   result_graph_resource->m_name = json_data["name"]; | ||||
| 
 | ||||
|   // Load nodes
 | ||||
|   for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { | ||||
|     const json& json_node = json_data["nodes"][i]; | ||||
|     if (json_node["type"] != "AnimNodeResource") { | ||||
|       std::cerr | ||||
|           << "Invalid json object. Expected type 'AnimNodeResource' but got '" | ||||
|           << json_node["type"] << "'." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i); | ||||
|     blend_tree_resource.m_nodes.push_back(node); | ||||
|   } | ||||
| 
 | ||||
|   // Graph outputs
 | ||||
|   const json& graph_outputs = json_data["nodes"][0]["inputs"]; | ||||
|   for (const auto& graph_output : graph_outputs) { | ||||
|     AnimNodeResource* graph_node = blend_tree_resource.m_nodes[0]; | ||||
|     graph_node->m_socket_accessor->m_inputs.push_back( | ||||
|         sJsonToSocket(graph_output)); | ||||
|   } | ||||
| 
 | ||||
|   // Graph inputs (optional)
 | ||||
|   if (json_data["nodes"][1].contains("outputs")) { | ||||
|     const json& graph_inputs = json_data["nodes"][1]["outputs"]; | ||||
|     for (const auto& graph_input : graph_inputs) { | ||||
|       AnimNodeResource* graph_node = blend_tree_resource.m_nodes[1]; | ||||
|       graph_node->m_socket_accessor->m_outputs.push_back( | ||||
|           sJsonToSocket(graph_input)); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Load connections
 | ||||
|   for (const auto& json_connection : json_data["connections"]) { | ||||
|     if (json_connection["type"] != "AnimGraphConnectionResource") { | ||||
|       std::cerr << "Invalid json object. Expected type " | ||||
|                    "'AnimGraphConnectionResource' " | ||||
|                    "but got '" | ||||
|                 << json_connection["type"] << "'." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     BlendTreeConnectionResource connection = | ||||
|         sAnimGraphConnectionFromJson(json_connection); | ||||
|     blend_tree_resource.m_connections.push_back(connection); | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool BlendTreeResource::ConnectSockets ( | ||||
|     const AnimNodeResource* source_node, | ||||
|     const std::string& source_socket_name, | ||||
|     const AnimNodeResource* target_node, | ||||
|     const std::string& target_socket_name) { | ||||
|   size_t source_node_index = GetNodeIndex(source_node); | ||||
|   size_t target_node_index = GetNodeIndex(target_node); | ||||
|    | ||||
|   if (source_node_index >= m_nodes.size() | ||||
|       || target_node_index >= m_nodes.size()) { | ||||
|     std::cerr << "Cannot connect nodes: could not find nodes." << std::endl; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   Socket* source_socket; | ||||
|   Socket* target_socket; | ||||
| 
 | ||||
|   if (target_node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* target_graph_resource = dynamic_cast<const AnimGraphResource*>(target_node); | ||||
|     AnimNodeResource* graph_output_node = target_graph_resource->m_blend_tree_resource.GetGraphInputNode(); | ||||
|     target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(target_socket_name.c_str()); | ||||
|   } else { | ||||
|     target_socket = | ||||
|         target_node->m_socket_accessor->GetInputSocket(target_socket_name.c_str()); | ||||
|   } | ||||
| 
 | ||||
|   if (source_node->m_node_type_name == "BlendTree") { | ||||
|     const AnimGraphResource* source_graph_resource = dynamic_cast<const AnimGraphResource*>(source_node); | ||||
|     AnimNodeResource* graph_output_node = source_graph_resource->m_blend_tree_resource.GetGraphOutputNode(); | ||||
|     source_socket = graph_output_node->m_socket_accessor->GetInputSocket(source_socket_name.c_str()); | ||||
|   } else { | ||||
|     source_socket = | ||||
|         source_node->m_socket_accessor->GetOutputSocket(source_socket_name.c_str()); | ||||
|   } | ||||
|    | ||||
|   if (source_socket == nullptr || target_socket == nullptr) { | ||||
|     std::cerr << "Cannot connect nodes: could not find sockets." << std::endl; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   BlendTreeConnectionResource connection; | ||||
|   connection.source_node_index = source_node_index; | ||||
|   connection.source_socket_name = source_socket_name; | ||||
|   connection.target_node_index = target_node_index; | ||||
|   connection.target_socket_name = target_socket_name; | ||||
|   m_connections.push_back(connection); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool AnimGraphResource::LoadFromFile(const char* filename) { | ||||
|   std::ifstream input_file; | ||||
|   input_file.open(filename); | ||||
| @ -253,7 +424,7 @@ bool AnimGraphResource::LoadFromFile(const char* filename) { | ||||
|   } | ||||
| 
 | ||||
|   if (json_data["node_type"] == "BlendTree") { | ||||
|     return LoadBlendTreeResourceFromJson(json_data); | ||||
|     return sAnimGraphResourceBlendTreeFromJson(json_data, this); | ||||
|   } else if (json_data["node_type"] == "StateMachine") { | ||||
|     return LoadStateMachineResourceFromJson(json_data); | ||||
|   } | ||||
| @ -265,69 +436,14 @@ bool AnimGraphResource::LoadFromFile(const char* filename) { | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| bool AnimGraphResource::LoadBlendTreeResourceFromJson(nlohmann::json const& json_data) { | ||||
|   m_blend_tree_resource.Reset(); | ||||
|   m_type = "BlendTree"; | ||||
|   m_name = json_data["name"]; | ||||
| 
 | ||||
|   // Load nodes
 | ||||
|   for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { | ||||
|     const json& json_node = json_data["nodes"][i]; | ||||
|     if (json_node["type"] != "AnimNodeResource") { | ||||
|       std::cerr | ||||
|           << "Invalid json object. Expected type 'AnimNodeResource' but got '" | ||||
|           << json_node["type"] << "'." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i); | ||||
|     m_blend_tree_resource.m_nodes.push_back(node); | ||||
|   } | ||||
| 
 | ||||
|   // Graph outputs
 | ||||
|   const json& graph_outputs = json_data["nodes"][0]["inputs"]; | ||||
|   for (const auto& graph_output : graph_outputs) { | ||||
|     AnimNodeResource& graph_node = m_blend_tree_resource.m_nodes[0]; | ||||
|     graph_node.m_socket_accessor->m_inputs.push_back( | ||||
|         sJsonToSocket(graph_output)); | ||||
|   } | ||||
| 
 | ||||
|   // Graph inputs (optional)
 | ||||
|   if (json_data["nodes"][1].contains("outputs")) { | ||||
|       const json& graph_inputs = json_data["nodes"][1]["outputs"]; | ||||
|       for (const auto& graph_input : graph_inputs) { | ||||
|         AnimNodeResource& graph_node = m_blend_tree_resource.m_nodes[1]; | ||||
|         graph_node.m_socket_accessor->m_outputs.push_back( | ||||
|             sJsonToSocket(graph_input)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|   // Load connections
 | ||||
|   for (const auto& json_connection : json_data["connections"]) { | ||||
|     if (json_connection["type"] != "AnimGraphConnectionResource") { | ||||
|       std::cerr << "Invalid json object. Expected type " | ||||
|                    "'AnimGraphConnectionResource' " | ||||
|                    "but got '" | ||||
|                 << json_connection["type"] << "'." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     BlendTreeConnectionResource connection = | ||||
|         sAnimGraphConnectionFromJson(json_connection); | ||||
|     m_blend_tree_resource.m_connections.push_back(connection); | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool AnimGraphResource::SaveToFile(const char* filename) const { | ||||
|   if (m_type == "BlendTree") { | ||||
|   if (m_graph_type_name == "BlendTree") { | ||||
|     return SaveBlendTreeResourceToFile(filename); | ||||
|   } else if (m_type == "StateMachine") { | ||||
|   } else if (m_graph_type_name == "StateMachine") { | ||||
|     return SaveStateMachineResourceToFile(filename); | ||||
|   } | ||||
| 
 | ||||
|   std::cerr << "Invalid AnimGraphResource type: " << m_type << "." << std::endl; | ||||
|   std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "." << std::endl; | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| @ -336,39 +452,8 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile( | ||||
|     const char* filename) const { | ||||
|   json result; | ||||
| 
 | ||||
|   result["name"] = m_name; | ||||
|   result["type"] = "AnimNodeResource"; | ||||
|   result["node_type"] = "BlendTree"; | ||||
| 
 | ||||
|   for (size_t i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { | ||||
|     const AnimNodeResource& node = m_blend_tree_resource.m_nodes[i]; | ||||
|     result["nodes"][i] = | ||||
|         sAnimGraphNodeToJson(node, i, m_blend_tree_resource.m_connections); | ||||
|   } | ||||
| 
 | ||||
|   for (size_t i = 0; i < m_blend_tree_resource.m_connections.size(); i++) { | ||||
|     const BlendTreeConnectionResource& connection = | ||||
|         m_blend_tree_resource.m_connections[i]; | ||||
|     result["connections"][i] = sAnimGraphConnectionToJson(connection); | ||||
|   } | ||||
| 
 | ||||
|   // Graph inputs and outputs
 | ||||
|   { | ||||
|     const AnimNodeResource& graph_output_node = | ||||
|         m_blend_tree_resource.m_nodes[0]; | ||||
|     const std::vector<Socket> graph_inputs = | ||||
|         graph_output_node.m_socket_accessor->m_inputs; | ||||
|     for (size_t i = 0; i < graph_inputs.size(); i++) { | ||||
|       result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]); | ||||
|     } | ||||
| 
 | ||||
|     const AnimNodeResource& graph_input_node = m_blend_tree_resource.m_nodes[1]; | ||||
|     const std::vector<Socket> graph_outputs = | ||||
|         graph_input_node.m_socket_accessor->m_outputs; | ||||
|     for (size_t i = 0; i < graph_outputs.size(); i++) { | ||||
|       result["nodes"][1]["outputs"][i] = sSocketToJson(graph_outputs[i]); | ||||
|     } | ||||
|   } | ||||
|   result = sAnimGraphResourceBlendTreeToJson( | ||||
|       *this); | ||||
| 
 | ||||
|   std::ofstream output_file; | ||||
|   output_file.open(filename); | ||||
| @ -380,9 +465,9 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile( | ||||
| 
 | ||||
| void AnimGraphResource::CreateBlendTreeInstance( | ||||
|     AnimGraphBlendTree& result) const { | ||||
|   if (m_type != "BlendTree") { | ||||
|   if (m_node_type_name != "BlendTree") { | ||||
|     std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" | ||||
|     << m_type << "'." << std::endl; | ||||
|     << m_graph_type_name << "'." << std::endl; | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
| @ -396,11 +481,10 @@ void AnimGraphResource::CreateBlendTreeInstance( | ||||
| 
 | ||||
| void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( | ||||
|     AnimGraphBlendTree& result) const { | ||||
|   for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { | ||||
|     const AnimNodeResource& node_resource = m_blend_tree_resource.m_nodes[i]; | ||||
|     AnimNode* node = AnimNodeFactory(node_resource.m_type_name); | ||||
|     node->m_name = node_resource.m_name; | ||||
|     node->m_node_type_name = node_resource.m_type_name; | ||||
|   for (auto node_resource : m_blend_tree_resource.m_nodes) { | ||||
|     AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); | ||||
|     node->m_name = node_resource->m_name; | ||||
|     node->m_node_type_name = node_resource->m_node_type_name; | ||||
|     result.m_nodes.push_back(node); | ||||
| 
 | ||||
|     // runtime node connections
 | ||||
| @ -414,9 +498,9 @@ void AnimGraphResource::PrepareBlendTreeIOData( | ||||
|   instance.m_node_descriptor = | ||||
|       AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]); | ||||
|   instance.m_node_descriptor->m_outputs = | ||||
|       m_blend_tree_resource.m_nodes[1].m_socket_accessor->m_outputs; | ||||
|       m_blend_tree_resource.m_nodes[1]->m_socket_accessor->m_outputs; | ||||
|   instance.m_node_descriptor->m_inputs = | ||||
|       m_blend_tree_resource.m_nodes[0].m_socket_accessor->m_inputs; | ||||
|       m_blend_tree_resource.m_nodes[0]->m_socket_accessor->m_inputs; | ||||
| 
 | ||||
|   //
 | ||||
|   // graph inputs
 | ||||
| @ -466,9 +550,9 @@ void AnimGraphResource::PrepareBlendTreeIOData( | ||||
|   // TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
 | ||||
|   size_t connection_data_storage_size = 0; | ||||
|   for (const auto& connection : m_blend_tree_resource.m_connections) { | ||||
|     const AnimNodeResource& source_node = | ||||
|     const AnimNodeResource* source_node = | ||||
|         m_blend_tree_resource.m_nodes[connection.source_node_index]; | ||||
|     Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket( | ||||
|     Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket( | ||||
|         connection.source_socket_name.c_str()); | ||||
|     connection_data_storage_size += source_socket->m_type_size; | ||||
|   } | ||||
| @ -483,7 +567,7 @@ void AnimGraphResource::PrepareBlendTreeIOData( | ||||
|       nullptr); | ||||
|   for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { | ||||
|     instance_node_descriptors[i] = AnimNodeDescriptorFactory( | ||||
|         m_blend_tree_resource.m_nodes[i].m_type_name, | ||||
|         m_blend_tree_resource.m_nodes[i]->m_node_type_name, | ||||
|         instance.m_nodes[i]); | ||||
|   } | ||||
| 
 | ||||
| @ -579,13 +663,13 @@ void AnimGraphResource::PrepareBlendTreeIOData( | ||||
| void AnimGraphResource::SetRuntimeNodeProperties( | ||||
|     AnimGraphBlendTree& result) const { | ||||
|   for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) { | ||||
|     const AnimNodeResource& node_resource = m_blend_tree_resource.m_nodes[i]; | ||||
|     const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i]; | ||||
| 
 | ||||
|     NodeDescriptorBase* node_instance_accessor = | ||||
|         AnimNodeDescriptorFactory(node_resource.m_type_name, result.m_nodes[i]); | ||||
|         AnimNodeDescriptorFactory(node_resource->m_node_type_name, result.m_nodes[i]); | ||||
| 
 | ||||
|     std::vector<Socket>& resource_properties = | ||||
|         node_resource.m_socket_accessor->m_properties; | ||||
|         node_resource->m_socket_accessor->m_properties; | ||||
|     for (const auto& property : resource_properties) { | ||||
|       const std::string& name = property.m_name; | ||||
| 
 | ||||
|  | ||||
| @ -14,22 +14,16 @@ struct AnimGraphBlendTree; | ||||
| struct AnimGraphStateMachine; | ||||
| 
 | ||||
| struct AnimNodeResource { | ||||
|   virtual ~AnimNodeResource() = default; | ||||
| 
 | ||||
|   std::string m_name; | ||||
|   std::string m_type_name; | ||||
|   std::string m_node_type_name; | ||||
|   AnimNode* m_anim_node = nullptr; | ||||
|   NodeDescriptorBase* m_socket_accessor = nullptr; | ||||
|   float m_position[2] = {0.f, 0.f}; | ||||
| }; | ||||
| 
 | ||||
| static inline AnimNodeResource AnimNodeResourceFactory( | ||||
|     const std::string& node_type_name) { | ||||
|   AnimNodeResource result; | ||||
|   result.m_type_name = node_type_name; | ||||
|   result.m_anim_node = AnimNodeFactory(node_type_name); | ||||
|   result.m_socket_accessor = | ||||
|       AnimNodeDescriptorFactory(node_type_name, result.m_anim_node); | ||||
|   return result; | ||||
| } | ||||
| static inline AnimNodeResource* AnimNodeResourceFactory(const std::string& node_type_name); | ||||
| 
 | ||||
| struct BlendTreeConnectionResource { | ||||
|   size_t source_node_index = -1; | ||||
| @ -39,27 +33,41 @@ struct BlendTreeConnectionResource { | ||||
| }; | ||||
| 
 | ||||
| struct BlendTreeResource { | ||||
|   std::vector<AnimNodeResource> m_nodes; | ||||
|   std::vector<AnimNodeResource*> m_nodes; | ||||
|   std::vector<BlendTreeConnectionResource> m_connections; | ||||
| 
 | ||||
|   ~BlendTreeResource() { CleanupNodes(); | ||||
|   } | ||||
| 
 | ||||
|   void Reset() { | ||||
|     m_nodes.clear(); | ||||
|     CleanupNodes(); | ||||
| 
 | ||||
|     m_connections.clear(); | ||||
|   } | ||||
| 
 | ||||
|   void InitGraphConnectors() { | ||||
|     m_nodes.push_back(AnimNodeResourceFactory("BlendTree")); | ||||
|     m_nodes[0].m_name = "Outputs"; | ||||
|     m_nodes.push_back(AnimNodeResourceFactory("BlendTree")); | ||||
|     m_nodes[1].m_name = "Inputs"; | ||||
|   void CleanupNodes() { | ||||
|     for (AnimNodeResource* node_resource : m_nodes) { | ||||
|       delete node_resource->m_anim_node; | ||||
|       delete node_resource->m_socket_accessor; | ||||
|       delete node_resource; | ||||
|     } | ||||
| 
 | ||||
|     m_nodes.clear(); | ||||
|   } | ||||
| 
 | ||||
|   AnimNodeResource& GetGraphOutputNode() { return m_nodes[0]; } | ||||
|   AnimNodeResource& GetGraphInputNode() { return m_nodes[1]; } | ||||
|   void InitGraphConnectors() { | ||||
|     m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets")); | ||||
|     m_nodes[0]->m_name = "Outputs"; | ||||
|     m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets")); | ||||
|     m_nodes[1]->m_name = "Inputs"; | ||||
|   } | ||||
| 
 | ||||
|   size_t GetNodeIndex(const AnimNodeResource& node_resource) const { | ||||
|   [[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { return m_nodes[0]; } | ||||
|   [[nodiscard]] AnimNodeResource* GetGraphInputNode() const { return m_nodes[1]; } | ||||
| 
 | ||||
|   size_t GetNodeIndex(const AnimNodeResource* node_resource) const { | ||||
|     for (size_t i = 0, n = m_nodes.size(); i < n; i++) { | ||||
|       if (&m_nodes[i] == &node_resource) { | ||||
|       if (m_nodes[i] == node_resource) { | ||||
|         return i; | ||||
|       } | ||||
|     } | ||||
| @ -68,38 +76,10 @@ struct BlendTreeResource { | ||||
|   } | ||||
| 
 | ||||
|   bool ConnectSockets ( | ||||
|       const AnimNodeResource& source_node, | ||||
|       const AnimNodeResource* source_node, | ||||
|       const std::string& source_socket_name, | ||||
|       const AnimNodeResource& target_node, | ||||
|       const std::string& target_socket_name) { | ||||
|     size_t source_node_index = GetNodeIndex(source_node); | ||||
|     size_t target_node_index = GetNodeIndex(target_node); | ||||
| 
 | ||||
|     if (source_node_index >= m_nodes.size() | ||||
|         || target_node_index >= m_nodes.size()) { | ||||
|       std::cerr << "Cannot connect nodes: could not find nodes." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     Socket* source_socket = | ||||
|         source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str()); | ||||
|     Socket* target_socket = | ||||
|         target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str()); | ||||
| 
 | ||||
|     if (source_socket == nullptr || target_socket == nullptr) { | ||||
|       std::cerr << "Cannot connect nodes: could not find sockets." << std::endl; | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     BlendTreeConnectionResource connection; | ||||
|     connection.source_node_index = source_node_index; | ||||
|     connection.source_socket_name = source_socket_name; | ||||
|     connection.target_node_index = target_node_index; | ||||
|     connection.target_socket_name = target_socket_name; | ||||
|     m_connections.push_back(connection); | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
|       const AnimNodeResource* target_node, | ||||
|       const std::string& target_socket_name); | ||||
| 
 | ||||
|   std::vector<Socket*> GetConstantNodeInputs( | ||||
|       std::vector<NodeDescriptorBase*>& instance_node_descriptors) const { | ||||
| @ -112,7 +92,7 @@ struct BlendTreeResource { | ||||
|         Socket& input = instance_node_descriptors[i]->m_inputs[j]; | ||||
| 
 | ||||
|         if (*input.m_reference.ptr_ptr == nullptr) { | ||||
|           memcpy(&input.m_value, &m_nodes[i].m_socket_accessor->m_inputs[j].m_value, sizeof(Socket::SocketValue)); | ||||
|           memcpy(&input.m_value, &m_nodes[i]->m_socket_accessor->m_inputs[j].m_value, sizeof(Socket::SocketValue)); | ||||
|           result.push_back(&input); | ||||
|         } | ||||
|       } | ||||
| @ -134,8 +114,8 @@ struct StateMachineResource { | ||||
|   std::vector<StateMachineTransitionResources> m_transitions; | ||||
| }; | ||||
| 
 | ||||
| struct AnimGraphResource { | ||||
|   std::string m_type; | ||||
| struct AnimGraphResource: AnimNodeResource { | ||||
|   std::string m_graph_type_name; | ||||
|   std::string m_name; | ||||
| 
 | ||||
|   BlendTreeResource m_blend_tree_resource; | ||||
| @ -150,7 +130,6 @@ struct AnimGraphResource { | ||||
|  private: | ||||
|   // BlendTree
 | ||||
|   bool SaveBlendTreeResourceToFile(const char* filename) const; | ||||
|   bool LoadBlendTreeResourceFromJson(nlohmann::json const& json_data); | ||||
|   void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; | ||||
|   void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const; | ||||
|   void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; | ||||
| @ -159,4 +138,30 @@ struct AnimGraphResource { | ||||
|   bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data); | ||||
| }; | ||||
| 
 | ||||
| static inline AnimNodeResource* AnimNodeResourceFactory( | ||||
|     const std::string& node_type_name) { | ||||
|   AnimNodeResource* result; | ||||
| 
 | ||||
|   if (node_type_name == "BlendTree") { | ||||
|     AnimGraphResource* blend_tree_resource = new AnimGraphResource(); | ||||
|     blend_tree_resource->m_blend_tree_resource.InitGraphConnectors(); | ||||
|     result = blend_tree_resource; | ||||
|   } else { | ||||
|     result = new AnimNodeResource(); | ||||
|   } | ||||
| 
 | ||||
|   result->m_node_type_name = node_type_name; | ||||
| 
 | ||||
|   if (node_type_name == "BlendTreeSockets") { | ||||
|     result->m_anim_node = AnimNodeFactory("BlendTree"); | ||||
|     result->m_socket_accessor = new NodeDescriptorBase(); | ||||
|   } else { | ||||
|     result->m_anim_node = AnimNodeFactory(node_type_name); | ||||
|     result->m_socket_accessor = | ||||
|         AnimNodeDescriptorFactory(node_type_name, result->m_anim_node); | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| #endif  //ANIMTESTBED_ANIMGRAPHRESOURCE_H
 | ||||
|  | ||||
| @ -55,24 +55,23 @@ TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { | ||||
| TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource; | ||||
|   graph_resource.m_name = "AnimSamplerBlendTree"; | ||||
|   graph_resource.m_type = "BlendTree"; | ||||
|   graph_resource.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = graph_resource.m_blend_tree_resource; | ||||
|   blend_tree_resource.Reset(); | ||||
|   blend_tree_resource.InitGraphConnectors(); | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("AnimSampler")); | ||||
|   size_t walk_node_index = blend_tree_resource.m_nodes.size() - 1; | ||||
| 
 | ||||
|   AnimNodeResource& walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node.m_name = "WalkAnim"; | ||||
|   walk_node.m_socket_accessor->SetPropertyValue( | ||||
|   AnimNodeResource* walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node->m_name = "WalkAnim"; | ||||
|   walk_node->m_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Walking-loop.ozz")); | ||||
| 
 | ||||
|   AnimNodeResource& graph_node = blend_tree_resource.m_nodes[0]; | ||||
|   graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
|   AnimNodeResource* graph_node = blend_tree_resource.m_nodes[0]; | ||||
|   graph_node->m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
| 
 | ||||
|   blend_tree_resource.ConnectSockets( | ||||
|       walk_node, | ||||
| @ -80,10 +79,10 @@ TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") { | ||||
|       blend_tree_resource.GetGraphOutputNode(), | ||||
|       "GraphOutput"); | ||||
| 
 | ||||
|   graph_resource.SaveToFile("AnimSamplerBlendTree.json"); | ||||
|   graph_resource.SaveToFile("TestGraphAnimSamplerBlendTree.json"); | ||||
| 
 | ||||
|   AnimGraphResource graph_resource_loaded; | ||||
|   graph_resource_loaded.LoadFromFile("AnimSamplerBlendTree.json"); | ||||
|   graph_resource_loaded.LoadFromFile("TestGraphAnimSamplerBlendTree.json"); | ||||
| 
 | ||||
|   AnimGraphBlendTree anim_graph_blend_tree; | ||||
|   graph_resource_loaded.CreateBlendTreeInstance(anim_graph_blend_tree); | ||||
| @ -96,8 +95,8 @@ TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") { | ||||
|   REQUIRE(anim_graph_blend_tree.Init(graph_context)); | ||||
| 
 | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes.size() == 3); | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes[0]->m_node_type_name == "BlendTree"); | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes[1]->m_node_type_name == "BlendTree"); | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes[0]->m_node_type_name == "BlendTreeSockets"); | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes[1]->m_node_type_name == "BlendTreeSockets"); | ||||
|   REQUIRE(anim_graph_blend_tree.m_nodes[2]->m_node_type_name == "AnimSampler"); | ||||
| 
 | ||||
|   // connections within the graph
 | ||||
| @ -139,7 +138,7 @@ TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") { | ||||
| TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource; | ||||
|   graph_resource.m_name = "AnimSamplerSpeedScaleGraph"; | ||||
|   graph_resource.m_type = "BlendTree"; | ||||
|   graph_resource.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = graph_resource.m_blend_tree_resource; | ||||
|   blend_tree_resource.Reset(); | ||||
| @ -152,22 +151,22 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|   blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("SpeedScale")); | ||||
|   size_t speed_scale_node_index = blend_tree_resource.m_nodes.size() - 1; | ||||
| 
 | ||||
|   AnimNodeResource& walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node.m_name = "WalkAnim"; | ||||
|   walk_node.m_socket_accessor->SetPropertyValue( | ||||
|   AnimNodeResource* walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node->m_name = "WalkAnim"; | ||||
|   walk_node->m_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Walking-loop.ozz")); | ||||
| 
 | ||||
|   AnimNodeResource& speed_scale_node = | ||||
|   AnimNodeResource* speed_scale_node = | ||||
|       blend_tree_resource.m_nodes[speed_scale_node_index]; | ||||
|   speed_scale_node.m_name = "SpeedScale"; | ||||
|   speed_scale_node->m_name = "SpeedScale"; | ||||
|   float speed_scale_value = 1.35f; | ||||
|   speed_scale_node.m_socket_accessor->SetInputValue( | ||||
|   speed_scale_node->m_socket_accessor->SetInputValue( | ||||
|       "SpeedScale", | ||||
|       speed_scale_value); | ||||
| 
 | ||||
|   AnimNodeResource& graph_node = blend_tree_resource.m_nodes[0]; | ||||
|   graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
|   AnimNodeResource* graph_node = blend_tree_resource.GetGraphOutputNode(); | ||||
|   graph_node->m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
| 
 | ||||
|   blend_tree_resource.ConnectSockets(walk_node, "Output", speed_scale_node, "Input"); | ||||
| 
 | ||||
| @ -177,16 +176,15 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
|       blend_tree_resource.GetGraphOutputNode(), | ||||
|       "GraphOutput"); | ||||
| 
 | ||||
|   graph_resource.SaveToFile("AnimSamplerSpeedScaleGraph.animgraph.json"); | ||||
|   graph_resource.SaveToFile("TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"); | ||||
|   AnimGraphResource graph_resource_loaded; | ||||
|   graph_resource_loaded.LoadFromFile( | ||||
|       "AnimSamplerSpeedScaleGraph.animgraph.json"); | ||||
|       "TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"); | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource_loaded = graph_resource_loaded.m_blend_tree_resource; | ||||
| 
 | ||||
|   Socket* speed_scale_resource_loaded_input = | ||||
|       blend_tree_resource_loaded.m_nodes[speed_scale_node_index] | ||||
|           .m_socket_accessor->GetInputSocket("SpeedScale"); | ||||
|       blend_tree_resource_loaded.m_nodes[speed_scale_node_index]->m_socket_accessor->GetInputSocket("SpeedScale"); | ||||
|   REQUIRE(speed_scale_resource_loaded_input != nullptr); | ||||
| 
 | ||||
|   REQUIRE_THAT( | ||||
| @ -203,7 +201,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { | ||||
| TEST_CASE("Blend2Graph", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource; | ||||
|   graph_resource.m_name = "WalkRunBlendGraph"; | ||||
|   graph_resource.m_type = "BlendTree"; | ||||
|   graph_resource.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = graph_resource.m_blend_tree_resource; | ||||
|   blend_tree_resource.Reset(); | ||||
| @ -219,25 +217,25 @@ TEST_CASE("Blend2Graph", "[AnimGraphResource]") { | ||||
|   blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("Blend2")); | ||||
|   size_t blend_node_index = blend_tree_resource.m_nodes.size() - 1; | ||||
| 
 | ||||
|   AnimNodeResource& walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node.m_name = "WalkAnim"; | ||||
|   walk_node.m_socket_accessor->SetPropertyValue( | ||||
|   AnimNodeResource* walk_node = blend_tree_resource.m_nodes[walk_node_index]; | ||||
|   walk_node->m_name = "WalkAnim"; | ||||
|   walk_node->m_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Walking-loop.ozz")); | ||||
|   AnimNodeResource& run_node = blend_tree_resource.m_nodes[run_node_index]; | ||||
|   run_node.m_socket_accessor->SetPropertyValue( | ||||
|   AnimNodeResource* run_node = blend_tree_resource.m_nodes[run_node_index]; | ||||
|   run_node->m_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Running0-loop.ozz")); | ||||
|   run_node.m_name = "RunAnim"; | ||||
|   AnimNodeResource& blend_node = blend_tree_resource.m_nodes[blend_node_index]; | ||||
|   blend_node.m_name = "BlendWalkRun"; | ||||
|   run_node->m_name = "RunAnim"; | ||||
|   AnimNodeResource* blend_node = blend_tree_resource.m_nodes[blend_node_index]; | ||||
|   blend_node->m_name = "BlendWalkRun"; | ||||
| 
 | ||||
|   AnimNodeResource& graph_node = blend_tree_resource.m_nodes[0]; | ||||
|   graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
|   AnimNodeResource* graph_node = blend_tree_resource.GetGraphOutputNode(); | ||||
|   graph_node->m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr); | ||||
| 
 | ||||
|   REQUIRE(graph_node.m_socket_accessor->m_inputs.size() == 1); | ||||
|   REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input0") == 0); | ||||
|   REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input1") == 1); | ||||
|   REQUIRE(graph_node->m_socket_accessor->m_inputs.size() == 1); | ||||
|   REQUIRE(blend_node->m_socket_accessor->GetInputIndex("Input0") == 0); | ||||
|   REQUIRE(blend_node->m_socket_accessor->GetInputIndex("Input1") == 1); | ||||
| 
 | ||||
|   blend_tree_resource.ConnectSockets(walk_node, "Output", blend_node, "Input0"); | ||||
|   blend_tree_resource.ConnectSockets(run_node, "Output", blend_node, "Input1"); | ||||
| @ -247,10 +245,10 @@ TEST_CASE("Blend2Graph", "[AnimGraphResource]") { | ||||
|       blend_tree_resource.GetGraphOutputNode(), | ||||
|       "GraphOutput"); | ||||
| 
 | ||||
|   graph_resource.SaveToFile("Blend2Graph.animgraph.json"); | ||||
|   graph_resource.SaveToFile("TestGraphBlend2Graph.animgraph.json"); | ||||
| 
 | ||||
|   AnimGraphResource graph_resource_loaded; | ||||
|   graph_resource_loaded.LoadFromFile("Blend2Graph.animgraph.json"); | ||||
|   graph_resource_loaded.LoadFromFile("TestGraphBlend2Graph.animgraph.json"); | ||||
| 
 | ||||
|   AnimGraphBlendTree blend_tree_graph; | ||||
|   graph_resource_loaded.CreateBlendTreeInstance(blend_tree_graph); | ||||
| @ -263,8 +261,8 @@ TEST_CASE("Blend2Graph", "[AnimGraphResource]") { | ||||
|   REQUIRE(blend_tree_graph.Init(graph_context)); | ||||
| 
 | ||||
|   REQUIRE(blend_tree_graph.m_nodes.size() == 5); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[0]->m_node_type_name == "BlendTree"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[1]->m_node_type_name == "BlendTree"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[0]->m_node_type_name == "BlendTreeSockets"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[1]->m_node_type_name == "BlendTreeSockets"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[2]->m_node_type_name == "AnimSampler"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[3]->m_node_type_name == "AnimSampler"); | ||||
|   REQUIRE(blend_tree_graph.m_nodes[4]->m_node_type_name == "Blend2"); | ||||
| @ -349,7 +347,7 @@ TEST_CASE("Blend2Graph", "[AnimGraphResource]") { | ||||
| TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource_origin; | ||||
|   graph_resource_origin.m_name = "TestInputOutputGraph"; | ||||
|   graph_resource_origin.m_type = "BlendTree"; | ||||
|   graph_resource_origin.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = graph_resource_origin.m_blend_tree_resource; | ||||
|   blend_tree_resource.Reset(); | ||||
| @ -359,23 +357,23 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|   blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("MathFloatToVec3Node")); | ||||
|   size_t float_to_vec3_node_index = blend_tree_resource.m_nodes.size() - 1; | ||||
| 
 | ||||
|   AnimNodeResource& graph_output_node = | ||||
|   AnimNodeResource* graph_output_node = | ||||
|       blend_tree_resource.GetGraphOutputNode(); | ||||
|   graph_output_node.m_socket_accessor->RegisterInput<float>( | ||||
|   graph_output_node->m_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloatOutput", | ||||
|       nullptr); | ||||
|   graph_output_node.m_socket_accessor->RegisterInput<Vec3>( | ||||
|   graph_output_node->m_socket_accessor->RegisterInput<Vec3>( | ||||
|       "GraphVec3Output", | ||||
|       nullptr); | ||||
| 
 | ||||
|   AnimNodeResource& graph_input_node = | ||||
|   AnimNodeResource* graph_input_node = | ||||
|       blend_tree_resource.GetGraphInputNode(); | ||||
|   graph_input_node.m_socket_accessor->RegisterOutput<float>( | ||||
|   graph_input_node->m_socket_accessor->RegisterOutput<float>( | ||||
|       "GraphFloatInput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   AnimNodeResource& float_to_vec3_node = | ||||
|   AnimNodeResource* float_to_vec3_node = | ||||
|       blend_tree_resource.m_nodes[float_to_vec3_node_index]; | ||||
| 
 | ||||
|   REQUIRE(blend_tree_resource.ConnectSockets( | ||||
| @ -407,39 +405,39 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
|       "GraphVec3Output")); | ||||
| 
 | ||||
|   WHEN("Saving and loading graph resource") { | ||||
|     const char* filename = "ResourceSaveLoadGraphInputs.json"; | ||||
|     const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; | ||||
|     graph_resource_origin.SaveToFile(filename); | ||||
| 
 | ||||
|     AnimGraphResource graph_resource_loaded; | ||||
|     graph_resource_loaded.LoadFromFile(filename); | ||||
| 
 | ||||
|     BlendTreeResource graph_blend_tree_loaded = graph_resource_loaded.m_blend_tree_resource; | ||||
|     BlendTreeResource& graph_blend_tree_loaded = graph_resource_loaded.m_blend_tree_resource; | ||||
| 
 | ||||
|     const AnimNodeResource& graph_loaded_output_node = | ||||
|     const AnimNodeResource* graph_loaded_output_node = | ||||
|         graph_blend_tree_loaded.m_nodes[0]; | ||||
|     const AnimNodeResource& graph_loaded_input_node = | ||||
|     const AnimNodeResource* graph_loaded_input_node = | ||||
|         graph_blend_tree_loaded.m_nodes[1]; | ||||
| 
 | ||||
|     THEN("Graph inputs and outputs must be in loaded resource as well.") { | ||||
|       REQUIRE( | ||||
|           graph_output_node.m_socket_accessor->m_inputs.size() | ||||
|           == graph_loaded_output_node.m_socket_accessor->m_inputs.size()); | ||||
|           graph_output_node->m_socket_accessor->m_inputs.size() | ||||
|           == graph_loaded_output_node->m_socket_accessor->m_inputs.size()); | ||||
| 
 | ||||
|       REQUIRE( | ||||
|           graph_input_node.m_socket_accessor->m_outputs.size() | ||||
|           == graph_loaded_input_node.m_socket_accessor->m_outputs.size()); | ||||
|           graph_input_node->m_socket_accessor->m_outputs.size() | ||||
|           == graph_loaded_input_node->m_socket_accessor->m_outputs.size()); | ||||
| 
 | ||||
|       REQUIRE( | ||||
|           graph_loaded_input_node.m_socket_accessor->GetOutputSocket( | ||||
|           graph_loaded_input_node->m_socket_accessor->GetOutputSocket( | ||||
|               "GraphFloatInput") | ||||
|           != nullptr); | ||||
| 
 | ||||
|       REQUIRE( | ||||
|           graph_loaded_output_node.m_socket_accessor->GetInputSocket( | ||||
|           graph_loaded_output_node->m_socket_accessor->GetInputSocket( | ||||
|               "GraphFloatOutput") | ||||
|           != nullptr); | ||||
|       REQUIRE( | ||||
|           graph_loaded_output_node.m_socket_accessor->GetInputSocket( | ||||
|           graph_loaded_output_node->m_socket_accessor->GetInputSocket( | ||||
|               "GraphVec3Output") | ||||
|           != nullptr); | ||||
| 
 | ||||
| @ -488,7 +486,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { | ||||
| TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|   AnimGraphResource graph_resource_origin; | ||||
|   graph_resource_origin.m_name = "TestSimpleMathGraph"; | ||||
|   graph_resource_origin.m_type = "BlendTree"; | ||||
|   graph_resource_origin.m_graph_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& blend_tree_resource = graph_resource_origin.m_blend_tree_resource; | ||||
|   blend_tree_resource.Reset(); | ||||
| @ -501,29 +499,29 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|   blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("MathAddNode")); | ||||
|   size_t math_add1_node_index = blend_tree_resource.m_nodes.size() - 1; | ||||
| 
 | ||||
|   AnimNodeResource& graph_output_node = | ||||
|   AnimNodeResource* graph_output_node = | ||||
|       blend_tree_resource.GetGraphOutputNode(); | ||||
| 
 | ||||
|   graph_output_node.m_socket_accessor->RegisterInput<float>( | ||||
|   graph_output_node->m_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloat0Output", | ||||
|       nullptr); | ||||
|   graph_output_node.m_socket_accessor->RegisterInput<float>( | ||||
|   graph_output_node->m_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloat1Output", | ||||
|       nullptr); | ||||
|   graph_output_node.m_socket_accessor->RegisterInput<float>( | ||||
|   graph_output_node->m_socket_accessor->RegisterInput<float>( | ||||
|       "GraphFloat2Output", | ||||
|       nullptr); | ||||
| 
 | ||||
|   AnimNodeResource& graph_input_node = | ||||
|   AnimNodeResource* graph_input_node = | ||||
|       blend_tree_resource.GetGraphInputNode(); | ||||
|   graph_input_node.m_socket_accessor->RegisterOutput<float>( | ||||
|   graph_input_node->m_socket_accessor->RegisterOutput<float>( | ||||
|       "GraphFloatInput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Prepare graph inputs and outputs
 | ||||
|   AnimNodeResource& math_add0_node = | ||||
|   AnimNodeResource* math_add0_node = | ||||
|       blend_tree_resource.m_nodes[math_add0_node_index]; | ||||
|   AnimNodeResource& math_add1_node = | ||||
|   AnimNodeResource* math_add1_node = | ||||
|       blend_tree_resource.m_nodes[math_add1_node_index]; | ||||
| 
 | ||||
|   // direct output
 | ||||
| @ -572,7 +570,7 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|       "GraphFloat2Output")); | ||||
| 
 | ||||
|   WHEN("Saving and loading graph resource") { | ||||
|     const char* filename = "ResourceSaveLoadGraphInputs.json"; | ||||
|     const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; | ||||
|     graph_resource_origin.SaveToFile(filename); | ||||
| 
 | ||||
|     AnimGraphResource graph_resource_loaded; | ||||
| @ -618,4 +616,154 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //
 | ||||
| // Tests whether BlendTree within a BlendTree can be saved and loaded
 | ||||
| //
 | ||||
| //   +-----------Parent BlendTree-------------+
 | ||||
| //   |                                        |
 | ||||
| //   |           +-----Embd Btree---+         |
 | ||||
| //   |  AnmSmpl--+-\                |         |
 | ||||
| //   |           |  \-SpdScale------+--Out    |
 | ||||
| //   |           |------------------+         |
 | ||||
| //   |                                        |
 | ||||
| //   +----------------------------------------+
 | ||||
| //
 | ||||
| TEST_CASE("EmbeddedBlendTreeSaveLoad", "[AnimGraphResource]") { | ||||
|   const char* parent_graph_filename = "TestGraphEmbeddedBlendTree.json"; | ||||
|   AnimGraphResource parent_graph_resource; | ||||
|   parent_graph_resource.m_name = "ParentBlendTree"; | ||||
|   parent_graph_resource.m_graph_type_name = "BlendTree"; | ||||
|   parent_graph_resource.m_node_type_name = "BlendTree"; | ||||
| 
 | ||||
|   BlendTreeResource& parent_blend_tree_resource = | ||||
|       parent_graph_resource.m_blend_tree_resource; | ||||
|   parent_blend_tree_resource.Reset(); | ||||
|   parent_blend_tree_resource.InitGraphConnectors(); | ||||
| 
 | ||||
|   // Setup parent outputs
 | ||||
|   AnimNodeResource* parent_blend_tree_outputs = parent_blend_tree_resource.GetGraphOutputNode(); | ||||
|   parent_blend_tree_outputs->m_socket_accessor->RegisterInput<AnimData>("Output", nullptr); | ||||
| 
 | ||||
|   // Parent AnimSampler
 | ||||
|   parent_blend_tree_resource.m_nodes.push_back( | ||||
|       AnimNodeResourceFactory("AnimSampler")); | ||||
| 
 | ||||
|   AnimNodeResource* walk_node = | ||||
|       parent_blend_tree_resource.m_nodes.back(); | ||||
|   walk_node->m_name = "WalkAnim"; | ||||
|   walk_node->m_socket_accessor->SetPropertyValue( | ||||
|       "Filename", | ||||
|       std::string("media/Walking-loop.ozz")); | ||||
| 
 | ||||
|   //
 | ||||
|   // Embedded Tree
 | ||||
|   //
 | ||||
|   parent_blend_tree_resource.m_nodes.push_back( | ||||
|       AnimNodeResourceFactory("BlendTree")); | ||||
|   size_t embedded_blend_tree_node_index = | ||||
|       parent_blend_tree_resource.m_nodes.size() - 1; | ||||
|   AnimGraphResource* embedded_graph = | ||||
|       dynamic_cast<AnimGraphResource*>(parent_blend_tree_resource.m_nodes.back()); | ||||
|   embedded_graph->m_name = "EmbeddedBlendTree"; | ||||
|   embedded_graph->m_node_type_name = "BlendTree"; | ||||
|   BlendTreeResource* embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource; | ||||
| 
 | ||||
|   // Embedded: outputs
 | ||||
|   AnimNodeResource* embedded_outputs = | ||||
|       embedded_blend_tree_resource->GetGraphOutputNode(); | ||||
|   embedded_outputs->m_socket_accessor->RegisterInput<AnimData>( | ||||
|       "AnimOutput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Embedded: inputs
 | ||||
|   AnimNodeResource* embedded_inputs = | ||||
|       embedded_blend_tree_resource->GetGraphInputNode(); | ||||
|   embedded_inputs->m_socket_accessor->RegisterOutput<AnimData>( | ||||
|       "AnimInput", | ||||
|       nullptr); | ||||
| 
 | ||||
|   // Embedded: SpeedScale node
 | ||||
|   embedded_blend_tree_resource->m_nodes.push_back( | ||||
|       AnimNodeResourceFactory("SpeedScale")); | ||||
|   AnimNodeResource* embedded_speed_scale_resource = embedded_blend_tree_resource->m_nodes.back(); | ||||
| 
 | ||||
|   embedded_blend_tree_resource->ConnectSockets(embedded_inputs, "AnimInput", embedded_speed_scale_resource, "Input"); | ||||
|   embedded_blend_tree_resource->ConnectSockets(embedded_speed_scale_resource, "Output", embedded_outputs, "AnimOutput"); | ||||
| 
 | ||||
|   //
 | ||||
|   // Parent: setup connections
 | ||||
|   //
 | ||||
|   REQUIRE(parent_blend_tree_resource.ConnectSockets(walk_node, "Output", embedded_graph, "AnimInput")); | ||||
|   REQUIRE(parent_blend_tree_resource.ConnectSockets(embedded_graph, "AnimOutput", parent_blend_tree_outputs, "Output")); | ||||
| 
 | ||||
|   parent_graph_resource.SaveToFile(parent_graph_filename); | ||||
| 
 | ||||
|   //
 | ||||
|   // Load the graph
 | ||||
|   //
 | ||||
|   AnimGraphResource parent_graph_resource_loaded; | ||||
|   parent_graph_resource_loaded.LoadFromFile(parent_graph_filename); | ||||
| 
 | ||||
|   //
 | ||||
|   // Check the loaded parent graph
 | ||||
|   //
 | ||||
|   CHECK(parent_graph_resource.m_name == parent_graph_resource_loaded.m_name); | ||||
|   CHECK(parent_graph_resource.m_graph_type_name == parent_graph_resource_loaded.m_graph_type_name); | ||||
|   CHECK(parent_graph_resource.m_node_type_name | ||||
|       == parent_graph_resource_loaded.m_node_type_name); | ||||
|    | ||||
|   const BlendTreeResource& parent_blend_tree_resource_loaded = parent_graph_resource_loaded.m_blend_tree_resource;  | ||||
|    | ||||
|   CHECK(parent_blend_tree_resource.m_nodes.size() == parent_blend_tree_resource_loaded.m_nodes.size()); | ||||
|   for (size_t i = 0; i < parent_blend_tree_resource.m_nodes.size(); i++) { | ||||
|     const AnimNodeResource* parent_node = parent_blend_tree_resource.m_nodes[i]; | ||||
|     const AnimNodeResource* parent_node_loaded = parent_blend_tree_resource_loaded.m_nodes[i]; | ||||
|      | ||||
|     CHECK(parent_node->m_name == parent_node_loaded->m_name); | ||||
|     CHECK(parent_node->m_node_type_name == parent_node_loaded->m_node_type_name); | ||||
|   } | ||||
|    | ||||
|   CHECK(parent_blend_tree_resource.m_connections.size() == parent_blend_tree_resource_loaded.m_connections.size()); | ||||
|   for (size_t i = 0; i < parent_blend_tree_resource.m_connections.size(); i++) { | ||||
|     const BlendTreeConnectionResource& parent_connection = parent_blend_tree_resource.m_connections[i]; | ||||
|     const BlendTreeConnectionResource& parent_connection_loaded = parent_blend_tree_resource_loaded.m_connections[i]; | ||||
|      | ||||
|     CHECK(parent_connection.source_node_index == parent_connection_loaded.source_node_index); | ||||
|     CHECK(parent_connection.source_socket_name == parent_connection_loaded.source_socket_name); | ||||
|     CHECK(parent_connection.target_node_index == parent_connection_loaded.target_node_index); | ||||
|     CHECK(parent_connection.target_socket_name == parent_connection_loaded.target_socket_name); | ||||
|   } | ||||
| 
 | ||||
|   //
 | ||||
|   // Check the loaded embedded graph
 | ||||
|   //
 | ||||
|   REQUIRE(parent_blend_tree_resource_loaded.m_nodes[3]->m_node_type_name == "BlendTree"); | ||||
|    | ||||
|   const AnimGraphResource* embedded_graph_loaded = dynamic_cast<AnimGraphResource*>(parent_blend_tree_resource_loaded.m_nodes[3]); | ||||
|   const BlendTreeResource& embedded_blend_tree_resource_loaded = embedded_graph_loaded->m_blend_tree_resource; | ||||
|    | ||||
|   CHECK(embedded_blend_tree_resource->m_nodes.size() == embedded_blend_tree_resource_loaded.m_nodes.size()); | ||||
|   CHECK(embedded_blend_tree_resource->m_connections.size() == embedded_blend_tree_resource_loaded.m_connections.size()); | ||||
|    | ||||
|   for (size_t i = 0; i < embedded_blend_tree_resource->m_nodes.size(); i++) { | ||||
|     const AnimNodeResource* parent_node = embedded_blend_tree_resource->m_nodes[i]; | ||||
|     const AnimNodeResource* parent_node_loaded = embedded_blend_tree_resource_loaded.m_nodes[i]; | ||||
|      | ||||
|     CHECK(parent_node->m_name == parent_node_loaded->m_name); | ||||
|     CHECK(parent_node->m_node_type_name == parent_node_loaded->m_node_type_name); | ||||
|   } | ||||
|    | ||||
|   CHECK(embedded_blend_tree_resource->m_connections.size() == embedded_blend_tree_resource_loaded.m_connections.size()); | ||||
|   for (size_t i = 0; i < embedded_blend_tree_resource->m_connections.size(); i++) { | ||||
|     const BlendTreeConnectionResource& embedded_connection = embedded_blend_tree_resource->m_connections[i]; | ||||
|     const BlendTreeConnectionResource& embedded_connection_loaded = embedded_blend_tree_resource_loaded.m_connections[i]; | ||||
|      | ||||
|     CHECK(embedded_connection.source_node_index == embedded_connection_loaded.source_node_index); | ||||
|     CHECK(embedded_connection.source_socket_name == embedded_connection_loaded.source_socket_name); | ||||
|     CHECK(embedded_connection.target_node_index == embedded_connection_loaded.target_node_index); | ||||
|     CHECK(embedded_connection.target_socket_name == embedded_connection_loaded.target_socket_name); | ||||
|   } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user