Added serialization and deserialization of properties.
parent
ee6a8eb29a
commit
9069a8192b
|
@ -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++) {
|
||||
|
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
||||
|
@ -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,7 +360,7 @@ 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;
|
||||
|
@ -292,3 +368,5 @@ void* AnimGraph::GetInput(const std::string& name) {
|
|||
|
||||
return *(socket->m_value.ptr_ptr);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Socket>& 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 <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>
|
||||
bool RegisterSocket(
|
||||
std::vector<Socket>& 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<T, float>::value) {
|
||||
socket->m_type = SocketType::SocketTypeFloat;
|
||||
|
@ -194,20 +231,20 @@ struct NodeSocketAccessorBase {
|
|||
return RegisterSocket(m_properties, name, value);
|
||||
}
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
T* GetInput(const std::string& name, T* value) {
|
||||
|
@ -224,8 +261,8 @@ struct NodeSocketAccessorBase {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
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<Blend2Node> : public NodeSocketAccessorBase {
|
|||
Blend2Node* node = dynamic_cast<Blend2Node*>(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<bool>("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<SpeedScaleNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* 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);
|
||||
|
||||
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<Socket>& GetGraphOutputs() { return m_socket_accessor->m_inputs; }
|
||||
std::vector<Socket>& 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
|
||||
|
|
Loading…
Reference in New Issue