Initial graph editor and graph instantiation from file.

This commit is contained in:
Martin Felis
2022-02-11 16:51:18 +01:00
parent fd4785afcd
commit c5b0d1f976
48 changed files with 87227 additions and 41 deletions
+118
View File
@@ -0,0 +1,118 @@
//
// Created by martin on 11.02.22.
//
#include "AnimGraphEditor.h"
#include "imnodes.h"
#include "AnimGraphResource.h"
static AnimGraphResource gGraphResource;
constexpr int NodeInputAttributeFlag = 2 << 16;
constexpr int NodeOutputAttributeFlag = 2 << 17;
void AnimGraphEditorUpdate() {
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
gGraphResource.saveToFile("editor_graph.json");
}
if (ImGui::Button("Load")) {
gGraphResource.loadFromFile("editor_graph.json");
for (size_t i = 0, 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]));
}
}
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));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
gGraphResource.m_name = graph_name_buffer;
}
ImGui::EndMenuBar();
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);
}
for (size_t i = 0, n = gGraphResource.m_nodes.size(); i < n; i++) {
AnimNodeResource& node_resource = gGraphResource.m_nodes[i];
ImNodes::BeginNode(i);
// Header
ImNodes::BeginNodeTitleBar();
ImGui::TextUnformatted(node_resource.m_type_name.c_str());
ImNodes::EndNodeTitleBar();
// 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));
ImGui::Text(socket.m_name.c_str());
ImNodes::EndInputAttribute();
}
// 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));
ImGui::Text(socket.m_name.c_str());
ImNodes::EndInputAttribute();
}
// Save state in node resource
ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
node_resource.m_position[0] = node_pos[0];
node_resource.m_position[1] = node_pos[1];
ImNodes::EndNode();
}
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);
ImNodes::Link(i, start_attr, end_attr);
}
ImNodes::EndNodeEditor();
int start_attr, end_attr;
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);
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;
AnimGraphConnection connection;
connection.m_source_node_index = node_start_id;
connection.m_source_socket_index = node_start_output_index;
connection.m_target_node_index = node_end_id;
connection.m_target_socket_index = node_end_input_index;
gGraphResource.m_connections.push_back(connection);
}
}
+10
View File
@@ -0,0 +1,10 @@
//
// Created by martin on 11.02.22.
//
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
#define ANIMTESTBED_ANIMGRAPHEDITOR_H
void AnimGraphEditorUpdate();
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
+161
View File
@@ -0,0 +1,161 @@
//
// Created by martin on 04.02.22.
//
#include <fstream>
#include "AnimGraphResource.h"
#include "3rdparty/json/json.hpp"
using json = nlohmann::json;
std::string sPinTypeToStr (SocketType pin_type) {
std::string result = "unknown";
switch (pin_type) {
case SocketType::SocketTypeFloat: result = "Float"; break;
case SocketType::SocketTypeAnimation: result = "AnimationData"; break;
default: result = "Unknown";
}
return result;
}
json sSocketToJson(const Socket& pin) {
json result;
result["name"] = pin.m_name;
result["type"] = sPinTypeToStr(pin.m_type);
return result;
}
json sAnimGraphNodeToJson(const AnimNodeResource& node) {
json result;
result["name"] = node.m_name;
result["type"] = "AnimNodeResource";
result["node_type"] = node.m_type_name;
for (size_t j = 0; j < 2; j++) {
result["position"][j] = node.m_position[j];
}
return result;
}
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
AnimNodeResource result;
result.m_name = json_node["name"];
result.m_type_name = json_node["node_type"];
result.m_position[0] = json_node["position"][0];
result.m_position[1] = json_node["position"][1];
result.m_anim_node = AnimNodeFactory(result.m_type_name);
result.m_socket_accessor = AnimNodeAccessorFactory(result.m_type_name, result.m_anim_node);
return result;
}
json sAnimGraphConnectionToJson(const AnimGraphConnection& connection) {
json result;
result["type"] = "AnimGraphConnection";
result["source_node_index"] = connection.m_source_node_index;
result["source_socket_index"] = connection.m_source_socket_index;
result["target_node_index"] = connection.m_target_node_index;
result["target_socket_index"] = connection.m_target_socket_index;
return result;
}
AnimGraphConnection sAnimGraphConnectionFromJson(const json& json_node) {
AnimGraphConnection connection;
connection.m_source_node_index = json_node["source_node_index"];
connection.m_source_socket_index = json_node["source_socket_index"];
connection.m_target_node_index = json_node["target_node_index"];
connection.m_target_socket_index = json_node["target_socket_index"];
return connection;
}
void AnimGraphResource::clear() {
m_name = "";
m_nodes.clear();
m_connections.clear();
}
bool AnimGraphResource::saveToFile (const char* filename) const {
json result;
result["name"] = m_name;
result["type"] = "AnimGraphResource";
for (size_t i = 0; i < m_nodes.size(); i++) {
const AnimNodeResource& node = m_nodes[i];
result["nodes"][i] = sAnimGraphNodeToJson(node);
}
for (size_t i = 0; i < m_connections.size(); i++) {
const AnimGraphConnection& connection = m_connections[i];
result["connections"][i] = sAnimGraphConnectionToJson(connection);
}
std::ofstream output_file;
output_file.open (filename);
output_file << to_string(result) << std::endl;
output_file.close();
return true;
}
bool AnimGraphResource::loadFromFile (const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
buffer << input_file.rdbuf();
json json_data = json::parse(buffer.str(), nullptr, false);
if (json_data.is_discarded()) {
std::cerr << "Error parsing json of file '" << filename << "'." << std::endl;
}
if (json_data["type"] != "AnimGraphResource") {
std::cerr << "Invalid json object. Expected type 'AnimGraphResource' but got '" << json_data["type"] << "'." << std::endl;
}
clear();
m_name = json_data["name"];
for (size_t i = 0; i < json_data["nodes"].size(); i++) {
const json& json_node = json_data["nodes"][i];
if (json_node["type"] != "AnimNodeResource") {
std::cerr << "Invalid json object. Expected type 'AnimNodeResource' but got '" << json_node["type"] << "'." << std::endl;
return false;
}
AnimNodeResource node = sAnimGraphNodeFromJson(json_node);
m_nodes.push_back(node);
}
for (size_t i = 0; i < json_data["connections"].size(); i++) {
const json& json_connection = json_data["connections"][i];
if (json_connection["type"] != "AnimGraphConnection") {
std::cerr << "Invalid json object. Expected type 'AnimGraphConnection' but got '" << json_connection["type"] << "'." << std::endl;
return false;
}
AnimGraphConnection connection = sAnimGraphConnectionFromJson(json_connection);
m_connections.push_back(connection);
}
return false;
}
+556
View File
@@ -0,0 +1,556 @@
//
// Created by martin on 04.02.22.
//
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include "SyncTrack.h"
enum class SocketType {
SocketTypeUndefined,
SocketTypeBool,
SocketTypeAnimation,
SocketTypeFloat,
SocketTypeVec3,
SocketTypeQuat,
SocketTypeString
};
struct AnimData {
float m_bone_transforms[16];
};
typedef float Vec3[3];
typedef float Quat[4];
inline int GenerateInputAttributeId(int node_id, int input_index) {
return ((input_index + 1) << 14) + node_id;
}
inline void
SplitInputAttributeId(int attribute_id, int* node_id, int* input_index) {
*node_id = attribute_id & ((1 << 14) - 1);
*input_index = (attribute_id >> 14) - 1;
}
inline int GenerateOutputAttributeId(int node_id, int output_index) {
return ((output_index + 1) << 23) + node_id;
}
inline void
SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
*node_id = attribute_id & ((1 << 14) - 1);
*output_index = (attribute_id >> 23) - 1;
}
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;
};
Value m_value;
};
struct AnimNode;
struct NodeSocketAccessorBase;
struct AnimNodeResource {
std::string m_name;
std::string m_type_name;
AnimNode* m_anim_node = nullptr;
NodeSocketAccessorBase* m_socket_accessor = nullptr;
float m_position[2] = {0.f, 0.f};
};
struct AnimNode {
virtual ~AnimNode(){};
std::string m_name;
std::string m_node_type_name;
bool m_is_time_synced;
float m_time_now;
float m_time_last;
SyncTrack m_sync_track;
};
struct NodeSocketAccessorBase {
NodeSocketAccessorBase(AnimNode* node) {}
virtual ~NodeSocketAccessorBase() {}
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
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::string& name) {
Socket* socket = FindSocket(m_properties, name);
if (socket == nullptr) {
return SocketType::SocketTypeUndefined;
}
return socket->m_type;
}
template <typename T>
bool RegisterSocket(
std::vector<Socket>& sockets,
const std::string& name,
T* value_ptr) {
Socket* socket = FindSocket(sockets, name);
if (socket != nullptr) {
std::cerr << "Socket " << name << " already registered." << std::endl;
return false;
}
sockets.push_back(Socket());
socket = &sockets[sockets.size() - 1];
socket->m_name = name;
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;
}
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);
}
template <typename T>
bool SetProperty(const std::string& name, T value) {
return SetSocketValue(m_properties, name, value);
}
template <typename T>
T GetProperty(const std::string& name, T value) {
return GetSocketValue(m_properties, name, 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);
}
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);
}
SocketType GetInputType(const std::string& name) {
return GetSocketType(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);
}
std::vector<Socket> m_properties;
std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs;
};
template <typename T>
struct NodeSocketAccessor : public NodeSocketAccessorBase {
virtual ~NodeSocketAccessor() {}
};
struct NodeRegistry {
AnimNode* createNode(const std::string& node_type);
NodeSocketAccessorBase* createNodeAccessor(
const std::string& node_type,
AnimNode* node);
};
struct Blend2Node : public AnimNode {
AnimData m_input0;
AnimData m_input1;
AnimData* m_output = nullptr;
float m_blend_weight = 0.f;
bool m_sync_blend = false;
};
template <>
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
RegisterInput("Input0", &node->m_input0);
RegisterInput("Input1", &node->m_input1);
RegisterInput("Weight", &node->m_blend_weight);
RegisterOutput("Output", &node->m_output);
RegisterProperty("Sync", &node->m_sync_blend);
}
};
struct SpeedScaleNode : public AnimNode {
AnimData m_input;
AnimData* m_output = nullptr;
float m_speed_scale = 0.f;
};
template <>
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
RegisterInput("SpeedScale", &node->m_speed_scale);
RegisterInput("Input", &node->m_input);
RegisterOutput("Output", &node->m_output);
}
};
struct AnimSamplerNode : public AnimNode {
AnimData* m_output = nullptr;
std::string m_filename;
};
template <>
struct NodeSocketAccessor<AnimSamplerNode> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {
AnimSamplerNode* node = dynamic_cast<AnimSamplerNode*>(node_);
RegisterOutput("Output", &node->m_output);
RegisterProperty("Filename", &node->m_filename);
}
};
struct AnimGraphConnection {
int m_source_node_index;
int m_source_socket_index;
int m_target_node_index;
int m_target_socket_index;
};
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;
void clear();
bool saveToFile(const char* filename) const;
bool loadFromFile(const char* filename);
};
static inline AnimNode* AnimNodeFactory(const std::string& name) {
if (name == "Blend2") {
return new Blend2Node;
} else if (name == "SpeedScale") {
return new SpeedScaleNode;
} else if (name == "AnimSampler") {
return new AnimSamplerNode;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
static inline NodeSocketAccessorBase* AnimNodeAccessorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return new NodeSocketAccessor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return new NodeSocketAccessor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return new NodeSocketAccessor<AnimSamplerNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
static inline AnimNodeResource AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource result;
result.m_type_name = node_type_name;
result.m_anim_node = AnimNodeFactory(node_type_name);
result.m_socket_accessor =
AnimNodeAccessorFactory(node_type_name, result.m_anim_node);
return result;
}
struct AnimGraph {
void UpdateTime(float dt);
void Evaluate();
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<std::vector<AnimNode*> > m_node_inputs;
AnimNode* getAnimNode(const char* name) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i]->m_name == name) {
return m_nodes[i];
}
}
return nullptr;
}
size_t getAnimNodeIndex(AnimNode* node) {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (m_nodes[i] == node) {
return i;
}
}
std::cerr << "Could not find node " << node << "." << std::endl;
return -1;
}
static AnimGraph createFromResource(const AnimGraphResource& resource) {
AnimGraph result;
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());
node->m_name = node_resource.m_name;
node->m_node_type_name = node_resource.m_type_name;
result.m_nodes.push_back(node);
result.m_node_inputs.push_back(std::vector<AnimNode*>());
}
for (int i = 0; i < resource.m_connections.size(); i++) {
const AnimGraphConnection& connection = resource.m_connections[i];
std::string source_node_type = "";
std::string target_node_type = "";
std::string source_node_name = "";
std::string target_node_name = "";
AnimNode* source_node = nullptr;
AnimNode* target_node = nullptr;
NodeSocketAccessorBase* source_node_accessor = nullptr;
NodeSocketAccessorBase* target_node_accessor = nullptr;
SocketType source_type;
SocketType target_type;
if (connection.m_source_node_index >= 0) {
source_node = result.m_nodes[connection.m_source_node_index];
source_node_name = source_node->m_name;
source_node_type = source_node->m_node_type_name;
source_node_accessor =
AnimNodeAccessorFactory(source_node_type, source_node);
}
if (connection.m_target_node_index >= 0) {
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);
}
assert(source_node != nullptr);
assert(target_node != nullptr);
if (connection.m_source_socket_index
> source_node_accessor->m_outputs.size()) {
std::cerr << "Invalid source socket index "
<< connection.m_source_socket_index << ". Only "
<< source_node_accessor->m_outputs.size()
<< " output sockets found." << std::endl;
continue;
}
if (connection.m_target_socket_index
> target_node_accessor->m_inputs.size()) {
std::cerr << "Invalid source socket index "
<< connection.m_target_socket_index << ". Only "
<< source_node_accessor->m_inputs.size()
<< " input sockets found." << std::endl;
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) {
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);
size_t target_node_index = result.getAnimNodeIndex(target_node);
result.m_node_inputs[target_node_index].push_back(source_node);
}
return result;
}
};
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
+61 -27
View File
@@ -5,6 +5,7 @@
//------------------------------------------------------------------------------
#include "imgui.h"
#include "imnodes.h"
#define SOKOL_IMPL
#define SOKOL_GLCORE33
#include "sokol_gfx.h"
@@ -16,6 +17,7 @@
#include "Camera.h"
#include "SkinnedMesh.h"
#include "AnimGraphEditor.h"
#include "GLFW/glfw3.h"
const int Width = 1024;
@@ -108,6 +110,15 @@ enum class ControlMode {
ControlMode gControlMode = ControlMode::ControlModeNone;
enum class AppMode {
AnimRuntime = 0,
GraphEditor = 1,
Debug = 2
};
const char* AppModeNames[] = {"Runtime", "Graph Editor", "Debug"};
AppMode gAppMode = AppMode::GraphEditor;
// io buffers for skeleton and animation data files, we know the max file size upfront
static uint8_t skel_data_buffer[4 * 1024];
static uint8_t anim_data_buffer[32 * 1024];
@@ -262,6 +273,9 @@ int main() {
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
// ImNodes
ImNodes::CreateContext();
// dynamic vertex- and index-buffers for imgui-generated geometry
sg_buffer_desc vbuf_desc = {};
vbuf_desc.usage = SG_USAGE_STREAM;
@@ -356,14 +370,6 @@ int main() {
io.DeltaTime = (float)stm_sec(stm_laptime(&last_time));
ImGui::NewFrame();
ImGui::Begin("Camera");
ImGui::SliderFloat3("pos", state.camera.pos, -100.f, 100.f);
ImGui::SliderFloat("near", &state.camera.near, 0.001f, 10.f);
ImGui::SliderFloat("far", &state.camera.far, 1.0f, 10000.f);
ImGui::SliderFloat("heading", &state.camera.heading, -180.0f, 180.f);
ImGui::SliderFloat("pitch", &state.camera.pitch, -179.0f, 179.f);
ImGui::End();
// handle input
handle_mouse (w, &gGuiInputState);
@@ -427,29 +433,59 @@ int main() {
if (ImGui::BeginMainMenuBar()) {
ImGui::Text("AnimTestbed");
int mode_current = static_cast<int>(gAppMode);
if (ImGui::Combo("Mode", &mode_current, AppModeNames, sizeof(AppModeNames) / sizeof(char*))) {
switch (mode_current) {
case 0: gAppMode = AppMode::AnimRuntime; break;
case 1: gAppMode = AppMode::GraphEditor; break;
case 2: gAppMode = AppMode::Debug; break;
default: break;
}
}
ImGui::Checkbox("ImGui Demo", &show_imgui_demo_window);
ImGui::EndMainMenuBar();
}
if (gAppMode == AppMode::AnimRuntime) {
ImGui::Begin("Camera");
ImGui::SliderFloat3("pos", state.camera.pos, -100.f, 100.f);
ImGui::SliderFloat("near", &state.camera.near, 0.001f, 10.f);
ImGui::SliderFloat("far", &state.camera.far, 1.0f, 10000.f);
ImGui::SliderFloat("heading", &state.camera.heading, -180.0f, 180.f);
ImGui::SliderFloat("pitch", &state.camera.pitch, -179.0f, 179.f);
ImGui::End();
draw_grid();
ImGui::SetNextWindowPos(ImVec2 (600, 60), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2 (300, 400), ImGuiCond_FirstUseEver);
skinned_mesh.DrawDebugUi();
animation_controller.DrawDebugUi();
ImGui::SetNextWindowPos(ImVec2(600, 60), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 400), ImGuiCond_FirstUseEver);
if (!skinned_mesh.m_sync_track_override) {
animation_controller.UpdateTime(state.time.frame);
animation_controller.Evaluate();
draw_grid();
skinned_mesh.DrawDebugUi();
animation_controller.DrawDebugUi();
if (!skinned_mesh.m_sync_track_override) {
animation_controller.UpdateTime(state.time.frame);
animation_controller.Evaluate();
}
skinned_mesh.CalcModelMatrices();
sgl_defaults();
sgl_matrix_mode_projection();
sgl_load_matrix((const float*)&state.camera.mtxProj);
sgl_matrix_mode_modelview();
sgl_load_matrix((const float*)&state.camera.mtxView);
RenderSkinnedMesh(skinned_mesh);
} else if (gAppMode == AppMode::GraphEditor) {
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
ImGui::Begin("Graph Editor", nullptr, ImGuiWindowFlags_MenuBar);
AnimGraphEditorUpdate();
ImGui::End();
} else if (gAppMode == AppMode::Debug) {
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
ImGui::Begin("Debug");
ImGui::End();
}
skinned_mesh.CalcModelMatrices();
sgl_defaults();
sgl_matrix_mode_projection();
sgl_load_matrix((const float*)&state.camera.mtxProj);
sgl_matrix_mode_modelview();
sgl_load_matrix((const float*)&state.camera.mtxView);
RenderSkinnedMesh(skinned_mesh);
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
if (show_imgui_demo_window) {
@@ -457,7 +493,6 @@ int main() {
ImGui::ShowDemoWindow();
}
// the sokol_gfx draw pass
sg_begin_default_pass(&pass_action, cur_width, cur_height);
sgl_draw();
@@ -469,9 +504,8 @@ int main() {
glfwPollEvents();
}
// state.ozz = nullptr;
/* cleanup */
ImNodes::DestroyContext();
ImGui::DestroyContext();
sgl_shutdown();
sg_shutdown();