From 9069a8192bb43461043276d8ceee995bae055912 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Fri, 18 Feb 2022 23:33:30 +0100 Subject: [PATCH] Added serialization and deserialization of properties. --- src/AnimGraphEditor.cc | 13 +++++- src/AnimGraphResource.cc | 84 +++++++++++++++++++++++++++++++++++-- src/AnimGraphResource.h | 90 +++++++++++++++++++++++++++++++--------- 3 files changed, 164 insertions(+), 23 deletions(-) diff --git a/src/AnimGraphEditor.cc b/src/AnimGraphEditor.cc index bef8d62..1a8bcc9 100644 --- a/src/AnimGraphEditor.cc +++ b/src/AnimGraphEditor.cc @@ -7,6 +7,8 @@ #include "AnimGraphResource.h" #include "imnodes.h" +using namespace AnimGraphCode; + ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { switch (socket_type) { case SocketType::SocketTypeAnimation: @@ -173,10 +175,16 @@ void AnimGraphEditorUpdate() { 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]; + + ImColor socket_color = ImColor(255, 255, 255, 255); + if (socket.m_flags & SocketFlagAffectsTime) { + socket_color = ImColor(255, 128, 128, 255); + } + ImNodes::BeginInputAttribute( GenerateInputAttributeId(i, j), sGetSocketShapeFromSocketType(socket.m_type), - ImColor(255, 255, 255, 255)); + socket_color); ImGui::Text(socket.m_name.c_str()); ImNodes::PushAttributeFlag( @@ -232,6 +240,9 @@ void AnimGraphEditorUpdate() { node_resource.m_position[0] = node_pos[0]; node_resource.m_position[1] = node_pos[1]; 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++) { diff --git a/src/AnimGraphResource.cc b/src/AnimGraphResource.cc index 3eb407e..177a957 100644 --- a/src/AnimGraphResource.cc +++ b/src/AnimGraphResource.cc @@ -8,6 +8,8 @@ #include "3rdparty/json/json.hpp" +namespace AnimGraphCode { + using json = nlohmann::json; // @@ -45,6 +47,31 @@ json sSocketToJson(const Socket& socket) { json result; result["name"] = socket.m_name; result["type"] = sSocketTypeToStr(socket.m_type); + + if (socket.m_value.ptr != nullptr) { + if (socket.m_type == SocketType::SocketTypeBool) { + result["value"] = *reinterpret_cast(socket.m_value.ptr); + } else if (socket.m_type == SocketType::SocketTypeAnimation) { + } else if (socket.m_type == SocketType::SocketTypeFloat) { + result["value"] = *reinterpret_cast(socket.m_value.ptr); + } else if (socket.m_type == SocketType::SocketTypeVec3) { + Vec3& vec3 = *reinterpret_cast(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(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(socket.m_value.ptr); + } else { + std::cerr << "Invalid socket type '" << static_cast(socket.m_type) + << "'." << std::endl; + } + } return result; } @@ -95,6 +122,12 @@ json sAnimGraphNodeToJson(const AnimNodeResource& node) { 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; } @@ -110,6 +143,49 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) { result.m_socket_accessor = 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( + 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( + property.m_name, + json_property["value"]); + } else if (property.m_type == SocketType::SocketTypeVec3) { + Vec3* property_vec3 = reinterpret_cast(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(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( + property.m_name, + json_property["value"]); + } else { + std::cerr << "Invalid type for property '" << property.m_name + << "'. Cannot parse json to type '" + << static_cast(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; } @@ -272,10 +348,10 @@ bool AnimGraphResource::loadFromFile(const char* filename) { sJsonToSocket(graph_inputs[i])); } - return false; + return true; } -void* AnimGraph::GetOutput(const std::string& name) { +void* AnimGraph::GetOutput(const std::string& name) const { Socket* socket = m_socket_accessor->FindInputSocket(name); if (socket == nullptr) { return nullptr; @@ -284,11 +360,13 @@ void* AnimGraph::GetOutput(const std::string& name) { 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); if (socket == nullptr) { return nullptr; } return *(socket->m_value.ptr_ptr); +} + } \ No newline at end of file diff --git a/src/AnimGraphResource.h b/src/AnimGraphResource.h index 41ebdc7..469f20d 100644 --- a/src/AnimGraphResource.h +++ b/src/AnimGraphResource.h @@ -14,6 +14,8 @@ #include "SyncTrack.h" +namespace AnimGraphCode { + enum class SocketType { SocketTypeUndefined, SocketTypeBool, @@ -24,6 +26,8 @@ enum class SocketType { SocketTypeString }; +enum SocketFlags { SocketFlagAffectsTime = 1 }; + struct AnimData { float m_bone_transforms[16]; }; @@ -61,6 +65,7 @@ struct Socket { void** ptr_ptr; }; SocketValue m_value = {nullptr}; + int m_flags = 0; size_t m_type_size = 0; }; @@ -91,6 +96,8 @@ struct NodeSocketAccessorBase { NodeSocketAccessorBase() {} virtual ~NodeSocketAccessorBase() {} + virtual void UpdateFlags(){}; + Socket* FindSocket(std::vector& sockets, const std::string& name) { Socket* result = nullptr; for (size_t i = 0, n = sockets.size(); i < n; i++) { @@ -140,11 +147,40 @@ struct NodeSocketAccessorBase { return -1; } + template + T GetSocketValue( + const std::vector& sockets, + const std::string& name, + T default_value) { + const Socket* socket = FindSocket(sockets, name); + if (socket == nullptr) { + return default_value; + } + + return *static_cast(socket->m_value.ptr); + } + + template + void SetSocketValue( + const std::vector& 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(socket->m_value.ptr) = value; + } + template bool RegisterSocket( std::vector& sockets, const std::string& name, - T* value_ptr) { + T* value_ptr, + int flags = 0) { Socket* socket = FindSocket(sockets, name); if (socket != nullptr) { std::cerr << "Socket " << name << " already registered." << std::endl; @@ -155,6 +191,7 @@ struct NodeSocketAccessorBase { socket = &sockets[sockets.size() - 1]; socket->m_name = name; socket->m_type_size = sizeof(T); + socket->m_flags = flags; if constexpr (std::is_same::value) { socket->m_type = SocketType::SocketTypeFloat; @@ -194,20 +231,20 @@ struct NodeSocketAccessorBase { return RegisterSocket(m_properties, name, value); } template - bool SetProperty(const std::string& name, T value) { - return SetSocketValue(m_properties, name, value); + void SetProperty(const std::string& name, const T& value) { + SetSocketValue(m_properties, name, value); } template - T GetProperty(const std::string& name, T value) { - return GetSocketValue(m_properties, name, value); + T GetProperty(const std::string& name, T default_value) { + return GetSocketValue(m_properties, name, default_value); } SocketType GetPropertyType(const std::string& name) { return GetSocketType(m_properties, name); } template - bool RegisterInput(const std::string& name, T* value) { - return RegisterSocket(m_inputs, name, value); + bool RegisterInput(const std::string& name, T* value, int flags = 0) { + return RegisterSocket(m_inputs, name, value, flags); } template T* GetInput(const std::string& name, T* value) { @@ -224,8 +261,8 @@ struct NodeSocketAccessorBase { } template - bool RegisterOutput(const std::string& name, T** value) { - return RegisterSocket(m_outputs, name, value); + bool RegisterOutput(const std::string& name, T** value, int flags = 0) { + return RegisterSocket(m_outputs, name, value, flags); } SocketType GetOutputType(const std::string& name) { return GetSocketType(m_outputs, name); @@ -275,12 +312,26 @@ struct NodeSocketAccessor : public NodeSocketAccessorBase { Blend2Node* node = dynamic_cast(node_); RegisterInput("Input0", &node->m_input0); RegisterInput("Input1", &node->m_input1); - RegisterInput("Weight", &node->m_blend_weight); + RegisterInput( + "Weight", + &node->m_blend_weight, + SocketFlags::SocketFlagAffectsTime); RegisterOutput("Output", &node->m_output); 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("Sync", false) == true) { + weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime; + } else { + weight_input_socket->m_flags = 0; + } + } }; struct SpeedScaleNode : public AnimNode { @@ -293,7 +344,10 @@ template <> struct NodeSocketAccessor : public NodeSocketAccessorBase { NodeSocketAccessor(AnimNode* node_) { SpeedScaleNode* node = dynamic_cast(node_); - RegisterInput("SpeedScale", &node->m_speed_scale); + RegisterInput( + "SpeedScale", + &node->m_speed_scale, + SocketFlags::SocketFlagAffectsTime); RegisterInput("Input", &node->m_input); RegisterOutput("Output", &node->m_output); @@ -342,13 +396,9 @@ struct AnimGraphResource { bool saveToFile(const char* filename) const; bool loadFromFile(const char* filename); - AnimNodeResource& getGraphOutputNode() { - return m_nodes[0]; - } + AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; } - AnimNodeResource& getGraphInputNode() { - return m_nodes[1]; - } + AnimNodeResource& getGraphInputNode() { return m_nodes[1]; } size_t addNode(AnimNodeResource node_resource) { m_nodes.push_back(node_resource); @@ -478,8 +528,8 @@ struct AnimGraph { std::vector& GetGraphOutputs() { return m_socket_accessor->m_inputs; } std::vector& GetGraphInputs() { return m_socket_accessor->m_outputs; } - void* GetOutput(const std::string& name); - void* GetInput(const std::string& name); + void* GetOutput(const std::string& name) const; + void* GetInput(const std::string& name) const; AnimNode* getAnimNode(const char* name) { for (size_t i = 0; i < m_nodes.size(); i++) { @@ -653,4 +703,6 @@ struct AnimGraph { } }; +} + #endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H