Compare commits

..

3 Commits

Author SHA1 Message Date
Martin Felis
bbab4d8608 Graph editor allows instantiation of different node types. 2022-02-12 12:06:25 +01:00
Martin Felis
2b7cbe9d4c Simple graph output connection working. 2022-02-12 11:46:50 +01:00
Martin Felis
c2ae0a11d2 WIP AnimGraph instantiation. 2022-02-12 10:14:26 +01:00
4 changed files with 257 additions and 195 deletions

View File

@ -4,16 +4,14 @@
#include "AnimGraphEditor.h"
#include "imnodes.h"
#include "AnimGraphResource.h"
#include "imnodes.h"
static AnimGraphResource gGraphResource;
constexpr int NodeInputAttributeFlag = 2 << 16;
constexpr int NodeOutputAttributeFlag = 2 << 17;
void AnimGraphEditorUpdate() {
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
@ -22,17 +20,22 @@ void AnimGraphEditorUpdate() {
if (ImGui::Button("Load")) {
gGraphResource.loadFromFile("editor_graph.json");
for (size_t i = 0, n = gGraphResource.m_nodes.size(); i < n; i++) {
for (size_t i = 1, n = gGraphResource.m_nodes.size(); i < n; i++) {
const AnimNodeResource& node_resource = gGraphResource.m_nodes[i];
ImNodes::SetNodeGridSpacePos(i, ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
ImNodes::SetNodeGridSpacePos(
i,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
}
if (ImGui::Button("Clear")) {
gGraphResource.clear();
}
char graph_name_buffer[256];
memset (graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy (graph_name_buffer, gGraphResource.m_name.c_str(), sizeof(graph_name_buffer));
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy(
graph_name_buffer,
gGraphResource.m_name.c_str(),
sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
gGraphResource.m_name = graph_name_buffer;
}
@ -41,15 +44,48 @@ void AnimGraphEditorUpdate() {
ImNodes::BeginNodeEditor();
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImNodes::IsEditorHovered()
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right)) {
AnimNodeResource node_resource = AnimNodeResourceFactory("Blend2");
size_t node_id = gGraphResource.m_nodes.size();
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
gGraphResource.m_nodes.push_back(node_resource);
// Popup menu
{
const bool open_popup =
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
&& ImNodes::IsEditorHovered()
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (!ImGui::IsAnyItemHovered() && open_popup) {
ImGui::OpenPopup("add node");
}
if (ImGui::BeginPopup("add node")) {
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
std::string node_type_name = "";
if (ImGui::MenuItem("AnimSampler")) {
node_type_name = "AnimSampler";
}
if (ImGui::MenuItem("Blend2")) {
node_type_name = "Blend2";
}
if (ImGui::MenuItem("SpeedScale")) {
node_type_name = "SpeedScale";
}
if (node_type_name != "") {
AnimNodeResource node_resource =
AnimNodeResourceFactory(node_type_name);
size_t node_id = gGraphResource.m_nodes.size();
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
gGraphResource.m_nodes.push_back(node_resource);
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
}
for (size_t i = 0, n = gGraphResource.m_nodes.size(); i < n; i++) {
for (size_t i = 1, n = gGraphResource.m_nodes.size(); i < n; i++) {
AnimNodeResource& node_resource = gGraphResource.m_nodes[i];
ImNodes::BeginNode(i);
@ -60,7 +96,8 @@ void AnimGraphEditorUpdate() {
ImNodes::EndNodeTitleBar();
// Inputs
const std::vector<Socket>& node_inputs =node_resource.m_socket_accessor->m_inputs;
const std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
const Socket& socket = node_inputs[j];
ImNodes::BeginInputAttribute(GenerateInputAttributeId(i, j));
@ -69,7 +106,8 @@ void AnimGraphEditorUpdate() {
}
// Outputs
const std::vector<Socket>& node_outputs =node_resource.m_socket_accessor->m_outputs;
const std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
const Socket& socket = node_outputs[j];
ImNodes::BeginOutputAttribute(GenerateOutputAttributeId(i, j));
@ -87,8 +125,12 @@ void AnimGraphEditorUpdate() {
for (size_t i = 0, n = gGraphResource.m_connections.size(); i < n; i++) {
const AnimGraphConnection& connection = gGraphResource.m_connections[i];
int start_attr, end_attr;
start_attr = GenerateOutputAttributeId(connection.m_source_node_index, connection.m_source_socket_index);
end_attr = GenerateInputAttributeId(connection.m_target_node_index, connection.m_target_socket_index);
start_attr = GenerateOutputAttributeId(
connection.m_source_node_index,
connection.m_source_socket_index);
end_attr = GenerateInputAttributeId(
connection.m_target_node_index,
connection.m_target_socket_index);
ImNodes::Link(i, start_attr, end_attr);
}
@ -98,14 +140,18 @@ void AnimGraphEditorUpdate() {
if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
int node_start_id;
int node_start_output_index;
SplitOutputAttributeId(start_attr, &node_start_id, &node_start_output_index);
SplitOutputAttributeId(
start_attr,
&node_start_id,
&node_start_output_index);
int node_end_id;
int node_end_input_index;
SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);
std::cout << "Link created: " << node_start_id << ", " << node_start_output_index << " -> "
<< node_end_id << ", " << node_end_input_index << std::endl;
std::cout << "Link created: " << node_start_id << ", "
<< node_start_output_index << " -> " << node_end_id << ", "
<< node_end_input_index << std::endl;
AnimGraphConnection connection;
connection.m_source_node_index = node_start_id;

View File

@ -88,6 +88,9 @@ AnimGraphConnection sAnimGraphConnectionFromJson(const json& json_node) {
void AnimGraphResource::clear() {
m_name = "";
m_nodes.clear();
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
m_connections.clear();
}
@ -131,7 +134,8 @@ bool AnimGraphResource::loadFromFile (const char* filename) {
std::cerr << "Invalid json object. Expected type 'AnimGraphResource' but got '" << json_data["type"] << "'." << std::endl;
}
clear();
m_nodes.clear();
m_connections.clear();
m_name = json_data["name"];
for (size_t i = 0; i < json_data["nodes"].size(); i++) {

View File

@ -5,6 +5,7 @@
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
#include <cstring>
#include <iostream>
#include <map>
#include <string>
@ -54,22 +55,13 @@ struct AnimNodeResource;
struct Socket {
std::string m_name;
SocketType m_type;
union Value {
bool* flag;
AnimData* anim_data;
float* real;
Vec3* vec3;
Quat* quat;
std::string* str;
bool** flag_ptr;
AnimData** anim_data_ptr;
float** real_ptr;
float** vec3_ptr;
float** quat_ptr;
std::string** str_ptr;
SocketType m_type = SocketType::SocketTypeUndefined;
union SocketValue {
void* ptr;
void** ptr_ptr;
};
Value m_value;
SocketValue m_value = {nullptr};
size_t m_type_size = 0;
};
struct AnimNode;
@ -96,7 +88,7 @@ struct AnimNode {
};
struct NodeSocketAccessorBase {
NodeSocketAccessorBase(AnimNode* node) {}
NodeSocketAccessorBase() {}
virtual ~NodeSocketAccessorBase() {}
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
@ -111,16 +103,43 @@ struct NodeSocketAccessorBase {
return result;
}
const Socket* FindSocket(
const std::vector<Socket>& sockets,
const std::string& name) const {
const Socket* result = nullptr;
for (size_t i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
result = &sockets[i];
break;
}
}
return result;
}
SocketType GetSocketType(
const std::vector<Socket> sockets,
const std::vector<Socket>& sockets,
const std::string& name) {
Socket* socket = FindSocket(m_properties, name);
const Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
return SocketType::SocketTypeUndefined;
}
return socket->m_type;
}
size_t GetSocketIndex(
const std::vector<Socket>& sockets,
const std::string& name) const {
for (size_t i = 0, n = sockets.size(); i < n; i++) {
std::cout << i << ", " << sockets[i].m_name << std::endl;
if (sockets[i].m_name == name) {
return i;
}
}
return -1;
}
template <typename T>
bool RegisterSocket(
std::vector<Socket>& sockets,
@ -135,137 +154,41 @@ struct NodeSocketAccessorBase {
sockets.push_back(Socket());
socket = &sockets[sockets.size() - 1];
socket->m_name = name;
socket->m_type_size = sizeof(T);
if constexpr (std::is_same<T, float>::value) {
socket->m_value.real = value_ptr;
socket->m_type = SocketType::SocketTypeFloat;
} else if constexpr (std::is_same<T, bool>::value) {
socket->m_value.flag = value_ptr;
socket->m_type = SocketType::SocketTypeBool;
} else if constexpr (std::is_same<T, Vec3>::value) {
socket->m_value.vec3 = value_ptr;
socket->m_type = SocketType::SocketTypeVec3;
} else if constexpr (std::is_same<T, Quat>::value) {
socket->m_value.quat = value_ptr;
socket->m_type = SocketType::SocketTypeQuat;
} else if constexpr (std::is_same<T, AnimData>::value) {
socket->m_value.anim_data = value_ptr;
socket->m_type = SocketType::SocketTypeAnimation;
} else if constexpr (std::is_same<T, std::string>::value) {
socket->m_value.str = value_ptr;
socket->m_type = SocketType::SocketTypeString;
} else if constexpr (std::is_same<T, float*>::value) {
socket->m_value.real_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeFloat;
} else if constexpr (std::is_same<T, bool*>::value) {
socket->m_value.flag_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeBool;
} else if constexpr (std::is_same<T, Vec3*>::value) {
socket->m_value.vec3_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeVec3;
} else if constexpr (std::is_same<T, Quat*>::value) {
socket->m_value.quat_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeQuat;
} else if constexpr (std::is_same<T, AnimData*>::value) {
socket->m_value.anim_data_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeAnimation;
} else if constexpr (std::is_same<T, std::string*>::value) {
socket->m_value.str_ptr = value_ptr;
socket->m_type = SocketType::SocketTypeString;
} else {
std::cerr << "Cannot register socket, invalid type." << std::endl;
return false;
}
socket->m_value.ptr = value_ptr;
return true;
}
template <typename T>
bool SetSocketValue(
std::vector<Socket>& sockets,
const std::string& name,
T value) {
Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
return false;
}
if constexpr (std::is_same<T, float>::value) {
(*socket->m_value.real) = value;
} else if constexpr (std::is_same<T, bool>::value) {
(*socket->m_value.flag) = value;
} else if constexpr (std::is_same<T, Vec3>::value) {
(*socket->m_value.vec3) = value;
} else if constexpr (std::is_same<T, Quat>::value) {
(*socket->m_value.vec3) = value;
} else if constexpr (std::is_same<T, AnimData>::value) {
(*socket->m_value.vec3) = value;
} else if constexpr (std::is_same<T, std::string>::value) {
(*socket->m_value.str) = value;
} else {
std::cerr << "Invalid type!" << std::endl;
return false;
}
return true;
}
template <typename T>
bool SetSocketRef(
std::vector<Socket>& sockets,
const std::string& name,
T* value) {
Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
return false;
} else if constexpr (std::is_same<T, float>::value) {
(*socket->m_value.real_ptr) = value;
} else if constexpr (std::is_same<T, bool>::value) {
(*socket->m_value.flag_ptr) = value;
} else if constexpr (std::is_same<T, Vec3>::value) {
(*socket->m_value.vec3_ptr) = value;
} else if constexpr (std::is_same<T, Quat>::value) {
(*socket->m_value.quat_ptr) = value;
} else if constexpr (std::is_same<T, AnimData>::value) {
(*socket->m_value.anim_data_ptr) = value;
} else if constexpr (std::is_same<T, std::string>::value) {
(*socket->m_value.str_ptr) = value;
} else {
std::cerr << "Invalid type!" << std::endl;
return false;
}
return true;
}
template <typename T>
T* GetSocketValue(
std::vector<Socket>& sockets,
const std::string& name,
T* default_value) {
Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
return default_value;
}
if constexpr (std::is_same<T, float>::value) {
return socket->m_value.real;
} else if constexpr (std::is_same<T, Vec3>::value) {
return socket->m_value.vec3;
} else if constexpr (std::is_same<T, bool>::value) {
return socket->m_value.flag;
} else if constexpr (std::is_same<T, Quat>::value) {
return socket->m_value.vec3;
} else if constexpr (std::is_same<T, AnimData>::value) {
return socket->m_value.anim_data;
} else if constexpr (std::is_same<T, std::string>::value) {
return socket->m_value.str;
}
std::cerr << "Invalid type!" << std::endl;
return nullptr;
}
template <typename T>
bool RegisterProperty(const std::string& name, T* value) {
return RegisterSocket(m_properties, name, value);
@ -287,28 +210,32 @@ struct NodeSocketAccessorBase {
return RegisterSocket(m_inputs, name, value);
}
template <typename T>
bool SetInput(const std::string& name, T value) {
return SetSocketValue(m_inputs, name, value);
}
template <typename T>
T* GetInput(const std::string& name, T* value) {
return GetSocketValue(m_inputs, name, value);
}
Socket* FindInputSocket(const std::string& name) {
return FindSocket(m_inputs, name);
}
SocketType GetInputType(const std::string& name) {
return GetSocketType(m_inputs, name);
}
size_t GetInputIndex(const std::string& name) {
return GetSocketIndex(m_inputs, name);
}
template <typename T>
bool RegisterOutput(const std::string& name, T** value) {
return RegisterSocket(m_outputs, name, value);
}
template <typename T>
bool SetOutputRef(const std::string& name, T* value) {
return SetSocketRef(m_outputs, name, value);
}
SocketType GetOutputType(const std::string& name) {
return GetSocketType(m_outputs, name);
}
Socket* FindOutputSocket(const std::string& name) {
return FindSocket(m_outputs, name);
}
size_t GetOutputIndex(const std::string& name) {
return GetSocketIndex(m_outputs, name);
}
std::vector<Socket> m_properties;
std::vector<Socket> m_inputs;
@ -327,6 +254,13 @@ struct NodeRegistry {
AnimNode* node);
};
struct BlendTreeNode : public AnimNode {};
template <>
struct NodeSocketAccessor<BlendTreeNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) {}
};
struct Blend2Node : public AnimNode {
AnimData m_input0;
AnimData m_input1;
@ -337,7 +271,7 @@ struct Blend2Node : public AnimNode {
template <>
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
NodeSocketAccessor(AnimNode* node_) {
Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
RegisterInput("Input0", &node->m_input0);
RegisterInput("Input1", &node->m_input1);
@ -357,7 +291,7 @@ struct SpeedScaleNode : public AnimNode {
template <>
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
NodeSocketAccessor(AnimNode* node_) {
SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
RegisterInput("SpeedScale", &node->m_speed_scale);
RegisterInput("Input", &node->m_input);
@ -373,7 +307,7 @@ struct AnimSamplerNode : public AnimNode {
template <>
struct NodeSocketAccessor<AnimSamplerNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
NodeSocketAccessor(AnimNode* node_) {
AnimSamplerNode* node = dynamic_cast<AnimSamplerNode*>(node_);
RegisterOutput("Output", &node->m_output);
@ -392,21 +326,41 @@ struct AnimGraphResource {
std::string m_name;
std::vector<AnimNodeResource> m_nodes;
std::vector<AnimGraphConnection> m_connections;
std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs;
~AnimGraphResource() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
delete m_nodes[i].m_anim_node;
delete m_nodes[i].m_socket_accessor;
}
}
AnimGraphResource() { clear(); }
void clear();
bool saveToFile(const char* filename) const;
bool loadFromFile(const char* filename);
size_t addNode(AnimNodeResource node_resource) {
m_nodes.push_back(node_resource);
return m_nodes.size() - 1;
}
};
static inline AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
return new Blend2Node;
result = new Blend2Node;
} else if (name == "SpeedScale") {
return new SpeedScaleNode;
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
return new AnimSamplerNode;
result = new AnimSamplerNode;
} else if (name == "BlendTree") {
result = new BlendTreeNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
@ -422,6 +376,8 @@ static inline NodeSocketAccessorBase* AnimNodeAccessorFactory(
return new NodeSocketAccessor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return new NodeSocketAccessor<AnimSamplerNode>(node);
} else if (node_type_name == "BlendTree") {
return new NodeSocketAccessor<BlendTreeNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
@ -446,6 +402,9 @@ struct AnimGraph {
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<std::vector<AnimNode*> > m_node_inputs;
NodeSocketAccessorBase* m_socket_accessor;
char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr;
AnimNode* getAnimNode(const char* name) {
for (size_t i = 0; i < m_nodes.size(); i++) {
@ -470,6 +429,8 @@ struct AnimGraph {
static AnimGraph createFromResource(const AnimGraphResource& resource) {
AnimGraph result;
// create nodes
for (int i = 0; i < resource.m_nodes.size(); i++) {
const AnimNodeResource& node_resource = resource.m_nodes[i];
AnimNode* node = AnimNodeFactory(node_resource.m_type_name.c_str());
@ -479,6 +440,27 @@ struct AnimGraph {
result.m_node_inputs.push_back(std::vector<AnimNode*>());
}
// Prepare graph inputs
const AnimNodeResource& graph_node = resource.m_nodes[0];
result.m_socket_accessor = AnimNodeAccessorFactory("BlendTree", result.m_nodes[0]);
int input_block_size = 0;
const std::vector<Socket>& graph_inputs =
graph_node.m_socket_accessor->m_inputs;
for (int i = 0; i < graph_inputs.size(); i++) {
input_block_size += graph_inputs[i].m_type_size;
}
result.m_input_buffer = new char[input_block_size];
memset(result.m_input_buffer, 0, input_block_size);
int input_block_offset = 0;
for (int i = 0; i < graph_inputs.size(); i++) {
if (graph_inputs[i].m_type == SocketType::SocketTypeAnimation) {
result.m_socket_accessor->RegisterInput<AnimData>(graph_inputs[i].m_name, static_cast<AnimData*>( (void*) &result.m_input_buffer[input_block_offset]));
}
input_block_offset += graph_inputs[i].m_type_size;
}
// connect the nodes
for (int i = 0; i < resource.m_connections.size(); i++) {
const AnimGraphConnection& connection = resource.m_connections[i];
std::string source_node_type = "";
@ -504,8 +486,12 @@ struct AnimGraph {
target_node = result.m_nodes[connection.m_target_node_index];
target_node_name = target_node->m_name;
target_node_type = target_node->m_node_type_name;
target_node_accessor =
AnimNodeAccessorFactory(target_node_type, target_node);
if (connection.m_target_node_index == 0) {
target_node_accessor = result.m_socket_accessor;
} else {
target_node_accessor =
AnimNodeAccessorFactory(target_node_type, target_node);
}
}
assert(source_node != nullptr);
@ -529,23 +515,26 @@ struct AnimGraph {
continue;
}
if (source_node_accessor->m_outputs[connection.m_source_socket_index].m_type
!= target_node_accessor->m_inputs[connection.m_target_socket_index].m_type) {
if (source_node_accessor->m_outputs[connection.m_source_socket_index]
.m_type
!= target_node_accessor->m_inputs[connection.m_target_socket_index]
.m_type) {
std::cerr << "Invalid connection connecting nodes '" << source_node_name
<< "' and '" << target_node_name << "'." << std::endl;
return result;
}
// TODO! How to wire up the different types?
// std::string socket_name = connection.m_target_socket_name;
// AnimData* input_ref =
// target_node_accessor->GetInput<AnimData>(socket_name, nullptr);
// source_node_accessor->SetOutputRef(
// connection.m_source_socket_name,
// input_ref);
Socket* source_socket =
&source_node_accessor->m_outputs[connection.m_source_socket_index];
Socket* target_socket =
&target_node_accessor->m_inputs[connection.m_target_socket_index];
if (source_socket->m_type != target_socket->m_type) {
std::cerr << "Cannot connect sockets: invalid types!" << std::endl;
}
(*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr;
size_t target_node_index = result.getAnimNodeIndex(target_node);
result.m_node_inputs[target_node_index].push_back(source_node);
}

View File

@ -8,41 +8,56 @@
TEST_CASE("BasicGraph", "[AnimGraphResource]") {
AnimGraphResource graph_resource;
graph_resource.clear();
graph_resource.m_name = "WalkRunBlendGraph";
AnimNodeResource walk_node;
// Prepare graph inputs and outputs
size_t walk_node_index =
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
size_t run_node_index =
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
size_t blend_node_index =
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
walk_node.m_name = "WalkAnim";
walk_node.m_type_name = "AnimSampler";
graph_resource.m_nodes.push_back(walk_node);
AnimNodeResource& run_node = graph_resource.m_nodes[run_node_index];
walk_node.m_name = "RunAnim";
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
walk_node.m_name = "BlendWalkRun";
AnimNodeResource run_node;
run_node.m_name = "RunAnim";
run_node.m_type_name = "AnimSampler";
graph_resource.m_nodes.push_back(run_node);
AnimNodeResource& graph_node = graph_resource.m_nodes[0];
graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
AnimNodeResource blend_node;
blend_node.m_name = "BlendWalkRun";
blend_node.m_type_name = "Blend2";
graph_resource.m_nodes.push_back(blend_node);
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);
AnimGraphConnection walk_to_blend;
walk_to_blend.m_source_node = &walk_node;
walk_to_blend.m_source_socket_name = "Output";
walk_to_blend.m_target_node = &blend_node;
walk_to_blend.m_target_socket_name = "Input0";
walk_to_blend.m_source_node_index = walk_node_index;
walk_to_blend.m_source_socket_index =
walk_node.m_socket_accessor->GetOutputIndex("Output");
walk_to_blend.m_target_node_index = blend_node_index;
walk_to_blend.m_target_socket_index =
blend_node.m_socket_accessor->GetInputIndex("Input0");
graph_resource.m_connections.push_back(walk_to_blend);
AnimGraphConnection run_to_blend;
run_to_blend.m_source_node = &run_node;
run_to_blend.m_source_socket_name = "Output";
run_to_blend.m_target_node = &blend_node;
run_to_blend.m_target_socket_name = "Input1";
run_to_blend.m_source_node_index = run_node_index;
run_to_blend.m_source_socket_index =
run_node.m_socket_accessor->GetOutputIndex("Output");
run_to_blend.m_target_node_index = blend_node_index;
run_to_blend.m_target_socket_index =
blend_node.m_socket_accessor->GetInputIndex("Input1");
graph_resource.m_connections.push_back(run_to_blend);
AnimGraphConnection blend_to_output;
blend_to_output.m_source_node = &blend_node;
blend_to_output.m_source_socket_name = "Output";
blend_to_output.m_target_socket_name = "GraphOutput";
blend_to_output.m_source_node_index = blend_node_index;
blend_to_output.m_source_socket_index =
blend_node.m_socket_accessor->GetOutputIndex("Output");
blend_to_output.m_target_node_index = 0;
blend_to_output.m_target_socket_index =
graph_node.m_socket_accessor->GetInputIndex("GraphOutput");
graph_resource.m_connections.push_back(blend_to_output);
graph_resource.saveToFile("WalkGraph.animgraph.json");
@ -55,19 +70,27 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
<< std::endl;
}
CHECK(graph.m_nodes.size() == 3);
CHECK(graph.m_nodes[0]->m_node_type_name == "AnimSampler");
CHECK(graph.m_nodes[1]->m_node_type_name == "AnimSampler");
CHECK(graph.m_nodes[2]->m_node_type_name == "Blend2");
REQUIRE(graph.m_nodes.size() == 4);
REQUIRE(graph.m_nodes[0]->m_node_type_name == "BlendTree");
REQUIRE(graph.m_nodes[1]->m_node_type_name == "AnimSampler");
REQUIRE(graph.m_nodes[2]->m_node_type_name == "AnimSampler");
REQUIRE(graph.m_nodes[3]->m_node_type_name == "Blend2");
// connections within the graph
AnimSamplerNode* anim_sampler_instance0 =
dynamic_cast<AnimSamplerNode*>(graph.m_nodes[0]);
AnimSamplerNode* anim_sampler_instance1 =
dynamic_cast<AnimSamplerNode*>(graph.m_nodes[1]);
Blend2Node* blend2_instance = dynamic_cast<Blend2Node*>(graph.m_nodes[2]);
AnimSamplerNode* anim_sampler_instance1 =
dynamic_cast<AnimSamplerNode*>(graph.m_nodes[2]);
Blend2Node* blend2_instance = dynamic_cast<Blend2Node*>(graph.m_nodes[3]);
CHECK(anim_sampler_instance0->m_output == &blend2_instance->m_input0);
CHECK(anim_sampler_instance1->m_output == &blend2_instance->m_input1);
// connections from graph to the graph node
CHECK(graph.m_socket_accessor->m_inputs.size() == 1);
CHECK(graph.m_socket_accessor->FindInputSocket("GraphOutput"));
CHECK(reinterpret_cast<char*>(blend2_instance->m_output) == graph.m_input_buffer);
// check node input dependencies
size_t anim_sampler_index0 = graph.getAnimNodeIndex(anim_sampler_instance0);
size_t anim_sampler_index1 = graph.getAnimNodeIndex(anim_sampler_instance1);
size_t blend_index = graph.getAnimNodeIndex(blend2_instance);