AnimTestbed/src/AnimGraph/AnimGraphResource.cc

898 lines
31 KiB
C++
Raw Normal View History

2024-04-05 00:18:07 +02:00
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
//
// Created by martin on 17.03.24.
//
#include "AnimGraphResource.h"
#include <cstring>
2023-04-02 16:26:24 +02:00
#include <fstream>
2022-02-14 22:37:19 +01:00
#include "3rdparty/json/json.hpp"
#include "AnimGraphBlendTree.h"
#include "AnimGraphNodes.h"
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);
2022-02-14 22:37:19 +01:00
//
// Socket <-> json
//
std::string sSocketTypeToStr(SocketType pin_type) {
if (pin_type < SocketType::SocketTypeUndefined
|| pin_type >= SocketType::SocketTypeLast) {
return "Unknown";
}
return SocketTypeNames[static_cast<int>(pin_type)];
}
2022-02-14 22:37:19 +01:00
json sSocketToJson(const Socket& socket) {
json result;
2022-02-14 22:37:19 +01:00
result["name"] = socket.m_name;
result["type"] = sSocketTypeToStr(socket.m_type);
2023-04-02 16:26:24 +02:00
if (socket.m_type == SocketType::SocketTypeString
2023-04-15 22:07:22 +02:00
&& !socket.m_value_string.empty()) {
result["value"] = socket.m_value_string;
} else if (socket.m_value.flag) {
if (socket.m_type == SocketType::SocketTypeBool) {
result["value"] = socket.m_value.flag;
} else if (socket.m_type == SocketType::SocketTypeAnimation) {
2023-04-02 21:40:49 +02:00
} else if (socket.m_type == SocketType::SocketTypeInt) {
result["value"] = socket.m_value.int_value;
} else if (socket.m_type == SocketType::SocketTypeFloat) {
result["value"] = socket.m_value.float_value;
} else if (socket.m_type == SocketType::SocketTypeVec3) {
result["value"][0] = socket.m_value.vec3.v[0];
result["value"][1] = socket.m_value.vec3.v[1];
result["value"][2] = socket.m_value.vec3.v[2];
} else if (socket.m_type == SocketType::SocketTypeQuat) {
result["value"][0] = socket.m_value.quat.v[0];
result["value"][1] = socket.m_value.quat.v[1];
result["value"][2] = socket.m_value.quat.v[2];
result["value"][3] = socket.m_value.quat.v[3];
} else {
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
<< "'." << std::endl;
}
}
2022-02-14 22:37:19 +01:00
return result;
}
Socket sJsonToSocket(const json& json_data) {
Socket result;
result.m_type = SocketType::SocketTypeUndefined;
result.m_reference.ptr = &result.m_value.int_value;
2022-02-14 22:37:19 +01:00
result.m_name = json_data["name"];
std::string type_string = json_data["type"];
bool have_value = json_data.contains("value");
2022-02-14 22:37:19 +01:00
if (type_string == "Bool") {
result.m_type = SocketType::SocketTypeBool;
result.m_type_size = sizeof(bool);
result.m_reference.ptr = &result.m_value.int_value;
if (have_value) {
result.m_value.flag = json_data["value"];
}
2022-02-14 22:37:19 +01:00
} else if (type_string == "Animation") {
result.m_type = SocketType::SocketTypeAnimation;
result.m_type_size = sizeof(AnimData);
2023-04-02 21:40:49 +02:00
} else if (type_string == "Int") {
result.m_type = SocketType::SocketTypeInt;
result.m_type_size = sizeof(int);
if (have_value) {
result.m_value.int_value = json_data["value"];
}
2022-02-14 22:37:19 +01:00
} else if (type_string == "Float") {
result.m_type = SocketType::SocketTypeFloat;
result.m_type_size = sizeof(float);
if (have_value) {
result.m_value.float_value = json_data["value"];
}
2022-02-14 22:37:19 +01:00
} else if (type_string == "Vec3") {
result.m_type = SocketType::SocketTypeVec3;
result.m_type_size = sizeof(Vec3);
if (have_value) {
result.m_value.vec3.x = json_data["value"][0];
result.m_value.vec3.y = json_data["value"][1];
result.m_value.vec3.z = json_data["value"][2];
}
2022-02-14 22:37:19 +01:00
} else if (type_string == "Quat") {
result.m_type = SocketType::SocketTypeQuat;
result.m_type_size = sizeof(Quat);
if (have_value) {
result.m_value.quat.x = json_data["value"][0];
result.m_value.quat.y = json_data["value"][1];
result.m_value.quat.z = json_data["value"][2];
result.m_value.quat.w = json_data["value"][3];
}
2022-02-14 22:37:19 +01:00
} else if (type_string == "String") {
result.m_type = SocketType::SocketTypeString;
result.m_type_size = sizeof(std::string);
if (have_value) {
result.m_value_string = json_data["value"];
}
2022-02-14 22:37:19 +01:00
} else {
std::cerr << "Invalid socket type '" << type_string << "'." << std::endl;
}
return result;
}
2022-02-14 22:37:19 +01:00
//
// AnimGraphNode <-> json
//
json sAnimGraphNodeToJson(
const AnimNodeResource* node,
2023-04-15 22:07:22 +02:00
size_t node_index,
const std::vector<BlendTreeConnectionResource>& connections) {
json result;
result["name"] = node->m_name;
result["type"] = "AnimNodeResource";
result["node_type"] = node->m_node_type_name;
for (size_t j = 0; j < 2; j++) {
result["position"][j] = node->m_position[j];
}
if (node->m_node_type_name == "BlendTree") {
}
for (const auto& socket : node->m_socket_accessor->m_inputs) {
if (socket.m_type == SocketType::SocketTypeAnimation) {
continue;
}
bool socket_connected = false;
for (const auto& connection : connections) {
2023-04-15 22:07:22 +02:00
if (connection.source_node_index == node_index
&& connection.source_socket_name == socket.m_name) {
socket_connected = true;
break;
}
}
if (!socket_connected) {
2023-04-02 16:26:24 +02:00
result["inputs"].push_back(sSocketToJson(socket));
}
}
for (auto& property : node->m_socket_accessor->m_properties) {
result["properties"][property.m_name] = sSocketToJson(property);
}
return result;
}
AnimNodeResource* sAnimGraphNodeFromJson(
const json& json_node,
size_t node_index) {
std::string node_type = json_node["node_type"];
if (node_type == "BlendTree") {
AnimGraphResource* result = new AnimGraphResource();
sAnimGraphResourceBlendTreeFromJson(json_node, result);
return result;
}
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]);
}
2023-04-02 16:26:24 +02:00
if (node_index != 0 && node_index != 1 && json_node.contains("inputs")) {
for (size_t j = 0, n = json_node["inputs"].size(); j < n; j++) {
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());
2023-04-02 16:26:24 +02:00
if (input_socket == nullptr) {
std::cerr << "Could not find input socket with name " << input_name
<< " for node type " << result->m_node_type_name << std::endl;
2023-04-02 16:26:24 +02:00
abort();
}
*input_socket = sJsonToSocket(json_node["inputs"][j]);
}
}
return result;
}
2022-02-14 22:37:19 +01:00
//
// AnimGraphConnectionResource <-> Json
2022-02-14 22:37:19 +01:00
//
json sAnimGraphConnectionToJson(const BlendTreeConnectionResource& connection) {
json result;
result["type"] = "AnimGraphConnectionResource";
result["source_node_index"] = connection.source_node_index;
result["source_socket_name"] = connection.source_socket_name;
result["target_node_index"] = connection.target_node_index;
result["target_socket_name"] = connection.target_socket_name;
return result;
}
BlendTreeConnectionResource sAnimGraphConnectionFromJson(
const json& json_node) {
BlendTreeConnectionResource connection;
connection.source_node_index = json_node["source_node_index"];
connection.source_socket_name = json_node["source_socket_name"];
connection.target_node_index = json_node["target_node_index"];
connection.target_socket_name = json_node["target_socket_name"];
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
2023-04-02 16:26:24 +02:00
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") {
2022-02-14 22:37:19 +01:00
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);
}
2024-03-20 22:33:31 +01:00
// 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(
2023-04-15 22:07:22 +02:00
sJsonToSocket(graph_output));
}
2024-03-20 22:33:31 +01:00
// 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));
2024-03-20 22:33:31 +01:00
}
}
// 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 =
2023-04-15 22:07:22 +02:00
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) {
std::cerr << "Cannot connect nodes: could not find source socket '"
<< source_socket_name << "'." << std::endl;
}
if (target_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find target socket '"
<< target_socket_name << "'." << std::endl;
}
if (target_socket == nullptr || source_socket == nullptr) {
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);
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 false;
}
if (json_data["type"] != "AnimNodeResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_data["type"] << "'." << std::endl;
return false;
}
if (json_data["node_type"] == "BlendTree") {
return sAnimGraphResourceBlendTreeFromJson(json_data, this);
} else if (json_data["node_type"] == "StateMachine") {
return LoadStateMachineResourceFromJson(json_data);
}
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
"'StateMachine' but got '"
<< json_data["node_type"] << "'." << std::endl;
return false;
}
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 {
if (m_node_type_name != "BlendTree") {
std::cerr
<< "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
<< m_graph_type_name << "'." << std::endl;
return;
}
result.m_name = m_name;
NodeSocketDataOffsetMap node_offset_map;
CreateBlendTreeRuntimeNodeInstances(result);
PrepareBlendTreeIOData(result, node_offset_map);
SetRuntimeNodeProperties(result);
CreateBlendTreeConnectionInstances(result, node_offset_map);
result.UpdateOrderedNodes();
result.ResetNodeStates();
}
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
AnimGraphBlendTree& result) const {
for (auto node_resource : m_blend_tree_resource.m_nodes) {
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
if (node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* embedded_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(node_resource);
assert(embedded_blend_tree_resource != nullptr);
AnimGraphBlendTree* embedded_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(node);
assert(embedded_blend_tree != nullptr);
embedded_blend_tree_resource->CreateBlendTreeInstance(
*embedded_blend_tree);
embedded_blend_tree_resource->m_socket_accessor->m_inputs =
embedded_blend_tree->m_node_descriptor->m_outputs;
embedded_blend_tree_resource->m_socket_accessor->m_outputs =
embedded_blend_tree->m_node_descriptor->m_inputs;
}
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
result.m_node_input_connections.emplace_back();
result.m_node_output_connections.emplace_back();
}
}
void AnimGraphResource::PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const {
instance.m_node_descriptor =
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
instance.m_node_descriptor->m_outputs =
m_blend_tree_resource.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;
2023-04-02 16:26:24 +02:00
//
// graph inputs
//
int input_block_size = 0;
std::vector<Socket>& graph_inputs = instance.GetGraphInputs();
for (int i = 0; i < graph_inputs.size(); i++) {
input_block_size += sizeof(void*);
}
if (input_block_size > 0) {
instance.m_input_buffer = new char[input_block_size];
memset(instance.m_input_buffer, 0, input_block_size);
}
int input_block_offset = 0;
for (int i = 0; i < graph_inputs.size(); i++) {
graph_inputs[i].m_reference.ptr =
(void*)&instance.m_input_buffer[input_block_offset];
instance.m_node_descriptor->m_outputs[i].m_reference.ptr =
&instance.m_input_buffer[input_block_offset];
input_block_offset += sizeof(void*);
}
2023-04-02 16:26:24 +02:00
//
// graph outputs
//
int output_block_size = 0;
std::vector<Socket>& graph_outputs = instance.GetGraphOutputs();
for (int i = 0; i < graph_outputs.size(); i++) {
output_block_size += sizeof(void*);
}
if (output_block_size > 0) {
instance.m_output_buffer = new char[output_block_size];
memset(instance.m_output_buffer, 0, output_block_size);
}
int output_block_offset = 0;
for (int i = 0; i < graph_outputs.size(); i++) {
instance.m_node_descriptor->m_inputs[i].m_reference.ptr =
&instance.m_output_buffer[output_block_offset];
output_block_offset += sizeof(void*);
}
//
// connecton data storage
//
2023-04-15 22:07:22 +02:00
size_t connection_data_storage_size = 0;
for (const auto& connection : m_blend_tree_resource.m_connections) {
const AnimNodeResource* source_node =
m_blend_tree_resource.m_nodes[connection.source_node_index];
Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket(
connection.source_socket_name.c_str());
NodeSocketPair source_socket_pair{source_node, source_socket->m_name};
if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) {
node_offset_map.insert(
{source_socket_pair, connection_data_storage_size});
connection_data_storage_size += source_socket->m_type_size;
}
}
if (connection_data_storage_size > 0) {
instance.m_connection_data_storage = new char[connection_data_storage_size];
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
}
}
void AnimGraphResource::CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const {
std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_blend_tree_resource.m_nodes.size(),
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_node_type_name,
instance.m_nodes[i]);
if (i > 1
&& m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
instance_node_descriptors[i]->m_inputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs;
instance_node_descriptors[i]->m_outputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_outputs;
}
}
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;
instance_node_descriptors[1]->m_outputs =
instance.m_node_descriptor->m_outputs;
for (const auto& connection : m_blend_tree_resource.m_connections) {
NodeDescriptorBase* source_node_descriptor =
instance_node_descriptors[connection.source_node_index];
NodeDescriptorBase* target_node_descriptor =
instance_node_descriptors[connection.target_node_index];
AnimNode* source_node = instance.m_nodes[connection.source_node_index];
AnimNode* target_node = instance.m_nodes[connection.target_node_index];
Socket* source_socket = source_node_descriptor->GetOutputSocket(
connection.source_socket_name.c_str());
Socket* target_socket = target_node_descriptor->GetInputSocket(
connection.target_socket_name.c_str());
AnimGraphConnection instance_connection;
if (source_node->m_node_type_name == "BlendTree") {
// For embedded subgraphs we have to ensure two things:
// 1. The parent graph target node must activate the source node within
// the embedded subgraph.
// 2. The parent graph target node must activate the AnimNode of the
// parent graph which contains the embedded subgraph. Otherwise, the
// embedded graph does not get evaluated.
// For each case we have to add a connection here. First we insert the
// default connection within the graph (i.e. for case 2). Then we alter
// the source node such that it points to the node within the embedded
// subgraph.
AnimGraphConnection embedded_graph_activation_connection;
embedded_graph_activation_connection.m_source_node = source_node;
embedded_graph_activation_connection.m_source_socket_name =
source_socket->m_name;
embedded_graph_activation_connection.m_target_node = target_node;
embedded_graph_activation_connection.m_target_socket_name =
target_socket->m_name;
embedded_graph_activation_connection.m_socket = *target_socket;
instance.m_node_input_connections[connection.target_node_index].push_back(
embedded_graph_activation_connection);
instance.m_node_output_connections[connection.source_node_index]
.push_back(embedded_graph_activation_connection);
AnimGraphResource* source_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(
m_blend_tree_resource.m_nodes[connection.source_node_index]);
AnimGraphBlendTree* source_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(source_node);
size_t source_blend_tree_output_node_index =
source_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForOutputSocket(connection.source_socket_name);
source_node =
source_blend_tree->m_nodes[source_blend_tree_output_node_index];
instance_connection.m_crosses_hierarchy = true;
} else if (target_node->m_node_type_name == "BlendTree") {
// When a connection points to an embedded blend tree we have to ensure
// that the embedded node knows about its connection partner in the parent
// tree. This allows the embedded node to properly activate the node in
// the parent graph.
AnimGraphResource* target_blend_tree_resource =
dynamic_cast<AnimGraphResource*>(
m_blend_tree_resource.m_nodes[connection.target_node_index]);
AnimGraphBlendTree* target_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(target_node);
size_t target_blend_tree_output_node_index =
target_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForInputSocket(connection.target_socket_name);
target_node =
target_blend_tree->m_nodes[target_blend_tree_output_node_index];
// Make sure that the embedded node input activates the parent graph node.
std::vector<AnimGraphConnection>& embeded_target_node_inputs =
target_blend_tree
->m_node_input_connections[target_blend_tree_output_node_index];
for (size_t j = 0; j < embeded_target_node_inputs.size(); j++) {
AnimGraphConnection& embedded_target_connection =
embeded_target_node_inputs[j];
if (embedded_target_connection.m_source_socket_name
== target_socket->m_name) {
embedded_target_connection.m_source_node = source_node;
embedded_target_connection.m_crosses_hierarchy = true;
// In addition: make sure we expose the embedded socket to the
// connection in the parent blend tree. That way
// parent_tree.SetValue<>() correctly propagates the value to the
// embedded node socket.
target_socket = &embedded_target_connection.m_socket;
}
}
}
instance_connection.m_source_node = source_node;
instance_connection.m_source_socket_name = source_socket->m_name;
instance_connection.m_target_node = target_node;
instance_connection.m_target_socket_name = target_socket->m_name;
instance_connection.m_socket = *target_socket;
size_t socket_data_offset = 0;
if (connection.source_node_index == 1) {
instance_connection.m_socket = *target_socket;
} else {
if (connection.target_node_index == 0) {
instance_connection.m_socket = *source_socket;
}
NodeSocketPair node_socket_pair{
m_blend_tree_resource.m_nodes[connection.source_node_index],
source_socket->m_name};
NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter =
node_offset_map.find(node_socket_pair);
if (socket_data_offset_iter != node_offset_map.end()) {
socket_data_offset = socket_data_offset_iter->second;
} else {
std::cerr << "Error: could not find data offset for node '"
<< source_node->m_name << "' and socket '"
<< source_socket->m_name << "'." << std::endl;
assert(false);
abort();
}
*target_socket->m_reference.ptr_ptr =
&instance.m_connection_data_storage[socket_data_offset];
*source_socket->m_reference.ptr_ptr =
&instance.m_connection_data_storage[socket_data_offset];
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
instance.m_animdata_blocks.push_back(
(AnimData*)(&instance
.m_connection_data_storage[socket_data_offset]));
}
}
instance.m_node_input_connections[connection.target_node_index].push_back(
instance_connection);
instance.m_node_output_connections[connection.source_node_index].push_back(
instance_connection);
}
2023-04-02 16:26:24 +02:00
//
// const node inputs
//
std::vector<Socket*> const_inputs =
m_blend_tree_resource.GetConstantNodeInputs(instance_node_descriptors);
2023-04-15 22:07:22 +02:00
size_t const_node_inputs_buffer_size = 0;
for (auto& const_input : const_inputs) {
2023-04-15 22:07:22 +02:00
if (const_input->m_type == SocketType::SocketTypeString) {
2023-04-02 16:26:24 +02:00
// TODO: implement string const node input support
std::cerr << "Error: const inputs for strings not yet implemented!"
<< std::endl;
abort();
}
2023-04-15 22:07:22 +02:00
const_node_inputs_buffer_size += const_input->m_type_size;
2023-04-02 16:26:24 +02:00
}
if (const_node_inputs_buffer_size > 0) {
instance.m_const_node_inputs = new char[const_node_inputs_buffer_size];
memset(instance.m_const_node_inputs, '\0', const_node_inputs_buffer_size);
}
2023-04-15 22:07:22 +02:00
size_t const_input_buffer_offset = 0;
for (auto& i : const_inputs) {
2023-04-15 22:07:22 +02:00
Socket* const_input = i;
2023-04-02 16:26:24 +02:00
// TODO: implement string const node input support
assert(const_input->m_type != SocketType::SocketTypeString);
*const_input->m_reference.ptr_ptr =
&instance.m_const_node_inputs[const_input_buffer_offset];
memcpy(
*const_input->m_reference.ptr_ptr,
&const_input->m_value,
i->m_type_size);
2023-04-02 16:26:24 +02:00
2023-04-15 22:07:22 +02:00
const_input_buffer_offset += i->m_type_size;
2023-04-02 16:26:24 +02:00
}
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
delete instance_node_descriptors[i];
}
}
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];
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
node_resource->m_node_type_name,
result.m_nodes[i]);
std::vector<Socket>& resource_properties =
node_resource->m_socket_accessor->m_properties;
for (const auto& property : resource_properties) {
const std::string& name = property.m_name;
switch (property.m_type) {
case SocketType::SocketTypeBool:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.flag);
break;
2023-04-02 21:40:49 +02:00
case SocketType::SocketTypeInt:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.int_value);
break;
case SocketType::SocketTypeFloat:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.float_value);
break;
case SocketType::SocketTypeVec3:
node_instance_accessor->SetProperty<Vec3>(
name.c_str(),
property.m_value.vec3);
break;
case SocketType::SocketTypeQuat:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.quat);
break;
case SocketType::SocketTypeString:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value_string);
break;
default:
std::cerr << "Invalid socket type "
<< static_cast<int>(property.m_type) << std::endl;
}
}
delete node_instance_accessor;
}
}
2023-04-02 16:26:24 +02:00
bool AnimGraphResource::SaveStateMachineResourceToFile(
const char* filename) const {
assert(false && "Not yet implemented");
2023-04-02 16:26:24 +02:00
return false;
}
2023-04-02 16:26:24 +02:00
bool AnimGraphResource::LoadStateMachineResourceFromJson(
nlohmann::json const& json_data) {
assert(false && "Not yet implemented");
2023-04-02 16:26:24 +02:00
return false;
2024-04-05 00:18:07 +02:00
}
#pragma clang diagnostic pop