Refactored BlendTreeResources to be a subclass of AnimGraphResource.

The latter is used to abstract saving/loading for both BlendTrees and StateMachines.
This commit is contained in:
Martin Felis 2025-03-16 00:14:40 +01:00
parent f351939d61
commit 2ea2c56bbb
6 changed files with 547 additions and 611 deletions

View File

@ -16,11 +16,11 @@
using json = nlohmann::json; using json = nlohmann::json;
// forward declarations // forward declarations
static json sAnimGraphResourceBlendTreeToJson( static json sBlendTreeToJson(const BlendTreeResource& anim_graph_resource);
const AnimGraphResource& anim_graph_resource);
static bool sAnimGraphResourceBlendTreeFromJson( static bool sBlendTreeFromJson(
const json& json_data, const json& json_data,
AnimGraphResource* result_graph_resource); BlendTreeResource* result_graph_resource);
// //
// Socket <-> json // Socket <-> json
@ -134,7 +134,7 @@ Socket sJsonToSocket(const json& json_data) {
// //
json sAnimGraphNodeToJson( json sAnimGraphNodeToJson(
const AnimNodeResource* node, const AnimNodeResource* node,
size_t node_index, const size_t node_index,
const std::vector<BlendTreeConnectionResource>& connections) { const std::vector<BlendTreeConnectionResource>& connections) {
json result; json result;
@ -181,9 +181,9 @@ AnimNodeResource* sAnimGraphNodeFromJson(
std::string node_type = json_node["node_type"]; std::string node_type = json_node["node_type"];
if (node_type == "BlendTree") { if (node_type == "BlendTree") {
AnimGraphResource* result = BlendTreeResource* result =
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree")); dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
sAnimGraphResourceBlendTreeFromJson(json_node, result); sBlendTreeFromJson(json_node, result);
return result; return result;
} }
@ -244,26 +244,25 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson(
return connection; return connection;
} }
static json sAnimGraphResourceBlendTreeToJson( //
const AnimGraphResource& anim_graph_resource) { // BlendTreeResource <-> Json
//
static json sBlendTreeToJson(const BlendTreeResource& blend_tree_resource) {
json result; json result;
result["name"] = anim_graph_resource.m_name; result["name"] = blend_tree_resource.m_name;
result["type"] = "AnimNodeResource"; result["type"] = "AnimNodeResource";
result["node_type"] = "BlendTree"; result["node_type"] = "BlendTree";
result["position"][0] = anim_graph_resource.m_position[0]; result["position"][0] = blend_tree_resource.m_position[0];
result["position"][1] = anim_graph_resource.m_position[1]; result["position"][1] = blend_tree_resource.m_position[1];
const BlendTreeResource& blend_tree_resource =
anim_graph_resource.m_blend_tree_resource;
for (size_t i = 0; i < blend_tree_resource.GetNumNodes(); i++) { for (size_t i = 0; i < blend_tree_resource.GetNumNodes(); i++) {
const AnimNodeResource* node = blend_tree_resource.GetNode(i); const AnimNodeResource* node = blend_tree_resource.GetNode(i);
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); result["nodes"][i] = sBlendTreeToJson(*blend_tree_resource);
} else { } else {
result["nodes"][i] = result["nodes"][i] =
sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections()); sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections());
@ -295,20 +294,16 @@ static json sAnimGraphResourceBlendTreeToJson(
return result; return result;
} }
static bool sAnimGraphResourceBlendTreeFromJson( static bool sBlendTreeFromJson(
const json& json_data, const json& json_data,
AnimGraphResource* result_graph_resource) { BlendTreeResource* result_blend_tree_resource) {
BlendTreeResource& blend_tree_resource = result_blend_tree_resource->m_node_type_name = "BlendTree";
result_graph_resource->m_blend_tree_resource; result_blend_tree_resource->m_name = json_data["name"];
result_blend_tree_resource->m_position[0] = json_data["position"][0];
result_graph_resource->m_graph_type_name = "BlendTree"; result_blend_tree_resource->m_position[1] = json_data["position"][1];
result_graph_resource->m_node_type_name = "BlendTree";
result_graph_resource->m_name = json_data["name"];
result_graph_resource->m_position[0] = json_data["position"][0];
result_graph_resource->m_position[1] = json_data["position"][1];
// Clear all nodes as we overwrite them here anyway. // Clear all nodes as we overwrite them here anyway.
blend_tree_resource.ClearAllNodes(); result_blend_tree_resource->ClearAllNodes();
// Load nodes // Load nodes
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
@ -321,14 +316,14 @@ static bool sAnimGraphResourceBlendTreeFromJson(
} }
AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i); AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i);
blend_tree_resource.AddNode(node); result_blend_tree_resource->AddNode(node);
} }
// Graph outputs // Graph outputs
if (json_data["nodes"][0].contains("inputs")) { if (json_data["nodes"][0].contains("inputs")) {
const json& graph_outputs = json_data["nodes"][0]["inputs"]; const json& graph_outputs = json_data["nodes"][0]["inputs"];
for (const auto& graph_output : graph_outputs) { for (const auto& graph_output : graph_outputs) {
result_graph_resource->RegisterBlendTreeOutputSocket( result_blend_tree_resource->RegisterBlendTreeOutputSocket(
sJsonToSocket(graph_output)); sJsonToSocket(graph_output));
} }
} }
@ -337,7 +332,7 @@ static bool sAnimGraphResourceBlendTreeFromJson(
if (json_data["nodes"][1].contains("outputs")) { if (json_data["nodes"][1].contains("outputs")) {
const json& graph_inputs = json_data["nodes"][1]["outputs"]; const json& graph_inputs = json_data["nodes"][1]["outputs"];
for (const auto& graph_input : graph_inputs) { for (const auto& graph_input : graph_inputs) {
result_graph_resource->RegisterBlendTreeInputSocket( result_blend_tree_resource->RegisterBlendTreeInputSocket(
sJsonToSocket(graph_input)); sJsonToSocket(graph_input));
} }
} }
@ -356,10 +351,10 @@ static bool sAnimGraphResourceBlendTreeFromJson(
BlendTreeConnectionResource connection = BlendTreeConnectionResource connection =
sAnimGraphConnectionFromJson(json_connection); sAnimGraphConnectionFromJson(json_connection);
blend_tree_resource.ConnectSockets( result_blend_tree_resource->ConnectSockets(
blend_tree_resource.GetNode(connection.source_node_index), result_blend_tree_resource->GetNode(connection.source_node_index),
connection.source_socket_name, connection.source_socket_name,
blend_tree_resource.GetNode(connection.target_node_index), result_blend_tree_resource->GetNode(connection.target_node_index),
connection.target_socket_name); connection.target_socket_name);
} }
} }
@ -375,6 +370,92 @@ static bool sAnimGraphResourceStateMachineFromJson(
return false; return false;
} }
AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
buffer << input_file.rdbuf();
json json_data = json::parse(buffer.str(), nullptr, false);
if (json_data.is_discarded()) {
std::cerr << "Error parsing json of file '" << filename << "'."
<< std::endl;
return nullptr;
}
if (json_data["type"] != "AnimNodeResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_data["type"] << "'." << std::endl;
return nullptr;
}
AnimGraphResource* result = nullptr;
if (json_data["node_type"] == "BlendTree") {
result = BlendTreeResource::CreateFromFile(filename);
} else if (json_data["node_type"] == "StateMachine") {
sAnimGraphResourceStateMachineFromJson(json_data, result);
} else {
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
"'StateMachine' but got '"
<< json_data["node_type"] << "'." << std::endl;
}
return result;
}
bool BlendTreeResource::SaveToFile(const char* filename) const {
json result;
result = sBlendTreeToJson(*this);
std::ofstream output_file;
output_file.open(filename);
output_file << result.dump(4, ' ') << std::endl;
output_file.close();
return true;
}
BlendTreeResource* BlendTreeResource::CreateFromFile(const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
buffer << input_file.rdbuf();
json json_data = json::parse(buffer.str(), nullptr, false);
if (json_data.is_discarded()) {
std::cerr << "Error parsing json of file '" << filename << "'."
<< std::endl;
return nullptr;
}
if (json_data["type"] != "AnimNodeResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_data["type"] << "'." << std::endl;
return nullptr;
}
if (json_data["node_type"] != "BlendTree") {
std::cerr << "Invalid node type. Expected type 'BlendTree' but got '"
<< json_data["node_type"] << "'." << std::endl;
return nullptr;
}
BlendTreeResource* result = nullptr;
result =
dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
sBlendTreeFromJson(json_data, result);
return result;
}
void BlendTreeResource::RemoveConnectionsForSocket( void BlendTreeResource::RemoveConnectionsForSocket(
const AnimNodeResource* node_resource, const AnimNodeResource* node_resource,
const Socket& socket) { const Socket& socket) {
@ -563,12 +644,10 @@ Socket* BlendTreeResource::GetNodeOutputSocket(
} }
if (output_socket == nullptr && node->m_node_type_name == "BlendTree") { if (output_socket == nullptr && node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_socket = output_socket =
blend_tree_resource.GetGraphOutputSocket(output_socket_name.c_str()); blend_tree_resource->GetGraphOutputSocket(output_socket_name.c_str());
} }
return output_socket; return output_socket;
@ -582,12 +661,10 @@ const Socket* BlendTreeResource::GetNodeOutputSocketByIndex(
if (node->m_virtual_socket_accessor) { if (node->m_virtual_socket_accessor) {
output_sockets = &node->m_virtual_socket_accessor->m_outputs; output_sockets = &node->m_virtual_socket_accessor->m_outputs;
} else if (node->m_node_type_name == "BlendTree") { } else if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_sockets = &blend_tree_resource.GetGraphOutputNode() output_sockets = &blend_tree_resource->GetGraphOutputNode()
->m_virtual_socket_accessor->m_outputs; ->m_virtual_socket_accessor->m_outputs;
} }
@ -610,12 +687,10 @@ Socket* BlendTreeResource::GetNodeInputSocket(
} }
if (input_socket == nullptr && node->m_node_type_name == "BlendTree") { if (input_socket == nullptr && node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
input_socket = input_socket =
blend_tree_resource.GetGraphInputSocket(input_socket_name.c_str()); blend_tree_resource->GetGraphInputSocket(input_socket_name.c_str());
} }
return input_socket; return input_socket;
@ -629,12 +704,10 @@ const Socket* BlendTreeResource::GetNodeInputSocketByIndex(
if (node->m_virtual_socket_accessor) { if (node->m_virtual_socket_accessor) {
output_sockets = &node->m_virtual_socket_accessor->m_inputs; output_sockets = &node->m_virtual_socket_accessor->m_inputs;
} else if (node->m_node_type_name == "BlendTree") { } else if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_sockets = &blend_tree_resource.GetGraphOutputNode() output_sockets = &blend_tree_resource->GetGraphOutputNode()
->m_virtual_socket_accessor->m_outputs; ->m_virtual_socket_accessor->m_outputs;
} }
@ -653,11 +726,10 @@ std::vector<Socket> BlendTreeResource::GetNodeOutputSockets(
} }
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource; return blend_tree_resource->GetGraphOutputNode()
return blend_tree_resource.GetGraphOutputNode()
->m_virtual_socket_accessor->m_inputs; ->m_virtual_socket_accessor->m_inputs;
} }
@ -671,11 +743,10 @@ std::vector<Socket> BlendTreeResource::GetNodeInputSockets(
} }
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = const BlendTreeResource* blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node); dynamic_cast<const BlendTreeResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource; return blend_tree_resource->GetGraphInputNode()
return blend_tree_resource.GetGraphInputNode()
->m_virtual_socket_accessor->m_outputs; ->m_virtual_socket_accessor->m_outputs;
} }
@ -824,92 +895,12 @@ void BlendTreeResource::UpdateNodeSubtrees() {
} }
} }
AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) { void BlendTreeResource::CreateBlendTreeInstance(
if (graph_type == AnimGraphType::GraphTypeBlendTree) {
m_graph_type_name = "BlendTree";
m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree");
m_blend_tree_resource.InitGraphConnectors();
RegisterBlendTreeOutputSocket<AnimData>(
AnimGraphResource::DefaultAnimOutput);
} else {
std::cerr
<< "Warning: construction of state machine graphs not yet implemented!"
<< std::endl;
}
}
AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
buffer << input_file.rdbuf();
json json_data = json::parse(buffer.str(), nullptr, false);
if (json_data.is_discarded()) {
std::cerr << "Error parsing json of file '" << filename << "'."
<< std::endl;
return nullptr;
}
if (json_data["type"] != "AnimNodeResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_data["type"] << "'." << std::endl;
return nullptr;
}
AnimGraphResource* result = nullptr;
if (json_data["node_type"] == "BlendTree") {
result =
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree"));
sAnimGraphResourceBlendTreeFromJson(json_data, result);
} else if (json_data["node_type"] == "StateMachine") {
sAnimGraphResourceStateMachineFromJson(json_data, result);
} else {
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
"'StateMachine' but got '"
<< json_data["node_type"] << "'." << std::endl;
}
return result;
}
bool AnimGraphResource::SaveToFile(const char* filename) const {
if (m_graph_type_name == "BlendTree") {
return SaveBlendTreeResourceToFile(filename);
} else if (m_graph_type_name == "StateMachine") {
return SaveStateMachineResourceToFile(filename);
}
std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "."
<< std::endl;
return false;
}
bool AnimGraphResource::SaveBlendTreeResourceToFile(
const char* filename) const {
json result;
result = sAnimGraphResourceBlendTreeToJson(*this);
std::ofstream output_file;
output_file.open(filename);
output_file << result.dump(4, ' ') << std::endl;
output_file.close();
return true;
}
void AnimGraphResource::CreateBlendTreeInstance(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
if (m_node_type_name != "BlendTree") { if (m_node_type_name != "BlendTree") {
std::cerr std::cerr
<< "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
<< m_graph_type_name << "'." << std::endl; << m_node_type_name << "'." << std::endl;
return; return;
} }
@ -926,15 +917,14 @@ void AnimGraphResource::CreateBlendTreeInstance(
result.ResetNodeStates(); result.ResetNodeStates();
} }
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( void BlendTreeResource::CreateBlendTreeRuntimeNodeInstances(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
for (const AnimNodeResource* node_resource : for (const AnimNodeResource* node_resource : GetNodes()) {
m_blend_tree_resource.GetNodes()) {
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
if (node_resource->m_node_type_name == "BlendTree") { if (node_resource->m_node_type_name == "BlendTree") {
const AnimGraphResource* embedded_blend_tree_resource = const BlendTreeResource* embedded_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node_resource); dynamic_cast<const BlendTreeResource*>(node_resource);
assert(embedded_blend_tree_resource != nullptr); assert(embedded_blend_tree_resource != nullptr);
AnimGraphBlendTree* embedded_blend_tree = AnimGraphBlendTree* embedded_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(node); dynamic_cast<AnimGraphBlendTree*>(node);
@ -958,18 +948,16 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
} }
} }
void AnimGraphResource::PrepareBlendTreeIOData( void BlendTreeResource::PrepareBlendTreeIOData(
AnimGraphBlendTree& instance, AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const { NodeSocketDataOffsetMap& node_offset_map) const {
instance.m_node_descriptor = instance.m_node_descriptor =
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]); AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
instance.m_node_descriptor->m_outputs = instance.m_node_descriptor->m_outputs =
m_blend_tree_resource.GetGraphInputNode() GetGraphInputNode()->m_virtual_socket_accessor->m_outputs;
->m_virtual_socket_accessor->m_outputs;
instance.m_node_descriptor->m_inputs = instance.m_node_descriptor->m_inputs =
m_blend_tree_resource.GetGraphOutputNode() GetGraphOutputNode()->m_virtual_socket_accessor->m_inputs;
->m_virtual_socket_accessor->m_inputs;
// //
// graph inputs // graph inputs
@ -1019,15 +1007,13 @@ void AnimGraphResource::PrepareBlendTreeIOData(
// connecton data storage // connecton data storage
// //
size_t connection_data_storage_size = 0; size_t connection_data_storage_size = 0;
for (const BlendTreeConnectionResource& connection : for (const BlendTreeConnectionResource& connection : GetConnections()) {
m_blend_tree_resource.GetConnections()) { const AnimNodeResource* source_node = GetNode(connection.source_node_index);
const AnimNodeResource* source_node =
m_blend_tree_resource.GetNode(connection.source_node_index);
Socket* source_socket = Socket* source_socket =
source_node->m_virtual_socket_accessor->GetOutputSocket( source_node->m_virtual_socket_accessor->GetOutputSocket(
connection.source_socket_name.c_str()); connection.source_socket_name.c_str());
NodeSocketPair source_socket_pair{source_node, source_socket->m_name}; NodeSocketNamePair source_socket_pair{source_node, source_socket->m_name};
if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) { if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) {
node_offset_map.insert( node_offset_map.insert(
{source_socket_pair, connection_data_storage_size}); {source_socket_pair, connection_data_storage_size});
@ -1042,24 +1028,22 @@ void AnimGraphResource::PrepareBlendTreeIOData(
} }
} }
void AnimGraphResource::CreateBlendTreeConnectionInstances( void BlendTreeResource::CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance, AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const { NodeSocketDataOffsetMap& node_offset_map) const {
std::vector<NodeDescriptorBase*> instance_node_descriptors( std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_blend_tree_resource.GetNumNodes(), GetNumNodes(),
nullptr); nullptr);
for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) { for (int i = 0; i < GetNumNodes(); i++) {
instance_node_descriptors[i] = AnimNodeDescriptorFactory( instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_blend_tree_resource.GetNode(i)->m_node_type_name, GetNode(i)->m_node_type_name,
instance.m_nodes[i]); instance.m_nodes[i]);
if (i > 1 if (i > 1 && GetNode(i)->m_node_type_name == "BlendTree") {
&& m_blend_tree_resource.GetNode(i)->m_node_type_name == "BlendTree") {
instance_node_descriptors[i]->m_inputs = instance_node_descriptors[i]->m_inputs =
m_blend_tree_resource.GetNode(i)->m_virtual_socket_accessor->m_inputs; GetNode(i)->m_virtual_socket_accessor->m_inputs;
instance_node_descriptors[i]->m_outputs = instance_node_descriptors[i]->m_outputs =
m_blend_tree_resource.GetNode(i) GetNode(i)->m_virtual_socket_accessor->m_outputs;
->m_virtual_socket_accessor->m_outputs;
} }
} }
@ -1067,8 +1051,7 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
instance_node_descriptors[1]->m_outputs = instance_node_descriptors[1]->m_outputs =
instance.m_node_descriptor->m_outputs; instance.m_node_descriptor->m_outputs;
for (const BlendTreeConnectionResource& connection : for (const BlendTreeConnectionResource& connection : GetConnections()) {
m_blend_tree_resource.GetConnections()) {
NodeDescriptorBase* source_node_descriptor = NodeDescriptorBase* source_node_descriptor =
instance_node_descriptors[connection.source_node_index]; instance_node_descriptors[connection.source_node_index];
NodeDescriptorBase* target_node_descriptor = NodeDescriptorBase* target_node_descriptor =
@ -1110,15 +1093,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
instance.m_node_output_connections[connection.source_node_index] instance.m_node_output_connections[connection.source_node_index]
.push_back(embedded_graph_activation_connection); .push_back(embedded_graph_activation_connection);
const AnimGraphResource* source_blend_tree_resource = const BlendTreeResource* source_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>( dynamic_cast<const BlendTreeResource*>(
m_blend_tree_resource.GetNode(connection.source_node_index)); GetNode(connection.source_node_index));
AnimGraphBlendTree* source_blend_tree = AnimGraphBlendTree* source_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(source_node); dynamic_cast<AnimGraphBlendTree*>(source_node);
size_t source_blend_tree_output_node_index = size_t source_blend_tree_output_node_index =
source_blend_tree_resource->m_blend_tree_resource source_blend_tree_resource->GetNodeIndexForOutputSocket(
.GetNodeIndexForOutputSocket(connection.source_socket_name); connection.source_socket_name);
source_node = source_node =
source_blend_tree->m_nodes[source_blend_tree_output_node_index]; source_blend_tree->m_nodes[source_blend_tree_output_node_index];
instance_connection.m_crosses_hierarchy = true; instance_connection.m_crosses_hierarchy = true;
@ -1127,15 +1110,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
// that the embedded node knows about its connection partner in the parent // that the embedded node knows about its connection partner in the parent
// tree. This allows the embedded node to properly activate the node in // tree. This allows the embedded node to properly activate the node in
// the parent graph. // the parent graph.
const AnimGraphResource* target_blend_tree_resource = const BlendTreeResource* target_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>( dynamic_cast<const BlendTreeResource*>(
m_blend_tree_resource.GetNode(connection.target_node_index)); GetNode(connection.target_node_index));
AnimGraphBlendTree* target_blend_tree = AnimGraphBlendTree* target_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(target_node); dynamic_cast<AnimGraphBlendTree*>(target_node);
size_t target_blend_tree_output_node_index = size_t target_blend_tree_output_node_index =
target_blend_tree_resource->m_blend_tree_resource target_blend_tree_resource->GetNodeIndexForInputSocket(
.GetNodeIndexForInputSocket(connection.target_socket_name); connection.target_socket_name);
target_node = target_node =
target_blend_tree->m_nodes[target_blend_tree_output_node_index]; target_blend_tree->m_nodes[target_blend_tree_output_node_index];
@ -1175,8 +1158,8 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
instance_connection.m_socket = *source_socket; instance_connection.m_socket = *source_socket;
} }
NodeSocketPair node_socket_pair{ NodeSocketNamePair node_socket_pair{
m_blend_tree_resource.GetNode(connection.source_node_index), GetNode(connection.source_node_index),
source_socket->m_name}; source_socket->m_name};
NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter = NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter =
@ -1214,7 +1197,7 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
// const node inputs // const node inputs
// //
std::vector<Socket*> const_inputs = std::vector<Socket*> const_inputs =
m_blend_tree_resource.GetConstantNodeInputs(instance_node_descriptors); GetConstantNodeInputs(instance_node_descriptors);
size_t const_node_inputs_buffer_size = 0; size_t const_node_inputs_buffer_size = 0;
for (auto& const_input : const_inputs) { for (auto& const_input : const_inputs) {
if (const_input->m_type == SocketType::SocketTypeString) { if (const_input->m_type == SocketType::SocketTypeString) {
@ -1248,15 +1231,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
const_input_buffer_offset += i->m_type_size; const_input_buffer_offset += i->m_type_size;
} }
for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) { for (int i = 0; i < GetNumNodes(); i++) {
delete instance_node_descriptors[i]; delete instance_node_descriptors[i];
} }
} }
void AnimGraphResource::SetRuntimeNodeProperties( void BlendTreeResource::SetRuntimeNodeProperties(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
for (int i = 2; i < m_blend_tree_resource.GetNumNodes(); i++) { for (int i = 2; i < GetNumNodes(); i++) {
const AnimNodeResource* node_resource = m_blend_tree_resource.GetNode(i); const AnimNodeResource* node_resource = GetNode(i);
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory( NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
node_resource->m_node_type_name, node_resource->m_node_type_name,
@ -1308,17 +1291,4 @@ void AnimGraphResource::SetRuntimeNodeProperties(
} }
} }
bool AnimGraphResource::SaveStateMachineResourceToFile( #pragma clang diagnostic pop
const char* filename) const {
assert(false && "Not yet implemented");
return false;
}
bool AnimGraphResource::LoadStateMachineResourceFromJson(
nlohmann::json const& json_data) {
assert(false && "Not yet implemented");
return false;
}
#pragma clang diagnostic pop

View File

@ -10,9 +10,10 @@
struct AnimGraphBlendTree; struct AnimGraphBlendTree;
struct AnimGraphStateMachine; struct AnimGraphStateMachine;
struct BlendTreeResource;
struct AnimNodeResource { struct AnimNodeResource {
virtual ~AnimNodeResource() { delete m_virtual_socket_accessor; }; virtual ~AnimNodeResource() { delete m_virtual_socket_accessor; }
std::string m_name; std::string m_name;
std::string m_node_type_name; std::string m_node_type_name;
@ -23,6 +24,30 @@ struct AnimNodeResource {
static inline AnimNodeResource* AnimNodeResourceFactory( static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name); const std::string& node_type_name);
struct StateMachineTransitionResources {
size_t source_state_index = -1;
size_t target_state_index = -1;
float blend_time = 0.f;
bool sync_blend = false;
};
struct StateMachineResource {
std::vector<AnimNodeResource> m_states;
std::vector<StateMachineTransitionResources> m_transitions;
};
struct AnimGraphResource : AnimNodeResource {
~AnimGraphResource() override = default;
static constexpr char DefaultAnimOutput[] = "Output";
[[maybe_unused]] virtual bool SaveToFile(const char* filename) const = 0;
static AnimGraphResource* CreateFromFile(const char* filename);
};
typedef std::unique_ptr<AnimGraphResource> AnimGraphResourcePtr;
struct BlendTreeConnectionResource { struct BlendTreeConnectionResource {
int source_node_index = -1; int source_node_index = -1;
std::string source_socket_name; std::string source_socket_name;
@ -39,12 +64,27 @@ struct BlendTreeConnectionResource {
} }
}; };
struct BlendTreeResource { struct BlendTreeResource : AnimGraphResource {
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketNamePair;
typedef std::map<NodeSocketNamePair, int> NodeSocketDataOffsetMap;
std::vector<std::vector<size_t> > m_node_input_connection_indices; std::vector<std::vector<size_t> > m_node_input_connection_indices;
std::vector<std::vector<size_t> > m_node_inputs_subtree; std::vector<std::vector<size_t> > m_node_inputs_subtree;
BlendTreeResource() {
m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree");
InitGraphConnectors();
RegisterBlendTreeOutputSocket<AnimData>(
AnimGraphResource::DefaultAnimOutput);
}
~BlendTreeResource() { ClearAllNodes(); } ~BlendTreeResource() { ClearAllNodes(); }
[[maybe_unused]] bool SaveToFile(const char* filename) const override;
static BlendTreeResource* CreateFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
void Reset() { void Reset() {
ClearAllNodes(); ClearAllNodes();
@ -65,18 +105,77 @@ struct BlendTreeResource {
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { [[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
return m_nodes[0]; return m_nodes[0];
} }
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const { [[nodiscard]] AnimNodeResource* GetGraphInputNode() const {
return m_nodes[1]; return m_nodes[1];
} }
Socket* GetGraphOutputSocket(const char* socket_name) const { Socket* GetGraphOutputSocket(const char* socket_name) const {
return GetGraphOutputNode()->m_virtual_socket_accessor->GetInputSocket( return GetGraphOutputNode()->m_virtual_socket_accessor->GetInputSocket(
socket_name); socket_name);
} }
Socket* GetGraphInputSocket(const char* socket_name) const { Socket* GetGraphInputSocket(const char* socket_name) const {
return GetGraphInputNode()->m_virtual_socket_accessor->GetOutputSocket( return GetGraphInputNode()->m_virtual_socket_accessor->GetOutputSocket(
socket_name); socket_name);
} }
template <typename T>
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeInputSocket(socket);
}
bool RegisterBlendTreeInputSocket(const Socket& socket) {
AnimNodeResource* input_node = GetGraphInputNode();
Socket* input_socket = GetGraphInputSocket(socket.m_name.c_str());
if (input_socket != nullptr) {
std::cerr << "Error: cannot register output socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
input_node->m_virtual_socket_accessor->m_outputs.push_back(socket);
m_virtual_socket_accessor->m_inputs =
input_node->m_virtual_socket_accessor->m_outputs;
return true;
}
template <typename T>
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeOutputSocket(socket);
}
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
AnimNodeResource* output_node = GetGraphOutputNode();
Socket* output_socket = GetGraphOutputSocket(socket.m_name.c_str());
if (output_socket != nullptr) {
std::cerr << "Error: cannot register output socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
output_node->m_virtual_socket_accessor->m_inputs.push_back(socket);
m_virtual_socket_accessor->m_outputs =
output_node->m_virtual_socket_accessor->m_inputs;
return true;
}
int GetNodeIndex(const AnimNodeResource* node_resource) const { int GetNodeIndex(const AnimNodeResource* node_resource) const {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (m_nodes[i] == node_resource) { if (m_nodes[i] == node_resource) {
@ -125,14 +224,18 @@ struct BlendTreeResource {
void RemoveConnectionsForSocket( void RemoveConnectionsForSocket(
const AnimNodeResource* node_resource, const AnimNodeResource* node_resource,
const Socket& socket); const Socket& socket);
void RemoveNodeConnections(AnimNodeResource* node_resource); void RemoveNodeConnections(AnimNodeResource* node_resource);
[[maybe_unused]] bool RemoveNode(AnimNodeResource* node_resource); [[maybe_unused]] bool RemoveNode(AnimNodeResource* node_resource);
[[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); } [[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); }
[[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; } [[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; }
[[nodiscard]] const AnimNodeResource* GetNode(size_t i) const { [[nodiscard]] const AnimNodeResource* GetNode(size_t i) const {
return m_nodes[i]; return m_nodes[i];
} }
[[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const { [[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const {
return m_nodes; return m_nodes;
} }
@ -172,6 +275,7 @@ struct BlendTreeResource {
const size_t socket_input_index) const; const size_t socket_input_index) const;
std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const; std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const;
std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const; std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const;
bool ConnectSockets( bool ConnectSockets(
@ -265,6 +369,18 @@ struct BlendTreeResource {
} }
private: private:
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
void InitGraphConnectors() { void InitGraphConnectors() {
AddNode(AnimNodeResourceFactory("BlendTreeSockets")); AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* output_node = GetGraphOutputNode(); AnimNodeResource* output_node = GetGraphOutputNode();
@ -281,7 +397,9 @@ struct BlendTreeResource {
m_node_eval_order.clear(); m_node_eval_order.clear();
UpdateNodeEvalOrderRecursive(0); UpdateNodeEvalOrderRecursive(0);
} }
void UpdateNodeEvalOrderRecursive(size_t node_index); void UpdateNodeEvalOrderRecursive(size_t node_index);
void UpdateNodeSubtrees(); void UpdateNodeSubtrees();
std::vector<AnimNodeResource*> m_nodes; std::vector<AnimNodeResource*> m_nodes;
@ -291,123 +409,12 @@ struct BlendTreeResource {
friend class AnimGraphResource; friend class AnimGraphResource;
}; };
struct StateMachineTransitionResources {
size_t source_state_index = -1;
size_t target_state_index = -1;
float blend_time = 0.f;
bool sync_blend = false;
};
struct StateMachineResource {
std::vector<AnimNodeResource> m_states;
std::vector<StateMachineTransitionResources> m_transitions;
};
struct AnimGraphResource : AnimNodeResource {
explicit AnimGraphResource(AnimGraphType graph_type);
virtual ~AnimGraphResource() { Clear(); };
static constexpr char DefaultAnimOutput[] = "Output";
std::string m_graph_type_name;
BlendTreeResource m_blend_tree_resource;
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketPair;
typedef std::map<NodeSocketPair, int> NodeSocketDataOffsetMap;
StateMachineResource m_state_machine_resource;
void Clear() { m_blend_tree_resource.Reset(); }
[[maybe_unused]] bool SaveToFile(const char* filename) const;
static AnimGraphResource* CreateFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
template <typename T>
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeInputSocket(socket);
}
bool RegisterBlendTreeInputSocket(const Socket& socket) {
AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode();
Socket* input_socket =
m_blend_tree_resource.GetGraphInputSocket(socket.m_name.c_str());
if (input_socket != nullptr) {
std::cerr << "Error: cannot register output socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
input_node->m_virtual_socket_accessor->m_outputs.push_back(socket);
m_virtual_socket_accessor->m_inputs =
input_node->m_virtual_socket_accessor->m_outputs;
return true;
}
template <typename T>
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeOutputSocket(socket);
}
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
Socket* output_socket =
m_blend_tree_resource.GetGraphOutputSocket(socket.m_name.c_str());
if (output_socket != nullptr) {
std::cerr << "Error: cannot register output socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
output_node->m_virtual_socket_accessor->m_inputs.push_back(socket);
m_virtual_socket_accessor->m_outputs =
output_node->m_virtual_socket_accessor->m_inputs;
return true;
}
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
private:
// BlendTree
bool SaveBlendTreeResourceToFile(const char* filename) const;
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
bool SaveStateMachineResourceToFile(const char* filename) const;
bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data);
};
typedef std::unique_ptr<AnimGraphResource> AnimGraphResourcePtr;
inline AnimNodeResource* AnimNodeResourceFactory( inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name) { const std::string& node_type_name) {
AnimNodeResource* result; AnimNodeResource* result;
if (node_type_name == "BlendTree") { if (node_type_name == "BlendTree") {
AnimGraphResource* blend_tree_resource = AnimGraphResource* blend_tree_resource = new BlendTreeResource();
new AnimGraphResource(AnimGraphType::GraphTypeBlendTree);
result = blend_tree_resource; result = blend_tree_resource;
} else { } else {
result = new AnimNodeResource(); result = new AnimNodeResource();

View File

@ -214,9 +214,15 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
} }
} }
void AnimGraphEditorRenderSidebar( void BlendTreeEditorRenderSidebar(
BlendTreeResource& blend_tree_resource, BlendTreeResource* blend_tree_resource,
AnimNodeResource* node_resource) { AnimNodeResource* node_resource) {
BlendTreeResource* current_blend_tree_resource =
dynamic_cast<BlendTreeResource*>(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]);
assert(current_blend_tree_resource != nullptr);
ImGui::Text( ImGui::Text(
"[%s (%2.2f, %2.2f)]", "[%s (%2.2f, %2.2f)]",
node_resource->m_node_type_name.c_str(), node_resource->m_node_type_name.c_str(),
@ -276,7 +282,7 @@ void AnimGraphEditorRenderSidebar(
} }
} }
if (node_resource == blend_tree_resource.GetGraphOutputNode()) { if (node_resource == blend_tree_resource->GetGraphOutputNode()) {
ImGui::Text("Outputs"); ImGui::Text("Outputs");
// Graph outputs are the inputs of the output node! // Graph outputs are the inputs of the output node!
@ -319,17 +325,14 @@ void AnimGraphEditorRenderSidebar(
ImGui::PopStyleVar(); ImGui::PopStyleVar();
if (ImGui::Button("+")) { if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource = current_blend_tree_resource->RegisterBlendTreeOutputSocket<float>(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
"GraphFloatOutput" "GraphFloatOutput"
+ std::to_string(current_graph_resource->m_blend_tree_resource + std::to_string(current_blend_tree_resource->GetGraphOutputNode()
.GetGraphOutputNode()
->m_virtual_socket_accessor->m_inputs.size())); ->m_virtual_socket_accessor->m_inputs.size()));
} }
} }
if (node_resource == blend_tree_resource.GetGraphInputNode()) { if (node_resource == blend_tree_resource->GetGraphInputNode()) {
ImGui::Text("Inputs"); ImGui::Text("Inputs");
// Graph inputs are the outputs of the input node! // Graph inputs are the outputs of the input node!
@ -346,7 +349,7 @@ void AnimGraphEditorRenderSidebar(
current_graph_resource->m_virtual_socket_accessor->m_inputs = inputs; current_graph_resource->m_virtual_socket_accessor->m_inputs = inputs;
} }
if (ImGui::Button("X")) { if (ImGui::Button("X")) {
blend_tree_resource.RemoveConnectionsForSocket(node_resource, input); blend_tree_resource->RemoveConnectionsForSocket(node_resource, input);
iter = inputs.erase(iter); iter = inputs.erase(iter);
} else { } else {
iter++; iter++;
@ -355,13 +358,10 @@ void AnimGraphEditorRenderSidebar(
} }
if (ImGui::Button("+")) { if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource = current_blend_tree_resource->RegisterBlendTreeInputSocket<float>(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeInputSocket<float>(
"GraphFloatInput" "GraphFloatInput"
+ std::to_string( + std::to_string(current_blend_tree_resource->GetGraphInputNode()
current_graph_resource->m_blend_tree_resource.GetGraphInputNode() ->m_virtual_socket_accessor->m_outputs.size()));
->m_virtual_socket_accessor->m_outputs.size()));
} }
} }
} }
@ -439,12 +439,15 @@ void BlendTreeEditorNodePopup() {
} }
if (!node_type_name.empty()) { if (!node_type_name.empty()) {
BlendTreeResource* current_blend_tree_resource =
dynamic_cast<BlendTreeResource*>(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]);
AnimNodeResource* node_resource = AnimNodeResourceFactory(node_type_name); AnimNodeResource* node_resource = AnimNodeResourceFactory(node_type_name);
ax::NodeEditor::SetNodePosition( ax::NodeEditor::SetNodePosition(
ax::NodeEditor::NodeId(node_resource), ax::NodeEditor::NodeId(node_resource),
sEditorState.mousePopupStart); sEditorState.mousePopupStart);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] current_blend_tree_resource->AddNode(node_resource);
->m_blend_tree_resource.AddNode(node_resource);
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -559,7 +562,7 @@ void AnimGraphEditorBreadcrumbNavigation() {
} }
} }
void HandleConnectionCreation(BlendTreeResource& current_blend_tree) { void BlendTreeHandleConnectionCreation(BlendTreeResource* current_blend_tree) {
if (ax::NodeEditor::BeginCreate()) { if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id; ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) { if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
@ -603,14 +606,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) {
&source_node_index, &source_node_index,
&source_node_socket_index); &source_node_socket_index);
source_node = current_blend_tree.GetNode(source_node_index); source_node = current_blend_tree->GetNode(source_node_index);
if (source_node != nullptr) { if (source_node != nullptr) {
if (source_node->m_virtual_socket_accessor->m_outputs.size() if (source_node->m_virtual_socket_accessor->m_outputs.size()
< source_node_socket_index) { < source_node_socket_index) {
source_node_socket_index = -1; source_node_socket_index = -1;
} else { } else {
source_socket = current_blend_tree.GetNodeOutputSocketByIndex( source_socket = current_blend_tree->GetNodeOutputSocketByIndex(
source_node, source_node,
source_node_socket_index); source_node_socket_index);
} }
@ -628,14 +631,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) {
&target_node_index, &target_node_index,
&target_node_socket_index); &target_node_socket_index);
target_node = current_blend_tree.GetNode(target_node_index); target_node = current_blend_tree->GetNode(target_node_index);
if (target_node != nullptr) { if (target_node != nullptr) {
if (target_node->m_virtual_socket_accessor->m_inputs.size() if (target_node->m_virtual_socket_accessor->m_inputs.size()
< target_node_socket_index) { < target_node_socket_index) {
target_node_socket_index = -1; target_node_socket_index = -1;
} else { } else {
target_socket = current_blend_tree.GetNodeInputSocketByIndex( target_socket = current_blend_tree->GetNodeInputSocketByIndex(
target_node, target_node,
target_node_socket_index); target_node_socket_index);
} }
@ -658,14 +661,14 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) {
if (!source_pin.Invalid && !target_pin.Invalid) { if (!source_pin.Invalid && !target_pin.Invalid) {
if (source_socket == nullptr || target_socket == nullptr if (source_socket == nullptr || target_socket == nullptr
|| !current_blend_tree.IsConnectionValid( || !current_blend_tree->IsConnectionValid(
source_node, source_node,
source_socket->m_name, source_socket->m_name,
target_node, target_node,
target_socket->m_name)) { target_socket->m_name)) {
ax::NodeEditor::RejectNewItem(); ax::NodeEditor::RejectNewItem();
} else if (ax::NodeEditor::AcceptNewItem()) { } else if (ax::NodeEditor::AcceptNewItem()) {
current_blend_tree.ConnectSockets( current_blend_tree->ConnectSockets(
source_node, source_node,
source_socket->m_name, source_socket->m_name,
target_node, target_node,
@ -678,12 +681,12 @@ void HandleConnectionCreation(BlendTreeResource& current_blend_tree) {
} }
void BlendTreeRenderNodes( void BlendTreeRenderNodes(
BlendTreeResource& current_blend_tree, BlendTreeResource* current_blend_tree,
ax::NodeEditor::Utilities::BlueprintNodeBuilder& builder) { ax::NodeEditor::Utilities::BlueprintNodeBuilder& builder) {
for (size_t node_index = 0, n = current_blend_tree.GetNumNodes(); for (size_t node_index = 0, n = current_blend_tree->GetNumNodes();
node_index < n; node_index < n;
node_index++) { node_index++) {
AnimNodeResource* node_resource = current_blend_tree.GetNode(node_index); AnimNodeResource* node_resource = current_blend_tree->GetNode(node_index);
ax::NodeEditor::NodeId node_id(node_resource); ax::NodeEditor::NodeId node_id(node_resource);
@ -706,7 +709,7 @@ void BlendTreeRenderNodes(
// Inputs // Inputs
std::vector<Socket> node_inputs = std::vector<Socket> node_inputs =
current_blend_tree.GetNodeInputSockets(node_resource); current_blend_tree->GetNodeInputSockets(node_resource);
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) { for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j]; Socket& socket = node_inputs[j];
ax::NodeEditor::PinId input_pin = NodeIndexAndSocketIndexToInputPinId( ax::NodeEditor::PinId input_pin = NodeIndexAndSocketIndexToInputPinId(
@ -718,7 +721,7 @@ void BlendTreeRenderNodes(
DrawSocketIcon( DrawSocketIcon(
socket.m_type, socket.m_type,
current_blend_tree.IsSocketConnected(node_resource, socket.m_name)); current_blend_tree->IsSocketConnected(node_resource, socket.m_name));
ImGui::Spring(0); ImGui::Spring(0);
//ImGui::PushItemWidth(100.0f); //ImGui::PushItemWidth(100.0f);
@ -729,7 +732,7 @@ void BlendTreeRenderNodes(
// Outputs // Outputs
std::vector<Socket> node_outputs = std::vector<Socket> node_outputs =
current_blend_tree.GetNodeOutputSockets(node_resource); current_blend_tree->GetNodeOutputSockets(node_resource);
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) { for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j]; Socket& socket = node_outputs[j];
builder.Output(NodeIndexAndSocketIndexToOutputPinId( builder.Output(NodeIndexAndSocketIndexToOutputPinId(
@ -741,7 +744,7 @@ void BlendTreeRenderNodes(
ImGui::Spring(0); ImGui::Spring(0);
DrawSocketIcon( DrawSocketIcon(
socket.m_type, socket.m_type,
current_blend_tree.IsSocketConnected(node_resource, socket.m_name)); current_blend_tree->IsSocketConnected(node_resource, socket.m_name));
builder.EndOutput(); builder.EndOutput();
} }
@ -754,21 +757,21 @@ void BlendTreeRenderNodes(
} }
} }
void BlendTreeRenderConnections(BlendTreeResource& current_blend_tree) { void BlendTreeRenderConnections(BlendTreeResource* current_blend_tree) {
for (size_t connection_id = 0, n = current_blend_tree.GetNumConnections(); for (size_t connection_id = 0, n = current_blend_tree->GetNumConnections();
connection_id < n; connection_id < n;
connection_id++) { connection_id++) {
const BlendTreeConnectionResource* connection_resource = const BlendTreeConnectionResource* connection_resource =
current_blend_tree.GetConnection(connection_id); current_blend_tree->GetConnection(connection_id);
const AnimNodeResource* source_node_resource = const AnimNodeResource* source_node_resource =
current_blend_tree.GetNode(connection_resource->source_node_index); current_blend_tree->GetNode(connection_resource->source_node_index);
int source_socket_index = int source_socket_index =
source_node_resource->m_virtual_socket_accessor->GetOutputIndex( source_node_resource->m_virtual_socket_accessor->GetOutputIndex(
connection_resource->source_socket_name.c_str()); connection_resource->source_socket_name.c_str());
const AnimNodeResource* target_node_resource = const AnimNodeResource* target_node_resource =
current_blend_tree.GetNode(connection_resource->target_node_index); current_blend_tree->GetNode(connection_resource->target_node_index);
int target_socket_index = int target_socket_index =
target_node_resource->m_virtual_socket_accessor->GetInputIndex( target_node_resource->m_virtual_socket_accessor->GetInputIndex(
connection_resource->target_socket_name.c_str()); connection_resource->target_socket_name.c_str());
@ -786,7 +789,8 @@ void BlendTreeRenderConnections(BlendTreeResource& current_blend_tree) {
target_socket_pin_id); target_socket_pin_id);
} }
} }
void AnimGraphEditorDebugWidget() {
void BlendTreeEditorDebugWidget() {
ImGui::Begin("Connection Debug Panel"); ImGui::Begin("Connection Debug Panel");
ImGui::BeginTable("Connection", 3); ImGui::BeginTable("Connection", 3);
ImGui::TableNextRow(); ImGui::TableNextRow();
@ -882,17 +886,19 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
graph_size.y -= 20; graph_size.y -= 20;
ax::NodeEditor::Begin("Graph Editor", graph_size); ax::NodeEditor::Begin("Graph Editor", graph_size);
AnimGraphResource* current_graph = BlendTreeResource* current_blend_tree_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; dynamic_cast<BlendTreeResource*>(
BlendTreeResource& current_blend_tree = current_graph->m_blend_tree_resource; sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]);
ax::NodeEditor::Utilities::BlueprintNodeBuilder builder; if (current_blend_tree_resource) {
ax::NodeEditor::Utilities::BlueprintNodeBuilder builder;
BlendTreeRenderNodes(current_blend_tree, builder); BlendTreeRenderNodes(current_blend_tree_resource, builder);
BlendTreeRenderConnections(current_blend_tree); BlendTreeRenderConnections(current_blend_tree_resource);
BlendTreeHandleConnectionCreation(current_blend_tree_resource);
HandleConnectionCreation(current_blend_tree); BlendTreeEditorNodePopup();
BlendTreeEditorNodePopup(); }
ax::NodeEditor::End(); ax::NodeEditor::End();
@ -903,22 +909,22 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
// //
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if (ax::NodeEditor::GetSelectedObjectCount() > 0) { if (current_blend_tree_resource
&& ax::NodeEditor::GetSelectedObjectCount() > 0) {
ax::NodeEditor::NodeId selected_node_id = 0; ax::NodeEditor::NodeId selected_node_id = 0;
ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1); ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1);
if (selected_node_id.Get() != 0) { if (selected_node_id.Get() != 0) {
AnimGraphEditorRenderSidebar( BlendTreeEditorRenderSidebar(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] current_blend_tree_resource,
->m_blend_tree_resource,
selected_node_id.AsPointer<AnimNodeResource>()); selected_node_id.AsPointer<AnimNodeResource>());
} }
} }
ImGui::EndTable(); ImGui::EndTable();
AnimGraphEditorDebugWidget(); BlendTreeEditorDebugWidget();
// Clear flag, however it may be re-set further down when handling double // Clear flag, however it may be re-set further down when handling double
// clicking into subgraphs. // clicking into subgraphs.
@ -962,15 +968,14 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
BlendTreeConnectionResource* connection_resource = BlendTreeConnectionResource* connection_resource =
hovered_link.AsPointer<BlendTreeConnectionResource>(); hovered_link.AsPointer<BlendTreeConnectionResource>();
if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) { if (connection_resource && current_blend_tree_resource
BlendTreeResource* blend_tree_resource = && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
&sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] current_blend_tree_resource->DisconnectSockets(
->m_blend_tree_resource; current_blend_tree_resource->GetNode(
connection_resource->source_node_index),
blend_tree_resource->DisconnectSockets(
blend_tree_resource->GetNode(connection_resource->source_node_index),
connection_resource->source_socket_name, connection_resource->source_socket_name,
blend_tree_resource->GetNode(connection_resource->target_node_index), current_blend_tree_resource->GetNode(
connection_resource->target_node_index),
connection_resource->target_socket_name); connection_resource->target_socket_name);
ax::NodeEditor::DeleteLink(hovered_link); ax::NodeEditor::DeleteLink(hovered_link);
@ -982,19 +987,20 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
AnimNodeResource* node_resource = AnimNodeResource* node_resource =
hovered_node.AsPointer<AnimNodeResource>(); hovered_node.AsPointer<AnimNodeResource>();
if (node_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) { if (node_resource && current_blend_tree_resource
AnimGraphResource* current_graph_resource = && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; current_blend_tree_resource->RemoveNodeConnections(node_resource);
current_blend_tree_resource->RemoveNode(node_resource);
current_graph_resource->m_blend_tree_resource.RemoveNodeConnections(
node_resource);
current_graph_resource->m_blend_tree_resource.RemoveNode(node_resource);
} }
} }
ax::NodeEditor::SetCurrentEditor(nullptr); ax::NodeEditor::SetCurrentEditor(nullptr);
} }
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) { void AnimGraphEditorGetRuntimeBlendTree(AnimGraphBlendTree& blend_tree) {
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree); BlendTreeResource* root_blend_tree_resource =
dynamic_cast<BlendTreeResource*>(sEditorState.rootGraphResource);
assert(root_blend_tree_resource);
root_blend_tree_resource->CreateBlendTreeInstance(blend_tree);
} }

View File

@ -83,6 +83,6 @@ void AnimGraphEditorClear();
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context); void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph); void AnimGraphEditorGetRuntimeBlendTree(AnimGraphBlendTree& anim_graph);
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H #endif //ANIMTESTBED_ANIMGRAPHEDITOR_H

View File

@ -745,7 +745,7 @@ int main() {
if (ImGui::Button("Update Runtime Graph")) { if (ImGui::Button("Update Runtime Graph")) {
anim_graph.dealloc(); anim_graph.dealloc();
AnimGraphEditorGetRuntimeGraph(anim_graph); AnimGraphEditorGetRuntimeBlendTree(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton; anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
anim_graph.Init(anim_graph_context); anim_graph.Init(anim_graph_context);

View File

@ -11,22 +11,26 @@
#include "ozz/base/io/stream.h" #include "ozz/base/io/stream.h"
#include "ozz/base/log.h" #include "ozz/base/log.h"
class SimpleAnimSamplerGraphResource { class BlendTreeResourceFixture {
protected: public:
AnimGraphResource graph_resource; BlendTreeResourceFixture() {
blend_tree_resource =
dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
}
virtual ~BlendTreeResourceFixture() { delete blend_tree_resource; }
BlendTreeResource* blend_tree_resource = nullptr; BlendTreeResource* blend_tree_resource = nullptr;
};
class SimpleAnimSamplerBlendTreeResourceFixture
: public BlendTreeResourceFixture {
protected:
size_t walk_node_index = -1; size_t walk_node_index = -1;
AnimNodeResource* walk_node = nullptr; AnimNodeResource* walk_node = nullptr;
public: public:
SimpleAnimSamplerGraphResource() SimpleAnimSamplerBlendTreeResourceFixture() {
: graph_resource(AnimGraphType::GraphTypeBlendTree) {
graph_resource.m_name = "AnimSamplerBlendTree";
graph_resource.m_node_type_name = "BlendTree";
graph_resource.m_graph_type_name = "BlendTree";
blend_tree_resource = &graph_resource.m_blend_tree_resource;
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
walk_node_index = walk_node_index =
blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler")); blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
@ -49,10 +53,8 @@ class SimpleAnimSamplerGraphResource {
} }
}; };
class Blend2GraphResource { class Blend2BlendTreeResource : public BlendTreeResourceFixture {
protected: protected:
AnimGraphResourcePtr graph_resource;
BlendTreeResource* blend_tree_resource = nullptr;
size_t walk_node_index = -1; size_t walk_node_index = -1;
size_t run_node_index = -1; size_t run_node_index = -1;
size_t blend_node_index = -1; size_t blend_node_index = -1;
@ -61,13 +63,8 @@ class Blend2GraphResource {
AnimNodeResource* blend_node = nullptr; AnimNodeResource* blend_node = nullptr;
public: public:
Blend2GraphResource() Blend2BlendTreeResource() {
: graph_resource( blend_tree_resource->m_name = "WalkRunBlendGraph";
dynamic_cast<AnimGraphResource*>(
AnimNodeResourceFactory("BlendTree"))) {
graph_resource->m_name = "WalkRunBlendGraph";
blend_tree_resource = &graph_resource->m_blend_tree_resource;
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
walk_node_index = walk_node_index =
@ -129,12 +126,8 @@ class Blend2GraphResource {
// | | // | |
// +----------------------------------------+ // +----------------------------------------+
// //
class EmbeddedBlendTreeGraphResource { class EmbeddedBlendTreeGraphResource : public BlendTreeResourceFixture {
protected: protected:
AnimGraphResource parent_graph_resource;
BlendTreeResource* parent_blend_tree_resource = nullptr;
AnimGraphResource* embedded_graph = nullptr;
BlendTreeResource* embedded_blend_tree_resource = nullptr; BlendTreeResource* embedded_blend_tree_resource = nullptr;
size_t walk_node_index = -1; size_t walk_node_index = -1;
@ -143,19 +136,14 @@ class EmbeddedBlendTreeGraphResource {
size_t embedded_speed_scale_index = -1; size_t embedded_speed_scale_index = -1;
public: public:
EmbeddedBlendTreeGraphResource() EmbeddedBlendTreeGraphResource() {
: parent_graph_resource(AnimGraphType::GraphTypeBlendTree) { blend_tree_resource->m_name = "ParentBlendTree";
parent_graph_resource.m_name = "ParentBlendTree";
parent_graph_resource.m_graph_type_name = "BlendTree";
parent_graph_resource.m_node_type_name = "BlendTree";
parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource;
// Parent AnimSampler // Parent AnimSampler
walk_node_index = parent_blend_tree_resource->AddNode( walk_node_index =
AnimNodeResourceFactory("AnimSampler")); blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index); walk_node_resource = blend_tree_resource->GetNode(walk_node_index);
walk_node_resource->m_name = "WalkAnim"; walk_node_resource->m_name = "WalkAnim";
walk_node_resource->m_virtual_socket_accessor->SetPropertyValue( walk_node_resource->m_virtual_socket_accessor->SetPropertyValue(
"Filename", "Filename",
@ -164,14 +152,11 @@ class EmbeddedBlendTreeGraphResource {
// //
// Embedded Tree // Embedded Tree
// //
embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode( embedded_blend_tree_node_index =
AnimNodeResourceFactory("BlendTree")); blend_tree_resource->AddNode(AnimNodeResourceFactory("BlendTree"));
embedded_graph = dynamic_cast<AnimGraphResource*>( embedded_blend_tree_resource = dynamic_cast<BlendTreeResource*>(
parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index)); blend_tree_resource->GetNode(embedded_blend_tree_node_index));
embedded_graph->m_name = "EmbeddedBlendTree"; embedded_blend_tree_resource->m_name = "EmbeddedBlendTree";
embedded_graph->m_node_type_name = "BlendTree";
embedded_graph->m_graph_type_name = "BlendTree";
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
// Embedded: outputs // Embedded: outputs
AnimNodeResource* embedded_outputs = AnimNodeResource* embedded_outputs =
@ -210,15 +195,15 @@ class EmbeddedBlendTreeGraphResource {
// Parent: setup connections // Parent: setup connections
const AnimNodeResource* parent_blend_tree_outputs = const AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode(); blend_tree_resource->GetGraphOutputNode();
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
walk_node_resource, walk_node_resource,
"Output", "Output",
embedded_graph, embedded_blend_tree_resource,
"AnimInput")); "AnimInput"));
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
embedded_graph, embedded_blend_tree_resource,
"AnimOutput", "AnimOutput",
parent_blend_tree_outputs, parent_blend_tree_outputs,
"Output")); "Output"));
@ -238,12 +223,8 @@ class EmbeddedBlendTreeGraphResource {
// | | // | |
// +----------------------------------------+ // +----------------------------------------+
// //
class EmbeddedTreeBlend2GraphResource { class EmbeddedTreeBlend2GraphResource : public BlendTreeResourceFixture {
protected: protected:
AnimGraphResource parent_graph_resource;
BlendTreeResource* parent_blend_tree_resource = nullptr;
AnimGraphResource* embedded_graph = nullptr;
BlendTreeResource* embedded_blend_tree_resource = nullptr; BlendTreeResource* embedded_blend_tree_resource = nullptr;
size_t walk_node_index = -1; size_t walk_node_index = -1;
@ -256,26 +237,21 @@ class EmbeddedTreeBlend2GraphResource {
AnimNodeResource* embedded_run_node_resource = nullptr; AnimNodeResource* embedded_run_node_resource = nullptr;
public: public:
EmbeddedTreeBlend2GraphResource() EmbeddedTreeBlend2GraphResource() {
: parent_graph_resource(AnimGraphType::GraphTypeBlendTree) { blend_tree_resource->m_name = "ParentBlendTree";
parent_graph_resource.m_name = "ParentBlendTree";
parent_graph_resource.m_graph_type_name = "BlendTree";
parent_graph_resource.m_node_type_name = "BlendTree";
parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource;
// Setup parent inputs // Setup parent inputs
AnimNodeResource* parent_blend_tree_inputs = AnimNodeResource* parent_blend_tree_inputs =
parent_blend_tree_resource->GetGraphInputNode(); blend_tree_resource->GetGraphInputNode();
parent_blend_tree_inputs->m_virtual_socket_accessor->RegisterOutput<float>( parent_blend_tree_inputs->m_virtual_socket_accessor->RegisterOutput<float>(
"EmbeddedBlend2Weight", "EmbeddedBlend2Weight",
nullptr); nullptr);
// Parent AnimSampler // Parent AnimSampler
walk_node_index = parent_blend_tree_resource->AddNode( walk_node_index =
AnimNodeResourceFactory("AnimSampler")); blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index); walk_node_resource = blend_tree_resource->GetNode(walk_node_index);
walk_node_resource->m_name = "WalkAnim"; walk_node_resource->m_name = "WalkAnim";
walk_node_resource->m_virtual_socket_accessor->SetPropertyValue( walk_node_resource->m_virtual_socket_accessor->SetPropertyValue(
"Filename", "Filename",
@ -284,18 +260,17 @@ class EmbeddedTreeBlend2GraphResource {
// //
// Embedded Tree // Embedded Tree
// //
embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode( embedded_blend_tree_node_index =
AnimNodeResourceFactory("BlendTree")); blend_tree_resource->AddNode(AnimNodeResourceFactory("BlendTree"));
embedded_graph = dynamic_cast<AnimGraphResource*>( embedded_blend_tree_resource = dynamic_cast<BlendTreeResource*>(
parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index)); blend_tree_resource->GetNode(embedded_blend_tree_node_index));
embedded_graph->m_name = "EmbeddedTreeBlend2GraphResource"; embedded_blend_tree_resource->m_name = "EmbeddedTreeBlend2GraphResource";
embedded_graph->m_node_type_name = "BlendTree";
embedded_graph->m_graph_type_name = "BlendTree";
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
// Embedded: inputs // Embedded: inputs
embedded_graph->RegisterBlendTreeInputSocket<AnimData>("AnimInput"); embedded_blend_tree_resource->RegisterBlendTreeInputSocket<AnimData>(
embedded_graph->RegisterBlendTreeInputSocket<float>("BlendWeight"); "AnimInput");
embedded_blend_tree_resource->RegisterBlendTreeInputSocket<float>(
"BlendWeight");
// Embedded nodes // Embedded nodes
embedded_blend2_node_index = embedded_blend_tree_resource->AddNode( embedded_blend2_node_index = embedded_blend_tree_resource->AddNode(
@ -342,22 +317,22 @@ class EmbeddedTreeBlend2GraphResource {
// Parent: setup connections // Parent: setup connections
AnimNodeResource* parent_blend_tree_outputs = AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode(); blend_tree_resource->GetGraphOutputNode();
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
walk_node_resource, walk_node_resource,
"Output", "Output",
embedded_graph, embedded_blend_tree_resource,
"AnimInput")); "AnimInput"));
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
embedded_graph, embedded_blend_tree_resource,
AnimGraphResource::DefaultAnimOutput, AnimGraphResource::DefaultAnimOutput,
parent_blend_tree_outputs, parent_blend_tree_outputs,
AnimGraphResource::DefaultAnimOutput)); AnimGraphResource::DefaultAnimOutput));
REQUIRE(parent_blend_tree_resource->ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
parent_blend_tree_inputs, parent_blend_tree_inputs,
"EmbeddedBlend2Weight", "EmbeddedBlend2Weight",
embedded_graph, embedded_blend_tree_resource,
"BlendWeight")); "BlendWeight"));
} }
}; };
@ -386,6 +361,14 @@ bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) {
void CheckBlendTreeResourcesEqual( void CheckBlendTreeResourcesEqual(
const BlendTreeResource* blend_tree_resource_reference, const BlendTreeResource* blend_tree_resource_reference,
const BlendTreeResource* blend_tree_resource_rhs) { const BlendTreeResource* blend_tree_resource_rhs) {
REQUIRE(
blend_tree_resource_reference->m_node_type_name
== blend_tree_resource_rhs->m_node_type_name);
REQUIRE(blend_tree_resource_reference->m_node_type_name == "BlendTree");
REQUIRE(
blend_tree_resource_reference->m_name == blend_tree_resource_rhs->m_name);
REQUIRE( REQUIRE(
blend_tree_resource_reference->GetNumNodes() blend_tree_resource_reference->GetNumNodes()
== blend_tree_resource_rhs->GetNumNodes()); == blend_tree_resource_rhs->GetNumNodes());
@ -420,27 +403,6 @@ void CheckBlendTreeResourcesEqual(
} }
} }
void CheckAnimGraphResourceEqual(
const AnimGraphResource& graph_resource_reference,
const AnimGraphResource& graph_resource_rhs) {
REQUIRE(
graph_resource_reference.m_graph_type_name
== graph_resource_rhs.m_graph_type_name);
REQUIRE(graph_resource_reference.m_name == graph_resource_rhs.m_name);
REQUIRE(graph_resource_reference.m_graph_type_name == "BlendTree");
const BlendTreeResource* blend_tree_resource_reference =
&graph_resource_reference.m_blend_tree_resource;
const BlendTreeResource* blend_tree_resource_rhs =
&graph_resource_rhs.m_blend_tree_resource;
CheckBlendTreeResourcesEqual(
blend_tree_resource_reference,
blend_tree_resource_rhs);
}
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
int node_id = 3321; int node_id = 3321;
int input_index = 221; int input_index = 221;
@ -461,24 +423,26 @@ TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
} }
TEST_CASE_METHOD( TEST_CASE_METHOD(
SimpleAnimSamplerGraphResource, SimpleAnimSamplerBlendTreeResourceFixture,
"SimpleAnimSamplerGraphResource saving and loading results in same " "SimpleAnimSamplerGraphResource saving and loading results in same "
"resource", "resource",
"[SimpleAnimSamplerGraphResource]") { "[SimpleAnimSamplerGraphResource]") {
graph_resource.SaveToFile("TestGraphAnimSamplerBlendTree.json"); blend_tree_resource->SaveToFile("TestGraphAnimSamplerBlendTree.json");
std::unique_ptr<AnimGraphResource> graph_resource_loaded( std::unique_ptr<BlendTreeResource> blend_tree_resource_loaded(
AnimGraphResource::CreateFromFile("TestGraphAnimSamplerBlendTree.json")); BlendTreeResource::CreateFromFile("TestGraphAnimSamplerBlendTree.json"));
CheckAnimGraphResourceEqual(graph_resource, *graph_resource_loaded); CheckBlendTreeResourcesEqual(
blend_tree_resource,
blend_tree_resource_loaded.get());
} }
TEST_CASE_METHOD( TEST_CASE_METHOD(
SimpleAnimSamplerGraphResource, SimpleAnimSamplerBlendTreeResourceFixture,
"SimpleAnimSamplerGraphResource emulated evaluation", "SimpleAnimSamplerGraphResource emulated evaluation",
"[SimpleAnimSamplerGraphResource]") { "[SimpleAnimSamplerGraphResource]") {
AnimGraphBlendTree anim_graph_blend_tree; AnimGraphBlendTree anim_graph_blend_tree;
graph_resource.CreateBlendTreeInstance(anim_graph_blend_tree); blend_tree_resource->CreateBlendTreeInstance(anim_graph_blend_tree);
AnimGraphContext graph_context; AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton; ozz::animation::Skeleton skeleton;
@ -537,29 +501,25 @@ TEST_CASE_METHOD(
// Checks that node const inputs are properly set. // Checks that node const inputs are properly set.
// //
TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") { TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
AnimGraphResourcePtr graph_resource( BlendTreeResource* blend_tree_resource =
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree"))); dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
graph_resource->m_name = "AnimSamplerSpeedScaleGraph"; blend_tree_resource->m_name = "AnimSamplerSpeedScaleBlendTree";
graph_resource->m_graph_type_name = "BlendTree";
BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
size_t walk_node_index = size_t walk_node_index =
blend_tree_resource.AddNode(AnimNodeResourceFactory("AnimSampler")); blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
size_t speed_scale_node_index = size_t speed_scale_node_index =
blend_tree_resource.AddNode(AnimNodeResourceFactory("SpeedScale")); blend_tree_resource->AddNode(AnimNodeResourceFactory("SpeedScale"));
AnimNodeResource* walk_node = blend_tree_resource.GetNode(walk_node_index); AnimNodeResource* walk_node = blend_tree_resource->GetNode(walk_node_index);
walk_node->m_name = "WalkAnim"; walk_node->m_name = "WalkAnim";
walk_node->m_virtual_socket_accessor->SetPropertyValue( walk_node->m_virtual_socket_accessor->SetPropertyValue(
"Filename", "Filename",
std::string("media/Walking-loop.ozz")); std::string("media/Walking-loop.ozz"));
AnimNodeResource* speed_scale_node = AnimNodeResource* speed_scale_node =
blend_tree_resource.GetNode(speed_scale_node_index); blend_tree_resource->GetNode(speed_scale_node_index);
speed_scale_node->m_name = "SpeedScale"; speed_scale_node->m_name = "SpeedScale";
float speed_scale_value = 1.35f; float speed_scale_value = 1.35f;
speed_scale_node->m_virtual_socket_accessor->SetInputValue( speed_scale_node->m_virtual_socket_accessor->SetInputValue(
@ -567,25 +527,23 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
speed_scale_value); speed_scale_value);
blend_tree_resource blend_tree_resource
.ConnectSockets(walk_node, "Output", speed_scale_node, "Input"); ->ConnectSockets(walk_node, "Output", speed_scale_node, "Input");
blend_tree_resource.ConnectSockets( blend_tree_resource->ConnectSockets(
speed_scale_node, speed_scale_node,
"Output", "Output",
blend_tree_resource.GetGraphOutputNode(), blend_tree_resource->GetGraphOutputNode(),
AnimGraphResource::DefaultAnimOutput); AnimGraphResource::DefaultAnimOutput);
graph_resource->SaveToFile( constexpr char filename[] =
"TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"); "TestGraphAnimSamplerSpeedScaleGraph.animgraph.json";
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile(
"TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"));
BlendTreeResource& blend_tree_resource_loaded = REQUIRE(blend_tree_resource->SaveToFile(filename));
graph_resource_loaded->m_blend_tree_resource; BlendTreeResource* blend_tree_resource_loaded =
BlendTreeResource::CreateFromFile(filename);
Socket* speed_scale_resource_loaded_input = Socket* speed_scale_resource_loaded_input =
blend_tree_resource_loaded.GetNode(speed_scale_node_index) blend_tree_resource_loaded->GetNode(speed_scale_node_index)
->m_virtual_socket_accessor->GetInputSocket("SpeedScale"); ->m_virtual_socket_accessor->GetInputSocket("SpeedScale");
REQUIRE(speed_scale_resource_loaded_input != nullptr); REQUIRE(speed_scale_resource_loaded_input != nullptr);
@ -594,7 +552,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
Catch::Matchers::WithinAbs(speed_scale_value, 0.1)); Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
AnimGraphBlendTree blend_tree; AnimGraphBlendTree blend_tree;
graph_resource_loaded->CreateBlendTreeInstance(blend_tree); blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree);
REQUIRE_THAT( REQUIRE_THAT(
*dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index]) *dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index])
@ -603,7 +561,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
WHEN("Checking node eval order and node subtrees") { WHEN("Checking node eval order and node subtrees") {
const std::vector<size_t>& eval_order = const std::vector<size_t>& eval_order =
graph_resource_loaded->m_blend_tree_resource.GetNodeEvalOrder(); blend_tree_resource_loaded->GetNodeEvalOrder();
THEN("Walk node gets evaluated before speed scale node") { THEN("Walk node gets evaluated before speed scale node") {
CHECK(eval_order.size() == 2); CHECK(eval_order.size() == 2);
@ -613,23 +571,26 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
THEN("Subtree of the speed scale node contains only the walk node") { THEN("Subtree of the speed scale node contains only the walk node") {
CHECK( CHECK(
graph_resource_loaded->m_blend_tree_resource blend_tree_resource_loaded
.m_node_inputs_subtree[speed_scale_node_index] ->m_node_inputs_subtree[speed_scale_node_index]
.size() .size()
== 1); == 1);
CHECK( CHECK(
graph_resource_loaded->m_blend_tree_resource blend_tree_resource_loaded
.m_node_inputs_subtree[speed_scale_node_index][0] ->m_node_inputs_subtree[speed_scale_node_index][0]
== walk_node_index); == walk_node_index);
} }
} }
delete blend_tree_resource_loaded;
delete blend_tree_resource;
} }
// //
// Checks that connections additions and removals are properly validated. // Checks that connections additions and removals are properly validated.
// //
TEST_CASE_METHOD( TEST_CASE_METHOD(
Blend2GraphResource, Blend2BlendTreeResource,
"Connectivity Tests", "Connectivity Tests",
"[AnimGraphResource][Blend2GraphResource]") { "[AnimGraphResource][Blend2GraphResource]") {
INFO("Removing Blend2 -> Output Connection") INFO("Removing Blend2 -> Output Connection")
@ -704,18 +665,16 @@ TEST_CASE("FreeAnimGraphResource", "[Test]") {
} }
TEST_CASE_METHOD( TEST_CASE_METHOD(
Blend2GraphResource, Blend2BlendTreeResource,
"Blend2GraphResource saving and loading results in same resource", "Blend2GraphResource saving and loading results in same resource",
"[Blend2GraphResource]") { "[Blend2GraphResource]") {
graph_resource->SaveToFile("TestGraphBlend2Graph.animgraph.json"); constexpr char filename[] = "TestGraphBlend2Graph.animgraph.json";
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile("TestGraphBlend2Graph.animgraph.json"));
CheckAnimGraphResourceEqual(*graph_resource, *graph_resource_loaded);
REQUIRE(blend_tree_resource->SaveToFile(filename));
BlendTreeResource* blend_tree_resource_loaded = BlendTreeResource* blend_tree_resource_loaded =
&graph_resource_loaded->m_blend_tree_resource; BlendTreeResource::CreateFromFile(filename);
CheckBlendTreeResourcesEqual(blend_tree_resource, blend_tree_resource_loaded);
// Check that the constant weight of the Blend2 node was properly applied when // Check that the constant weight of the Blend2 node was properly applied when
// loading the resource. // loading the resource.
@ -729,14 +688,16 @@ TEST_CASE_METHOD(
Catch::Matchers::WithinAbs( Catch::Matchers::WithinAbs(
blend2_node_descriptor_loaded->GetInputValue<float>("Weight"), blend2_node_descriptor_loaded->GetInputValue<float>("Weight"),
0.01)); 0.01));
delete blend_tree_resource_loaded;
} }
TEST_CASE_METHOD( TEST_CASE_METHOD(
Blend2GraphResource, Blend2BlendTreeResource,
"Blend2GraphResource graph unsynced evaluation", "Blend2GraphResource graph unsynced evaluation",
"[Blend2GraphResource]") { "[Blend2GraphResource]") {
AnimGraphBlendTree blend_tree_graph; AnimGraphBlendTree blend_tree_graph;
graph_resource->CreateBlendTreeInstance(blend_tree_graph); blend_tree_resource->CreateBlendTreeInstance(blend_tree_graph);
AnimGraphContext graph_context; AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton; ozz::animation::Skeleton skeleton;
@ -835,19 +796,16 @@ TEST_CASE_METHOD(
// //
// //
TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") { TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree); BlendTreeResource* blend_tree_resource =
graph_resource_origin.m_name = "TestInputOutputGraph"; dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
graph_resource_origin.m_graph_type_name = "BlendTree"; blend_tree_resource->m_name = "TestInputOutputGraph";
BlendTreeResource& blend_tree_resource =
graph_resource_origin.m_blend_tree_resource;
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
size_t float_to_vec3_node_index = blend_tree_resource.AddNode( size_t float_to_vec3_node_index = blend_tree_resource->AddNode(
AnimNodeResourceFactory("MathFloatToVec3Node")); AnimNodeResourceFactory("MathFloatToVec3Node"));
AnimNodeResource* graph_output_node = AnimNodeResource* graph_output_node =
blend_tree_resource.GetGraphOutputNode(); blend_tree_resource->GetGraphOutputNode();
graph_output_node->m_virtual_socket_accessor->RegisterInput<float>( graph_output_node->m_virtual_socket_accessor->RegisterInput<float>(
"GraphFloatOutput", "GraphFloatOutput",
nullptr); nullptr);
@ -856,38 +814,38 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
nullptr); nullptr);
AnimNodeResource* graph_input_node_resource = AnimNodeResource* graph_input_node_resource =
blend_tree_resource.GetGraphInputNode(); blend_tree_resource->GetGraphInputNode();
graph_input_node_resource->m_virtual_socket_accessor->RegisterOutput<float>( graph_input_node_resource->m_virtual_socket_accessor->RegisterOutput<float>(
"GraphFloatInput", "GraphFloatInput",
nullptr); nullptr);
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
AnimNodeResource* float_to_vec3_node_resource = AnimNodeResource* float_to_vec3_node_resource =
blend_tree_resource.GetNode(float_to_vec3_node_index); blend_tree_resource->GetNode(float_to_vec3_node_index);
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node_resource, graph_input_node_resource,
"GraphFloatInput", "GraphFloatInput",
graph_output_node, graph_output_node,
"GraphFloatOutput")); "GraphFloatOutput"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node_resource, graph_input_node_resource,
"GraphFloatInput", "GraphFloatInput",
float_to_vec3_node_resource, float_to_vec3_node_resource,
"Input0")); "Input0"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node_resource, graph_input_node_resource,
"GraphFloatInput", "GraphFloatInput",
float_to_vec3_node_resource, float_to_vec3_node_resource,
"Input1")); "Input1"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node_resource, graph_input_node_resource,
"GraphFloatInput", "GraphFloatInput",
float_to_vec3_node_resource, float_to_vec3_node_resource,
"Input2")); "Input2"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
float_to_vec3_node_resource, float_to_vec3_node_resource,
"Output", "Output",
graph_output_node, graph_output_node,
@ -895,18 +853,15 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
WHEN("Saving and loading graph resource") { WHEN("Saving and loading graph resource") {
const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; const char* filename = "TestGraphResourceSaveLoadGraphInputs.json";
graph_resource_origin.SaveToFile(filename); REQUIRE(blend_tree_resource->SaveToFile(filename));
AnimGraphResourcePtr graph_resource_loaded( BlendTreeResource* blend_tree_resource_loaded =
AnimGraphResource::CreateFromFile(filename)); BlendTreeResource::CreateFromFile(filename);
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.GetGraphOutputNode(); blend_tree_resource_loaded->GetGraphOutputNode();
const AnimNodeResource* graph_loaded_input_node = const AnimNodeResource* graph_loaded_input_node =
graph_blend_tree_loaded.GetGraphInputNode(); blend_tree_resource_loaded->GetGraphInputNode();
THEN("Graph inputs and outputs must be in loaded resource as well.") { THEN("Graph inputs and outputs must be in loaded resource as well.") {
REQUIRE( REQUIRE(
@ -935,7 +890,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
WHEN("Instantiating an AnimGraph") { WHEN("Instantiating an AnimGraph") {
AnimGraphBlendTree blend_tree_node; AnimGraphBlendTree blend_tree_node;
graph_resource_loaded->CreateBlendTreeInstance(blend_tree_node); blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree_node);
float graph_float_input = 123.456f; float graph_float_input = 123.456f;
blend_tree_node.SetInput("GraphFloatInput", &graph_float_input); blend_tree_node.SetInput("GraphFloatInput", &graph_float_input);
@ -975,7 +930,11 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
} }
} }
} }
delete blend_tree_resource_loaded;
} }
delete blend_tree_resource;
} }
// //
@ -992,21 +951,18 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
// GraphFloat1Output -> GraphFLoatInputSingle * 3 // GraphFloat1Output -> GraphFLoatInputSingle * 3
// //
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") { TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree); BlendTreeResource* blend_tree_resource =
graph_resource_origin.m_name = "TestSimpleMathGraph"; dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
graph_resource_origin.m_graph_type_name = "BlendTree"; blend_tree_resource->m_name = "TestSimpleMathGraph";
BlendTreeResource& blend_tree_resource =
graph_resource_origin.m_blend_tree_resource;
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
size_t math_add0_node_index = size_t math_add0_node_index =
blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode")); blend_tree_resource->AddNode(AnimNodeResourceFactory("MathAddNode"));
size_t math_add1_node_index = size_t math_add1_node_index =
blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode")); blend_tree_resource->AddNode(AnimNodeResourceFactory("MathAddNode"));
AnimNodeResource* graph_output_node = AnimNodeResource* graph_output_node =
blend_tree_resource.GetGraphOutputNode(); blend_tree_resource->GetGraphOutputNode();
graph_output_node->m_virtual_socket_accessor->RegisterInput<float>( graph_output_node->m_virtual_socket_accessor->RegisterInput<float>(
"GraphFloat0Output", "GraphFloat0Output",
@ -1018,57 +974,57 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
"GraphFloat2Output", "GraphFloat2Output",
nullptr); nullptr);
AnimNodeResource* graph_input_node = blend_tree_resource.GetGraphInputNode(); AnimNodeResource* graph_input_node = blend_tree_resource->GetGraphInputNode();
graph_input_node->m_virtual_socket_accessor->RegisterOutput<float>( graph_input_node->m_virtual_socket_accessor->RegisterOutput<float>(
"GraphFloatInput", "GraphFloatInput",
nullptr); nullptr);
// Prepare graph inputs and outputs // Prepare graph inputs and outputs
AnimNodeResource* math_add0_node = AnimNodeResource* math_add0_node =
blend_tree_resource.GetNode(math_add0_node_index); blend_tree_resource->GetNode(math_add0_node_index);
AnimNodeResource* math_add1_node = AnimNodeResource* math_add1_node =
blend_tree_resource.GetNode(math_add1_node_index); blend_tree_resource->GetNode(math_add1_node_index);
// direct output // direct output
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node, graph_input_node,
"GraphFloatInput", "GraphFloatInput",
graph_output_node, graph_output_node,
"GraphFloat0Output")); "GraphFloat0Output"));
// add0 node // add0 node
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node, graph_input_node,
"GraphFloatInput", "GraphFloatInput",
math_add0_node, math_add0_node,
"Input0")); "Input0"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node, graph_input_node,
"GraphFloatInput", "GraphFloatInput",
math_add0_node, math_add0_node,
"Input1")); "Input1"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
math_add0_node, math_add0_node,
"Output", "Output",
graph_output_node, graph_output_node,
"GraphFloat1Output")); "GraphFloat1Output"));
// add1 node // add1 node
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
math_add0_node, math_add0_node,
"Output", "Output",
math_add1_node, math_add1_node,
"Input0")); "Input0"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
graph_input_node, graph_input_node,
"GraphFloatInput", "GraphFloatInput",
math_add1_node, math_add1_node,
"Input1")); "Input1"));
REQUIRE(blend_tree_resource.ConnectSockets( REQUIRE(blend_tree_resource->ConnectSockets(
math_add1_node, math_add1_node,
"Output", "Output",
graph_output_node, graph_output_node,
@ -1076,14 +1032,14 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
WHEN("Saving and loading graph resource") { WHEN("Saving and loading graph resource") {
const char* filename = "TestGraphResourceSaveLoadGraphInputs.json"; const char* filename = "TestGraphResourceSaveLoadGraphInputs.json";
graph_resource_origin.SaveToFile(filename); REQUIRE(blend_tree_resource->SaveToFile(filename));
AnimGraphResourcePtr graph_resource_loaded( BlendTreeResource* blend_tree_resource_loaded =
AnimGraphResource::CreateFromFile(filename)); BlendTreeResource::CreateFromFile(filename);
WHEN("Instantiating an AnimGraph") { WHEN("Instantiating an AnimGraph") {
AnimGraphBlendTree blend_tree; AnimGraphBlendTree blend_tree;
graph_resource_loaded->CreateBlendTreeInstance(blend_tree); blend_tree_resource_loaded->CreateBlendTreeInstance(blend_tree);
float graph_float_input = 123.456f; float graph_float_input = 123.456f;
blend_tree.SetInput("GraphFloatInput", &graph_float_input); blend_tree.SetInput("GraphFloatInput", &graph_float_input);
@ -1117,7 +1073,11 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
context.freeAnimations(); context.freeAnimations();
} }
} }
delete blend_tree_resource_loaded;
} }
delete blend_tree_resource;
} }
// //
@ -1137,33 +1097,28 @@ TEST_CASE_METHOD(
"EmbeddedBlendTreeGraphResource saving and loading results in same " "EmbeddedBlendTreeGraphResource saving and loading results in same "
"resource", "resource",
"[EmbeddedBlendTreeGraphResource]") { "[EmbeddedBlendTreeGraphResource]") {
parent_graph_resource.SaveToFile("TestGraphEmbeddedBlendTree.json"); constexpr char filename[] = "TestGraphEmbeddedBlendTree.json";
REQUIRE(blend_tree_resource->SaveToFile(filename));
AnimGraphResourcePtr parent_graph_resource_loaded( BlendTreeResource* blend_tree_resource_loaded =
AnimGraphResource::CreateFromFile("TestGraphEmbeddedBlendTree.json")); BlendTreeResource::CreateFromFile(filename);
// Check the loaded parent graph // Check the loaded parent graph
CheckAnimGraphResourceEqual( CheckBlendTreeResourcesEqual(blend_tree_resource, blend_tree_resource_loaded);
parent_graph_resource,
*parent_graph_resource_loaded);
const BlendTreeResource& parent_blend_tree_resource_loaded =
parent_graph_resource_loaded->m_blend_tree_resource;
// Check the loaded embedded graph // Check the loaded embedded graph
REQUIRE( REQUIRE(
parent_blend_tree_resource_loaded.GetNode(3)->m_node_type_name blend_tree_resource_loaded->GetNode(3)->m_node_type_name == "BlendTree");
== "BlendTree");
const AnimGraphResource* embedded_graph_loaded =
dynamic_cast<const AnimGraphResource*>(
parent_blend_tree_resource_loaded.GetNode(3));
const BlendTreeResource* embedded_blend_tree_resource_loaded = const BlendTreeResource* embedded_blend_tree_resource_loaded =
&embedded_graph_loaded->m_blend_tree_resource; dynamic_cast<const BlendTreeResource*>(
blend_tree_resource_loaded->GetNode(3));
CheckBlendTreeResourcesEqual( CheckBlendTreeResourcesEqual(
embedded_blend_tree_resource, embedded_blend_tree_resource,
embedded_blend_tree_resource_loaded); embedded_blend_tree_resource_loaded);
delete blend_tree_resource_loaded;
} }
TEST_CASE_METHOD( TEST_CASE_METHOD(
@ -1172,7 +1127,7 @@ TEST_CASE_METHOD(
"[EmbeddedBlendTreeGraphResource]") { "[EmbeddedBlendTreeGraphResource]") {
AnimGraphBlendTree blend_tree; AnimGraphBlendTree blend_tree;
parent_graph_resource.CreateBlendTreeInstance(blend_tree); blend_tree_resource->CreateBlendTreeInstance(blend_tree);
AnimGraphContext graph_context; AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton; ozz::animation::Skeleton skeleton;
@ -1249,7 +1204,7 @@ TEST_CASE_METHOD(
"[EmbeddedTreeBlend2GraphResource]") { "[EmbeddedTreeBlend2GraphResource]") {
AnimGraphBlendTree blend_tree; AnimGraphBlendTree blend_tree;
parent_graph_resource.CreateBlendTreeInstance(blend_tree); blend_tree_resource->CreateBlendTreeInstance(blend_tree);
AnimGraphContext graph_context; AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton; ozz::animation::Skeleton skeleton;
@ -1332,21 +1287,19 @@ TEST_CASE_METHOD(
TEST_CASE( TEST_CASE(
"Register AnimGraphResource Blendtree Sockets", "Register AnimGraphResource Blendtree Sockets",
"[AnimGraphResource]") { "[AnimGraphResource]") {
AnimNodeResource* blend_tree_anim_node_resource = BlendTreeResource* blend_tree_resource =
AnimNodeResourceFactory("BlendTree"); dynamic_cast<BlendTreeResource*>(AnimNodeResourceFactory("BlendTree"));
AnimGraphResource* blend_tree_graph_resource =
dynamic_cast<AnimGraphResource*>(blend_tree_anim_node_resource);
Socket socket; Socket socket;
socket.m_name = "FloatSocket"; socket.m_name = "FloatSocket";
socket.m_type = SocketType::SocketTypeFloat; socket.m_type = SocketType::SocketTypeFloat;
socket.m_reference.ptr = nullptr; socket.m_reference.ptr = nullptr;
CHECK(blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket)); CHECK(blend_tree_resource->RegisterBlendTreeInputSocket(socket));
CHECK(!blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket)); CHECK(!blend_tree_resource->RegisterBlendTreeInputSocket(socket));
CHECK(blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket)); CHECK(blend_tree_resource->RegisterBlendTreeOutputSocket(socket));
CHECK(!blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket)); CHECK(!blend_tree_resource->RegisterBlendTreeOutputSocket(socket));
delete blend_tree_anim_node_resource; delete blend_tree_resource;
} }