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