Added support of saving and loading of embedded blend tree resources.

RefactorUnifiedBlendTreeStateMachineHandling
Martin Felis 2024-03-24 21:50:22 +01:00
parent 3a7f470acf
commit 116bf7699b
6 changed files with 483 additions and 255 deletions

View File

@ -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};

View File

@ -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();

View File

@ -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") {

View File

@ -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;

View File

@ -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,7 +33,7 @@ struct BlendTreeConnectionResource {
};
struct BlendTreeResource {
std::vector<AnimNodeResource> m_nodes;
std::vector<AnimNodeResource*> m_nodes;
std::vector<BlendTreeConnectionResource> m_connections;
~BlendTreeResource() { CleanupNodes();
@ -52,27 +46,28 @@ struct BlendTreeResource {
}
void CleanupNodes() {
for (AnimNodeResource& node_resource : m_nodes) {
delete node_resource.m_anim_node;
delete node_resource.m_socket_accessor;
for (AnimNodeResource* node_resource : m_nodes) {
delete node_resource->m_anim_node;
delete node_resource->m_socket_accessor;
delete node_resource;
}
m_nodes.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";
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets"));
m_nodes[0]->m_name = "Outputs";
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets"));
m_nodes[1]->m_name = "Inputs";
}
AnimNodeResource& GetGraphOutputNode() { return m_nodes[0]; }
AnimNodeResource& GetGraphInputNode() { return m_nodes[1]; }
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { return m_nodes[0]; }
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const { return m_nodes[1]; }
size_t GetNodeIndex(const AnimNodeResource& node_resource) const {
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;
}
}
@ -81,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 {
@ -125,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);
}
}
@ -147,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;
@ -163,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;
@ -172,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

View File

@ -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,7 +405,7 @@ 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;
@ -415,31 +413,31 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
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;
@ -619,3 +617,153 @@ 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);
}
}