Compare commits

...

3 Commits

Author SHA1 Message Date
Martin Felis
bd304bde4e Give access to node inputs in runtime graph, initial works for marking of active nodes. 2022-02-19 00:25:51 +01:00
Martin Felis
9069a8192b Added serialization and deserialization of properties. 2022-02-18 23:33:30 +01:00
Martin Felis
ee6a8eb29a Basic node properties editor. 2022-02-18 22:24:19 +01:00
4 changed files with 326 additions and 39 deletions

View File

@ -7,6 +7,8 @@
#include "AnimGraphResource.h" #include "AnimGraphResource.h"
#include "imnodes.h" #include "imnodes.h"
using namespace AnimGraphCode;
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
switch (socket_type) { switch (socket_type) {
case SocketType::SocketTypeAnimation: case SocketType::SocketTypeAnimation:
@ -26,6 +28,52 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
return ImNodesPinShape_Quad; return ImNodesPinShape_Quad;
} }
void AnimGraphEditorRenderSidebar(AnimNodeResource& node_resource) {
ImGui::Text("[%s]", node_resource.m_type_name.c_str());
char node_name_buffer[256];
memset(node_name_buffer, 0, sizeof(node_name_buffer));
strncpy(
node_name_buffer,
node_resource.m_name.c_str(),
std::min(node_resource.m_name.size(), sizeof(node_name_buffer)));
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
node_resource.m_name = node_name_buffer;
}
int num_properties = node_resource.m_socket_accessor->m_properties.size();
for (int i = 0; i < num_properties; i++) {
Socket& property = node_resource.m_socket_accessor->m_properties[i];
if (property.m_type == SocketType::SocketTypeFloat) {
ImGui::SliderFloat(
property.m_name.c_str(),
reinterpret_cast<float*>(property.m_value.ptr),
-100.f,
100.f);
} else if (property.m_type == SocketType::SocketTypeBool) {
ImGui::Checkbox(
property.m_name.c_str(),
reinterpret_cast<bool*>(property.m_value.ptr));
} else if (property.m_type == SocketType::SocketTypeString) {
std::string* property_string =
reinterpret_cast<std::string*>(property.m_value.ptr);
char string_buf[256];
memset(string_buf, 0, sizeof(string_buf));
strncpy(
string_buf,
property_string->c_str(),
std::min(property_string->size(), sizeof(string_buf)));
if (ImGui::InputText(
property.m_name.c_str(),
string_buf,
sizeof(string_buf))) {
(*property_string) = string_buf;
}
}
}
}
void AnimGraphEditorUpdate() { void AnimGraphEditorUpdate() {
static AnimGraphResource graph_resource = AnimGraphResource(); static AnimGraphResource graph_resource = AnimGraphResource();
@ -58,6 +106,11 @@ void AnimGraphEditorUpdate() {
ImGui::EndMenuBar(); ImGui::EndMenuBar();
ImGui::Columns(2);
//
// Node editor canvas
//
ImNodes::BeginNodeEditor(); ImNodes::BeginNodeEditor();
// Popup menu // Popup menu
@ -122,13 +175,20 @@ void AnimGraphEditorUpdate() {
node_resource.m_socket_accessor->m_inputs; node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) { for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
const Socket& socket = node_inputs[j]; const Socket& socket = node_inputs[j];
ImColor socket_color = ImColor(255, 255, 255, 255);
if (socket.m_flags & SocketFlagAffectsTime) {
socket_color = ImColor(255, 128, 128, 255);
}
ImNodes::BeginInputAttribute( ImNodes::BeginInputAttribute(
GenerateInputAttributeId(i, j), GenerateInputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type), sGetSocketShapeFromSocketType(socket.m_type),
ImColor(255, 255, 255, 255)); socket_color);
ImGui::Text(socket.m_name.c_str()); ImGui::Text(socket.m_name.c_str());
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick); ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute(); ImNodes::EndInputAttribute();
} }
@ -142,19 +202,21 @@ void AnimGraphEditorUpdate() {
sGetSocketShapeFromSocketType(socket.m_type), sGetSocketShapeFromSocketType(socket.m_type),
ImColor(255, 255, 255, 255)); ImColor(255, 255, 255, 255));
ImGui::Text(socket.m_name.c_str()); ImGui::Text(socket.m_name.c_str());
ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick); ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute(); ImNodes::EndInputAttribute();
} }
// Graph output node // Graph output node
if (i == 0) { if (i == 0) {
if (ImGui::Button("+Output")) { if (ImGui::Button("+Output")) {
AnimNodeResource& graph_output_node = graph_resource.getGraphOutputNode(); AnimNodeResource& graph_output_node =
graph_resource.getGraphOutputNode();
static float bla = 0.f; static float bla = 0.f;
std::string socket_name = "Output"; std::string socket_name = "Output";
socket_name += socket_name += std::to_string(
std::to_string(graph_output_node.m_socket_accessor->m_inputs.size()); graph_output_node.m_socket_accessor->m_inputs.size());
graph_output_node.m_socket_accessor->RegisterInput<float>( graph_output_node.m_socket_accessor->RegisterInput<float>(
socket_name, socket_name,
nullptr); nullptr);
@ -165,8 +227,8 @@ void AnimGraphEditorUpdate() {
static float bla = 0.f; static float bla = 0.f;
std::string socket_name = "Input"; std::string socket_name = "Input";
socket_name += socket_name += std::to_string(
std::to_string(graph_input_node.m_socket_accessor->m_outputs.size()); graph_input_node.m_socket_accessor->m_outputs.size());
graph_input_node.m_socket_accessor->RegisterOutput<float>( graph_input_node.m_socket_accessor->RegisterOutput<float>(
socket_name, socket_name,
nullptr); nullptr);
@ -178,6 +240,9 @@ void AnimGraphEditorUpdate() {
node_resource.m_position[0] = node_pos[0]; node_resource.m_position[0] = node_pos[0];
node_resource.m_position[1] = node_pos[1]; node_resource.m_position[1] = node_pos[1];
ImNodes::EndNode(); ImNodes::EndNode();
// Ensure flags such as SocketFlagAffectsTime are properly set.
node_resource.m_socket_accessor->UpdateFlags();
} }
for (size_t i = 0, n = graph_resource.m_connections.size(); i < n; i++) { for (size_t i = 0, n = graph_resource.m_connections.size(); i < n; i++) {
@ -220,6 +285,25 @@ void AnimGraphEditorUpdate() {
// Handle link detachements. // Handle link detachements.
int link_id = 0; int link_id = 0;
if (ImNodes::IsLinkDestroyed(&link_id)) { if (ImNodes::IsLinkDestroyed(&link_id)) {
graph_resource.m_connections.erase(graph_resource.m_connections.begin() + link_id); graph_resource.m_connections.erase(
graph_resource.m_connections.begin() + link_id);
}
int selected_nodes[ImNodes::NumSelectedNodes()];
ImNodes::GetSelectedNodes(selected_nodes);
//
// Sidebar
//
ImGui::NextColumn();
if (ImNodes::NumSelectedNodes() == 1) {
if (selected_nodes[0] < graph_resource.m_nodes.size()) {
AnimNodeResource& selected_node =
graph_resource.m_nodes[selected_nodes[0]];
AnimGraphEditorRenderSidebar(selected_node);
} }
} }
ImGui::Columns(1);
}

View File

@ -8,6 +8,8 @@
#include "3rdparty/json/json.hpp" #include "3rdparty/json/json.hpp"
namespace AnimGraphCode {
using json = nlohmann::json; using json = nlohmann::json;
// //
@ -45,6 +47,31 @@ json sSocketToJson(const Socket& socket) {
json result; json result;
result["name"] = socket.m_name; result["name"] = socket.m_name;
result["type"] = sSocketTypeToStr(socket.m_type); result["type"] = sSocketTypeToStr(socket.m_type);
if (socket.m_value.ptr != nullptr) {
if (socket.m_type == SocketType::SocketTypeBool) {
result["value"] = *reinterpret_cast<bool*>(socket.m_value.ptr);
} else if (socket.m_type == SocketType::SocketTypeAnimation) {
} else if (socket.m_type == SocketType::SocketTypeFloat) {
result["value"] = *reinterpret_cast<float*>(socket.m_value.ptr);
} else if (socket.m_type == SocketType::SocketTypeVec3) {
Vec3& vec3 = *reinterpret_cast<Vec3*>(socket.m_value.ptr);
result["value"][0] = vec3[0];
result["value"][1] = vec3[1];
result["value"][2] = vec3[2];
} else if (socket.m_type == SocketType::SocketTypeQuat) {
Quat& quat = *reinterpret_cast<Quat*>(socket.m_value.ptr);
result["value"][0] = quat[0];
result["value"][1] = quat[1];
result["value"][2] = quat[2];
result["value"][3] = quat[3];
} else if (socket.m_type == SocketType::SocketTypeString) {
result["value"] = *reinterpret_cast<std::string*>(socket.m_value.ptr);
} else {
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
<< "'." << std::endl;
}
}
return result; return result;
} }
@ -95,6 +122,12 @@ json sAnimGraphNodeToJson(const AnimNodeResource& node) {
result["position"][j] = node.m_position[j]; result["position"][j] = node.m_position[j];
} }
for (size_t j = 0, n = node.m_socket_accessor->m_properties.size(); j < n;
j++) {
Socket& property = node.m_socket_accessor->m_properties[j];
result["properties"][property.m_name] = sSocketToJson(property);
}
return result; return result;
} }
@ -110,6 +143,49 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
result.m_socket_accessor = result.m_socket_accessor =
AnimNodeAccessorFactory(result.m_type_name, result.m_anim_node); AnimNodeAccessorFactory(result.m_type_name, result.m_anim_node);
for (size_t j = 0, n = result.m_socket_accessor->m_properties.size(); j < n;
j++) {
Socket& property = result.m_socket_accessor->m_properties[j];
json json_property = json_node["properties"][property.m_name];
if (sSocketTypeToStr(property.m_type) == json_property["type"]) {
if (property.m_type == SocketType::SocketTypeBool) {
result.m_socket_accessor->SetProperty<bool>(
property.m_name,
json_property["value"]);
} else if (property.m_type == SocketType::SocketTypeAnimation) {
} else if (property.m_type == SocketType::SocketTypeFloat) {
result.m_socket_accessor->SetProperty<float>(
property.m_name,
json_property["value"]);
} else if (property.m_type == SocketType::SocketTypeVec3) {
Vec3* property_vec3 = reinterpret_cast<Vec3*>(property.m_value.ptr);
(*property_vec3)[0] = json_property["value"][0];
(*property_vec3)[1] = json_property["value"][1];
(*property_vec3)[2] = json_property["value"][2];
} else if (property.m_type == SocketType::SocketTypeQuat) {
Quat* property_quat = reinterpret_cast<Quat*>(property.m_value.ptr);
(*property_quat)[0] = json_property["value"][0];
(*property_quat)[1] = json_property["value"][1];
(*property_quat)[2] = json_property["value"][2];
(*property_quat)[3] = json_property["value"][3];
} else if (property.m_type == SocketType::SocketTypeString) {
result.m_socket_accessor->SetProperty<std::string>(
property.m_name,
json_property["value"]);
} else {
std::cerr << "Invalid type for property '" << property.m_name
<< "'. Cannot parse json to type '"
<< static_cast<int>(property.m_type) << std::endl;
break;
}
} else {
std::cerr << "Invalid type for property '" << property.m_name
<< "': expected " << sSocketTypeToStr(property.m_type)
<< " but got " << json_property["type"] << std::endl;
}
}
return result; return result;
} }
@ -144,6 +220,14 @@ AnimGraphConnection sAnimGraphConnectionFromJson(const json& json_node) {
void AnimGraphResource::clear() { void AnimGraphResource::clear() {
m_name = ""; m_name = "";
clearNodes();
m_connections.clear();
initGraphConnectors();
}
void AnimGraphResource::clearNodes() {
for (size_t i = 0; i < m_nodes.size(); i++) { for (size_t i = 0; i < m_nodes.size(); i++) {
delete m_nodes[i].m_socket_accessor; delete m_nodes[i].m_socket_accessor;
m_nodes[i].m_socket_accessor = nullptr; m_nodes[i].m_socket_accessor = nullptr;
@ -151,13 +235,13 @@ void AnimGraphResource::clear() {
m_nodes[i].m_anim_node = nullptr; m_nodes[i].m_anim_node = nullptr;
} }
m_nodes.clear(); m_nodes.clear();
}
void AnimGraphResource::initGraphConnectors() {
m_nodes.push_back(AnimNodeResourceFactory("BlendTree")); m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
m_nodes[0].m_name = "Outputs"; m_nodes[0].m_name = "Outputs";
m_nodes.push_back(AnimNodeResourceFactory("BlendTree")); m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
m_nodes[1].m_name = "Inputs"; m_nodes[1].m_name = "Inputs";
m_connections.clear();
} }
bool AnimGraphResource::saveToFile(const char* filename) const { bool AnimGraphResource::saveToFile(const char* filename) const {
@ -220,6 +304,7 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
} }
clear(); clear();
clearNodes();
m_name = json_data["name"]; m_name = json_data["name"];
for (size_t i = 0; i < json_data["nodes"].size(); i++) { for (size_t i = 0; i < json_data["nodes"].size(); i++) {
@ -263,10 +348,16 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
sJsonToSocket(graph_inputs[i])); sJsonToSocket(graph_inputs[i]));
} }
return false; return true;
} }
void* AnimGraph::GetOutput(const std::string& name) { void AnimGraph::MarkActiveNodes() {
m_frame_counter++;
// TODO: start from output and trigger updating of the frame counter.
}
void* AnimGraph::GetOutput(const std::string& name) const {
Socket* socket = m_socket_accessor->FindInputSocket(name); Socket* socket = m_socket_accessor->FindInputSocket(name);
if (socket == nullptr) { if (socket == nullptr) {
return nullptr; return nullptr;
@ -275,7 +366,7 @@ void* AnimGraph::GetOutput(const std::string& name) {
return socket->m_value.ptr; return socket->m_value.ptr;
} }
void* AnimGraph::GetInput(const std::string& name) { void* AnimGraph::GetInput(const std::string& name) const {
Socket* socket = m_socket_accessor->FindOutputSocket(name); Socket* socket = m_socket_accessor->FindOutputSocket(name);
if (socket == nullptr) { if (socket == nullptr) {
return nullptr; return nullptr;
@ -283,3 +374,5 @@ void* AnimGraph::GetInput(const std::string& name) {
return *(socket->m_value.ptr_ptr); return *(socket->m_value.ptr_ptr);
} }
}

View File

@ -14,6 +14,8 @@
#include "SyncTrack.h" #include "SyncTrack.h"
namespace AnimGraphCode {
enum class SocketType { enum class SocketType {
SocketTypeUndefined, SocketTypeUndefined,
SocketTypeBool, SocketTypeBool,
@ -24,6 +26,8 @@ enum class SocketType {
SocketTypeString SocketTypeString
}; };
enum SocketFlags { SocketFlagAffectsTime = 1 };
struct AnimData { struct AnimData {
float m_bone_transforms[16]; float m_bone_transforms[16];
}; };
@ -61,6 +65,7 @@ struct Socket {
void** ptr_ptr; void** ptr_ptr;
}; };
SocketValue m_value = {nullptr}; SocketValue m_value = {nullptr};
int m_flags = 0;
size_t m_type_size = 0; size_t m_type_size = 0;
}; };
@ -84,13 +89,21 @@ struct AnimNode {
bool m_is_time_synced; bool m_is_time_synced;
float m_time_now; float m_time_now;
float m_time_last; float m_time_last;
int m_frame_counter = 0;
SyncTrack m_sync_track; SyncTrack m_sync_track;
}; };
struct AnimNodeInput {
AnimNode* m_node;
std::string m_input_name;
};
struct NodeSocketAccessorBase { struct NodeSocketAccessorBase {
NodeSocketAccessorBase() {} NodeSocketAccessorBase() {}
virtual ~NodeSocketAccessorBase() {} virtual ~NodeSocketAccessorBase() {}
virtual void UpdateFlags(){};
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) { Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
Socket* result = nullptr; Socket* result = nullptr;
for (size_t i = 0, n = sockets.size(); i < n; i++) { for (size_t i = 0, n = sockets.size(); i < n; i++) {
@ -140,11 +153,40 @@ struct NodeSocketAccessorBase {
return -1; return -1;
} }
template <typename T>
T GetSocketValue(
const std::vector<Socket>& sockets,
const std::string& name,
T default_value) {
const Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
return default_value;
}
return *static_cast<T*>(socket->m_value.ptr);
}
template <typename T>
void SetSocketValue(
const std::vector<Socket>& sockets,
const std::string& name,
const T& value) {
const Socket* socket = FindSocket(sockets, name);
if (socket == nullptr) {
std::cerr << "Error: could not set value of socket with name " << name
<< ": no socket found." << std::endl;
return;
}
*static_cast<T*>(socket->m_value.ptr) = value;
}
template <typename T> template <typename T>
bool RegisterSocket( bool RegisterSocket(
std::vector<Socket>& sockets, std::vector<Socket>& sockets,
const std::string& name, const std::string& name,
T* value_ptr) { T* value_ptr,
int flags = 0) {
Socket* socket = FindSocket(sockets, name); Socket* socket = FindSocket(sockets, name);
if (socket != nullptr) { if (socket != nullptr) {
std::cerr << "Socket " << name << " already registered." << std::endl; std::cerr << "Socket " << name << " already registered." << std::endl;
@ -155,6 +197,7 @@ struct NodeSocketAccessorBase {
socket = &sockets[sockets.size() - 1]; socket = &sockets[sockets.size() - 1];
socket->m_name = name; socket->m_name = name;
socket->m_type_size = sizeof(T); socket->m_type_size = sizeof(T);
socket->m_flags = flags;
if constexpr (std::is_same<T, float>::value) { if constexpr (std::is_same<T, float>::value) {
socket->m_type = SocketType::SocketTypeFloat; socket->m_type = SocketType::SocketTypeFloat;
@ -194,20 +237,20 @@ struct NodeSocketAccessorBase {
return RegisterSocket(m_properties, name, value); return RegisterSocket(m_properties, name, value);
} }
template <typename T> template <typename T>
bool SetProperty(const std::string& name, T value) { void SetProperty(const std::string& name, const T& value) {
return SetSocketValue(m_properties, name, value); SetSocketValue(m_properties, name, value);
} }
template <typename T> template <typename T>
T GetProperty(const std::string& name, T value) { T GetProperty(const std::string& name, T default_value) {
return GetSocketValue(m_properties, name, value); return GetSocketValue(m_properties, name, default_value);
} }
SocketType GetPropertyType(const std::string& name) { SocketType GetPropertyType(const std::string& name) {
return GetSocketType(m_properties, name); return GetSocketType(m_properties, name);
} }
template <typename T> template <typename T>
bool RegisterInput(const std::string& name, T* value) { bool RegisterInput(const std::string& name, T* value, int flags = 0) {
return RegisterSocket(m_inputs, name, value); return RegisterSocket(m_inputs, name, value, flags);
} }
template <typename T> template <typename T>
T* GetInput(const std::string& name, T* value) { T* GetInput(const std::string& name, T* value) {
@ -224,8 +267,8 @@ struct NodeSocketAccessorBase {
} }
template <typename T> template <typename T>
bool RegisterOutput(const std::string& name, T** value) { bool RegisterOutput(const std::string& name, T** value, int flags = 0) {
return RegisterSocket(m_outputs, name, value); return RegisterSocket(m_outputs, name, value, flags);
} }
SocketType GetOutputType(const std::string& name) { SocketType GetOutputType(const std::string& name) {
return GetSocketType(m_outputs, name); return GetSocketType(m_outputs, name);
@ -275,12 +318,26 @@ struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
Blend2Node* node = dynamic_cast<Blend2Node*>(node_); Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
RegisterInput("Input0", &node->m_input0); RegisterInput("Input0", &node->m_input0);
RegisterInput("Input1", &node->m_input1); RegisterInput("Input1", &node->m_input1);
RegisterInput("Weight", &node->m_blend_weight); RegisterInput(
"Weight",
&node->m_blend_weight,
SocketFlags::SocketFlagAffectsTime);
RegisterOutput("Output", &node->m_output); RegisterOutput("Output", &node->m_output);
RegisterProperty("Sync", &node->m_sync_blend); RegisterProperty("Sync", &node->m_sync_blend);
} }
virtual void UpdateFlags() override {
Socket* weight_input_socket = FindSocket(m_inputs, "Weight");
assert(weight_input_socket != nullptr);
if (GetProperty<bool>("Sync", false) == true) {
weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
} else {
weight_input_socket->m_flags = 0;
}
}
}; };
struct SpeedScaleNode : public AnimNode { struct SpeedScaleNode : public AnimNode {
@ -293,7 +350,10 @@ template <>
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase { struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) { NodeSocketAccessor(AnimNode* node_) {
SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_); SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
RegisterInput("SpeedScale", &node->m_speed_scale); RegisterInput(
"SpeedScale",
&node->m_speed_scale,
SocketFlags::SocketFlagAffectsTime);
RegisterInput("Input", &node->m_input); RegisterInput("Input", &node->m_input);
RegisterOutput("Output", &node->m_output); RegisterOutput("Output", &node->m_output);
@ -337,16 +397,14 @@ struct AnimGraphResource {
AnimGraphResource() { clear(); } AnimGraphResource() { clear(); }
void clear(); void clear();
void clearNodes();
void initGraphConnectors();
bool saveToFile(const char* filename) const; bool saveToFile(const char* filename) const;
bool loadFromFile(const char* filename); bool loadFromFile(const char* filename);
AnimNodeResource& getGraphOutputNode() { AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
return m_nodes[0];
}
AnimNodeResource& getGraphInputNode() { AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }
return m_nodes[1];
}
size_t addNode(AnimNodeResource node_resource) { size_t addNode(AnimNodeResource node_resource) {
m_nodes.push_back(node_resource); m_nodes.push_back(node_resource);
@ -463,12 +521,19 @@ struct AnimGraph {
delete m_socket_accessor; delete m_socket_accessor;
} }
void MarkActiveNodes();
bool CheckNodeActive(int node_index) {
assert(node_index < m_nodes.size());
return m_nodes[node_index]->m_frame_counter == m_frame_counter;
}
void UpdateTime(float dt); void UpdateTime(float dt);
void Evaluate(); void Evaluate();
int m_frame_counter;
AnimData m_local_transforms; AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes; std::vector<AnimNode*> m_nodes;
std::vector<std::vector<AnimNode*> > m_node_inputs; std::vector<std::vector<AnimNodeInput> > m_node_inputs;
NodeSocketAccessorBase* m_socket_accessor; NodeSocketAccessorBase* m_socket_accessor;
char* m_input_buffer = nullptr; char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr; char* m_output_buffer = nullptr;
@ -476,8 +541,24 @@ struct AnimGraph {
std::vector<Socket>& GetGraphOutputs() { return m_socket_accessor->m_inputs; } std::vector<Socket>& GetGraphOutputs() { return m_socket_accessor->m_inputs; }
std::vector<Socket>& GetGraphInputs() { return m_socket_accessor->m_outputs; } std::vector<Socket>& GetGraphInputs() { return m_socket_accessor->m_outputs; }
void* GetOutput(const std::string& name); void* GetOutput(const std::string& name) const;
void* GetInput(const std::string& name); void* GetInput(const std::string& name) const;
AnimNode* getAnimNodeForInput(
size_t node_index,
const std::string& input_name) {
assert(node_index < m_nodes.size());
assert(node_index < m_node_inputs.size());
std::vector<AnimNodeInput>& node_inputs = m_node_inputs[node_index];
for (size_t i = 0, n = node_inputs.size(); i < n; i++) {
if (node_inputs[i].m_input_name == input_name) {
return node_inputs[i].m_node;
}
}
return nullptr;
}
AnimNode* getAnimNode(const char* name) { AnimNode* getAnimNode(const char* name) {
for (size_t i = 0; i < m_nodes.size(); i++) { for (size_t i = 0; i < m_nodes.size(); i++) {
@ -510,7 +591,21 @@ struct AnimGraph {
node->m_name = node_resource.m_name; node->m_name = node_resource.m_name;
node->m_node_type_name = node_resource.m_type_name; node->m_node_type_name = node_resource.m_type_name;
result.m_nodes.push_back(node); result.m_nodes.push_back(node);
result.m_node_inputs.push_back(std::vector<AnimNode*>());
assert(node_resource.m_socket_accessor != nullptr);
result.m_node_inputs.push_back(std::vector<AnimNodeInput>());
std::vector<AnimNodeInput>& node_inputs = result.m_node_inputs.back();
for (int j = 0, n = node_resource.m_socket_accessor->m_inputs.size();
j < n;
j++) {
const Socket& input_socket =
node_resource.m_socket_accessor->m_inputs[j];
AnimNodeInput input;
input.m_node = nullptr;
input.m_input_name = input_socket.m_name;
node_inputs.push_back(input);
}
} }
// Prepare graph inputs // Prepare graph inputs
@ -636,7 +731,14 @@ struct AnimGraph {
(*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr; (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr;
size_t target_node_index = result.getAnimNodeIndex(target_node); size_t target_node_index = result.getAnimNodeIndex(target_node);
result.m_node_inputs[target_node_index].push_back(source_node);
std::vector<AnimNodeInput>& node_inputs =
result.m_node_inputs[target_node_index];
for (int j = 0, n = node_inputs.size(); j < n; j++) {
if (node_inputs[j].m_input_name == target_socket->m_name) {
node_inputs[j].m_node = source_node;
}
}
if (target_node_accessor != result.m_socket_accessor) { if (target_node_accessor != result.m_socket_accessor) {
delete target_node_accessor; delete target_node_accessor;
@ -651,4 +753,6 @@ struct AnimGraph {
} }
}; };
} // namespace AnimGraphCode
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H #endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H

View File

@ -5,6 +5,8 @@
#include "AnimGraphResource.h" #include "AnimGraphResource.h"
#include "catch.hpp" #include "catch.hpp"
using namespace AnimGraphCode;
TEST_CASE("BasicGraph", "[AnimGraphResource]") { TEST_CASE("BasicGraph", "[AnimGraphResource]") {
AnimGraphResource graph_resource; AnimGraphResource graph_resource;
@ -100,9 +102,10 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
CHECK(graph.m_node_inputs[anim_sampler_index0].size() == 0); CHECK(graph.m_node_inputs[anim_sampler_index0].size() == 0);
CHECK(graph.m_node_inputs[anim_sampler_index1].size() == 0); CHECK(graph.m_node_inputs[anim_sampler_index1].size() == 0);
CHECK(graph.m_node_inputs[blend_index].size() == 2); CHECK(graph.m_node_inputs[blend_index].size() == 3);
CHECK(graph.m_node_inputs[blend_index][0] == anim_sampler_instance0); CHECK(graph.m_node_inputs[blend_index][0].m_node == anim_sampler_instance0);
CHECK(graph.m_node_inputs[blend_index][1] == anim_sampler_instance1); CHECK(graph.m_node_inputs[blend_index][1].m_node == anim_sampler_instance1);
CHECK(graph.m_node_inputs[blend_index][2].m_node == nullptr);
} }
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
@ -324,12 +327,15 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
AnimData* graph_input0 = (AnimData*) anim_graph.GetInput("GraphAnimInput0"); AnimData* graph_input0 = (AnimData*) anim_graph.GetInput("GraphAnimInput0");
REQUIRE(graph_input0 == &blend2_node->m_input0); REQUIRE(graph_input0 == &blend2_node->m_input0);
REQUIRE(anim_graph.m_nodes[1] == anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
AnimData* graph_input1 = (AnimData*) anim_graph.GetInput("GraphAnimInput1"); AnimData* graph_input1 = (AnimData*) anim_graph.GetInput("GraphAnimInput1");
REQUIRE(graph_input1 == &blend2_node->m_input1); REQUIRE(graph_input1 == &blend2_node->m_input1);
REQUIRE(anim_graph.m_nodes[1] == anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
AnimData* graph_output = (AnimData*) anim_graph.GetOutput("GraphAnimOutput"); AnimData* graph_output = (AnimData*) anim_graph.GetOutput("GraphAnimOutput");
REQUIRE(graph_output == blend2_node->m_output); REQUIRE(graph_output == blend2_node->m_output);
REQUIRE(anim_graph.m_nodes[blend2_node_index] == anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
} }
} }
} }