Compare commits
3 Commits
c5b0d1f976
...
bbab4d8608
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bbab4d8608 | ||
![]() |
2b7cbe9d4c | ||
![]() |
c2ae0a11d2 |
@ -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,9 +20,11 @@ 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")) {
|
||||
@ -32,7 +32,10 @@ void AnimGraphEditorUpdate() {
|
||||
}
|
||||
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));
|
||||
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");
|
||||
// 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);
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = gGraphResource.m_nodes.size(); i < n; i++) {
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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++) {
|
||||
|
@ -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,9 +486,13 @@ 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;
|
||||
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);
|
||||
assert(target_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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user