Started working on graph initialization in ATP Editor.

AnimGraphEditor
Martin Felis 2023-03-26 23:39:11 +02:00
parent a1931185d8
commit e38c0b4934
10 changed files with 193 additions and 155 deletions

View File

@ -7,6 +7,8 @@
#include <cstring> #include <cstring>
bool AnimGraph::init(AnimGraphContext& context) { bool AnimGraph::init(AnimGraphContext& context) {
context.m_graph = this;
for (size_t i = 2; i < m_nodes.size(); i++) { for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) { if (!m_nodes[i]->Init(context)) {
return false; return false;

View File

@ -27,19 +27,26 @@ struct AnimGraph {
AnimDataAllocator m_anim_data_allocator; AnimDataAllocator m_anim_data_allocator;
~AnimGraph() { ~AnimGraph() { dealloc(); }
std::vector<AnimGraphConnection>& graph_outputs = m_node_input_connections[0];
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) { bool init(AnimGraphContext& context);
AnimGraphConnection& connection = graph_outputs[i]; void dealloc() {
if (connection.m_target_socket.m_type == SocketType::SocketTypeAnimation) { if (m_node_input_connections.size() > 0) {
AnimData* graph_anim_output = std::vector<AnimGraphConnection>& graph_outputs =
static_cast<AnimData*>(connection.m_target_socket.m_reference.ptr); m_node_input_connections[0];
assert(graph_anim_output != nullptr);
// we have to explicitly call the destructor as the AnimData* was for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
// initialized using a placement new operator. AnimGraphConnection& connection = graph_outputs[i];
graph_anim_output->m_local_matrices.vector::~vector(); if (connection.m_target_socket.m_type
== SocketType::SocketTypeAnimation) {
AnimData* graph_anim_output = static_cast<AnimData*>(
connection.m_target_socket.m_reference.ptr);
assert(graph_anim_output != nullptr);
// we have to explicitly call the destructor as the AnimData* was
// initialized using a placement new operator.
graph_anim_output->m_local_matrices.vector::~vector();
}
} }
} }
@ -53,8 +60,6 @@ struct AnimGraph {
delete m_socket_accessor; delete m_socket_accessor;
} }
bool init(AnimGraphContext& context);
void updateOrderedNodes(); void updateOrderedNodes();
void updateOrderedNodesRecursive(int node_index); void updateOrderedNodesRecursive(int node_index);
void markActiveNodes(); void markActiveNodes();
@ -70,7 +75,7 @@ struct AnimGraph {
void evalSyncTracks(); void evalSyncTracks();
void updateTime(float dt); void updateTime(float dt);
void evaluate(AnimGraphContext& context); void evaluate(AnimGraphContext& context);
void reset() { void resetNodeStates() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f; m_nodes[i]->m_time_now = 0.f;
m_nodes[i]->m_time_last = 0.f; m_nodes[i]->m_time_last = 0.f;
@ -102,7 +107,6 @@ struct AnimGraph {
return nullptr; return nullptr;
} }
int getNodeEvalOrderIndex(const AnimNode* node) { int getNodeEvalOrderIndex(const AnimNode* node) {
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
if (m_eval_ordered_nodes[i] == node) { if (m_eval_ordered_nodes[i] == node) {
@ -112,12 +116,13 @@ struct AnimGraph {
return -1; return -1;
} }
const AnimNode* getAnimNodeForInput ( const AnimNode* getAnimNodeForInput(
size_t node_index, size_t node_index,
const std::string& input_name) const { const std::string& input_name) const {
assert(node_index < m_nodes.size()); assert(node_index < m_nodes.size());
const std::vector<AnimGraphConnection>& input_connection = m_node_input_connections[node_index]; const std::vector<AnimGraphConnection>& input_connection =
m_node_input_connections[node_index];
for (size_t i = 0, n = input_connection.size(); i < n; i++) { for (size_t i = 0, n = input_connection.size(); i < n; i++) {
if (input_connection[i].m_target_socket.m_name == input_name) { if (input_connection[i].m_target_socket.m_name == input_name) {
return input_connection[i].m_source_node; return input_connection[i].m_source_node;
@ -137,7 +142,7 @@ struct AnimGraph {
return nullptr; return nullptr;
} }
size_t getAnimNodeIndex (AnimNode* node) { size_t getAnimNodeIndex(AnimNode* node) {
for (size_t i = 0; i < m_nodes.size(); i++) { for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) { if (m_nodes[i] == node) {
return i; return i;

View File

@ -9,10 +9,10 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <list>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <list>
#include "SyncTrack.h" #include "SyncTrack.h"
#include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/animation.h"
@ -76,16 +76,15 @@ struct AnimDataAllocator {
void free(AnimData* anim_data) { void free(AnimData* anim_data) {
#ifdef ANIM_DATA_ALLOCATOR_DEBUG #ifdef ANIM_DATA_ALLOCATOR_DEBUG
std::cout << "Storing buffer with size " << anim_data->m_local_matrices.size() std::cout << "Storing buffer with size "
<< " " << anim_data << std::endl; << anim_data->m_local_matrices.size() << " " << anim_data
<< std::endl;
#endif #endif
m_anim_data_list.push_front(anim_data); m_anim_data_list.push_front(anim_data);
} }
size_t size() { size_t size() { return m_anim_data_list.size(); }
return m_anim_data_list.size();
}
}; };
struct AnimGraphContext { struct AnimGraphContext {
@ -134,7 +133,7 @@ struct Socket {
float float_value; float float_value;
float vec3[3]; float vec3[3];
float quat[4]; float quat[4];
char str[cSocketStringValueMaxLength]; std::string* string_ptr;
}; };
SocketValue m_value = {0}; SocketValue m_value = {0};
union SocketReference { union SocketReference {
@ -265,6 +264,9 @@ struct NodeSocketAccessorBase {
socket->m_type = SocketType::SocketTypeAnimation; socket->m_type = SocketType::SocketTypeAnimation;
} else if constexpr (std::is_same<T, std::string>::value) { } else if constexpr (std::is_same<T, std::string>::value) {
socket->m_type = SocketType::SocketTypeString; socket->m_type = SocketType::SocketTypeString;
socket->m_value.string_ptr = value_ptr;
socket->m_reference.ptr = value_ptr;
return true;
} else if constexpr (std::is_same<T, float*>::value) { } else if constexpr (std::is_same<T, float*>::value) {
socket->m_type = SocketType::SocketTypeFloat; socket->m_type = SocketType::SocketTypeFloat;
} else if constexpr (std::is_same<T, bool*>::value) { } else if constexpr (std::is_same<T, bool*>::value) {
@ -381,6 +383,13 @@ inline void NodeSocketAccessorBase::SetSocketReferenceValue<const Quat&>(
static_cast<float*>(socket->m_reference.ptr)[3] = value[3]; static_cast<float*>(socket->m_reference.ptr)[3] = value[3];
} }
template <>
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string*>(
Socket* socket,
const std::string* value) {
socket->m_value.string_ptr = const_cast<std::string*>(value);
}
template <> template <>
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string&>( inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string&>(
Socket* socket, Socket* socket,
@ -433,20 +442,17 @@ inline void NodeSocketAccessorBase::SetSocketValue<const Quat&>(
} }
template <> template <>
inline void NodeSocketAccessorBase::SetSocketValue<const std::string&>( inline void NodeSocketAccessorBase::SetSocketValue<std::string>(
Socket* socket, Socket* socket,
const std::string& value) { std::string value) {
constexpr size_t string_max_length = sizeof(socket->m_value.str) - 1; *socket->m_value.string_ptr = value;
strncpy(socket->m_value.str, value.data(), string_max_length);
socket->m_value.str
[value.size() > string_max_length ? string_max_length : value.size()] = 0;
} }
template <> template <>
inline void NodeSocketAccessorBase::SetSocketValue<const char*>( inline void NodeSocketAccessorBase::SetSocketValue<const char*>(
Socket* socket, Socket* socket,
const char* value) { const char* value) {
SetSocketValue<const std::string&>(socket, value); SetSocketValue<std::string>(socket, value);
} }
template <typename T> template <typename T>

View File

@ -9,6 +9,8 @@
#include "imnodes.h" #include "imnodes.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
static AnimGraphResource sGraphGresource = AnimGraphResource();
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
switch (socket_type) { switch (socket_type) {
case SocketType::SocketTypeAnimation: case SocketType::SocketTypeAnimation:
@ -54,7 +56,7 @@ void RemoveConnectionsForSocket(
// AnimGraphConnectionResource& connection = *iter; // AnimGraphConnectionResource& connection = *iter;
// if (connection.m_source_node == &node_resource // if (connection.m_source_node == &node_resource
// && connection.m_source_socket == &socket) { // && connection.m_source_socket == &socket) {
// iter = graph_resource.m_connections.erase(iter); // iter = sGraphGresource.m_connections.erase(iter);
// } else { // } else {
// iter++; // iter++;
// } // }
@ -91,19 +93,13 @@ void AnimGraphEditorRenderSidebar(
property.m_name.c_str(), property.m_name.c_str(),
reinterpret_cast<bool*>(property.m_reference.ptr)); reinterpret_cast<bool*>(property.m_reference.ptr));
} else if (property.m_type == SocketType::SocketTypeString) { } else if (property.m_type == SocketType::SocketTypeString) {
std::string* property_string = char string_buf[1024];
reinterpret_cast<std::string*>(property.m_reference.ptr); memcpy (string_buf, property.m_value.string_ptr->c_str(), property.m_value.string_ptr->size() + 1);
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( if (ImGui::InputText(
property.m_name.c_str(), property.m_name.c_str(),
string_buf, string_buf,
sizeof(string_buf))) { sizeof(string_buf))) {
(*property_string) = string_buf; *property.m_value.string_ptr = string_buf;
} }
} }
} }
@ -152,33 +148,31 @@ void AnimGraphEditorRenderSidebar(
} }
void AnimGraphEditorUpdate() { void AnimGraphEditorUpdate() {
static AnimGraphResource graph_resource = AnimGraphResource();
ImGui::BeginMenuBar(); ImGui::BeginMenuBar();
if (ImGui::Button("Save")) { if (ImGui::Button("Save")) {
graph_resource.saveToFile("editor_graph.json"); sGraphGresource.saveToFile("editor_graph.json");
} }
if (ImGui::Button("Load")) { if (ImGui::Button("Load")) {
graph_resource.loadFromFile("editor_graph.json"); sGraphGresource.loadFromFile("editor_graph.json");
for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) { for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
const AnimNodeResource& node_resource = graph_resource.m_nodes[i]; const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::SetNodeGridSpacePos( ImNodes::SetNodeGridSpacePos(
i, i,
ImVec2(node_resource.m_position[0], node_resource.m_position[1])); ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
} }
} }
if (ImGui::Button("Clear")) { if (ImGui::Button("Clear")) {
graph_resource.clear(); sGraphGresource.clear();
} }
char graph_name_buffer[256]; char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer)); memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy( strncpy(
graph_name_buffer, graph_name_buffer,
graph_resource.m_name.c_str(), sGraphGresource.m_name.c_str(),
sizeof(graph_name_buffer)); sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) { if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
graph_resource.m_name = graph_name_buffer; sGraphGresource.m_name = graph_name_buffer;
} }
ImGui::EndMenuBar(); ImGui::EndMenuBar();
@ -228,9 +222,9 @@ void AnimGraphEditorUpdate() {
if (node_type_name != "") { if (node_type_name != "") {
AnimNodeResource node_resource = AnimNodeResource node_resource =
AnimNodeResourceFactory(node_type_name); AnimNodeResourceFactory(node_type_name);
size_t node_id = graph_resource.m_nodes.size(); size_t node_id = sGraphGresource.m_nodes.size();
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos()); ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
graph_resource.m_nodes.push_back(node_resource); sGraphGresource.m_nodes.push_back(node_resource);
} }
ImGui::EndPopup(); ImGui::EndPopup();
@ -239,17 +233,17 @@ void AnimGraphEditorUpdate() {
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding); ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
} }
for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) { for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
AnimNodeResource& node_resource = graph_resource.m_nodes[i]; AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::BeginNode(i); ImNodes::BeginNode(i);
ImGui::PushItemWidth(110.0f); ImGui::PushItemWidth(110.0f);
// Header // Header
ImNodes::BeginNodeTitleBar(); ImNodes::BeginNodeTitleBar();
if (&node_resource == &graph_resource.getGraphOutputNode()) { if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
ImGui::TextUnformatted("Graph Outputs"); ImGui::TextUnformatted("Graph Outputs");
} else if (&node_resource == &graph_resource.getGraphInputNode()) { } else if (&node_resource == &sGraphGresource.getGraphInputNode()) {
ImGui::TextUnformatted("Graph Inputs"); ImGui::TextUnformatted("Graph Inputs");
} else { } else {
ImGui::TextUnformatted(node_resource.m_type_name.c_str()); ImGui::TextUnformatted(node_resource.m_type_name.c_str());
@ -273,7 +267,8 @@ void AnimGraphEditorUpdate() {
socket_color); socket_color);
ImGui::TextUnformatted(socket.m_name.c_str()); ImGui::TextUnformatted(socket.m_name.c_str());
bool socket_connected = graph_resource.isSocketConnected(node_resource, socket.m_name); bool socket_connected =
sGraphGresource.isSocketConnected(node_resource, socket.m_name);
if (!socket_connected && if (!socket_connected &&
(socket.m_type == SocketType::SocketTypeFloat)) { (socket.m_type == SocketType::SocketTypeFloat)) {
ImGui::SameLine(); ImGui::SameLine();
@ -307,7 +302,7 @@ void AnimGraphEditorUpdate() {
if (i == 0) { if (i == 0) {
if (ImGui::Button("+Output")) { if (ImGui::Button("+Output")) {
AnimNodeResource& graph_output_node = AnimNodeResource& graph_output_node =
graph_resource.getGraphOutputNode(); sGraphGresource.getGraphOutputNode();
static float bla = 0.f; static float bla = 0.f;
std::string socket_name = "Output"; std::string socket_name = "Output";
@ -319,7 +314,8 @@ void AnimGraphEditorUpdate() {
} }
} else if (i == 1) { } else if (i == 1) {
if (ImGui::Button("+Input")) { if (ImGui::Button("+Input")) {
AnimNodeResource& graph_input_node = graph_resource.getGraphInputNode(); AnimNodeResource& graph_input_node =
sGraphGresource.getGraphInputNode();
static float bla = 0.f; static float bla = 0.f;
std::string socket_name = "Input"; std::string socket_name = "Input";
@ -343,18 +339,18 @@ void AnimGraphEditorUpdate() {
node_resource.m_socket_accessor->UpdateFlags(); 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 = sGraphGresource.m_connections.size(); i < n; i++) {
const AnimGraphConnectionResource& connection = const AnimGraphConnectionResource& connection =
graph_resource.m_connections[i]; sGraphGresource.m_connections[i];
int start_attr, end_attr; int start_attr, end_attr;
const AnimNodeResource& source_node = const AnimNodeResource& source_node =
graph_resource.m_nodes[connection.source_node_index]; sGraphGresource.m_nodes[connection.source_node_index];
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex( int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
connection.source_socket_name); connection.source_socket_name);
const AnimNodeResource& target_node = const AnimNodeResource& target_node =
graph_resource.m_nodes[connection.target_node_index]; sGraphGresource.m_nodes[connection.target_node_index];
int target_socket_index = target_node.m_socket_accessor->GetInputIndex( int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
connection.target_socket_name); connection.target_socket_name);
@ -386,24 +382,29 @@ void AnimGraphEditorUpdate() {
AnimGraphConnectionResource connection; AnimGraphConnectionResource connection;
connection.source_node_index = node_start_id; connection.source_node_index = node_start_id;
const AnimNodeResource& source_node = graph_resource.m_nodes[node_start_id]; const AnimNodeResource& source_node =
sGraphGresource.m_nodes[node_start_id];
connection.source_socket_name = connection.source_socket_name =
source_node.m_socket_accessor->m_outputs[node_start_output_index] source_node.m_socket_accessor->m_outputs[node_start_output_index]
.m_name; .m_name;
connection.target_node_index = node_end_id; connection.target_node_index = node_end_id;
const AnimNodeResource& target_node = graph_resource.m_nodes[node_end_id]; const AnimNodeResource& target_node = sGraphGresource.m_nodes[node_end_id];
connection.target_socket_name = connection.target_socket_name =
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name; target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
graph_resource.m_connections.push_back(connection); sGraphGresource.m_connections.push_back(connection);
}
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
std::cerr << "Delete key!" << std::endl;
} }
// 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( sGraphGresource.m_connections.erase(
graph_resource.m_connections.begin() + link_id); sGraphGresource.m_connections.begin() + link_id);
} }
int selected_nodes[ImNodes::NumSelectedNodes()]; int selected_nodes[ImNodes::NumSelectedNodes()];
@ -415,12 +416,16 @@ void AnimGraphEditorUpdate() {
ImGui::NextColumn(); ImGui::NextColumn();
if (ImNodes::NumSelectedNodes() == 1) { if (ImNodes::NumSelectedNodes() == 1) {
if (selected_nodes[0] < graph_resource.m_nodes.size()) { if (selected_nodes[0] < sGraphGresource.m_nodes.size()) {
AnimNodeResource& selected_node = AnimNodeResource& selected_node =
graph_resource.m_nodes[selected_nodes[0]]; sGraphGresource.m_nodes[selected_nodes[0]];
AnimGraphEditorRenderSidebar(graph_resource, selected_node); AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
} }
} }
ImGui::Columns(1); ImGui::Columns(1);
} }
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) {
sGraphGresource.createInstance(anim_graph);
}

View File

@ -5,6 +5,8 @@
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H #ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
#define ANIMTESTBED_ANIMGRAPHEDITOR_H #define ANIMTESTBED_ANIMGRAPHEDITOR_H
#include "AnimGraph.h"
inline int GenerateInputAttributeId(int node_id, int input_index) { inline int GenerateInputAttributeId(int node_id, int input_index) {
return ((input_index + 1) << 14) + node_id; return ((input_index + 1) << 14) + node_id;
} }
@ -27,4 +29,6 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
void AnimGraphEditorUpdate(); void AnimGraphEditorUpdate();
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H #endif //ANIMTESTBED_ANIMGRAPHEDITOR_H

View File

@ -43,7 +43,7 @@ json sSocketToJson(const Socket& socket) {
result["value"][2] = socket.m_value.quat[2]; result["value"][2] = socket.m_value.quat[2];
result["value"][3] = socket.m_value.quat[3]; result["value"][3] = socket.m_value.quat[3];
} else if (socket.m_type == SocketType::SocketTypeString) { } else if (socket.m_type == SocketType::SocketTypeString) {
result["value"] = std::string(socket.m_value.str); result["value"] = *socket.m_value.string_ptr;
} else { } else {
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type) std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
<< "'." << std::endl; << "'." << std::endl;
@ -142,17 +142,7 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
property.m_value.quat[2] = json_property["value"][2]; property.m_value.quat[2] = json_property["value"][2];
property.m_value.quat[3] = json_property["value"][3]; property.m_value.quat[3] = json_property["value"][3];
} else if (property.m_type == SocketType::SocketTypeString) { } else if (property.m_type == SocketType::SocketTypeString) {
std::string value_str = json_property["value"]; *(property.m_value.string_ptr) = json_property["value"].get<std::string>();
size_t string_length = value_str.size();
constexpr size_t string_max_length = sizeof(property.m_value.str) - 1;
if (string_length > string_max_length) {
std::cerr << "Warning: string '" << value_str
<< "' too long, truncating to " << string_max_length
<< " bytes." << std::endl;
string_length = string_max_length;
}
memcpy(property.m_value.str, value_str.data(), string_length);
property.m_value.str[string_length] = 0;
} else { } else {
std::cerr << "Invalid type for property '" << property.m_name std::cerr << "Invalid type for property '" << property.m_name
<< "'. Cannot parse json to type '" << "'. Cannot parse json to type '"
@ -263,7 +253,7 @@ bool AnimGraphResource::saveToFile(const char* filename) const {
std::ofstream output_file; std::ofstream output_file;
output_file.open(filename); output_file.open(filename);
output_file << to_string(result) << std::endl; output_file << result.dump(4, ' ') << std::endl;
output_file.close(); output_file.close();
return true; return true;
@ -340,18 +330,14 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
return true; return true;
} }
AnimGraph AnimGraphResource::createInstance() const { void AnimGraphResource::createInstance(AnimGraph& result) const {
AnimGraph result;
createRuntimeNodeInstances(result); createRuntimeNodeInstances(result);
prepareGraphIOData(result); prepareGraphIOData(result);
connectRuntimeNodes(result); connectRuntimeNodes(result);
setRuntimeNodeProperties(result); setRuntimeNodeProperties(result);
result.updateOrderedNodes(); result.updateOrderedNodes();
result.reset(); result.resetNodeStates();
return result;
} }
void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const { void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
@ -384,8 +370,11 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
for (int i = 0; i < graph_inputs.size(); i++) { for (int i = 0; i < graph_inputs.size(); i++) {
input_block_size += sizeof(void*); input_block_size += sizeof(void*);
} }
instance.m_input_buffer = new char[input_block_size];
memset(instance.m_input_buffer, 0, input_block_size); if (input_block_size > 0) {
instance.m_input_buffer = new char[input_block_size];
memset(instance.m_input_buffer, 0, input_block_size);
}
int input_block_offset = 0; int input_block_offset = 0;
for (int i = 0; i < graph_inputs.size(); i++) { for (int i = 0; i < graph_inputs.size(); i++) {
@ -400,8 +389,11 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
for (int i = 0; i < graph_outputs.size(); i++) { for (int i = 0; i < graph_outputs.size(); i++) {
output_block_size += graph_outputs[i].m_type_size; output_block_size += graph_outputs[i].m_type_size;
} }
instance.m_output_buffer = new char[output_block_size];
memset(instance.m_output_buffer, 0, output_block_size); if (output_block_size > 0) {
instance.m_output_buffer = new char[output_block_size];
memset(instance.m_output_buffer, 0, output_block_size);
}
int output_block_offset = 0; int output_block_offset = 0;
for (int i = 0; i < graph_outputs.size(); i++) { for (int i = 0; i < graph_outputs.size(); i++) {
@ -557,9 +549,9 @@ void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
property.m_value.quat); property.m_value.quat);
break; break;
case SocketType::SocketTypeString: case SocketType::SocketTypeString:
node_instance_accessor->SetPropertyReferenceValue( node_instance_accessor->SetPropertyValue(
name, name,
property.m_value.str); *property.m_value.string_ptr);
break; break;
default: default:
std::cerr << "Invalid socket type " std::cerr << "Invalid socket type "

View File

@ -140,7 +140,7 @@ struct AnimGraphResource {
return false; return false;
} }
AnimGraph createInstance() const; void createInstance(AnimGraph& result) const;
void createRuntimeNodeInstances(AnimGraph& instance) const; void createRuntimeNodeInstances(AnimGraph& instance) const;
void prepareGraphIOData(AnimGraph& instance) const; void prepareGraphIOData(AnimGraph& instance) const;

View File

@ -82,6 +82,7 @@ static struct {
float anim_ratio; float anim_ratio;
bool anim_ratio_ui_override; bool anim_ratio_ui_override;
bool paused; bool paused;
bool use_graph = false;
} time; } time;
} state; } state;
@ -311,7 +312,7 @@ int main() {
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHint(GLFW_SAMPLES, 16); glfwWindowHint(GLFW_SAMPLES, 16);
GLFWwindow* w = glfwCreateWindow(Width, Height, "AnimTestbed", 0, 0); GLFWwindow* w = glfwCreateWindow(Width, Height, "ATP Editor", 0, 0);
glfwMakeContextCurrent(w); glfwMakeContextCurrent(w);
glfwSwapInterval(1); glfwSwapInterval(1);
@ -378,9 +379,11 @@ int main() {
SkinnedMesh skinned_mesh; SkinnedMesh skinned_mesh;
skinned_mesh_resource.createInstance(skinned_mesh); skinned_mesh_resource.createInstance(skinned_mesh);
skinned_mesh.SetCurrentAnimation(0); skinned_mesh.SetCurrentAnimation(0);
AnimGraph anim_graph;
AnimGraphContext anim_graph_context;
state.time.factor = 1.0f; state.time.factor = 1.0f;
Camera_Init(&state.camera); Camera_Init(&state.camera);
@ -611,6 +614,13 @@ int main() {
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::Button("Update Runtime Graph")) {
anim_graph.dealloc();
AnimGraphEditorGetRuntimeGraph(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
anim_graph.init(anim_graph_context);
}
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
@ -654,8 +664,8 @@ int main() {
} }
if (gApplicationConfig.animation_player_widget.visible) { if (gApplicationConfig.animation_player_widget.visible) {
ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.skinned_mesh_widget.position[1]), ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.animation_player_widget.position[1]), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.skinned_mesh_widget.size[1]), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.animation_player_widget.size[1]), ImGuiCond_FirstUseEver);
ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible); ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible);
@ -667,16 +677,28 @@ int main() {
gApplicationConfig.animation_player_widget.size[0] = animation_player_widget_size.x; gApplicationConfig.animation_player_widget.size[0] = animation_player_widget_size.x;
gApplicationConfig.animation_player_widget.size[1] = animation_player_widget_size.y; gApplicationConfig.animation_player_widget.size[1] = animation_player_widget_size.y;
ImGui::Text("Animation"); if (anim_graph.m_nodes.size() > 0) {
ImGui::Checkbox("Use Graph", &state.time.use_graph);
const char* items[255] = {0}; } else {
static int selected = -1; state.time.use_graph = false;
for (int i = 0; i < skinned_mesh.m_animations.size(); i++) {
items[i] = skinned_mesh.m_animation_names[i].c_str();
} }
if (ImGui::Combo("Animation", &selected, items, skinned_mesh.m_animations.size())) { if (!state.time.use_graph) {
state.ozz.animation = skinned_mesh.m_animations[selected]; ImGui::Text("Animation");
const char* items[255] = {0};
static int selected = -1;
for (int i = 0; i < skinned_mesh.m_animations.size(); i++) {
items[i] = skinned_mesh.m_animation_names[i].c_str();
}
if (ImGui::Combo(
"Animation",
&selected,
items,
skinned_mesh.m_animations.size())) {
state.ozz.animation = skinned_mesh.m_animations[selected];
}
} }
if (state.time.paused) { if (state.time.paused) {

View File

@ -190,8 +190,8 @@ TEST_CASE_METHOD(
graph_context.m_animation_map["trans_y"] = animation_translate_y.get(); graph_context.m_animation_map["trans_y"] = animation_translate_y.get();
// Instantiate graph // Instantiate graph
AnimGraph graph = graph_resource.createInstance(); AnimGraph graph;
graph_context.m_graph = &graph; graph_resource.createInstance(graph);
graph.init(graph_context); graph.init(graph_context);
// Get runtime graph inputs and outputs // Get runtime graph inputs and outputs

View File

@ -74,9 +74,9 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
AnimGraphResource graph_resource_loaded; AnimGraphResource graph_resource_loaded;
graph_resource_loaded.loadFromFile("WalkGraph.animgraph.json"); graph_resource_loaded.loadFromFile("WalkGraph.animgraph.json");
AnimGraph graph = graph_resource_loaded.createInstance(); AnimGraph graph;
graph_resource_loaded.createInstance(graph);
AnimGraphContext graph_context; AnimGraphContext graph_context;
graph_context.m_graph = &graph;
ozz::animation::Skeleton skeleton; ozz::animation::Skeleton skeleton;
REQUIRE(load_skeleton(skeleton, "data/skeleton.ozz")); REQUIRE(load_skeleton(skeleton, "data/skeleton.ozz"));
@ -283,7 +283,8 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
!= nullptr); != nullptr);
WHEN("Instantiating an AnimGraph") { WHEN("Instantiating an AnimGraph") {
AnimGraph anim_graph = graph_resource_loaded.createInstance(); AnimGraph anim_graph;
graph_resource_loaded.createInstance(anim_graph);
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr); REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
REQUIRE( REQUIRE(
@ -418,7 +419,8 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
graph_resource_loaded.m_nodes[1]; graph_resource_loaded.m_nodes[1];
WHEN("Instantiating an AnimGraph") { WHEN("Instantiating an AnimGraph") {
AnimGraph anim_graph = graph_resource_loaded.createInstance(); AnimGraph anim_graph;
graph_resource_loaded.createInstance(anim_graph);
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr); REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
REQUIRE( REQUIRE(
@ -495,12 +497,12 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
} }
TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") { TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
AnimGraphResource graph_resource; AnimGraphResource sGraphGresource;
graph_resource.clear(); sGraphGresource.clear();
graph_resource.m_name = "TestGraphInputOutputConnectivity"; sGraphGresource.m_name = "TestGraphInputOutputConnectivity";
AnimNodeResource& graph_output_node = graph_resource.m_nodes[0]; AnimNodeResource& graph_output_node = sGraphGresource.m_nodes[0];
graph_output_node.m_socket_accessor->RegisterInput<float>( graph_output_node.m_socket_accessor->RegisterInput<float>(
"GraphFloatOutput", "GraphFloatOutput",
nullptr); nullptr);
@ -508,7 +510,7 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
"GraphAnimOutput", "GraphAnimOutput",
nullptr); nullptr);
AnimNodeResource& graph_input_node = graph_resource.m_nodes[1]; AnimNodeResource& graph_input_node = sGraphGresource.m_nodes[1];
graph_input_node.m_socket_accessor->RegisterOutput<float>( graph_input_node.m_socket_accessor->RegisterOutput<float>(
"GraphFloatInput", "GraphFloatInput",
nullptr); nullptr);
@ -523,13 +525,13 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
nullptr); nullptr);
WHEN("Connecting float input with float output") { WHEN("Connecting float input with float output") {
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphFloatInput", "GraphFloatInput",
graph_resource.getGraphOutputNode(), sGraphGresource.getGraphOutputNode(),
"GraphFloatOutput")); "GraphFloatOutput"));
AnimGraph anim_graph = graph_resource.createInstance(); AnimGraph anim_graph = sGraphGresource.createInstance();
THEN("Writing to the input pointer changes the value of the output.") { THEN("Writing to the input pointer changes the value of the output.") {
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput"); float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
@ -545,18 +547,18 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
WHEN("Connecting adding a Blend2 node") { WHEN("Connecting adding a Blend2 node") {
size_t blend2_node_index = size_t blend2_node_index =
graph_resource.addNode(AnimNodeResourceFactory("Blend2")); sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
AnimNodeResource& blend2_node_resource = AnimNodeResource& blend2_node_resource =
graph_resource.m_nodes[blend2_node_index]; sGraphGresource.m_nodes[blend2_node_index];
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphFloatInput", "GraphFloatInput",
blend2_node_resource, blend2_node_resource,
"Weight")); "Weight"));
THEN("Connected float input points to the blend weight.") { THEN("Connected float input points to the blend weight.") {
AnimGraph anim_graph = graph_resource.createInstance(); AnimGraph anim_graph = sGraphGresource.createInstance();
Blend2Node* blend2_node = Blend2Node* blend2_node =
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]); dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
@ -571,28 +573,28 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
WHEN( WHEN(
"Connecting AnimData inputs to blend2 node and blend2 output to graph " "Connecting AnimData inputs to blend2 node and blend2 output to graph "
"output.") { "output.") {
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphAnimInput0", "GraphAnimInput0",
blend2_node_resource, blend2_node_resource,
"Input0")); "Input0"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphAnimInput1", "GraphAnimInput1",
blend2_node_resource, blend2_node_resource,
"Input1")); "Input1"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
blend2_node_resource, blend2_node_resource,
"Output", "Output",
graph_resource.getGraphOutputNode(), sGraphGresource.getGraphOutputNode(),
"GraphAnimOutput")); "GraphAnimOutput"));
THEN( THEN(
"AnimData from output gets blended and result is written to " "AnimData from output gets blended and result is written to "
"Output.") { "Output.") {
AnimGraph anim_graph = graph_resource.createInstance(); AnimGraph anim_graph = sGraphGresource.createInstance();
Blend2Node* blend2_node = Blend2Node* blend2_node =
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]); dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
@ -623,57 +625,57 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
WHEN("Adding AnimSampler Nodes") { WHEN("Adding AnimSampler Nodes") {
size_t blend2_node_index = size_t blend2_node_index =
graph_resource.addNode(AnimNodeResourceFactory("Blend2")); sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
size_t sampler_node_index = size_t sampler_node_index =
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler")); sGraphGresource.addNode(AnimNodeResourceFactory("AnimSampler"));
size_t speed_scale_node_index = size_t speed_scale_node_index =
graph_resource.addNode(AnimNodeResourceFactory("SpeedScale")); sGraphGresource.addNode(AnimNodeResourceFactory("SpeedScale"));
AnimNodeResource& blend2_node_resource = AnimNodeResource& blend2_node_resource =
graph_resource.m_nodes[blend2_node_index]; sGraphGresource.m_nodes[blend2_node_index];
AnimNodeResource& sampler_node_resource = AnimNodeResource& sampler_node_resource =
graph_resource.m_nodes[sampler_node_index]; sGraphGresource.m_nodes[sampler_node_index];
AnimNodeResource& speed_scale_node_resource = AnimNodeResource& speed_scale_node_resource =
graph_resource.m_nodes[speed_scale_node_index]; sGraphGresource.m_nodes[speed_scale_node_index];
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphFloatInput", "GraphFloatInput",
blend2_node_resource, blend2_node_resource,
"Weight")); "Weight"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"SpeedScaleInput", "SpeedScaleInput",
speed_scale_node_resource, speed_scale_node_resource,
"SpeedScale")); "SpeedScale"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
graph_resource.getGraphInputNode(), sGraphGresource.getGraphInputNode(),
"GraphAnimInput0", "GraphAnimInput0",
blend2_node_resource, blend2_node_resource,
"Input0")); "Input0"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
sampler_node_resource, sampler_node_resource,
"Output", "Output",
speed_scale_node_resource, speed_scale_node_resource,
"Input")); "Input"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
speed_scale_node_resource, speed_scale_node_resource,
"Output", "Output",
blend2_node_resource, blend2_node_resource,
"Input1")); "Input1"));
REQUIRE(graph_resource.connectSockets( REQUIRE(sGraphGresource.connectSockets(
blend2_node_resource, blend2_node_resource,
"Output", "Output",
graph_resource.getGraphOutputNode(), sGraphGresource.getGraphOutputNode(),
"GraphAnimOutput")); "GraphAnimOutput"));
THEN("Data flow and node ordering must be correct.") { THEN("Data flow and node ordering must be correct.") {
AnimGraph anim_graph = graph_resource.createInstance(); AnimGraph anim_graph = sGraphGresource.createInstance();
Blend2Node* blend2_node = Blend2Node* blend2_node =
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]); dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>( SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
@ -727,7 +729,7 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
} }
WHEN("Instantiating graph") { WHEN("Instantiating graph") {
AnimGraph anim_graph = graph_resource.createInstance(); AnimGraph anim_graph = sGraphGresource.createInstance();
float* blend_weight_input = float* blend_weight_input =
reinterpret_cast<float*>(anim_graph.getInput("GraphFloatInput")); reinterpret_cast<float*>(anim_graph.getInput("GraphFloatInput"));