diff --git a/CMakeLists.txt b/CMakeLists.txt index ef46013..a959967 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,20 +48,18 @@ add_library(AnimTestbedCode OBJECT src/SkinnedMesh.h src/SyncTrack.cc src/SyncTrack.h - src/AnimNode.cc - src/AnimNodes/AnimSamplerNode.cc - src/AnimNodes/SpeedScaleNode.cc - src/AnimNodes/BlendSpace1D.cc - src/AnimNodes/BlendNode.cc - src/AnimNodes/LockTranslationNode.cc - src/AnimationController.cc src/ozzutils.cc 3rdparty/imgui/imgui.cpp 3rdparty/imgui/imgui_draw.cpp 3rdparty/imgui/imgui_widgets.cpp + 3rdparty/imgui/misc/cpp/imgui_stdlib.cpp 3rdparty/imnodes/imnodes.cpp - src/AnimGraphResource.cc - src/AnimGraphResource.h src/AnimGraphEditor.cc src/AnimGraphEditor.h) + src/AnimGraph/AnimGraphResource.cc + src/AnimGraph/AnimGraphResource.h + src/AnimGraph/AnimGraphEditor.cc + src/AnimGraph/AnimGraphEditor.h + src/AnimGraph/AnimGraph.cc + src/AnimGraph/AnimGraph.h src/AnimGraph/AnimGraphNodes.cc src/AnimGraph/AnimGraphNodes.h src/AnimGraph/AnimGraphData.cc src/AnimGraph/AnimGraphData.h) target_include_directories( AnimTestbedCode @@ -96,7 +94,6 @@ add_executable(runtests) target_sources(runtests PRIVATE tests/AnimGraphResourceTests.cc - tests/AnimSampleNodeTests.cc tests/SyncTrackTests.cc tests/main.cc ) diff --git a/src/AnimGraph/AnimGraph.cc b/src/AnimGraph/AnimGraph.cc new file mode 100644 index 0000000..f2a8371 --- /dev/null +++ b/src/AnimGraph/AnimGraph.cc @@ -0,0 +1,147 @@ +// +// Created by martin on 25.03.22. +// + +#include "AnimGraph.h" + +void AnimGraph::updateOrderedNodes() { + std::vector node_index_stack; + node_index_stack.push_back(0); + + m_eval_ordered_nodes.clear(); + + while (node_index_stack.size() > 0) { + std::vector& node_inputs = + m_node_inputs[node_index_stack.back()]; + node_index_stack.pop_back(); + + for (size_t i = 0, n = node_inputs.size(); i < n; i++) { + AnimNode* input_node = node_inputs[i].m_node; + if (input_node == nullptr) { + continue; + } + + int input_node_index = input_node->m_index; + bool is_node_processed = false; + for (size_t j = 0, m = m_eval_ordered_nodes.size(); j < m; j++) { + if (m_eval_ordered_nodes[j] == input_node) { + is_node_processed = true; + break; + } + } + + if (is_node_processed) { + continue; + } + + m_eval_ordered_nodes.push_back(input_node); + node_index_stack.push_back(input_node_index); + } + } +} + +void AnimGraph::markActiveNodes() { + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; + } + + const std::vector graph_output_inputs = m_node_inputs[0]; + for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { + const NodeInput& graph_input = graph_output_inputs[i]; + AnimNode* node = graph_input.m_node; + if (node != nullptr) { + node->m_state = AnimNodeEvalState::Activated; + } + } + + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (checkIsNodeActive(node)) { + int node_index = node->m_index; + node->MarkActiveInputs(m_node_inputs[node_index]); + + // Non-animation data inputs are always active. + for (size_t j = 0, nj = m_node_inputs[node_index].size(); j < nj; j++) { + const NodeInput& input = m_node_inputs[node_index][j]; + if (input.m_node != nullptr && input.m_type != SocketType::SocketTypeAnimation) { + input.m_node->m_state = AnimNodeEvalState::Activated; + } + } + } + } +} + +void AnimGraph::evalSyncTracks() { + for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { + AnimNode* node = m_eval_ordered_nodes[i]; + int node_index = node->m_index; + if (node->m_state == AnimNodeEvalState::Deactivated) { + continue; + } + + node->CalcSyncTrack(m_node_inputs[node_index]); + } +} + +void AnimGraph::updateTime(float dt) { + const std::vector graph_output_inputs = m_node_inputs[0]; + for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (node != nullptr) { + node->UpdateTime(node->m_time_now, node->m_time_now + dt); + } + } + + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (node->m_state != AnimNodeEvalState::TimeUpdated) { + continue; + } + + int node_index = node->m_index; + const std::vector node_inputs = + m_node_inputs[node_index]; + float node_time_now = node->m_time_now; + float node_time_last = node->m_time_last; + + for (size_t i = 0, n = node_inputs.size(); i < n; i++) { + AnimNode* input_node = node_inputs[i].m_node; + + // Only propagate time updates via animation sockets. + if (input_node != nullptr + && node_inputs[i].m_type == SocketType::SocketTypeAnimation + && input_node->m_state == AnimNodeEvalState::Activated) { + input_node->UpdateTime(node_time_last, node_time_now); + } + } + } +} + +void AnimGraph::evaluate() { + for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { + AnimNode* node = m_eval_ordered_nodes[i]; + if (node->m_state == AnimNodeEvalState::Deactivated) { + continue; + } + + node->Evaluate(); + } +} + +void* AnimGraph::getOutput(const std::string& name) const { + Socket* socket = m_socket_accessor->FindInputSocket(name); + if (socket == nullptr) { + return nullptr; + } + + return socket->m_value.ptr; +} + +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/AnimGraph/AnimGraph.h b/src/AnimGraph/AnimGraph.h new file mode 100644 index 0000000..184d615 --- /dev/null +++ b/src/AnimGraph/AnimGraph.h @@ -0,0 +1,291 @@ +// +// Created by martin on 25.03.22. +// + +#ifndef ANIMTESTBED_ANIMGRAPH_H +#define ANIMTESTBED_ANIMGRAPH_H + +#include "AnimGraphResource.h" + +// +// AnimGraph (Runtime) +// +struct AnimGraph { + AnimData m_local_transforms; + + std::vector m_nodes; + std::vector m_eval_ordered_nodes; + std::vector > m_node_inputs; + NodeSocketAccessorBase* m_socket_accessor; + char* m_input_buffer = nullptr; + char* m_output_buffer = nullptr; + + std::vector& getGraphOutputs() { return m_socket_accessor->m_inputs; } + std::vector& getGraphInputs() { return m_socket_accessor->m_outputs; } + + ~AnimGraph() { + delete[] m_input_buffer; + delete[] m_output_buffer; + + for (int i = 0; i < m_nodes.size(); i++) { + delete m_nodes[i]; + } + + delete m_socket_accessor; + } + + void updateOrderedNodes(); + void markActiveNodes(); + bool checkIsNodeActive(AnimNode* node) { + return node->m_state != AnimNodeEvalState::Deactivated; + } + void evalSyncTracks(); + void updateTime(float dt); + void evaluate(); + void reset() { + 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_last = 0.f; + m_nodes[i]->m_state = AnimNodeEvalState::Undefined; + } + } + + void* getOutput(const std::string& name) const; + void* getInput(const std::string& name) const; + + int getNodeEvalOrderIndex(const AnimNode* node) { + for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { + if (m_eval_ordered_nodes[i] == node) { + return i; + } + } + + return -1; + } + AnimNode* getAnimNodeForInput( + size_t node_index, + const std::string& input_name) { + assert(node_index < m_nodes.size()); + assert(node_index < m_node_inputs.size()); + + std::vector& node_inputs = m_node_inputs[node_index]; + for (size_t i = 0, n = node_inputs.size(); i < n; i++) { + if (node_inputs[i].m_input_name == input_name) { + return node_inputs[i].m_node; + } + } + + return nullptr; + } + + AnimNode* getAnimNode(const char* name) { + for (size_t i = 0; i < m_nodes.size(); i++) { + if (m_nodes[i]->m_name == name) { + return m_nodes[i]; + } + } + + return nullptr; + } + + static AnimGraph createFromResource(const AnimGraphResource& resource) { + AnimGraph result; + + // create nodes + 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; + node->m_index = i; + result.m_nodes.push_back(node); + + assert(node_resource.m_socket_accessor != nullptr); + result.m_node_inputs.push_back(std::vector()); + std::vector& node_inputs = result.m_node_inputs.back(); + + for (int j = 0, n = node_resource.m_socket_accessor->m_inputs.size(); + j < n; + j++) { + const Socket& input_socket = + node_resource.m_socket_accessor->m_inputs[j]; + + NodeInput input; + input.m_node = nullptr; + input.m_type = input_socket.m_type; + input.m_input_name = input_socket.m_name; + + node_inputs.push_back(input); + } + } + + // Prepare graph inputs + result.m_socket_accessor = + AnimNodeAccessorFactory("BlendTree", result.m_nodes[0]); + result.m_socket_accessor->m_outputs = + resource.m_nodes[1].m_socket_accessor->m_outputs; + result.m_socket_accessor->m_inputs = + resource.m_nodes[0].m_socket_accessor->m_inputs; + + // inputs + int input_block_size = 0; + std::vector& graph_inputs = result.getGraphInputs(); + for (int i = 0; i < graph_inputs.size(); i++) { + input_block_size += sizeof(void*); + } + result.m_input_buffer = new char[input_block_size]; + memset(result.m_input_buffer, 0, input_block_size); + + int input_block_offset = 0; + for (int i = 0; i < graph_inputs.size(); i++) { + if (graph_inputs[i].m_type == SocketType::SocketTypeAnimation) { + } + graph_inputs[i].m_value.ptr = + (void*)&result.m_input_buffer[input_block_offset]; + input_block_offset += sizeof(void*); + } + + // outputs + int output_block_size = 0; + std::vector& graph_outputs = result.getGraphOutputs(); + for (int i = 0; i < graph_outputs.size(); i++) { + output_block_size += graph_outputs[i].m_type_size; + } + result.m_output_buffer = new char[output_block_size]; + memset(result.m_output_buffer, 0, output_block_size); + + int output_block_offset = 0; + for (int i = 0; i < graph_outputs.size(); i++) { + if (graph_outputs[i].m_type == SocketType::SocketTypeAnimation) { + } + graph_outputs[i].m_value.ptr = + (void*)&result.m_output_buffer[output_block_offset]; + output_block_offset += graph_outputs[i].m_type_size; + } + + // connect the nodes + 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; + size_t source_socket_index = -1; + size_t target_socket_index = -1; + + if (connection.m_source_node != nullptr) { + size_t node_index = resource.getNodeIndex(*connection.m_source_node); + if (node_index == -1) { + std::cerr << "Could not find source node index." << std::endl; + continue; + } + source_node = result.m_nodes[node_index]; + source_node_name = source_node->m_name; + source_node_type = source_node->m_node_type_name; + if (node_index == 1) { + source_node_accessor = result.m_socket_accessor; + } else { + source_node_accessor = + AnimNodeAccessorFactory(source_node_type, source_node); + } + } + + if (connection.m_target_node != nullptr) { + size_t node_index = resource.getNodeIndex(*connection.m_target_node); + if (node_index == -1) { + std::cerr << "Could not find source node index." << std::endl; + continue; + } + target_node = result.m_nodes[node_index]; + target_node_name = target_node->m_name; + target_node_type = target_node->m_node_type_name; + if (node_index == 0) { + target_node_accessor = result.m_socket_accessor; + } else { + target_node_accessor = + AnimNodeAccessorFactory(target_node_type, target_node); + } + } + + assert(source_node != nullptr); + assert(target_node != nullptr); + + // + // Map resource node sockets to graph instance node sockets + // + if (connection.m_source_socket == nullptr) { + std::cerr << "Invalid source socket for connection " << i << "." + << std::endl; + continue; + } + + if (connection.m_target_socket == nullptr) { + std::cerr << "Invalid source socket for connection " << i << "." + << std::endl; + continue; + } + + source_socket_index = source_node_accessor->GetOutputIndex( + connection.m_source_socket->m_name); + if (source_socket_index == -1) { + std::cerr << "Invalid source socket " + << connection.m_source_socket->m_name << " for node " + << connection.m_source_node->m_name << "." << std::endl; + continue; + } + const Socket* source_socket = + &source_node_accessor->m_outputs[source_socket_index]; + + target_socket_index = target_node_accessor->GetInputIndex( + connection.m_target_socket->m_name); + if (target_socket_index == -1) { + std::cerr << "Invalid target socket " + << connection.m_target_socket->m_name << " for node " + << connection.m_target_node->m_name << "." << std::endl; + continue; + } + const Socket* target_socket = + &target_node_accessor->m_inputs[target_socket_index]; + + if (source_socket->m_type != target_socket->m_type) { + std::cerr << "Cannot connect sockets: invalid types!" << std::endl; + } + + // + // Wire up outputs to inputs. + // + (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr; + + size_t target_node_index = target_node->m_index; + + std::vector& node_inputs = + result.m_node_inputs[target_node_index]; + for (int j = 0, n = node_inputs.size(); j < n; j++) { + if (node_inputs[j].m_input_name == target_socket->m_name) { + node_inputs[j].m_node = source_node; + } + } + + if (target_node_accessor != result.m_socket_accessor) { + delete target_node_accessor; + } + + if (source_node_accessor != result.m_socket_accessor) { + delete source_node_accessor; + } + } + + result.updateOrderedNodes(); + result.reset(); + + return result; + } +}; + +#endif //ANIMTESTBED_ANIMGRAPH_H diff --git a/src/AnimGraph/AnimGraphData.cc b/src/AnimGraph/AnimGraphData.cc new file mode 100644 index 0000000..92a5626 --- /dev/null +++ b/src/AnimGraph/AnimGraphData.cc @@ -0,0 +1,5 @@ +// +// Created by martin on 25.03.22. +// + +#include "AnimGraphData.h" diff --git a/src/AnimGraph/AnimGraphData.h b/src/AnimGraph/AnimGraphData.h new file mode 100644 index 0000000..99f888d --- /dev/null +++ b/src/AnimGraph/AnimGraphData.h @@ -0,0 +1,244 @@ +// +// Created by martin on 25.03.22. +// + +#ifndef ANIMTESTBED_ANIMGRAPHDATA_H +#define ANIMTESTBED_ANIMGRAPHDATA_H + +#include +#include +#include + +#include "SyncTrack.h" + +// +// Data types +// + +struct AnimData { + float m_bone_transforms[16]; +}; + +typedef float Vec3[3]; +typedef float Quat[4]; + +enum class SocketType { + SocketTypeUndefined = 0, + SocketTypeBool, + SocketTypeAnimation, + SocketTypeFloat, + SocketTypeVec3, + SocketTypeQuat, + SocketTypeString, + SocketTypeLast +}; + +static const char* SocketTypeNames[] = + {"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"}; + +enum SocketFlags { SocketFlagAffectsTime = 1 }; + +struct Socket { + std::string m_name; + SocketType m_type = SocketType::SocketTypeUndefined; + union SocketValue { + void* ptr; + void** ptr_ptr; + }; + SocketValue m_value = {nullptr}; + int m_flags = 0; + size_t m_type_size = 0; +}; + +struct NodeSocketAccessorBase { + std::vector m_properties; + std::vector m_inputs; + std::vector m_outputs; + + 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++) { + if (sockets[i].m_name == name) { + result = &sockets[i]; + break; + } + } + + return result; + } + + const Socket* FindSocket( + const std::vector& sockets, + const std::string& name) const { + const 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& sockets, + const std::string& name) { + const Socket* socket = FindSocket(sockets, name); + if (socket == nullptr) { + return SocketType::SocketTypeUndefined; + } + return socket->m_type; + } + + size_t GetSocketIndex( + const std::vector& sockets, + const std::string& name) const { + for (size_t i = 0, n = sockets.size(); i < n; i++) { + if (sockets[i].m_name == name) { + return i; + } + } + + 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, + int flags = 0) { + 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; + socket->m_type_size = sizeof(T); + socket->m_flags = flags; + + if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeFloat; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeBool; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeVec3; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeQuat; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeAnimation; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeString; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeFloat; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeBool; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeVec3; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeQuat; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeAnimation; + } else if constexpr (std::is_same::value) { + socket->m_type = SocketType::SocketTypeString; + } else { + std::cerr << "Cannot register socket, invalid type." << std::endl; + return false; + } + + socket->m_value.ptr = value_ptr; + return true; + } + + template + bool RegisterProperty(const std::string& name, T* value) { + return RegisterSocket(m_properties, name, value); + } + template + void SetProperty(const std::string& name, const T& value) { + SetSocketValue(m_properties, name, value); + } + template + 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, int flags = 0) { + return RegisterSocket(m_inputs, name, value, flags); + } + template + T* GetInput(const std::string& name, T* value) { + return GetSocketValue(m_inputs, name, value); + } + Socket* FindInputSocket(const std::string& name) { + return FindSocket(m_inputs, name); + } + SocketType GetInputType(const std::string& name) { + return GetSocketType(m_inputs, name); + } + size_t GetInputIndex(const std::string& name) { + return GetSocketIndex(m_inputs, name); + } + + template + 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); + } + Socket* FindOutputSocket(const std::string& name) { + return FindSocket(m_outputs, name); + } + size_t GetOutputIndex(const std::string& name) { + return GetSocketIndex(m_outputs, name); + } +}; + +template +struct NodeSocketAccessor : public NodeSocketAccessorBase { + virtual ~NodeSocketAccessor() {} +}; + +#endif //ANIMTESTBED_ANIMGRAPHDATA_H diff --git a/src/AnimGraphEditor.cc b/src/AnimGraph/AnimGraphEditor.cc similarity index 72% rename from src/AnimGraphEditor.cc rename to src/AnimGraph/AnimGraphEditor.cc index 1e61a66..e9f6b7f 100644 --- a/src/AnimGraphEditor.cc +++ b/src/AnimGraph/AnimGraphEditor.cc @@ -5,9 +5,9 @@ #include "AnimGraphEditor.h" #include "AnimGraphResource.h" +#include "imgui.h" #include "imnodes.h" - -using namespace AniGraph; +#include "misc/cpp/imgui_stdlib.h" ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { switch (socket_type) { @@ -28,7 +28,39 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { return ImNodesPinShape_Quad; } -void AnimGraphEditorRenderSidebar(AnimNodeResource& node_resource) { +void NodeSocketEditor(Socket& socket) { + int mode_current = static_cast(socket.m_type); + ImGui::InputText("Name", &socket.m_name); + if (ImGui::Combo( + "Type", + &mode_current, + SocketTypeNames, + sizeof(SocketTypeNames) / sizeof(char*))) { + socket.m_type = static_cast(mode_current); + } +} + +void RemoveConnectionsForSocket( + AnimGraphResource& graph_resource, + AnimNodeResource& node_resource, + Socket& socket) { + std::vector::iterator iter = + graph_resource.m_connections.begin(); + + while (iter != graph_resource.m_connections.end()) { + AnimGraphConnection& connection = *iter; + if (connection.m_source_node == &node_resource + && connection.m_source_socket == &socket) { + iter = graph_resource.m_connections.erase(iter); + } else { + iter++; + } + } +} + +void AnimGraphEditorRenderSidebar( + AnimGraphResource& graph_resource, + AnimNodeResource& node_resource) { ImGui::Text("[%s]", node_resource.m_type_name.c_str()); char node_name_buffer[256]; @@ -72,6 +104,48 @@ void AnimGraphEditorRenderSidebar(AnimNodeResource& node_resource) { } } } + + if (&node_resource == &graph_resource.getGraphOutputNode()) { + ImGui::Text("Outputs"); + + // Graph outputs are the inputs of the output node! + std::vector& outputs = node_resource.m_socket_accessor->m_inputs; + + std::vector::iterator iter = outputs.begin(); + while (iter != outputs.end()) { + Socket& output = *iter; + ImGui::PushID(&output); + NodeSocketEditor(output); + if (ImGui::Button("X")) { + RemoveConnectionsForSocket(graph_resource, node_resource, output); + iter = outputs.erase(iter); + } else { + iter++; + } + ImGui::PopID(); + } + } + + if (&node_resource == &graph_resource.getGraphInputNode()) { + ImGui::Text("Inputs"); + + // Graph inputs are the outputs of the input node! + std::vector& inputs = node_resource.m_socket_accessor->m_outputs; + + std::vector::iterator iter = inputs.begin(); + while (iter != inputs.end()) { + Socket& input = *iter; + ImGui::PushID(&input); + NodeSocketEditor(input); + if (ImGui::Button("X")) { + RemoveConnectionsForSocket(graph_resource, node_resource, input); + iter = inputs.erase(iter); + } else { + iter++; + } + ImGui::PopID(); + } + } } void AnimGraphEditorUpdate() { @@ -248,12 +322,22 @@ void AnimGraphEditorUpdate() { for (size_t i = 0, n = graph_resource.m_connections.size(); i < n; i++) { const AnimGraphConnection& connection = graph_resource.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); + + int source_node_index = + graph_resource.getNodeIndex(*connection.m_source_node); + int source_socket_index = + connection.m_source_node->m_socket_accessor->GetOutputIndex( + connection.m_source_socket->m_name); + start_attr = + GenerateOutputAttributeId(source_node_index, source_socket_index); + + int target_node_index = + graph_resource.getNodeIndex(*connection.m_target_node); + int target_socket_index = + connection.m_target_node->m_socket_accessor->GetInputIndex( + connection.m_target_socket->m_name); + end_attr = GenerateInputAttributeId(target_node_index, target_socket_index); + ImNodes::Link(i, start_attr, end_attr); } @@ -274,11 +358,12 @@ void AnimGraphEditorUpdate() { SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index); AnimGraphConnection connection; - connection.m_source_node_index = node_start_id; - connection.m_source_socket_index = node_start_output_index; + connection.m_source_node = &graph_resource.m_nodes[node_start_id]; + connection.m_source_socket = &connection.m_source_node->m_socket_accessor->m_outputs[node_start_output_index]; + + connection.m_target_node = &graph_resource.m_nodes[node_end_id]; + connection.m_target_socket = &connection.m_target_node->m_socket_accessor->m_inputs[node_end_input_index]; - connection.m_target_node_index = node_end_id; - connection.m_target_socket_index = node_end_input_index; graph_resource.m_connections.push_back(connection); } @@ -301,7 +386,7 @@ void AnimGraphEditorUpdate() { if (selected_nodes[0] < graph_resource.m_nodes.size()) { AnimNodeResource& selected_node = graph_resource.m_nodes[selected_nodes[0]]; - AnimGraphEditorRenderSidebar(selected_node); + AnimGraphEditorRenderSidebar(graph_resource, selected_node); } } diff --git a/src/AnimGraph/AnimGraphEditor.h b/src/AnimGraph/AnimGraphEditor.h new file mode 100644 index 0000000..ba2cf6d --- /dev/null +++ b/src/AnimGraph/AnimGraphEditor.h @@ -0,0 +1,30 @@ +// +// Created by martin on 11.02.22. +// + +#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H +#define ANIMTESTBED_ANIMGRAPHEDITOR_H + +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; +} + +void AnimGraphEditorUpdate(); + +#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H diff --git a/src/AnimGraph/AnimGraphNodes.cc b/src/AnimGraph/AnimGraphNodes.cc new file mode 100644 index 0000000..455ff51 --- /dev/null +++ b/src/AnimGraph/AnimGraphNodes.cc @@ -0,0 +1,5 @@ +// +// Created by martin on 25.03.22. +// + +#include "AnimGraphNodes.h" diff --git a/src/AnimGraph/AnimGraphNodes.h b/src/AnimGraph/AnimGraphNodes.h new file mode 100644 index 0000000..d21a167 --- /dev/null +++ b/src/AnimGraph/AnimGraphNodes.h @@ -0,0 +1,208 @@ +// +// Created by martin on 25.03.22. +// + +#ifndef ANIMTESTBED_ANIMGRAPHNODES_H +#define ANIMTESTBED_ANIMGRAPHNODES_H + +#include + +#include "AnimGraphData.h" +#include "SyncTrack.h" + +struct AnimNode; + +struct NodeInput { + AnimNode* m_node; + SocketType m_type = SocketType::SocketTypeUndefined; + std::string m_input_name; +}; + +enum class AnimNodeEvalState { + Undefined, + Deactivated, + Activated, + SyncTrackUpdated, + TimeUpdated, + Evaluated +}; + +struct AnimNode { + std::string m_name; + std::string m_node_type_name; + float m_time_now = 0.f; + float m_time_last = 0.f; + size_t m_index = -1; + AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; + SyncTrack m_sync_track; + + virtual ~AnimNode(){}; + + virtual void MarkActiveInputs(const std::vector& inputs) { + for (size_t i = 0, n = inputs.size(); i < n; i++) { + AnimNode* input_node = inputs[i].m_node; + if (input_node != nullptr) { + input_node->m_state = AnimNodeEvalState::Activated; + } + } + } + + virtual void CalcSyncTrack(const std::vector& inputs) { + for (size_t i = 0, n = inputs.size(); i < n; i++) { + AnimNode* input_node = inputs[i].m_node; + if (input_node != nullptr + && inputs[i].m_type == SocketType::SocketTypeAnimation + && input_node->m_state != AnimNodeEvalState::Deactivated) { + m_sync_track = input_node->m_sync_track; + return; + } + } + } + + virtual void UpdateTime(float time_last, float time_now) { + m_time_last = time_last; + m_time_now = time_now; + m_state = AnimNodeEvalState::TimeUpdated; + } + + virtual void Evaluate(){}; +}; + + +// +// BlendTreeNode +// +struct BlendTreeNode : public AnimNode {}; + +template <> +struct NodeSocketAccessor : public NodeSocketAccessorBase { + NodeSocketAccessor(AnimNode* node_) {} +}; + +// +// Blend2Node +// +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; + + virtual void MarkActiveInputs(const std::vector& inputs) override { + for (size_t i = 0, n = inputs.size(); i < n; i++) { + AnimNode* input_node = inputs[i].m_node; + if (input_node == nullptr) { + continue; + } + + if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) { + input_node->m_state = AnimNodeEvalState::Activated; + continue; + } + + if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) { + input_node->m_state = AnimNodeEvalState::Activated; + continue; + } + } + } + + virtual void UpdateTime(float dt, std::vector& inputs) { + if (!m_sync_blend) { + m_time_now = m_time_now + dt; + } + + for (size_t i = 0, n = inputs.size(); i < n; i++) { + AnimNode* input_node = inputs[i].m_node; + if (input_node == nullptr) { + continue; + } + + if (input_node->m_state != AnimNodeEvalState::Deactivated) { + if (!m_sync_blend) { + input_node->m_time_now = m_time_now; + } + input_node->m_state = AnimNodeEvalState::TimeUpdated; + continue; + } + } + } +}; + +template <> +struct NodeSocketAccessor : public NodeSocketAccessorBase { + NodeSocketAccessor(AnimNode* node_) { + Blend2Node* node = dynamic_cast(node_); + RegisterInput("Input0", &node->m_input0); + RegisterInput("Input1", &node->m_input1); + 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; + } + } +}; + +// +// SpeedScaleNode +// +struct SpeedScaleNode : public AnimNode { + AnimData m_input; + AnimData* m_output = nullptr; + float m_speed_scale = 0.f; + + virtual void UpdateTime(float time_last, float time_now) { + m_time_last = time_last; + m_time_now = time_last + (time_now - time_last) * m_speed_scale; + m_state = AnimNodeEvalState::TimeUpdated; + } +}; + +template <> +struct NodeSocketAccessor : public NodeSocketAccessorBase { + NodeSocketAccessor(AnimNode* node_) { + SpeedScaleNode* node = dynamic_cast(node_); + RegisterInput( + "SpeedScale", + &node->m_speed_scale, + SocketFlags::SocketFlagAffectsTime); + RegisterInput("Input", &node->m_input); + + RegisterOutput("Output", &node->m_output); + } +}; + +// +// AnimSamplerNode +// +struct AnimSamplerNode : public AnimNode { + AnimData* m_output = nullptr; + std::string m_filename; +}; + +template <> +struct NodeSocketAccessor : public NodeSocketAccessorBase { + NodeSocketAccessor(AnimNode* node_) { + AnimSamplerNode* node = dynamic_cast(node_); + RegisterOutput("Output", &node->m_output); + + RegisterProperty("Filename", &node->m_filename); + } +}; + +#endif //ANIMTESTBED_ANIMGRAPHNODES_H diff --git a/src/AnimGraphResource.cc b/src/AnimGraph/AnimGraphResource.cc similarity index 73% rename from src/AnimGraphResource.cc rename to src/AnimGraph/AnimGraphResource.cc index 07a6421..c1204ff 100644 --- a/src/AnimGraphResource.cc +++ b/src/AnimGraph/AnimGraphResource.cc @@ -8,8 +8,6 @@ #include "3rdparty/json/json.hpp" -namespace AniGraph { - using json = nlohmann::json; // @@ -342,148 +340,4 @@ bool AnimGraphResource::loadFromFile(const char* filename) { } return true; -} - -void AnimGraph::updateOrderedNodes() { - std::vector node_index_stack; - node_index_stack.push_back(0); - - m_eval_ordered_nodes.clear(); - - while (node_index_stack.size() > 0) { - std::vector& node_inputs = - m_node_inputs[node_index_stack.back()]; - node_index_stack.pop_back(); - - for (size_t i = 0, n = node_inputs.size(); i < n; i++) { - AnimNode* input_node = node_inputs[i].m_node; - if (input_node == nullptr) { - continue; - } - - int input_node_index = input_node->m_index; - bool is_node_processed = false; - for (size_t j = 0, m = m_eval_ordered_nodes.size(); j < m; j++) { - if (m_eval_ordered_nodes[j] == input_node) { - is_node_processed = true; - break; - } - } - - if (is_node_processed) { - continue; - } - - m_eval_ordered_nodes.push_back(input_node); - node_index_stack.push_back(input_node_index); - } - } -} - -void AnimGraph::markActiveNodes() { - for (size_t i = 0, n = m_nodes.size(); i < n; i++) { - m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; - } - - const std::vector graph_output_inputs = m_node_inputs[0]; - for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { - const NodeInput& graph_input = graph_output_inputs[i]; - AnimNode* node = graph_input.m_node; - if (node != nullptr) { - node->m_state = AnimNodeEvalState::Activated; - } - } - - for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { - AnimNode* node = m_eval_ordered_nodes[i]; - if (checkIsNodeActive(node)) { - int node_index = node->m_index; - node->MarkActiveInputs(m_node_inputs[node_index]); - - // Non-animation data inputs are always active. - for (size_t j = 0, nj = m_node_inputs[node_index].size(); j < nj; j++) { - const NodeInput& input = m_node_inputs[node_index][j]; - if (input.m_node != nullptr && input.m_type != SocketType::SocketTypeAnimation) { - input.m_node->m_state = AnimNodeEvalState::Activated; - } - } - } - } -} - -void AnimGraph::evalSyncTracks() { - for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { - AnimNode* node = m_eval_ordered_nodes[i]; - int node_index = node->m_index; - if (node->m_state == AnimNodeEvalState::Deactivated) { - continue; - } - - node->CalcSyncTrack(m_node_inputs[node_index]); - } -} - -void AnimGraph::updateTime(float dt) { - const std::vector graph_output_inputs = m_node_inputs[0]; - for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { - AnimNode* node = m_eval_ordered_nodes[i]; - if (node != nullptr) { - node->UpdateTime(node->m_time_now, node->m_time_now + dt); - } - } - - for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { - AnimNode* node = m_eval_ordered_nodes[i]; - if (node->m_state != AnimNodeEvalState::TimeUpdated) { - continue; - } - - int node_index = node->m_index; - const std::vector node_inputs = - m_node_inputs[node_index]; - float node_time_now = node->m_time_now; - float node_time_last = node->m_time_last; - - for (size_t i = 0, n = node_inputs.size(); i < n; i++) { - AnimNode* input_node = node_inputs[i].m_node; - - // Only propagate time updates via animation sockets. - if (input_node != nullptr - && node_inputs[i].m_type == SocketType::SocketTypeAnimation - && input_node->m_state == AnimNodeEvalState::Activated) { - input_node->UpdateTime(node_time_last, node_time_now); - } - } - } -} - -void AnimGraph::evaluate() { - for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { - AnimNode* node = m_eval_ordered_nodes[i]; - if (node->m_state == AnimNodeEvalState::Deactivated) { - continue; - } - - node->Evaluate(); - } -} - -void* AnimGraph::getOutput(const std::string& name) const { - Socket* socket = m_socket_accessor->FindInputSocket(name); - if (socket == nullptr) { - return nullptr; - } - - return socket->m_value.ptr; -} - -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); -} - -} // namespace AniGraph \ No newline at end of file +} \ No newline at end of file diff --git a/src/AnimGraph/AnimGraphResource.h b/src/AnimGraph/AnimGraphResource.h new file mode 100644 index 0000000..025dd44 --- /dev/null +++ b/src/AnimGraph/AnimGraphResource.h @@ -0,0 +1,179 @@ +// +// Created by martin on 04.02.22. +// + +#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H +#define ANIMTESTBED_ANIMGRAPHRESOURCE_H + +#include +#include +#include +#include +#include +#include + +#include "SyncTrack.h" +#include "AnimGraphData.h" +#include "AnimGraphNodes.h" + +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}; +}; + + + +// +// AnimGraphResource +// +struct AnimGraphConnection { + const AnimNodeResource* m_source_node = nullptr; + const Socket* m_source_socket = nullptr; + const AnimNodeResource* m_target_node = nullptr; + const Socket* m_target_socket = nullptr; +}; + +struct AnimGraphResource { + std::string m_name; + std::vector m_nodes; + std::vector m_connections; + + ~AnimGraphResource() { + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + delete m_nodes[i].m_anim_node; + delete m_nodes[i].m_socket_accessor; + } + } + + AnimGraphResource() { clear(); } + + void clear(); + void clearNodes(); + void initGraphConnectors(); + bool saveToFile(const char* filename) const; + bool loadFromFile(const char* filename); + + AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; } + AnimNodeResource& getGraphInputNode() { return m_nodes[1]; } + + const AnimNodeResource& getGraphOutputNode() const { return m_nodes[0]; } + const AnimNodeResource& getGraphInputNode() const { return m_nodes[1]; } + + size_t getNodeIndex(const AnimNodeResource& node_resource) const { + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + if (&m_nodes[i] == &node_resource) { + return i; + } + } + + return -1; + } + + size_t addNode(AnimNodeResource node_resource) { + m_nodes.push_back(node_resource); + return m_nodes.size() - 1; + } + + bool connectSockets( + const AnimNodeResource& source_node, + const std::string& source_socket_name, + const AnimNodeResource& target_node, + const std::string& target_socket_name) { + size_t source_index = -1; + size_t target_index = -1; + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + if (&source_node == &m_nodes[i]) { + source_index = i; + } + + if (&target_node == &m_nodes[i]) { + target_index = i; + } + + if (source_index < m_nodes.size() && target_index < m_nodes.size()) { + break; + } + } + + if (source_index >= m_nodes.size() || target_index >= m_nodes.size()) { + std::cerr << "Cannot connect nodes: could not find nodes." << std::endl; + return false; + } + + Socket* source_socket = + source_node.m_socket_accessor->FindOutputSocket(source_socket_name); + Socket* target_socket = + target_node.m_socket_accessor->FindInputSocket(target_socket_name); + + if (source_socket == nullptr || target_socket == nullptr) { + std::cerr << "Cannot connect nodes: could not find sockets." << std::endl; + return false; + } + + AnimGraphConnection connection; + connection.m_source_node = &source_node; + connection.m_source_socket = source_socket; + connection.m_target_node = &target_node; + connection.m_target_socket = target_socket; + m_connections.push_back(connection); + + return true; + } +}; + +static inline AnimNode* AnimNodeFactory(const std::string& name) { + AnimNode* result; + if (name == "Blend2") { + result = new Blend2Node; + } else if (name == "SpeedScale") { + result = new SpeedScaleNode; + } else if (name == "AnimSampler") { + result = new AnimSamplerNode; + } else if (name == "BlendTree") { + result = new BlendTreeNode; + } + + if (result != nullptr) { + result->m_node_type_name = name; + return result; + } + + 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(node); + } else if (node_type_name == "SpeedScale") { + return new NodeSocketAccessor(node); + } else if (node_type_name == "AnimSampler") { + return new NodeSocketAccessor(node); + } else if (node_type_name == "BlendTree") { + return new NodeSocketAccessor(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; +} + +#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H diff --git a/src/AnimGraphEditor.h b/src/AnimGraphEditor.h deleted file mode 100644 index a1cf41b..0000000 --- a/src/AnimGraphEditor.h +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by martin on 11.02.22. -// - -#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H -#define ANIMTESTBED_ANIMGRAPHEDITOR_H - -void AnimGraphEditorUpdate(); - -#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H diff --git a/src/AnimGraphResource.h b/src/AnimGraphResource.h deleted file mode 100644 index 5bd4620..0000000 --- a/src/AnimGraphResource.h +++ /dev/null @@ -1,909 +0,0 @@ -// -// Created by martin on 04.02.22. -// - -#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H -#define ANIMTESTBED_ANIMGRAPHRESOURCE_H - -#include -#include -#include -#include -#include -#include - -#include "SyncTrack.h" - -namespace AniGraph { - -// -// Data types -// - -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; -} - -enum class SocketType { - SocketTypeUndefined = 0, - SocketTypeBool, - SocketTypeAnimation, - SocketTypeFloat, - SocketTypeVec3, - SocketTypeQuat, - SocketTypeString, - SocketTypeLast -}; - -static const char* SocketTypeNames[] = - {"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"}; - -enum SocketFlags { SocketFlagAffectsTime = 1 }; - -struct Socket { - std::string m_name; - SocketType m_type = SocketType::SocketTypeUndefined; - union SocketValue { - void* ptr; - void** ptr_ptr; - }; - SocketValue m_value = {nullptr}; - int m_flags = 0; - size_t m_type_size = 0; -}; - -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 NodeInput { - AnimNode* m_node; - SocketType m_type = SocketType::SocketTypeUndefined; - std::string m_input_name; -}; - -enum class AnimNodeEvalState { - Undefined, - Deactivated, - Activated, - SyncTrackUpdated, - TimeUpdated, - Evaluated -}; - -struct AnimNode { - std::string m_name; - std::string m_node_type_name; - float m_time_now = 0.f; - float m_time_last = 0.f; - size_t m_index = -1; - AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; - SyncTrack m_sync_track; - - virtual ~AnimNode(){}; - - virtual void MarkActiveInputs(const std::vector& inputs) { - for (size_t i = 0, n = inputs.size(); i < n; i++) { - AnimNode* input_node = inputs[i].m_node; - if (input_node != nullptr) { - input_node->m_state = AnimNodeEvalState::Activated; - } - } - } - - virtual void CalcSyncTrack(const std::vector& inputs) { - for (size_t i = 0, n = inputs.size(); i < n; i++) { - AnimNode* input_node = inputs[i].m_node; - if (input_node != nullptr - && inputs[i].m_type == SocketType::SocketTypeAnimation - && input_node->m_state != AnimNodeEvalState::Deactivated) { - m_sync_track = input_node->m_sync_track; - return; - } - } - } - - virtual void UpdateTime(float time_last, float time_now) { - m_time_last = time_last; - m_time_now = time_now; - m_state = AnimNodeEvalState::TimeUpdated; - } - - virtual void Evaluate(){}; -}; - -struct NodeSocketAccessorBase { - std::vector m_properties; - std::vector m_inputs; - std::vector m_outputs; - - 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++) { - if (sockets[i].m_name == name) { - result = &sockets[i]; - break; - } - } - - return result; - } - - const Socket* FindSocket( - const std::vector& sockets, - const std::string& name) const { - const 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& sockets, - const std::string& name) { - const Socket* socket = FindSocket(sockets, name); - if (socket == nullptr) { - return SocketType::SocketTypeUndefined; - } - return socket->m_type; - } - - size_t GetSocketIndex( - const std::vector& sockets, - const std::string& name) const { - for (size_t i = 0, n = sockets.size(); i < n; i++) { - if (sockets[i].m_name == name) { - return i; - } - } - - 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, - int flags = 0) { - 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; - socket->m_type_size = sizeof(T); - socket->m_flags = flags; - - if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeFloat; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeBool; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeVec3; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeQuat; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeAnimation; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeString; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeFloat; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeBool; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeVec3; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeQuat; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeAnimation; - } else if constexpr (std::is_same::value) { - socket->m_type = SocketType::SocketTypeString; - } else { - std::cerr << "Cannot register socket, invalid type." << std::endl; - return false; - } - - socket->m_value.ptr = value_ptr; - return true; - } - - template - bool RegisterProperty(const std::string& name, T* value) { - return RegisterSocket(m_properties, name, value); - } - template - void SetProperty(const std::string& name, const T& value) { - SetSocketValue(m_properties, name, value); - } - template - 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, int flags = 0) { - return RegisterSocket(m_inputs, name, value, flags); - } - template - T* GetInput(const std::string& name, T* value) { - return GetSocketValue(m_inputs, name, value); - } - Socket* FindInputSocket(const std::string& name) { - return FindSocket(m_inputs, name); - } - SocketType GetInputType(const std::string& name) { - return GetSocketType(m_inputs, name); - } - size_t GetInputIndex(const std::string& name) { - return GetSocketIndex(m_inputs, name); - } - - template - 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); - } - Socket* FindOutputSocket(const std::string& name) { - return FindSocket(m_outputs, name); - } - size_t GetOutputIndex(const std::string& name) { - return GetSocketIndex(m_outputs, name); - } -}; - -template -struct NodeSocketAccessor : public NodeSocketAccessorBase { - virtual ~NodeSocketAccessor() {} -}; - -struct NodeRegistry { - AnimNode* createNode(const std::string& node_type); - NodeSocketAccessorBase* createNodeAccessor( - const std::string& node_type, - AnimNode* node); -}; - -// -// BlendTreeNode -// -struct BlendTreeNode : public AnimNode {}; - -template <> -struct NodeSocketAccessor : public NodeSocketAccessorBase { - NodeSocketAccessor(AnimNode* node_) {} -}; - -// -// Blend2Node -// -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; - - virtual void MarkActiveInputs(const std::vector& inputs) override { - for (size_t i = 0, n = inputs.size(); i < n; i++) { - AnimNode* input_node = inputs[i].m_node; - if (input_node == nullptr) { - continue; - } - - if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) { - input_node->m_state = AnimNodeEvalState::Activated; - continue; - } - - if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) { - input_node->m_state = AnimNodeEvalState::Activated; - continue; - } - } - } - - virtual void UpdateTime(float dt, std::vector& inputs) { - if (!m_sync_blend) { - m_time_now = m_time_now + dt; - } - - for (size_t i = 0, n = inputs.size(); i < n; i++) { - AnimNode* input_node = inputs[i].m_node; - if (input_node == nullptr) { - continue; - } - - if (input_node->m_state != AnimNodeEvalState::Deactivated) { - if (!m_sync_blend) { - input_node->m_time_now = m_time_now; - } - input_node->m_state = AnimNodeEvalState::TimeUpdated; - continue; - } - } - } -}; - -template <> -struct NodeSocketAccessor : public NodeSocketAccessorBase { - NodeSocketAccessor(AnimNode* node_) { - Blend2Node* node = dynamic_cast(node_); - RegisterInput("Input0", &node->m_input0); - RegisterInput("Input1", &node->m_input1); - 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; - } - } -}; - -// -// SpeedScaleNode -// -struct SpeedScaleNode : public AnimNode { - AnimData m_input; - AnimData* m_output = nullptr; - float m_speed_scale = 0.f; - - virtual void UpdateTime(float time_last, float time_now) { - m_time_last = time_last; - m_time_now = time_last + (time_now - time_last) * m_speed_scale; - m_state = AnimNodeEvalState::TimeUpdated; - } -}; - -template <> -struct NodeSocketAccessor : public NodeSocketAccessorBase { - NodeSocketAccessor(AnimNode* node_) { - SpeedScaleNode* node = dynamic_cast(node_); - RegisterInput( - "SpeedScale", - &node->m_speed_scale, - SocketFlags::SocketFlagAffectsTime); - RegisterInput("Input", &node->m_input); - - RegisterOutput("Output", &node->m_output); - } -}; - -// -// AnimSamplerNode -// -struct AnimSamplerNode : public AnimNode { - AnimData* m_output = nullptr; - std::string m_filename; -}; - -template <> -struct NodeSocketAccessor : public NodeSocketAccessorBase { - NodeSocketAccessor(AnimNode* node_) { - AnimSamplerNode* node = dynamic_cast(node_); - RegisterOutput("Output", &node->m_output); - - RegisterProperty("Filename", &node->m_filename); - } -}; - -// -// AnimGraphResource -// -struct AnimGraphConnection { - const AnimNodeResource* m_source_node = nullptr; - const Socket* m_source_socket = nullptr; - const AnimNodeResource* m_target_node = nullptr; - const Socket* m_target_socket = nullptr; -}; - -struct AnimGraphResource { - std::string m_name; - std::vector m_nodes; - std::vector m_connections; - - ~AnimGraphResource() { - for (size_t i = 0, n = m_nodes.size(); i < n; i++) { - delete m_nodes[i].m_anim_node; - delete m_nodes[i].m_socket_accessor; - } - } - - AnimGraphResource() { clear(); } - - void clear(); - void clearNodes(); - void initGraphConnectors(); - bool saveToFile(const char* filename) const; - bool loadFromFile(const char* filename); - - AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; } - AnimNodeResource& getGraphInputNode() { return m_nodes[1]; } - - const AnimNodeResource& getGraphOutputNode() const { return m_nodes[0]; } - const AnimNodeResource& getGraphInputNode() const { return m_nodes[1]; } - - size_t getNodeIndex(const AnimNodeResource& node_resource) const { - for (size_t i = 0, n = m_nodes.size(); i < n; i++) { - if (&m_nodes[i] == &node_resource) { - return i; - } - } - - return -1; - } - - size_t addNode(AnimNodeResource node_resource) { - m_nodes.push_back(node_resource); - return m_nodes.size() - 1; - } - - bool connectSockets( - const AnimNodeResource& source_node, - const std::string& source_socket_name, - const AnimNodeResource& target_node, - const std::string& target_socket_name) { - size_t source_index = -1; - size_t target_index = -1; - for (size_t i = 0, n = m_nodes.size(); i < n; i++) { - if (&source_node == &m_nodes[i]) { - source_index = i; - } - - if (&target_node == &m_nodes[i]) { - target_index = i; - } - - if (source_index < m_nodes.size() && target_index < m_nodes.size()) { - break; - } - } - - if (source_index >= m_nodes.size() || target_index >= m_nodes.size()) { - std::cerr << "Cannot connect nodes: could not find nodes." << std::endl; - return false; - } - - Socket* source_socket = - source_node.m_socket_accessor->FindOutputSocket(source_socket_name); - Socket* target_socket = - target_node.m_socket_accessor->FindInputSocket(target_socket_name); - - if (source_socket == nullptr || target_socket == nullptr) { - std::cerr << "Cannot connect nodes: could not find sockets." << std::endl; - return false; - } - - AnimGraphConnection connection; - connection.m_source_node = &source_node; - connection.m_source_socket = source_socket; - connection.m_target_node = &target_node; - connection.m_target_socket = target_socket; - m_connections.push_back(connection); - - return true; - } -}; - -static inline AnimNode* AnimNodeFactory(const std::string& name) { - AnimNode* result; - if (name == "Blend2") { - result = new Blend2Node; - } else if (name == "SpeedScale") { - result = new SpeedScaleNode; - } else if (name == "AnimSampler") { - result = new AnimSamplerNode; - } else if (name == "BlendTree") { - result = new BlendTreeNode; - } - - if (result != nullptr) { - result->m_node_type_name = name; - return result; - } - - 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(node); - } else if (node_type_name == "SpeedScale") { - return new NodeSocketAccessor(node); - } else if (node_type_name == "AnimSampler") { - return new NodeSocketAccessor(node); - } else if (node_type_name == "BlendTree") { - return new NodeSocketAccessor(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; -} - -// -// AnimGraph (Runtime) -// -struct AnimGraph { - AnimData m_local_transforms; - - std::vector m_nodes; - std::vector m_eval_ordered_nodes; - std::vector > m_node_inputs; - NodeSocketAccessorBase* m_socket_accessor; - char* m_input_buffer = nullptr; - char* m_output_buffer = nullptr; - - std::vector& getGraphOutputs() { return m_socket_accessor->m_inputs; } - std::vector& getGraphInputs() { return m_socket_accessor->m_outputs; } - - ~AnimGraph() { - delete[] m_input_buffer; - delete[] m_output_buffer; - - for (int i = 0; i < m_nodes.size(); i++) { - delete m_nodes[i]; - } - - delete m_socket_accessor; - } - - void updateOrderedNodes(); - void markActiveNodes(); - bool checkIsNodeActive(AnimNode* node) { - return node->m_state != AnimNodeEvalState::Deactivated; - } - void evalSyncTracks(); - void updateTime(float dt); - void evaluate(); - void reset() { - 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_last = 0.f; - m_nodes[i]->m_state = AnimNodeEvalState::Undefined; - } - } - - void* getOutput(const std::string& name) const; - void* getInput(const std::string& name) const; - - int getNodeEvalOrderIndex(const AnimNode* node) { - for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) { - if (m_eval_ordered_nodes[i] == node) { - return i; - } - } - - return -1; - } - AnimNode* getAnimNodeForInput( - size_t node_index, - const std::string& input_name) { - assert(node_index < m_nodes.size()); - assert(node_index < m_node_inputs.size()); - - std::vector& node_inputs = m_node_inputs[node_index]; - for (size_t i = 0, n = node_inputs.size(); i < n; i++) { - if (node_inputs[i].m_input_name == input_name) { - return node_inputs[i].m_node; - } - } - - return nullptr; - } - - AnimNode* getAnimNode(const char* name) { - for (size_t i = 0; i < m_nodes.size(); i++) { - if (m_nodes[i]->m_name == name) { - return m_nodes[i]; - } - } - - return nullptr; - } - - static AnimGraph createFromResource(const AnimGraphResource& resource) { - AnimGraph result; - - // create nodes - 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; - node->m_index = i; - result.m_nodes.push_back(node); - - assert(node_resource.m_socket_accessor != nullptr); - result.m_node_inputs.push_back(std::vector()); - std::vector& node_inputs = result.m_node_inputs.back(); - - for (int j = 0, n = node_resource.m_socket_accessor->m_inputs.size(); - j < n; - j++) { - const Socket& input_socket = - node_resource.m_socket_accessor->m_inputs[j]; - - NodeInput input; - input.m_node = nullptr; - input.m_type = input_socket.m_type; - input.m_input_name = input_socket.m_name; - - node_inputs.push_back(input); - } - } - - // Prepare graph inputs - result.m_socket_accessor = - AnimNodeAccessorFactory("BlendTree", result.m_nodes[0]); - result.m_socket_accessor->m_outputs = - resource.m_nodes[1].m_socket_accessor->m_outputs; - result.m_socket_accessor->m_inputs = - resource.m_nodes[0].m_socket_accessor->m_inputs; - - // inputs - int input_block_size = 0; - std::vector& graph_inputs = result.getGraphInputs(); - for (int i = 0; i < graph_inputs.size(); i++) { - input_block_size += sizeof(void*); - } - result.m_input_buffer = new char[input_block_size]; - memset(result.m_input_buffer, 0, input_block_size); - - int input_block_offset = 0; - for (int i = 0; i < graph_inputs.size(); i++) { - if (graph_inputs[i].m_type == SocketType::SocketTypeAnimation) { - } - graph_inputs[i].m_value.ptr = - (void*)&result.m_input_buffer[input_block_offset]; - input_block_offset += sizeof(void*); - } - - // outputs - int output_block_size = 0; - std::vector& graph_outputs = result.getGraphOutputs(); - for (int i = 0; i < graph_outputs.size(); i++) { - output_block_size += graph_outputs[i].m_type_size; - } - result.m_output_buffer = new char[output_block_size]; - memset(result.m_output_buffer, 0, output_block_size); - - int output_block_offset = 0; - for (int i = 0; i < graph_outputs.size(); i++) { - if (graph_outputs[i].m_type == SocketType::SocketTypeAnimation) { - } - graph_outputs[i].m_value.ptr = - (void*)&result.m_output_buffer[output_block_offset]; - output_block_offset += graph_outputs[i].m_type_size; - } - - // connect the nodes - 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; - size_t source_socket_index = -1; - size_t target_socket_index = -1; - - if (connection.m_source_node != nullptr) { - size_t node_index = resource.getNodeIndex(*connection.m_source_node); - if (node_index == -1) { - std::cerr << "Could not find source node index." << std::endl; - continue; - } - source_node = result.m_nodes[node_index]; - source_node_name = source_node->m_name; - source_node_type = source_node->m_node_type_name; - if (node_index == 1) { - source_node_accessor = result.m_socket_accessor; - } else { - source_node_accessor = - AnimNodeAccessorFactory(source_node_type, source_node); - } - } - - if (connection.m_target_node != nullptr) { - size_t node_index = resource.getNodeIndex(*connection.m_target_node); - if (node_index == -1) { - std::cerr << "Could not find source node index." << std::endl; - continue; - } - target_node = result.m_nodes[node_index]; - target_node_name = target_node->m_name; - target_node_type = target_node->m_node_type_name; - if (node_index == 0) { - target_node_accessor = result.m_socket_accessor; - } else { - target_node_accessor = - AnimNodeAccessorFactory(target_node_type, target_node); - } - } - - assert(source_node != nullptr); - assert(target_node != nullptr); - - // - // Map resource node sockets to graph instance node sockets - // - if (connection.m_source_socket == nullptr) { - std::cerr << "Invalid source socket for connection " << i << "." - << std::endl; - continue; - } - - if (connection.m_target_socket == nullptr) { - std::cerr << "Invalid source socket for connection " << i << "." - << std::endl; - continue; - } - - source_socket_index = source_node_accessor->GetOutputIndex( - connection.m_source_socket->m_name); - if (source_socket_index == -1) { - std::cerr << "Invalid source socket " - << connection.m_source_socket->m_name << " for node " - << connection.m_source_node->m_name << "." << std::endl; - continue; - } - const Socket* source_socket = - &source_node_accessor->m_outputs[source_socket_index]; - - target_socket_index = target_node_accessor->GetInputIndex( - connection.m_target_socket->m_name); - if (target_socket_index == -1) { - std::cerr << "Invalid target socket " - << connection.m_target_socket->m_name << " for node " - << connection.m_target_node->m_name << "." << std::endl; - continue; - } - const Socket* target_socket = - &target_node_accessor->m_inputs[target_socket_index]; - - if (source_socket->m_type != target_socket->m_type) { - std::cerr << "Cannot connect sockets: invalid types!" << std::endl; - } - - // - // Wire up outputs to inputs. - // - (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr; - - size_t target_node_index = target_node->m_index; - - std::vector& node_inputs = - result.m_node_inputs[target_node_index]; - for (int j = 0, n = node_inputs.size(); j < n; j++) { - if (node_inputs[j].m_input_name == target_socket->m_name) { - node_inputs[j].m_node = source_node; - } - } - - if (target_node_accessor != result.m_socket_accessor) { - delete target_node_accessor; - } - - if (source_node_accessor != result.m_socket_accessor) { - delete source_node_accessor; - } - } - - result.updateOrderedNodes(); - result.reset(); - - return result; - } -}; - -} // namespace AniGraph - -#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H diff --git a/src/AnimNode.cc b/src/AnimNode.cc deleted file mode 100644 index e37d5d6..0000000 --- a/src/AnimNode.cc +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#include "AnimNode.h" diff --git a/src/AnimNode.h b/src/AnimNode.h deleted file mode 100644 index 446fcf2..0000000 --- a/src/AnimNode.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#ifndef ANIMTESTBED_ANIMNODE_H -#define ANIMTESTBED_ANIMNODE_H - -#include - -#include - -#include "AnimationController.h" -#include "SkinnedMesh.h" - -enum class AnimNodeType { Blend, SpeedScale, AnimSampler }; - -struct AnimNode { - AnimNode(AnimationController* animation_controller) - : m_animation_controller(animation_controller), - m_time_current(0.f), - m_is_time_synced(false) {} - virtual ~AnimNode(){}; - - AnimNodeType m_anim_node_type; - std::string m_name; - AnimationController* m_animation_controller; - - // When synced then current time is relative to the node's anim duration.:w - bool m_is_time_synced; - float m_time_current; - SyncTrack m_sync_track; - - virtual void Reset() { m_time_current = 0.f; } - - // Mark current node according to is_synced and propagate flag to animation inputs. - virtual void UpdateIsSynced(bool is_synced) = 0; - - // Evaluate the animation duration of this node. All input nodes must have already evaluated - // their anim durations. - virtual void UpdateSyncTrack() = 0; - - // Evaluate current time and propagate time step to inputs. - virtual void UpdateTime(float dt) = 0; - - // Evaluate the current node and write output to local_matrices. - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) = 0; - - // Returns a list of animation nodes that provide input for this node. - virtual void GetInputNodes(std::vector& input_nodes) const = 0 ; - - virtual void DrawDebugUi(){}; -}; - -#endif //ANIMTESTBED_ANIMNODE_H diff --git a/src/AnimNodes/AnimSamplerNode.cc b/src/AnimNodes/AnimSamplerNode.cc deleted file mode 100644 index 687f070..0000000 --- a/src/AnimNodes/AnimSamplerNode.cc +++ /dev/null @@ -1,74 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#include "AnimSamplerNode.h" - -#include "ozzutils.h" - -#include - -#include "../SkinnedMesh.h" -#include "../ozzutils.h" - -void AnimSamplerNode::SetAnimation(ozz::animation::Animation* animation, const SyncTrack& sync_track) { - m_animation = animation; - - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); - const int num_joints = skinned_mesh->m_skeleton.num_joints(); - m_local_matrices.resize(num_soa_joints); - m_sampling_cache.Resize(num_joints); - - m_sync_track = sync_track; -} - -void AnimSamplerNode::Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) { - ozz::animation::SamplingJob sampling_job; - sampling_job.animation = m_animation; - sampling_job.cache = &m_sampling_cache; - sampling_job.ratio = m_anim_ratio; - sampling_job.output = make_span(*local_matrices); - if (!sampling_job.Run()) { - ozz::log::Err() << "Error sampling animation." << std::endl; - } - - if (root_transform == nullptr) { - return; - } - - ozz::math::Transform root_trans_prev; - calc_bone_translation (m_anim_ratio_prev, 0, m_animation, root_trans_prev); - - ozz::math::Transform root_trans_cur; - calc_bone_translation (m_anim_ratio, 0, m_animation, root_trans_cur); - - root_transform->translation = root_trans_cur.translation - root_trans_prev.translation; -} - -void AnimSamplerNode::DrawDebugUi() { - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - int anim_count = skinned_mesh->m_animation_names.size(); - const char* items[255] = {0}; - int item_current = 0; - for (int i = 0; i < anim_count; i++) { - items[i] = skinned_mesh->m_animation_names[i].c_str(); - if (skinned_mesh->m_animations[i] == m_animation) { - item_current = i; - } - } - - if (ImGui::Combo("Animation", &item_current, items, anim_count)) { - m_animation = skinned_mesh->m_animations[item_current]; - m_sync_track = skinned_mesh->m_animation_sync_track[item_current]; - } - - ImGui::Checkbox("Override", &m_override_ratio); - ImGui::SameLine(); - ImGui::SliderFloat("Ratio", &m_anim_ratio, 0.f, 1.f); - - ImGui::Text("SyncTrack"); - m_sync_track.DrawDebugUi(); -} \ No newline at end of file diff --git a/src/AnimNodes/AnimSamplerNode.h b/src/AnimNodes/AnimSamplerNode.h deleted file mode 100644 index 58b3acd..0000000 --- a/src/AnimNodes/AnimSamplerNode.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#ifndef ANIMTESTBED_ANIMSAMPLERNODE_H -#define ANIMTESTBED_ANIMSAMPLERNODE_H - -#include "../AnimNode.h" - -struct AnimSamplerNode : public AnimNode { - AnimSamplerNode(AnimationController* animation_controller) - : AnimNode(animation_controller), - m_time_prev(0.f), - m_override_ratio(false), - m_anim_ratio(0.f), - m_root_bone_index(0) { - assert(m_time_current < 100.0f); - m_anim_node_type = AnimNodeType::AnimSampler; - }; - virtual ~AnimSamplerNode() {} - - ozz::animation::Animation* m_animation; - - float m_time_prev; - bool m_override_ratio; - float m_anim_ratio; - float m_anim_ratio_prev; - bool m_root_bone_index; - - ozz::vector m_local_matrices; - ozz::vector m_root_output; - ozz::animation::SamplingCache m_sampling_cache; - - void SetAnimation( - ozz::animation::Animation* animation, - const SyncTrack& sync_track); - - virtual void UpdateIsSynced(bool is_synced) override { - m_is_time_synced = is_synced; - } - - virtual void UpdateSyncTrack() override {} - - virtual void UpdateTime(float dt) override { - if (m_override_ratio) { - return; - } - - m_time_prev = m_time_current; - m_time_current += dt; - - if (m_is_time_synced) { - m_anim_ratio = m_sync_track.CalcRatioFromSyncTime(m_time_current); - float prev_sync_time = m_time_current - dt; - if (m_time_current < 0) { - prev_sync_time += m_sync_track.m_num_intervals; - } - m_anim_ratio_prev = m_sync_track.CalcRatioFromSyncTime(prev_sync_time); - } else { - m_anim_ratio = - fmodf((float)m_time_current / m_animation->duration(), 1.0f); - m_anim_ratio_prev = - fmodf((float)m_time_prev / m_animation->duration(), 1.0f); - - if (m_anim_ratio_prev < 0) { - m_anim_ratio_prev += 1.0f; - } - } - - if (m_anim_ratio < 0.f) { - m_anim_ratio += 1.0f; - } - } - - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) override; - - virtual void GetInputNodes( - std::vector& input_nodes) const override{}; - - virtual void DrawDebugUi(); -}; - -#endif //ANIMTESTBED_ANIMSAMPLERNODE_H diff --git a/src/AnimNodes/BlendNode.cc b/src/AnimNodes/BlendNode.cc deleted file mode 100644 index d61c085..0000000 --- a/src/AnimNodes/BlendNode.cc +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#include "BlendNode.h" - -#include -#include - -#include "../SkinnedMesh.h" - -BlendNode::BlendNode(AnimationController* animation_controller) - : AnimNode(animation_controller), - m_input_A(nullptr), - m_input_B(nullptr), - m_weight(0.f), - m_sync_inputs(false) { - m_anim_node_type = AnimNodeType::Blend; - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); - const int num_joints = skinned_mesh->m_skeleton.num_joints(); - m_local_matrices_A.resize(num_soa_joints); - m_local_matrices_B.resize(num_soa_joints); -} - -void BlendNode::Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) { - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - - m_input_A->Evaluate( - &m_local_matrices_A, - root_transform != nullptr ? &m_root_transform_A : nullptr); - m_input_B->Evaluate( - &m_local_matrices_B, - root_transform != nullptr ? &m_root_transform_B : nullptr); - - // perform blend - ozz::animation::BlendingJob::Layer layers[2]; - layers[0].transform = make_span(m_local_matrices_A); - layers[0].weight = (1.0f - m_weight); - - layers[1].transform = make_span(m_local_matrices_B); - layers[1].weight = (m_weight); - - ozz::animation::BlendingJob blend_job; - blend_job.threshold = ozz::animation::BlendingJob().threshold; - blend_job.layers = layers; - blend_job.bind_pose = skinned_mesh->m_skeleton.joint_bind_poses(); - blend_job.output = make_span(*local_matrices); - - if (!blend_job.Run()) { - ozz::log::Err() << "Error blending animations." << std::endl; - } -} - -void BlendNode::DrawDebugUi() { - ImGui::SliderFloat("Weight", &m_weight, 0.f, 1.f); - ImGui::Checkbox("Sync Inputs", &m_sync_inputs); - - ImGui::Text("SyncTrack"); - m_sync_track.DrawDebugUi(); -} diff --git a/src/AnimNodes/BlendNode.h b/src/AnimNodes/BlendNode.h deleted file mode 100644 index 7d187b5..0000000 --- a/src/AnimNodes/BlendNode.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#ifndef ANIMTESTBED_BLENDNODE_H -#define ANIMTESTBED_BLENDNODE_H - -#include "../AnimNode.h" - -struct BlendNode : public AnimNode { - BlendNode(AnimationController* animation_controller); - - virtual ~BlendNode() {} - - AnimNode* m_input_A; - AnimNode* m_input_B; - float m_weight; - bool m_sync_inputs; - - ozz::vector m_local_matrices_A; - ozz::vector m_local_matrices_B; - ozz::math::Transform m_root_transform_A; - ozz::math::Transform m_root_transform_B; - - virtual void Reset() { m_time_current = 0.f; } - - virtual void UpdateIsSynced(bool is_synced) override { - m_is_time_synced = is_synced; - - if (m_sync_inputs) { - m_is_time_synced = true; - } - - m_input_B->UpdateIsSynced(m_is_time_synced); - m_input_A->UpdateIsSynced(m_is_time_synced); - } - - virtual void UpdateSyncTrack() override { - if (m_is_time_synced) { - m_sync_track = SyncTrack::Blend( - m_weight, - m_input_A->m_sync_track, - m_input_B->m_sync_track); - } else { - assert(false); - } - } - - virtual void UpdateTime(float dt) { - if (m_is_time_synced) { - float sync_time_old = m_sync_track.CalcSyncFromAbsTime(m_time_current); - m_time_current = fmodf(m_time_current + dt, m_sync_track.m_duration); - float sync_time_dt = - m_sync_track.CalcSyncFromAbsTime(m_time_current) - sync_time_old; - - m_input_A->m_time_current = sync_time_old; - m_input_B->m_time_current = sync_time_old; - m_input_A->UpdateTime(sync_time_dt); - m_input_B->UpdateTime(sync_time_dt); - } else { - m_time_current += dt; - m_input_A->UpdateTime(dt); - m_input_B->UpdateTime(dt); - } - } - - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) override; - - virtual void GetInputNodes( - std::vector& input_nodes) const override { - input_nodes.push_back(m_input_A); - input_nodes.push_back(m_input_B); - }; - - virtual void DrawDebugUi(); -}; - -#endif //ANIMTESTBED_BLENDNODE_H diff --git a/src/AnimNodes/BlendSpace1D.cc b/src/AnimNodes/BlendSpace1D.cc deleted file mode 100644 index 4da8b77..0000000 --- a/src/AnimNodes/BlendSpace1D.cc +++ /dev/null @@ -1,67 +0,0 @@ -// -// Created by martin on 19.11.21. -// - -#include "BlendSpace1D.h" - -#include -#include - -#include "../SkinnedMesh.h" - -BlendSpace1D::BlendSpace1D(AnimationController* animation_controller) - : AnimNode(animation_controller), - m_input_0(nullptr), - m_weight_0(0.f), - m_input_1(nullptr), - m_weight_1(0.f), - m_weight(0.f), - m_sync_inputs(false) { - m_anim_node_type = AnimNodeType::Blend; - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); - const int num_joints = skinned_mesh->m_skeleton.num_joints(); - m_local_matrices_0.resize(num_soa_joints); - m_local_matrices_1.resize(num_soa_joints); -} - -void BlendSpace1D::Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) { - const SkinnedMesh* skinned_mesh = m_animation_controller->m_skinned_mesh; - - m_input_0->Evaluate( - &m_local_matrices_0, - root_transform != nullptr ? &m_root_transform_0 : nullptr); - m_input_1->Evaluate( - &m_local_matrices_1, - root_transform != nullptr ? &m_root_transform_1 : nullptr); - - // perform blend - ozz::animation::BlendingJob::Layer layers[2]; - layers[0].transform = make_span(m_local_matrices_0); - layers[0].weight = (1.0f - m_normalized_weight); - - layers[1].transform = make_span(m_local_matrices_1); - layers[1].weight = (m_normalized_weight); - - ozz::animation::BlendingJob blend_job; - blend_job.threshold = ozz::animation::BlendingJob().threshold; - blend_job.layers = layers; - blend_job.bind_pose = skinned_mesh->m_skeleton.joint_bind_poses(); - blend_job.output = make_span(*local_matrices); - - if (!blend_job.Run()) { - ozz::log::Err() << "Error blending animations." << std::endl; - } -} - -void BlendSpace1D::DrawDebugUi() { - float min_weight = m_input_weights[0]; - float max_weight = m_input_weights.back(); - ImGui::SliderFloat("Weight", &m_weight, min_weight, max_weight); - ImGui::Checkbox("Sync Inputs", &m_sync_inputs); - - ImGui::Text("SyncTrack"); - m_sync_track.DrawDebugUi(); -} diff --git a/src/AnimNodes/BlendSpace1D.h b/src/AnimNodes/BlendSpace1D.h deleted file mode 100644 index 6cfe4d0..0000000 --- a/src/AnimNodes/BlendSpace1D.h +++ /dev/null @@ -1,110 +0,0 @@ -// -// Created by martin on 19.11.21. -// - -#ifndef ANIMTESTBED_BLENDSPACE1D_H -#define ANIMTESTBED_BLENDSPACE1D_H - -#include "../AnimNode.h" - -struct BlendSpace1D : public AnimNode { - BlendSpace1D(AnimationController* animation_controller); - - virtual ~BlendSpace1D() {} - - int m_num_inputs; - std::vector m_input_weights; - std::vector m_inputs; - float m_weight; - bool m_sync_inputs; - - AnimNode* m_input_0; - AnimNode* m_input_1; - - float m_normalized_weight; - float m_weight_0; - float m_weight_1; - ozz::vector m_local_matrices_0; - ozz::vector m_local_matrices_1; - ozz::math::Transform m_root_transform_0; - ozz::math::Transform m_root_transform_1; - - virtual void Reset() { m_time_current = 0.f; } - - virtual void UpdateIsSynced(bool is_synced) override { - m_is_time_synced = is_synced; - - if (m_sync_inputs) { - m_is_time_synced = true; - } - - assert(m_input_weights.size() > 0); - assert( - m_weight >= m_input_weights[0] - && m_weight <= m_input_weights[m_input_weights.size() - 1]); - - int prev_idx = 0; - for (int next_idx = 1; next_idx < m_input_weights.size(); next_idx++) { - if (m_input_weights[prev_idx] <= m_weight - && m_input_weights[next_idx] >= m_weight) { - m_input_0 = m_inputs[prev_idx]; - m_weight_0 = m_input_weights[prev_idx]; - - m_input_1 = m_inputs[next_idx]; - m_weight_1 = m_input_weights[next_idx]; - - break; - } - prev_idx = next_idx; - } - - m_input_0->UpdateIsSynced(m_is_time_synced); - m_input_1->UpdateIsSynced(m_is_time_synced); - - m_normalized_weight = (m_weight - m_weight_0) / (m_weight_1 - m_weight_0); - } - - virtual void UpdateSyncTrack() override { - if (m_is_time_synced) { - m_sync_track = SyncTrack::Blend( - m_normalized_weight, - m_input_0->m_sync_track, - m_input_1->m_sync_track); - } else { - assert(false); - } - } - - virtual void UpdateTime(float dt) { - if (m_is_time_synced) { - float sync_time_old = m_sync_track.CalcSyncFromAbsTime(m_time_current); - m_time_current = fmodf(m_time_current + dt, m_sync_track.m_duration); - float sync_time_dt = - m_sync_track.CalcSyncFromAbsTime(m_time_current) - sync_time_old; - - m_input_0->m_time_current = sync_time_old; - m_input_1->m_time_current = sync_time_old; - m_input_0->UpdateTime(sync_time_dt); - m_input_1->UpdateTime(sync_time_dt); - } else { - m_time_current += dt; - m_input_0->UpdateTime(dt); - m_input_1->UpdateTime(dt); - } - } - - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) override; - - virtual void GetInputNodes( - std::vector& input_nodes) const override { - for (int i = 0; i < m_inputs.size(); i++) { - input_nodes.push_back(m_inputs[i]); - } - }; - - virtual void DrawDebugUi(); -}; - -#endif //ANIMTESTBED_BLENDSPACE1D_H diff --git a/src/AnimNodes/LockTranslationNode.cc b/src/AnimNodes/LockTranslationNode.cc deleted file mode 100644 index 23d016d..0000000 --- a/src/AnimNodes/LockTranslationNode.cc +++ /dev/null @@ -1,61 +0,0 @@ -// -// Created by martin on 16.11.21. -// - -#include "LockTranslationNode.h" - -#include - -#include "ozz/base/maths/soa_transform.h" - -void LockTranslationNode::Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) { - m_input->Evaluate(local_matrices, root_transform); - ozz::math::SoaFloat3 translation = - (*local_matrices)[m_locked_bone_index].translation; - float x[4]; - float y[4]; - float z[4]; - _mm_store_ps(x, translation.x); - _mm_store_ps(y, translation.y); - _mm_store_ps(z, translation.z); - - if (m_lock_x) { - x[0] = 0.f; - } - - if (m_lock_y) { - y[0] = 0.f; - } - - if (m_lock_z) { - z[0] = 0.f; - } - - translation.x = _mm_load_ps(x); - translation.y = _mm_load_ps(y); - translation.z = _mm_load_ps(z); - - //translation = ozz::math::SoaFloat3::zero(); - // ozz::math::SetX(translation, 0.f); - // ozz::math::SetZ(translation, 0.f); - (*local_matrices)[m_locked_bone_index].translation = translation; -} - -void LockTranslationNode::DrawDebugUi() { - const ozz::animation::Skeleton& skeleton = - m_animation_controller->m_skinned_mesh->m_skeleton; - ozz::span joint_names = skeleton.joint_names(); - - const char* items[255] = {0}; - int item_current = 0; - for (int i = 0; i < joint_names.size(); i++) { - items[i] = joint_names[i]; - } - - ImGui::Combo("Bone", &m_locked_bone_index, items, joint_names.size()); - ImGui::Checkbox("Lock X", &m_lock_x); - ImGui::Checkbox("Lock Y", &m_lock_y); - ImGui::Checkbox("Lock Z", &m_lock_z); -} \ No newline at end of file diff --git a/src/AnimNodes/LockTranslationNode.h b/src/AnimNodes/LockTranslationNode.h deleted file mode 100644 index 86d8997..0000000 --- a/src/AnimNodes/LockTranslationNode.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// Created by martin on 16.11.21. -// - -#ifndef ANIMTESTBED_LOCKBONES_H -#define ANIMTESTBED_LOCKBONES_H - -#include "../AnimNode.h" - -struct LockTranslationNode : public AnimNode { - LockTranslationNode(AnimationController* animation_controller) - : AnimNode(animation_controller), - m_input(nullptr), - m_locked_bone_index(0), - m_lock_x(false), - m_lock_y(false), - m_lock_z(false) {} - - virtual ~LockTranslationNode() {} - - AnimNode* m_input; - int m_locked_bone_index; - bool m_lock_x; - bool m_lock_y; - bool m_lock_z; - - virtual void Reset() { m_time_current = 0.f; } - - virtual void UpdateIsSynced(bool is_synced) override { - m_is_time_synced = is_synced; - - m_input->UpdateIsSynced(m_is_time_synced); - } - - virtual void UpdateSyncTrack() override { - m_sync_track = m_input->m_sync_track; - } - - virtual void UpdateTime(float dt) { m_input->UpdateTime(dt); } - - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform = nullptr) override; - - virtual void GetInputNodes( - std::vector& input_nodes) const override { - input_nodes.push_back(m_input); - }; - - virtual void DrawDebugUi() override; -}; - -#endif //ANIMTESTBED_LOCKBONES_H diff --git a/src/AnimNodes/SpeedScaleNode.cc b/src/AnimNodes/SpeedScaleNode.cc deleted file mode 100644 index 5df07e2..0000000 --- a/src/AnimNodes/SpeedScaleNode.cc +++ /dev/null @@ -1,20 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#include "SpeedScaleNode.h" - -#include - -void SpeedScaleNode::DrawDebugUi() { - bool is_negative = m_time_scale < 0.f; - if (ImGui::Checkbox("Reverse Time", &is_negative)) { - m_time_scale = m_time_scale * -1.f; - } - - // ensure m_time_scale is positive - m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); - ImGui::SliderFloat("Time Scale", &m_time_scale, 0.01f, 5.f); - // and back to the original negative or positive sign - m_time_scale = m_time_scale * (is_negative ? -1.f : 1.f); -} \ No newline at end of file diff --git a/src/AnimNodes/SpeedScaleNode.h b/src/AnimNodes/SpeedScaleNode.h deleted file mode 100644 index 04b98c3..0000000 --- a/src/AnimNodes/SpeedScaleNode.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#ifndef ANIMTESTBED_SPEEDSCALENODE_H -#define ANIMTESTBED_SPEEDSCALENODE_H - -#include "../AnimNode.h" - -struct SpeedScaleNode : public AnimNode { - SpeedScaleNode(AnimationController* animation_controller) - : AnimNode(animation_controller), m_time_scale(1.f) { - m_anim_node_type = AnimNodeType::SpeedScale; - } - - float m_time_scale; - AnimNode* m_input_node; - - virtual void Reset() { m_time_current = 0.f; } - - virtual void UpdateIsSynced(bool is_synced) override { - m_is_time_synced = is_synced; - m_input_node->UpdateIsSynced(is_synced); - } - - virtual void UpdateSyncTrack() override { - assert(fabs(m_time_scale) >= 0.01f); - m_sync_track = m_input_node->m_sync_track; - m_sync_track.m_duration = - fabsf(m_input_node->m_sync_track.m_duration / m_time_scale); - } - - virtual void UpdateTime(float dt) { - if (!m_is_time_synced) { - m_time_current += dt * m_time_scale; - m_input_node->UpdateTime(dt * m_time_scale); - } else { - m_time_current += dt; - m_input_node->UpdateTime(dt); - } - } - - virtual void Evaluate( - ozz::vector* local_matrices, - ozz::math::Transform* root_transform) override { - m_input_node->Evaluate(local_matrices, root_transform); - }; - - virtual void GetInputNodes( - std::vector& input_nodes) const override { - input_nodes.push_back(m_input_node); - }; - - virtual void DrawDebugUi() override; -}; - -#endif //ANIMTESTBED_SPEEDSCALENODE_H diff --git a/src/AnimationController.cc b/src/AnimationController.cc deleted file mode 100644 index 43d4136..0000000 --- a/src/AnimationController.cc +++ /dev/null @@ -1,242 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#include "AnimationController.h" - -#include -#include -#include - -#include - -#include "AnimNodes/AnimSamplerNode.h" -#include "AnimNodes/BlendNode.h" -#include "AnimNodes/BlendSpace1D.h" -#include "AnimNodes/LockTranslationNode.h" -#include "AnimNodes/SpeedScaleNode.h" -#include "SkinnedMesh.h" - -AnimationController::AnimationController(SkinnedMesh* skinned_mesh) - : m_current_time(0.f), m_paused(true), m_skinned_mesh(skinned_mesh) { - const int num_soa_joints = skinned_mesh->m_skeleton.num_soa_joints(); - const int num_joints = skinned_mesh->m_skeleton.num_joints(); - skinned_mesh->m_local_matrices.resize(num_soa_joints); - skinned_mesh->m_model_matrices.resize(num_joints); - - ResetAnims(); - - AnimSamplerNode* sampler_node0 = new AnimSamplerNode(this); - sampler_node0->m_name = "AnimSampler0"; - sampler_node0->SetAnimation( - skinned_mesh->m_animations[1], - skinned_mesh->m_animation_sync_track[1]); - m_anim_nodes.push_back(sampler_node0); - - AnimSamplerNode* sampler_node1 = new AnimSamplerNode(this); - sampler_node1->m_name = "AnimSampler1"; - sampler_node1->SetAnimation( - skinned_mesh->m_animations[2], - skinned_mesh->m_animation_sync_track[2]); - m_anim_nodes.push_back(sampler_node1); - - AnimSamplerNode* sampler_node2 = new AnimSamplerNode(this); - sampler_node2->m_name = "AnimSampler2"; - sampler_node2->SetAnimation( - skinned_mesh->m_animations[3], - skinned_mesh->m_animation_sync_track[3]); - m_anim_nodes.push_back(sampler_node2); - - BlendSpace1D* blend_space = new BlendSpace1D(this); - blend_space->m_name = "BlendSpace0"; - blend_space->m_weight = 0.f; - blend_space->m_inputs.push_back(sampler_node0); - blend_space->m_input_weights.push_back(-1.f); - blend_space->m_inputs.push_back(sampler_node1); - blend_space->m_input_weights.push_back(0.f); - blend_space->m_inputs.push_back(sampler_node2); - blend_space->m_input_weights.push_back(1.f); - m_anim_nodes.push_back(blend_space); - - SpeedScaleNode* speed_node = new SpeedScaleNode(this); - speed_node->m_name = "SpeedNode0"; - speed_node->m_input_node = sampler_node0; - m_anim_nodes.push_back(speed_node); - - BlendNode* blend_node = new BlendNode(this); - blend_node->m_name = "Blend0"; - blend_node->m_input_A = speed_node; - blend_node->m_input_B = sampler_node1; - blend_node->m_sync_inputs = true; - m_anim_nodes.push_back(blend_node); - - SpeedScaleNode* speed_node1 = new SpeedScaleNode(this); - speed_node1->m_name = "SpeedNode1"; - speed_node1->m_input_node = blend_space; - m_anim_nodes.push_back(speed_node1); - - LockTranslationNode* lock_node = new LockTranslationNode(this); - lock_node->m_name = "LockNode0"; - lock_node->m_locked_bone_index = 0; - lock_node->m_input = speed_node1; - m_anim_nodes.push_back(lock_node); - - m_output_node = m_anim_nodes.back(); - - UpdateOrderedNodes(); - - m_output_node->Reset(); -} - -AnimationController::~AnimationController() { - while (m_anim_nodes.size() > 0) { - delete m_anim_nodes[m_anim_nodes.size() - 1]; - m_anim_nodes.pop_back(); - } - - m_output_node = nullptr; -} - -void AnimationController::ResetAnims() { - for (int i = 0; i < m_ordered_nodes.size(); i++) { - m_ordered_nodes[i]->Reset(); - } -} - -void AnimationController::UpdateOrderedNodes() { - std::vector node_stack; - node_stack.push_back(m_output_node); - - m_ordered_nodes.clear(); - - while (node_stack.size() > 0) { - AnimNode* node = node_stack.back(); - m_ordered_nodes.push_back(node); - node_stack.pop_back(); - - std::vector node_inputs; - node->GetInputNodes(node_inputs); - for (int i = node_inputs.size() - 1; i >= 0; i--) { - node_stack.push_back(node_inputs[i]); - } - } -} - -void AnimationController::UpdateTime(float dt) { - if (m_output_node == nullptr) { - return; - } - - // Mark all nodes that evaluate time using sync tracks. - m_output_node->UpdateIsSynced(false); - - if (m_paused) { - return; - } - - // For all synced nodes calculate their current sync track durations - for (int i = m_ordered_nodes.size() - 1; i >= 0; i--) { - AnimNode* node = m_ordered_nodes[i]; - if (node->m_is_time_synced) { - node->UpdateSyncTrack(); - } - } - - // Update the time of all nodes. - m_output_node->UpdateTime(dt); -} - -void AnimationController::Evaluate() { - if (m_output_node == nullptr) { - return; - } - - m_output_node->Evaluate( - &m_skinned_mesh->m_local_matrices, - m_calc_root_transform ? &m_root_transform : nullptr); -}; - -void AnimationController::DrawDebugUi() { - ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_FirstUseEver); - ImGui::Begin("AnimationController"); - - if (ImGui::Button("Reset")) { - ResetAnims(); - } - ImGui::SameLine(); - if (m_paused) { - if (ImGui::Button("Play")) { - m_paused = false; - } - } else { - if (ImGui::Button("Pause")) { - m_paused = true; - } - } - ImGui::SameLine(); - if (ImGui::Button("Step")) { - bool was_paused = m_paused; - m_paused = false; - UpdateTime(0.1); - Evaluate(); - m_paused = was_paused; - } - - ImVec2 node_size(200, 100); - for (int i = 0; i < m_ordered_nodes.size(); i++) { - AnimNode* node = m_ordered_nodes[i]; - - ImGui::SetNextWindowSize(node_size, ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos( - ImVec2((m_ordered_nodes.size() - 1 - i) * node_size.x - i * 10, 300), - ImGuiCond_FirstUseEver); - ImGui::Begin(node->m_name.c_str()); - node->DrawDebugUi(); - ImGui::End(); - } - - ImGui::Text("Node States"); - - ImGui::Columns(4, "Node States"); // 4-ways, with border - ImGui::Separator(); - ImGui::Text("Name"); - ImGui::NextColumn(); - ImGui::Text("Synced"); - ImGui::NextColumn(); - ImGui::Text("Duration"); - ImGui::NextColumn(); - ImGui::Text("Time"); - ImGui::NextColumn(); - - ImGui::Separator(); - static int selected = -1; - - for (int i = 0; i < m_ordered_nodes.size(); i++) { - AnimNode* node = m_ordered_nodes[i]; - if (ImGui::Selectable( - node->m_name.c_str(), - selected == i, - ImGuiSelectableFlags_SpanAllColumns)) - selected = i; - bool hovered = ImGui::IsItemHovered(); - ImGui::NextColumn(); - - ImGui::Text(node->m_is_time_synced ? "X" : "-"); - ImGui::NextColumn(); - - ImGui::PushID((void*)&node->m_sync_track.m_duration); - ImGui::Text("%2.3f", node->m_sync_track.m_duration); - ImGui::NextColumn(); - ImGui::PopID(); - - ImGui::PushID((void*)&node->m_time_current); - ImGui::Text("%2.3f", node->m_time_current); - ImGui::NextColumn(); - ImGui::PopID(); - } - - ImGui::Columns(1); - ImGui::Separator(); - - ImGui::End(); -} \ No newline at end of file diff --git a/src/AnimationController.h b/src/AnimationController.h deleted file mode 100644 index 1d33963..0000000 --- a/src/AnimationController.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by martin on 12.11.21. -// - -#ifndef ANIMTESTBED_ANIMATIONCONTROLLER_H -#define ANIMTESTBED_ANIMATIONCONTROLLER_H - -#include -#include -#include -#include -#include -#include - -struct SkinnedMesh; -struct AnimNode; - -struct AnimationController { - explicit AnimationController(SkinnedMesh* skinned_mesh); - virtual ~AnimationController(); - - void ResetAnims(); - - // Creates a list of nodes where for node at index i for all inputs holds index > i. - void UpdateOrderedNodes(); - - // Updates all nodes. - void UpdateTime(float dt); - - // Recursively evaluates all nodes. - void Evaluate(); - - void DrawDebugUi(); - - float m_current_time; - bool m_paused; - bool m_calc_root_transform; - ozz::math::Transform m_root_transform; - - SkinnedMesh* m_skinned_mesh = nullptr; - - AnimNode* m_output_node; - - std::vector m_anim_nodes; - std::vector m_ordered_nodes; -}; - -#endif //ANIMTESTBED_ANIMATIONCONTROLLER_H diff --git a/src/main.cc b/src/main.cc index 4cbf358..a348720 100644 --- a/src/main.cc +++ b/src/main.cc @@ -17,7 +17,7 @@ #include "Camera.h" #include "SkinnedMesh.h" -#include "AnimGraphEditor.h" +#include "src/AnimGraph/AnimGraphEditor.h" #include "GLFW/glfw3.h" const int Width = 1024; @@ -28,6 +28,7 @@ const int MaxIndices = MaxVertices * 3; uint64_t last_time = 0; bool show_imgui_demo_window = false; bool show_another_window = false; +bool show_graph_editor = true; sg_pass_action pass_action; sg_pipeline pip; @@ -48,7 +49,6 @@ static void draw_imgui(ImDrawData*); #include // fmodf #include // std::unique_ptr, std::make_unique -#include "AnimationController.h" #include "SkinnedMeshRenderer.h" #include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/local_to_model_job.h" @@ -110,15 +110,6 @@ 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]; @@ -206,7 +197,7 @@ int main() { sgldesc.sample_count = 0; sgl_setup(&sgldesc); - printf ("default allocator: 0x%p\n", (void*)ozz::memory::default_allocator()); + // Animation setup SkinnedMesh skinned_mesh; skinned_mesh.LoadSkeleton("../media/MixamoYBot-skeleton.ozz"); skinned_mesh.LoadAnimation("../media/Idle-loop.ozz"); @@ -242,9 +233,6 @@ int main() { skinned_mesh.SetCurrentAnimation(0); - AnimationController animation_controller (&skinned_mesh); - -// state.ozz = std::make_unique(); state.time.factor = 1.0f; Camera_Init(&state.camera); @@ -433,20 +421,13 @@ int main() { if (ImGui::BeginMainMenuBar()) { ImGui::Text("AnimTestbed"); - int mode_current = static_cast(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("Graph Editor", &show_graph_editor); ImGui::Checkbox("ImGui Demo", &show_imgui_demo_window); ImGui::EndMainMenuBar(); } - if (gAppMode == AppMode::AnimRuntime) { + // Animation Runtime + { ImGui::Begin("Camera"); ImGui::SliderFloat3("pos", state.camera.pos, -100.f, 100.f); ImGui::SliderFloat("near", &state.camera.near, 0.001f, 10.f); @@ -460,13 +441,9 @@ int main() { 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(); + // TODO: add AnimGraph to calculate pose + // skinned_mesh.CalcModelMatrices(); sgl_defaults(); sgl_matrix_mode_projection(); @@ -474,19 +451,22 @@ int main() { sgl_matrix_mode_modelview(); sgl_load_matrix((const float*)&state.camera.mtxView); RenderSkinnedMesh(skinned_mesh); - } else if (gAppMode == AppMode::GraphEditor) { + } + + // Animation Graph Editor + if (show_graph_editor) { ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); - ImGui::Begin("Graph Editor", nullptr, ImGuiWindowFlags_MenuBar); + ImGui::Begin( + "Graph Editor", + &show_graph_editor, + ImGuiWindowFlags_MenuBar); AnimGraphEditorUpdate(); ImGui::End(); - } else if (gAppMode == AppMode::Debug) { - ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver); - ImGui::Begin("Debug"); - ImGui::End(); } + // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow() if (show_imgui_demo_window) { ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver); diff --git a/tests/AnimGraphResourceTests.cc b/tests/AnimGraphResourceTests.cc index 918580e..793c5cf 100644 --- a/tests/AnimGraphResourceTests.cc +++ b/tests/AnimGraphResourceTests.cc @@ -2,10 +2,11 @@ // Created by martin on 04.02.22. // -#include "AnimGraphResource.h" -#include "catch.hpp" +#include "AnimGraph/AnimGraphResource.h" +#include "AnimGraph/AnimGraph.h" +#include "AnimGraph/AnimGraphEditor.h" -using namespace AniGraph; +#include "catch.hpp" TEST_CASE("BasicGraph", "[AnimGraphResource]") { AnimGraphResource graph_resource; diff --git a/tests/AnimSampleNodeTests.cc b/tests/AnimSampleNodeTests.cc deleted file mode 100644 index df203eb..0000000 --- a/tests/AnimSampleNodeTests.cc +++ /dev/null @@ -1,126 +0,0 @@ -// -// Created by martin on 21.11.21. -// - -#include "AnimNodes/AnimSamplerNode.h" -#define OZZ_INCLUDE_PRIVATE_HEADER -#include "../src/animation/runtime/animation_keyframe.h" -#include "catch.hpp" -#include "ozzutils.h" - -void get_bone_transform( - int i_bone_idx, - const ozz::vector& i_local_matrices, - ozz::math::Transform& o_transform) { - int matrix_index = i_bone_idx / 4; - short simd_component = i_bone_idx % 4; - o_transform.translation.x = - i_local_matrices[matrix_index].translation.x[simd_component]; - o_transform.translation.y = - i_local_matrices[matrix_index].translation.y[simd_component]; - o_transform.translation.z = - i_local_matrices[matrix_index].translation.z[simd_component]; -} - -void sample_bone_transform( - float ratio, - int i_bone_idx, - const ozz::animation::Animation* i_animation, - ozz::animation::SamplingCache& io_cache, - ozz::vector& io_local_matrices, - ozz::math::Transform& o_transform) { - ozz::animation::SamplingJob sampling_job; - sampling_job.animation = i_animation; - sampling_job.cache = &io_cache; - sampling_job.ratio = ratio; - sampling_job.output = make_span(io_local_matrices); - - if (!sampling_job.Run()) { - ozz::log::Err() << "Error sampling animation." << std::endl; - } - - get_bone_transform(i_bone_idx, io_local_matrices, o_transform); -} - - -TEST_CASE("Sample single bone channel", "[AnimSamplerNode]") { - SkinnedMesh skinned_mesh; - skinned_mesh.LoadSkeleton("../media/MixamoYBot-skeleton.ozz"); - skinned_mesh.LoadAnimation("../media/Walking-loop.ozz"); - - ozz::animation::Animation* animation = skinned_mesh.m_animations[0]; - const int num_soa_joints = skinned_mesh.m_skeleton.num_soa_joints(); - const int num_joints = skinned_mesh.m_skeleton.num_joints(); - - ozz::vector local_matrices; - ozz::animation::SamplingCache sampling_cache; - - local_matrices.resize(num_soa_joints); - sampling_cache.Resize(num_joints); - - // sample at ratio 0. - float ratio = 0.f; - - ozz::animation::SamplingJob sampling_job; - sampling_job.animation = animation; - sampling_job.cache = &sampling_cache; - sampling_job.ratio = ratio; - sampling_job.output = make_span(local_matrices); - - if (!sampling_job.Run()) { - ozz::log::Err() << "Error sampling animation." << std::endl; - } - - ozz::math::Transform root_transform_0; - sample_bone_transform( - 0.f, - 0, - animation, - sampling_cache, - local_matrices, - root_transform_0); - - int n_samples = 53; - for (int i = 0; i <= n_samples; i++) { - float ratio = i * 1.f / n_samples; - ozz::math::Transform sampled_root_transform; - sample_bone_transform( - i * 1.f / n_samples, - 0, - animation, - sampling_cache, - local_matrices, - sampled_root_transform); - - ozz::math::Transform calculated_root_transform; - calc_bone_translation(ratio, 0, animation, calculated_root_transform); - -// std::cout << "ratio: " << ratio << "\t" << "err: " << ozz::math::Length(sampled_root_transform.translation - calculated_root_transform.translation) << std::endl; - } - - ozz::math::Transform root_transform_1; - calc_bone_translation(0.f, 0, animation, root_transform_0); - calc_bone_translation(1.f, 0, animation, root_transform_1); -} - -TEST_CASE("Root Bone Sampling", "[AnimSamplerNode]") { - SkinnedMesh skinned_mesh; - skinned_mesh.LoadSkeleton("../media/MixamoYBot-skeleton.ozz"); - int anim_idx = 0; - skinned_mesh.LoadAnimation("../media/Walking-loop.ozz"); - float walking_markers[] = {0.293, 0.762}; - skinned_mesh.m_animation_sync_track[anim_idx] = SyncTrack::CreateFromMarkers( - skinned_mesh.m_animations[anim_idx]->duration(), - 2, - walking_markers); - -// AnimationController anim_controller(&skinned_mesh); -// AnimSamplerNode test_node(&anim_controller); -// test_node.SetAnimation(skinned_mesh.m_animations[0], SyncTrack()); -// -// test_node.Reset(); -// test_node.UpdateTime(0.2f); -// -// ozz::math::Transform root_transform; -// test_node.Evaluate(&skinned_mesh.m_local_matrices, &root_transform); -} \ No newline at end of file