25 Commits
Author SHA1 Message Date
Martin Felis 99d5a5eb0f Minor refactor. 2024-05-07 18:50:38 +02:00
Martin Felis 698abbce4b BlendTree input and output sockets can now be registered via the AnimGraphResource. 2024-05-01 21:49:34 +02:00
Martin Felis b9789bd1e1 Minor refactor. 2024-05-01 13:17:25 +02:00
Martin Felis fd032c273b Links can now be removed by hovering link and pressing the Delete key. 2024-05-01 13:11:54 +02:00
Martin Felis da431a3879 Fixed setting location of new nodes and minor connection rejection refactor. 2024-05-01 13:03:57 +02:00
Martin Felis 84fc49af30 Properly store and restore node positions in editor. 2024-05-01 12:50:16 +02:00
Martin Felis 7c7a765455 New nodes now spawn at popup start location. 2024-05-01 11:59:30 +02:00
Martin Felis e3baa65c3b Added breadcrumb navigation for embedded graphs. 2024-05-01 10:58:33 +02:00
Martin Felis 44087d7a7c Removed legacy editor, started working on editing embedded graphs. 2024-04-30 18:40:54 +02:00
Martin Felis e8af30d10c Added embedded fonts. 2024-04-30 17:49:29 +02:00
Martin Felis 4d1990bea8 Reactivated the sidebar in the new node editor. 2024-04-25 21:40:09 +02:00
Martin Felis 5e34aaf3db Extremely basic blend tree editing. 2024-04-25 21:12:08 +02:00
Martin Felis 3fb2995b02 Fixed building of editor app, though crashes everywhere. 2024-04-24 21:58:47 +02:00
Martin Felis c267276be3 Fixed connection validation when one of the nodes is an embedded blend tree. 2024-04-24 21:38:11 +02:00
Martin Felis 53c0bff7a6 Minor cleanup. 2024-04-21 12:47:19 +02:00
Martin Felis 91e226945c Added basic connection validation and connection removal to BlendTreeResources. 2024-04-21 12:42:49 +02:00
Martin Felis d95bc9fb9c Made BlendTreeResource::m_nodes and ::m_connections private.
This is a prerequisite to properly track Node input/output connections and to compute eval order in the BlendTreeResources.
2024-04-16 22:18:11 +02:00
Martin Felis 2d5337ed1d Reduced connection data block size.
Previously we allocated a block for each connection. However when an output is reused by multiple connections this lead to duplicates. It also obfuscated which block is actually being used.
2024-04-08 21:52:03 +02:00
Martin Felis 9f9ac60f9c Cleaned up some compilation issues for the AnimGraphEditor. 2024-04-05 00:44:37 +02:00
Martin Felis 1741238a61 Fixed variable shadowing. 2024-04-05 00:43:41 +02:00
Martin Felis 8694a11416 Removed even more unused files. 2024-04-05 00:18:07 +02:00
Martin Felis 3444f8a625 Removed dead code and unused files. 2024-04-04 19:47:24 +02:00
Martin Felis cd56efca3d Only storing single socket in AnimGraphConnections and simplified wiring logic. 2024-04-04 19:36:20 +02:00
Martin Felis 28eca48a61 Added a test that checks socket propagation into an embedded BlendTree. 2024-04-01 17:59:25 +02:00
Martin Felis 76ea38f118 Added support of time updates for simple embedded graphs. 2024-04-01 12:33:23 +02:00
21 changed files with 12646 additions and 1977 deletions
+6 -8
View File
@@ -39,17 +39,13 @@ set(ThirdPartyIncludeDeps
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sokol> PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sokol>
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/vectorial/include> PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/vectorial/include>
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/> PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
) )
# Shared code by main executable and tests # Shared code by main executable and tests
add_library(AnimTestbedCode OBJECT add_library(AnimTestbedCode OBJECT
src/SyncTrack.cc src/SyncTrack.cc
src/SyncTrack.h src/SyncTrack.h
src/ozzutils.cc src/ozzutils.cc
# src/AnimGraph/AnimGraphBlendTreeResource.cc
# src/AnimGraph/AnimGraphBlendTreeResource.h
src/AnimGraph/AnimGraph.cc
src/AnimGraph/AnimGraph.h
src/AnimGraph/AnimGraphNodes.cc src/AnimGraph/AnimGraphNodes.cc
src/AnimGraph/AnimGraphNodes.h src/AnimGraph/AnimGraphNodes.h
src/AnimGraph/AnimGraphData.cc src/AnimGraph/AnimGraphData.cc
@@ -82,6 +78,7 @@ target_include_directories(
target_sources(AnimTestbed PRIVATE target_sources(AnimTestbed PRIVATE
src/main.cc src/main.cc
src/embedded_fonts.h
src/SkinnedMeshRenderer.cc src/SkinnedMeshRenderer.cc
src/AnimGraph/AnimGraphEditor.cc src/AnimGraph/AnimGraphEditor.cc
src/AnimGraph/AnimGraphEditor.h src/AnimGraph/AnimGraphEditor.h
@@ -112,7 +109,7 @@ target_sources(AnimTestbed PRIVATE
3rdparty/imgui-node-editor/imgui_extra_math.inl 3rdparty/imgui-node-editor/imgui_extra_math.inl
3rdparty/imgui-node-editor/crude_json.cpp 3rdparty/imgui-node-editor/crude_json.cpp
3rdparty/imgui-node-editor/crude_json.h 3rdparty/imgui-node-editor/crude_json.h
) )
target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz_animation ${OPENGL_LIBRARIES}) target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz_animation ${OPENGL_LIBRARIES})
@@ -124,16 +121,17 @@ set(ozz_offline_test_objs
3rdparty/ozz-animation/src/animation/offline/raw_skeleton.cc 3rdparty/ozz-animation/src/animation/offline/raw_skeleton.cc
3rdparty/ozz-animation/src/animation/offline/animation_builder.cc 3rdparty/ozz-animation/src/animation/offline/animation_builder.cc
3rdparty/ozz-animation/src/animation/offline/raw_animation.cc 3rdparty/ozz-animation/src/animation/offline/raw_animation.cc
) )
target_sources(runtests PRIVATE target_sources(runtests PRIVATE
tests/AnimGraphResourceTests.cc tests/AnimGraphResourceTests.cc
tests/AnimGraphEditorTests.cc
# tests/AnimGraphEvalTests.cc # tests/AnimGraphEvalTests.cc
tests/NodeDescriptorTests.cc tests/NodeDescriptorTests.cc
tests/SyncTrackTests.cc tests/SyncTrackTests.cc
tests/main.cc tests/main.cc
${ozz_offline_test_objs} ${ozz_offline_test_objs}
) )
target_include_directories( target_include_directories(
runtests runtests
-3
View File
@@ -1,3 +0,0 @@
//
// Created by martin on 25.03.22.
//
-38
View File
@@ -1,38 +0,0 @@
//
// Created by martin on 25.03.22.
//
#ifndef ANIMTESTBED_ANIMGRAPH_H
#define ANIMTESTBED_ANIMGRAPH_H
#include "AnimNode.h"
#include "AnimGraphData.h"
//
// AnimGraph (Runtime)
//
struct AnimGraph {
~AnimGraph() {}
bool Init(AnimGraphContext& context) {
return m_root_node->Init(context);
}
void UpdateTime(float dt) {
m_time_last = m_time_now;
m_time_now = m_time_now + dt;
m_root_node->UpdateTime(m_time_last, m_time_now);
}
void Evaluate(AnimGraphContext& context) {
}
AnimNode* m_root_node = nullptr;
float m_time_now = 0.f;
float m_time_last = 0.f;
Vec3 m_root_bone_translation = {};
Quat m_root_bone_rotation = {};
};
#endif // ANIMTESTBED_ANIMGRAPH_H
+49 -74
View File
@@ -1,3 +1,5 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
// //
// Created by martin on 17.03.24. // Created by martin on 17.03.24.
// //
@@ -5,7 +7,6 @@
#include "AnimGraphBlendTree.h" #include "AnimGraphBlendTree.h"
#include <algorithm> #include <algorithm>
#include <cstring>
bool AnimGraphBlendTree::Init(AnimGraphContext& context) { bool AnimGraphBlendTree::Init(AnimGraphContext& context) {
for (size_t i = 2; i < m_nodes.size(); i++) { for (size_t i = 2; i < m_nodes.size(); i++) {
@@ -32,8 +33,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
const std::vector<AnimGraphConnection>& node_input_connections = const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index]; m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) { for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
if (node_input_connections[i].m_crosses_hierarchy) {
continue;
}
int input_node_index = int input_node_index =
GetAnimNodeIndex(node_input_connections.at(i).m_source_node); GetAnimNodeIndex(node_input_connections[i].m_source_node);
if (input_node_index == 1) { if (input_node_index == 1) {
continue; continue;
@@ -58,9 +63,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
} }
} }
void AnimGraphBlendTree::MarkActiveInputs() { void AnimGraphBlendTree::MarkActiveInputs(
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { const std::vector<AnimGraphConnection>& input_connections) {
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated; for (AnimNode* node : m_nodes) {
if (node->m_tick_number != m_tick_number) {
node->m_state = AnimNodeEvalState::Deactivated;
}
} }
const std::vector<AnimGraphConnection>& graph_output_inputs = const std::vector<AnimGraphConnection>& graph_output_inputs =
@@ -69,15 +77,16 @@ void AnimGraphBlendTree::MarkActiveInputs() {
const AnimGraphConnection& graph_input = graph_output_inputs[i]; const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node; AnimNode* node = graph_input.m_source_node;
if (node != nullptr) { if (node != nullptr) {
node->m_tick_number = m_tick_number;
node->m_state = AnimNodeEvalState::Activated; node->m_state = AnimNodeEvalState::Activated;
} }
} }
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (checkIsNodeActive(node)) { if (CheckIsNodeActive(node)) {
node->MarkActiveInputs();
size_t node_index = GetAnimNodeIndex(node); size_t node_index = GetAnimNodeIndex(node);
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active. // Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size(); for (size_t j = 0, nj = m_node_input_connections[node_index].size();
@@ -86,8 +95,7 @@ void AnimGraphBlendTree::MarkActiveInputs() {
const AnimGraphConnection& input = const AnimGraphConnection& input =
m_node_input_connections[node_index][j]; m_node_input_connections[node_index][j];
if (input.m_source_node != nullptr if (input.m_source_node != nullptr
&& input.m_target_socket.m_type && input.m_socket.m_type != SocketType::SocketTypeAnimation) {
!= SocketType::SocketTypeAnimation) {
input.m_source_node->m_state = AnimNodeEvalState::Activated; input.m_source_node->m_state = AnimNodeEvalState::Activated;
} }
} }
@@ -95,14 +103,15 @@ void AnimGraphBlendTree::MarkActiveInputs() {
} }
} }
void AnimGraphBlendTree::CalcSyncTrack() { void AnimGraphBlendTree::CalcSyncTrack(
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) { const std::vector<AnimGraphConnection>& input_connections) {
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) { if (node->m_state == AnimNodeEvalState::Deactivated) {
continue; continue;
} }
node->CalcSyncTrack(); node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]);
} }
} }
@@ -111,37 +120,23 @@ void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
const std::vector<AnimGraphConnection>& graph_output_inputs = const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0]; m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { for (const AnimGraphConnection& graph_output_input : graph_output_inputs) {
AnimNode* node = graph_output_inputs[i].m_source_node; AnimNode* node = graph_output_input.m_source_node;
if (node != nullptr) { if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) {
node->UpdateTime(node->m_time_now, node->m_time_now + dt); node->UpdateTime(time_last, time_now);
} }
} }
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; --i) { for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) { if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue; continue;
} }
size_t node_index = GetAnimNodeIndex(node); PropagateTimeToNodeInputs(node);
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
AnimNode* input_node = node_input_connections[i].m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connections[i].m_target_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
}
}
} }
m_state = AnimNodeEvalState::TimeUpdated;
} }
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) { void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
@@ -156,46 +151,26 @@ void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
} }
} }
Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) { void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) { size_t node_index = GetAnimNodeIndex(node);
AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) { float node_time_now = node->m_time_now;
return &connection.m_source_socket; float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (const AnimGraphConnection& node_input_connection :
node_input_connections) {
AnimNode* input_node = node_input_connection.m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connection.m_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
} }
} }
return nullptr;
} }
Socket* AnimGraphBlendTree::GetOutputSocket(const std::string& name) { #pragma clang diagnostic pop
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
const Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) {
return &connection.m_source_socket;
}
}
return nullptr;
}
const Socket* AnimGraphBlendTree::GetOutputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
+74 -95
View File
@@ -5,6 +5,8 @@
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H #ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H #define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#include <algorithm>
#include "AnimNode.h" #include "AnimNode.h"
// //
@@ -17,6 +19,17 @@ struct AnimGraphBlendTree : public AnimNode {
std::vector<AnimNode*> m_eval_ordered_nodes; std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections; std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections; std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphInputConnections() const {
return m_node_output_connections[1];
}
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphOutputConnections() const {
return m_node_input_connections[0];
}
std::vector<AnimData*> m_animdata_blocks; std::vector<AnimData*> m_animdata_blocks;
NodeDescriptorBase* m_node_descriptor = nullptr; NodeDescriptorBase* m_node_descriptor = nullptr;
char* m_input_buffer = nullptr; char* m_input_buffer = nullptr;
@@ -24,20 +37,26 @@ struct AnimGraphBlendTree : public AnimNode {
char* m_connection_data_storage = nullptr; char* m_connection_data_storage = nullptr;
char* m_const_node_inputs = nullptr; char* m_const_node_inputs = nullptr;
std::vector<Socket>& getGraphOutputs() { return m_node_descriptor->m_inputs; } std::vector<Socket>& GetGraphOutputs() { return m_node_descriptor->m_inputs; }
std::vector<Socket>& getGraphInputs() { return m_node_descriptor->m_outputs; } std::vector<Socket>& GetGraphInputs() { return m_node_descriptor->m_outputs; }
AnimDataAllocator m_anim_data_allocator; AnimDataAllocator m_anim_data_allocator;
~AnimGraphBlendTree() override { dealloc(); } ~AnimGraphBlendTree() override { dealloc(); }
void StartUpdateTick() { m_tick_number++; }
// AnimNode overrides // AnimNode overrides
bool Init(AnimGraphContext& context) override; bool Init(AnimGraphContext& context) override;
void MarkActiveInputs() override; void MarkActiveInputs(
void CalcSyncTrack() override; const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override; void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
void PropagateTimeToNodeInputs(const AnimNode* node);
void dealloc() { void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) { for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector(); m_animdata_blocks[i]->m_local_matrices.vector::~vector();
@@ -62,8 +81,8 @@ struct AnimGraphBlendTree : public AnimNode {
void UpdateOrderedNodes(); void UpdateOrderedNodes();
void UpdateOrderedNodesRecursive(int node_index); void UpdateOrderedNodesRecursive(int node_index);
bool checkIsNodeActive(AnimNode* node) { bool CheckIsNodeActive(AnimNode* node) {
return node->m_state != AnimNodeEvalState::Deactivated; return node->m_tick_number == m_tick_number;
} }
void ResetNodeStates() { void ResetNodeStates() {
@@ -74,9 +93,6 @@ struct AnimGraphBlendTree : public AnimNode {
} }
} }
Socket* GetInputSocket(const std::string& name);
Socket* GetOutputSocket(const std::string& name);
const Socket* GetInputSocket(const std::string& name) const; const Socket* GetInputSocket(const std::string& name) const;
const Socket* GetOutputSocket(const std::string& name) const; const Socket* GetOutputSocket(const std::string& name) const;
@@ -90,17 +106,22 @@ struct AnimGraphBlendTree : public AnimNode {
void SetInput(const char* name, T* value_ptr) { void SetInput(const char* name, T* value_ptr) {
m_node_descriptor->SetOutput(name, value_ptr); m_node_descriptor->SetOutput(name, value_ptr);
std::vector<size_t> connected_node_indices;
for (int i = 0; i < m_node_output_connections[1].size(); i++) { for (int i = 0; i < m_node_output_connections[1].size(); i++) {
const AnimGraphConnection& graph_input_connection = const AnimGraphConnection& graph_input_connection =
m_node_output_connections[1][i]; m_node_output_connections[1][i];
if (graph_input_connection.m_source_socket.m_name == name) { if (graph_input_connection.m_source_socket_name == name) {
*graph_input_connection.m_target_socket.m_reference.ptr_ptr = value_ptr; *graph_input_connection.m_socket.m_reference.ptr_ptr = value_ptr;
} }
} }
} }
/** Sets the address that is used for the specified AnimGraph output Socket. /** Sets the address that is used for the specified AnimGraph output Socket.
*
* We update the pointer of the outputting node. We also have to ensure that
* all usages of that output use the same pointer.
* *
* @tparam T Type of the Socket. * @tparam T Type of the Socket.
* @param name Name of the Socket. * @param name Name of the Socket.
@@ -108,40 +129,41 @@ struct AnimGraphBlendTree : public AnimNode {
*/ */
template <typename T> template <typename T>
void SetOutput(const char* name, T* value_ptr) { void SetOutput(const char* name, T* value_ptr) {
m_node_descriptor->SetInput(name, value_ptr); const auto& graph_output_connection = std::find_if(
GetGraphOutputConnections().begin(),
GetGraphOutputConnections().end(),
[&name](const AnimGraphConnection& connection) {
return connection.m_target_socket_name == name;
});
for (int i = 0; i < m_node_input_connections[0].size(); i++) { if (graph_output_connection == GetGraphOutputConnections().end()) {
const AnimGraphConnection& graph_output_connection = std::cerr << "Error: could not find graph connection to output socket "
m_node_input_connections[0][i]; << name << "." << std::endl;
return;
}
if (graph_output_connection.m_target_socket.m_name == name) { if (graph_output_connection->m_source_node == m_nodes[1]
if (graph_output_connection.m_source_node == m_nodes[1] && graph_output_connection->m_target_node == m_nodes[0]) {
&& graph_output_connection.m_target_node == m_nodes[0]) { std::cerr << "Error: cannot set output for direct graph input to graph "
std::cerr << "Error: cannot set output for direct graph input to graph " "output connections. Use GetOutptPtr for output instead!"
"output connections. Use GetOutptPtr for output instead!" << std::endl;
<< std::endl;
return; return;
} }
*graph_output_connection.m_source_socket.m_reference.ptr_ptr = size_t output_node_index =
value_ptr; GetAnimNodeIndex(graph_output_connection->m_source_node);
// Make sure all other output connections of this pin use the same output pointer const std::vector<AnimGraphConnection>& node_output_connections =
int source_node_index = m_node_output_connections[output_node_index];
GetAnimNodeIndex(graph_output_connection.m_source_node); for (const AnimGraphConnection& connection : node_output_connections) {
for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) { if (connection.m_source_socket_name
const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j]; == graph_output_connection->m_source_socket_name) {
if (source_output_connection.m_target_node == m_nodes[0]) { *connection.m_socket.m_reference.ptr_ptr = value_ptr;
continue;
}
if (source_output_connection.m_source_socket.m_name == graph_output_connection.m_source_socket.m_name) {
*source_output_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
}
}
} }
} }
*graph_output_connection->m_socket.m_reference.ptr_ptr = value_ptr;
} }
/** Returns the address that is used for the specified AnimGraph output Socket. /** Returns the address that is used for the specified AnimGraph output Socket.
@@ -158,69 +180,17 @@ struct AnimGraphBlendTree : public AnimNode {
for (int i = 0; i < m_node_input_connections[0].size(); i++) { for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection = const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i]; m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) { if (graph_output_connection.m_target_socket_name == name) {
return static_cast<float*>(*graph_output_connection.m_source_socket.m_reference.ptr_ptr); return static_cast<T*>(
*graph_output_connection.m_socket.m_reference.ptr_ptr);
} }
} }
return nullptr; return nullptr;
} }
void* GetInputPtr(const std::string& name) const { int GetAnimNodeIndex(const AnimNode* node) const {
const Socket* input_socket = GetInputSocket(name); for (int i = 0; i < m_nodes.size(); i++) {
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
void* GetOutputPtr(const std::string& name) const {
const Socket* input_socket = GetOutputSocket(name);
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
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;
}
const AnimNode* getAnimNodeForInput(
size_t node_index,
const std::string& input_name) const {
assert(node_index < m_nodes.size());
const std::vector<AnimGraphConnection>& input_connection =
m_node_input_connections[node_index];
for (size_t i = 0, n = input_connection.size(); i < n; i++) {
if (input_connection[i].m_target_socket.m_name == input_name) {
return input_connection[i].m_source_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;
}
size_t GetAnimNodeIndex(AnimNode* node) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) { if (m_nodes[i] == node) {
return i; return i;
} }
@@ -230,5 +200,14 @@ struct AnimGraphBlendTree : public AnimNode {
} }
}; };
//
// BlendTreeSocketNode
//
struct BlendTreeSocketNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeSocketNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeSocketNode* node_) {}
};
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H #endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H
-616
View File
@@ -1,616 +0,0 @@
//
// Created by martin on 04.02.22.
//
#include <cstring>
#include <fstream>
#include "3rdparty/json/json.hpp"
#include "AnimGraphBlendTreeResource.h"
#include "AnimGraphNodes.h"
using json = nlohmann::json;
//
// Socket <-> json
//
std::string sSocketTypeToStr(SocketType pin_type) {
if (pin_type < SocketType::SocketTypeUndefined
|| pin_type >= SocketType::SocketTypeLast) {
return "Unknown";
}
return SocketTypeNames[static_cast<int>(pin_type)];
}
json sSocketToJson(const Socket& socket) {
json result;
result["name"] = socket.m_name;
result["type"] = sSocketTypeToStr(socket.m_type);
if (socket.m_type == SocketType::SocketTypeString
&& !socket.m_value_string.empty()) {
result["value"] = socket.m_value_string;
} else if (socket.m_value.flag) {
if (socket.m_type == SocketType::SocketTypeBool) {
result["value"] = socket.m_value.flag;
} else if (socket.m_type == SocketType::SocketTypeAnimation) {
} else if (socket.m_type == SocketType::SocketTypeInt) {
result["value"] = socket.m_value.int_value;
} else if (socket.m_type == SocketType::SocketTypeFloat) {
result["value"] = socket.m_value.float_value;
} else if (socket.m_type == SocketType::SocketTypeVec3) {
result["value"][0] = socket.m_value.vec3.v[0];
result["value"][1] = socket.m_value.vec3.v[1];
result["value"][2] = socket.m_value.vec3.v[2];
} else if (socket.m_type == SocketType::SocketTypeQuat) {
result["value"][0] = socket.m_value.quat.v[0];
result["value"][1] = socket.m_value.quat.v[1];
result["value"][2] = socket.m_value.quat.v[2];
result["value"][3] = socket.m_value.quat.v[3];
} else {
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
<< "'." << std::endl;
}
}
return result;
}
Socket sJsonToSocket(const json& json_data) {
Socket result;
result.m_type = SocketType::SocketTypeUndefined;
result.m_reference.ptr = &result.m_value.int_value;
result.m_name = json_data["name"];
std::string type_string = json_data["type"];
bool have_value = json_data.contains("value");
if (type_string == "Bool") {
result.m_type = SocketType::SocketTypeBool;
result.m_type_size = sizeof(bool);
result.m_reference.ptr = &result.m_value.int_value;
if (have_value) {
result.m_value.flag = json_data["value"];
}
} else if (type_string == "Animation") {
result.m_type = SocketType::SocketTypeAnimation;
result.m_type_size = sizeof(AnimData);
} else if (type_string == "Int") {
result.m_type = SocketType::SocketTypeInt;
result.m_type_size = sizeof(int);
if (have_value) {
result.m_value.int_value = json_data["value"];
}
} else if (type_string == "Float") {
result.m_type = SocketType::SocketTypeFloat;
result.m_type_size = sizeof(float);
if (have_value) {
result.m_value.float_value = json_data["value"];
}
} else if (type_string == "Vec3") {
result.m_type = SocketType::SocketTypeVec3;
result.m_type_size = sizeof(Vec3);
if (have_value) {
result.m_value.vec3.x = json_data["value"][0];
result.m_value.vec3.y = json_data["value"][1];
result.m_value.vec3.z = json_data["value"][2];
}
} else if (type_string == "Quat") {
result.m_type = SocketType::SocketTypeQuat;
result.m_type_size = sizeof(Quat);
if (have_value) {
result.m_value.quat.x = json_data["value"][0];
result.m_value.quat.y = json_data["value"][1];
result.m_value.quat.z = json_data["value"][2];
result.m_value.quat.w = json_data["value"][3];
}
} else if (type_string == "String") {
result.m_type = SocketType::SocketTypeString;
result.m_type_size = sizeof(std::string);
if (have_value) {
result.m_value_string = json_data["value"];
}
} else {
std::cerr << "Invalid socket type '" << type_string << "'." << std::endl;
}
return result;
}
//
// AnimGraphNode <-> json
//
json sAnimGraphNodeToJson(
const AnimNodeResource& node,
size_t node_index,
const std::vector<AnimGraphConnectionResource>& connections) {
json result;
result["name"] = node.m_name;
result["type"] = "AnimNodeResource";
result["node_type"] = node.m_type_name;
for (size_t j = 0; j < 2; j++) {
result["position"][j] = node.m_position[j];
}
for (const auto & socket : node.m_socket_accessor->m_inputs) {
if (socket.m_type == SocketType::SocketTypeAnimation) {
continue;
}
bool socket_connected = false;
for (const auto & connection : connections) {
if (connection.source_node_index == node_index
&& connection.source_socket_name == socket.m_name) {
socket_connected = true;
break;
}
}
if (!socket_connected) {
result["inputs"].push_back(sSocketToJson(socket));
}
}
for (auto & property : node.m_socket_accessor->m_properties) {
result["properties"][property.m_name] = sSocketToJson(property);
}
return result;
}
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node, size_t node_index) {
AnimNodeResource result;
result.m_name = json_node["name"];
result.m_type_name = json_node["node_type"];
result.m_position[0] = json_node["position"][0];
result.m_position[1] = json_node["position"][1];
result.m_anim_node = AnimNodeFactory(result.m_type_name);
result.m_socket_accessor =
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
for (auto & property : result.m_socket_accessor->m_properties) {
property = sJsonToSocket(json_node["properties"][property.m_name]);
}
if (node_index != 0 && node_index != 1 && json_node.contains("inputs")) {
for (size_t j = 0, n = json_node["inputs"].size(); j < n; j++) {
assert(json_node["inputs"][j].contains("name"));
std::string input_name = json_node["inputs"][j]["name"];
Socket* input_socket =
result.m_socket_accessor->GetInputSocket(input_name.c_str());
if (input_socket == nullptr) {
std::cerr << "Could not find input socket with name " << input_name
<< " for node type " << result.m_type_name << std::endl;
abort();
}
*input_socket = sJsonToSocket(json_node["inputs"][j]);
}
}
return result;
}
//
// AnimGraphConnectionResource <-> Json
//
json sAnimGraphConnectionToJson(
const AnimGraphConnectionResource& connection) {
json result;
result["type"] = "AnimGraphConnectionResource";
result["source_node_index"] = connection.source_node_index;
result["source_socket_name"] = connection.source_socket_name;
result["target_node_index"] = connection.target_node_index;
result["target_socket_name"] = connection.target_socket_name;
return result;
}
AnimGraphConnectionResource sAnimGraphConnectionFromJson(
const json& json_node) {
AnimGraphConnectionResource connection;
connection.source_node_index = json_node["source_node_index"];
connection.source_socket_name = json_node["source_socket_name"];
connection.target_node_index = json_node["target_node_index"];
connection.target_socket_name = json_node["target_socket_name"];
return connection;
}
void AnimGraphBlendTreeResource::clear() {
m_name = "";
clearNodes();
m_connections.clear();
initGraphConnectors();
}
void AnimGraphBlendTreeResource::clearNodes() {
for (auto & m_node : m_nodes) {
delete m_node.m_socket_accessor;
m_node.m_socket_accessor = nullptr;
delete m_node.m_anim_node;
m_node.m_anim_node = nullptr;
}
m_nodes.clear();
}
void AnimGraphBlendTreeResource::initGraphConnectors() {
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
m_nodes[0].m_name = "Outputs";
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
m_nodes[1].m_name = "Inputs";
}
bool AnimGraphBlendTreeResource::saveToFile(const char* filename) const {
json result;
result["name"] = m_name;
result["type"] = "AnimGraphResource";
for (size_t i = 0; i < m_nodes.size(); i++) {
const AnimNodeResource& node = m_nodes[i];
result["nodes"][i] = sAnimGraphNodeToJson(node, i, m_connections);
}
for (size_t i = 0; i < m_connections.size(); i++) {
const AnimGraphConnectionResource& connection = m_connections[i];
result["connections"][i] = sAnimGraphConnectionToJson(connection);
}
// Graph inputs and outputs
{
const AnimNodeResource& graph_output_node = m_nodes[0];
const std::vector<Socket> graph_inputs =
graph_output_node.m_socket_accessor->m_inputs;
for (size_t i = 0; i < graph_inputs.size(); i++) {
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
}
const AnimNodeResource& graph_input_node = m_nodes[1];
const std::vector<Socket> graph_outputs =
graph_input_node.m_socket_accessor->m_outputs;
for (size_t i = 0; i < graph_outputs.size(); i++) {
result["nodes"][1]["outputs"][i] = sSocketToJson(graph_outputs[i]);
}
}
std::ofstream output_file;
output_file.open(filename);
output_file << result.dump(4, ' ') << std::endl;
output_file.close();
return true;
}
bool AnimGraphBlendTreeResource::loadFromFile(const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
buffer << input_file.rdbuf();
json json_data = json::parse(buffer.str(), nullptr, false);
if (json_data.is_discarded()) {
std::cerr << "Error parsing json of file '" << filename << "'."
<< std::endl;
}
if (json_data["type"] != "AnimGraphResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimGraphResource' but got '"
<< json_data["type"] << "'." << std::endl;
}
clear();
clearNodes();
m_name = json_data["name"];
// Load nodes
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
const json& json_node = json_data["nodes"][i];
if (json_node["type"] != "AnimNodeResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_node["type"] << "'." << std::endl;
return false;
}
AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i);
m_nodes.push_back(node);
}
// Setup graph inputs and outputs
const json& graph_outputs = json_data["nodes"][0]["inputs"];
for (const auto & graph_output : graph_outputs) {
AnimNodeResource& graph_node = m_nodes[0];
graph_node.m_socket_accessor->m_inputs.push_back(
sJsonToSocket(graph_output));
}
const json& graph_inputs = json_data["nodes"][1]["outputs"];
for (const auto & graph_input : graph_inputs) {
AnimNodeResource& graph_node = m_nodes[1];
graph_node.m_socket_accessor->m_outputs.push_back(
sJsonToSocket(graph_input));
}
// Load connections
for (const auto & json_connection : json_data["connections"]) {
if (json_connection["type"] != "AnimGraphConnectionResource") {
std::cerr
<< "Invalid json object. Expected type 'AnimGraphConnectionResource' "
"but got '"
<< json_connection["type"] << "'." << std::endl;
return false;
}
AnimGraphConnectionResource connection =
sAnimGraphConnectionFromJson(json_connection);
m_connections.push_back(connection);
}
return true;
}
void AnimGraphBlendTreeResource::createRuntimeNodeInstances(AnimGraph& instance) const {
for (int i = 0; i < m_nodes.size(); i++) {
const AnimNodeResource& node_resource = m_nodes[i];
AnimNode* node = AnimNodeFactory(node_resource.m_type_name);
node->m_name = node_resource.m_name;
node->m_node_type_name = node_resource.m_type_name;
node->m_index = i;
instance.m_nodes.push_back(node);
// runtime node connections
instance.m_node_input_connections.emplace_back();
instance.m_node_output_connections.emplace_back();
}
}
void AnimGraphBlendTreeResource::prepareGraphIOData(AnimGraph& instance) const {
instance.m_node_descriptor =
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
instance.m_node_descriptor->m_outputs =
m_nodes[1].m_socket_accessor->m_outputs;
instance.m_node_descriptor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
//
// graph inputs
//
int input_block_size = 0;
std::vector<Socket>& graph_inputs = instance.getGraphInputs();
for (int i = 0; i < graph_inputs.size(); i++) {
input_block_size += sizeof(void*);
}
if (input_block_size > 0) {
instance.m_input_buffer = new char[input_block_size];
memset(instance.m_input_buffer, 0, input_block_size);
}
int input_block_offset = 0;
for (int i = 0; i < graph_inputs.size(); i++) {
graph_inputs[i].m_reference.ptr =
(void*)&instance.m_input_buffer[input_block_offset];
instance.m_node_descriptor->m_outputs[i].m_reference.ptr =
&instance.m_input_buffer[input_block_offset];
input_block_offset += sizeof(void*);
}
//
// graph outputs
//
int output_block_size = 0;
std::vector<Socket>& graph_outputs = instance.getGraphOutputs();
for (int i = 0; i < graph_outputs.size(); i++) {
output_block_size += sizeof(void*);
}
if (output_block_size > 0) {
instance.m_output_buffer = new char[output_block_size];
memset(instance.m_output_buffer, 0, output_block_size);
}
int output_block_offset = 0;
for (int i = 0; i < graph_outputs.size(); i++) {
instance.m_node_descriptor->m_inputs[i].m_reference.ptr =
&instance.m_output_buffer[output_block_offset];
output_block_offset += sizeof(void*);
}
// connections: make source and target sockets point to the same address in the connection data storage.
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
size_t connection_data_storage_size = 0;
for (const auto & connection : m_connections) {
const AnimNodeResource& source_node = m_nodes[connection.source_node_index];
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
connection.source_socket_name.c_str());
connection_data_storage_size += source_socket->m_type_size;
}
if (connection_data_storage_size > 0) {
instance.m_connection_data_storage = new char[connection_data_storage_size];
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
}
std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_nodes.size(),
nullptr);
for (int i = 0; i < m_nodes.size(); i++) {
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_nodes[i].m_type_name,
instance.m_nodes[i]);
}
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;
instance_node_descriptors[1]->m_outputs =
instance.m_node_descriptor->m_outputs;
size_t connection_data_offset = 0;
for (const auto & connection : m_connections) {
NodeDescriptorBase* source_node_descriptor =
instance_node_descriptors[connection.source_node_index];
NodeDescriptorBase* target_node_descriptor =
instance_node_descriptors[connection.target_node_index];
AnimNode* source_node = instance.m_nodes[connection.source_node_index];
AnimNode* target_node = instance.m_nodes[connection.target_node_index];
Socket* source_socket = source_node_descriptor->GetOutputSocket(
connection.source_socket_name.c_str());
Socket* target_socket = target_node_descriptor->GetInputSocket(
connection.target_socket_name.c_str());
AnimGraphConnection instance_connection;
instance_connection.m_source_node = source_node;
instance_connection.m_source_socket = *source_socket;
instance_connection.m_target_node = target_node;
instance_connection.m_target_socket = *target_socket;
instance.m_node_input_connections[connection.target_node_index].push_back(
instance_connection);
instance.m_node_output_connections[connection.source_node_index].push_back(
instance_connection);
source_node_descriptor->SetOutputUnchecked(
connection.source_socket_name.c_str(),
&instance.m_connection_data_storage[connection_data_offset]);
target_node_descriptor->SetInputUnchecked(
connection.target_socket_name.c_str(),
&instance.m_connection_data_storage[connection_data_offset]);
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
instance.m_animdata_blocks.push_back(
(AnimData*)(&instance
.m_connection_data_storage[connection_data_offset]));
}
connection_data_offset += source_socket->m_type_size;
}
//
// const node inputs
//
std::vector<Socket*> const_inputs =
getConstNodeInputs( instance_node_descriptors);
size_t const_node_inputs_buffer_size = 0;
for (auto & const_input : const_inputs) {
if (const_input->m_type == SocketType::SocketTypeString) {
// TODO: implement string const node input support
std::cerr << "Error: const inputs for strings not yet implemented!"
<< std::endl;
abort();
}
const_node_inputs_buffer_size += const_input->m_type_size;
}
if (const_node_inputs_buffer_size > 0) {
instance.m_const_node_inputs = new char[const_node_inputs_buffer_size];
memset(instance.m_const_node_inputs, '\0', const_node_inputs_buffer_size);
}
size_t const_input_buffer_offset = 0;
for (auto & i : const_inputs) {
Socket* const_input = i;
// TODO: implement string const node input support
assert(const_input->m_type != SocketType::SocketTypeString);
*const_input->m_reference.ptr_ptr =
&instance.m_const_node_inputs[const_input_buffer_offset];
memcpy (*const_input->m_reference.ptr_ptr, &const_input->m_value, i->m_type_size);
const_input_buffer_offset += i->m_type_size;
}
for (int i = 0; i < m_nodes.size(); i++) {
delete instance_node_descriptors[i];
}
}
void AnimGraphBlendTreeResource::setRuntimeNodeProperties(AnimGraph& instance) const {
for (int i = 2; i < m_nodes.size(); i++) {
const AnimNodeResource& node_resource = m_nodes[i];
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
node_resource.m_type_name,
instance.m_nodes[i]);
std::vector<Socket>& resource_properties =
node_resource.m_socket_accessor->m_properties;
for (const auto & property : resource_properties) {
const std::string& name = property.m_name;
switch (property.m_type) {
case SocketType::SocketTypeBool:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.flag);
break;
case SocketType::SocketTypeInt:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.int_value);
break;
case SocketType::SocketTypeFloat:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.float_value);
break;
case SocketType::SocketTypeVec3:
node_instance_accessor->SetProperty<Vec3>(
name.c_str(),
property.m_value.vec3);
break;
case SocketType::SocketTypeQuat:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value.quat);
break;
case SocketType::SocketTypeString:
node_instance_accessor->SetProperty(
name.c_str(),
property.m_value_string);
break;
default:
std::cerr << "Invalid socket type "
<< static_cast<int>(property.m_type) << std::endl;
}
}
delete node_instance_accessor;
}
}
std::vector<Socket*> AnimGraphBlendTreeResource::getConstNodeInputs(
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
std::vector<Socket*> result;
for (size_t i = 0; i < m_nodes.size(); i++) {
for (size_t j = 0, num_inputs = instance_node_descriptors[i]->m_inputs.size();
j < num_inputs;
j++) {
Socket& input = instance_node_descriptors[i]->m_inputs[j];
if (*input.m_reference.ptr_ptr == nullptr) {
memcpy(&input.m_value, &m_nodes[i].m_socket_accessor->m_inputs[j].m_value, sizeof(Socket::SocketValue));
result.push_back(&input);
}
}
}
return result;
}
-138
View File
@@ -1,138 +0,0 @@
//
// Created by martin on 04.02.22.
//
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
#define ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include "AnimGraph.h"
#include "AnimGraphData.h"
#include "AnimGraphNodes.h"
#include "SyncTrack.h"
struct AnimNode;
struct AnimNodeResource {
std::string m_name;
std::string m_node_type_name;
AnimNode* m_anim_node = nullptr;
NodeDescriptorBase* m_socket_accessor = nullptr;
float m_position[2] = {0.f, 0.f};
};
//
// AnimGraphResource
//
struct AnimGraphConnectionResource {
size_t source_node_index = -1;
std::string source_socket_name;
size_t target_node_index = -1;
std::string target_socket_name;
};
struct AnimGraphBlendTreeResource {
std::string m_name;
std::vector<AnimNodeResource> m_nodes;
std::vector<AnimGraphConnectionResource> m_connections;
~AnimGraphBlendTreeResource() {
for (auto & m_node : m_nodes) {
delete m_node.m_anim_node;
delete m_node.m_socket_accessor;
}
}
AnimGraphBlendTreeResource() { 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]; }
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(const 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_node_index = getNodeIndex(source_node);
size_t target_node_index = getNodeIndex(target_node);
if (source_node_index >= m_nodes.size()
|| target_node_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->GetOutputSocket(source_socket_name.c_str());
Socket* target_socket =
target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str());
if (source_socket == nullptr || target_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
return false;
}
AnimGraphConnectionResource connection;
connection.source_node_index = source_node_index;
connection.source_socket_name = source_socket_name;
connection.target_node_index = target_node_index;
connection.target_socket_name = target_socket_name;
m_connections.push_back(connection);
return true;
}
bool isSocketConnected(
const AnimNodeResource& node,
const std::string& socket_name) {
size_t node_index = getNodeIndex(node);
for (const auto & connection : m_connections) {
if ((connection.source_node_index == node_index
&& connection.source_socket_name == socket_name)
|| ((connection.target_node_index == node_index)
&& connection.target_socket_name == socket_name)) {
return true;
}
}
return false;
}
void createInstance(AnimGraph& result) const;
void createRuntimeNodeInstances(AnimGraph& instance) const;
void prepareGraphIOData(AnimGraph& instance) const;
void setRuntimeNodeProperties(AnimGraph& instance) const;
std::vector<Socket*> getConstNodeInputs(std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
};
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
+8 -6
View File
@@ -161,7 +161,7 @@ struct Socket {
SocketValue m_value = {0}; SocketValue m_value = {0};
std::string m_value_string; std::string m_value_string;
union SocketReference { union SocketReference {
void* ptr; void* ptr = nullptr;
void** ptr_ptr; void** ptr_ptr;
}; };
SocketReference m_reference = {0}; SocketReference m_reference = {0};
@@ -262,12 +262,13 @@ SocketType GetSocketType() {
struct AnimGraphConnection { struct AnimGraphConnection {
AnimNode* m_source_node = nullptr; AnimNode* m_source_node = nullptr;
Socket m_source_socket; std::string m_source_socket_name = "";
AnimNode* m_target_node = nullptr; AnimNode* m_target_node = nullptr;
Socket m_target_socket; std::string m_target_socket_name = "";
Socket m_socket;
bool m_crosses_hierarchy = false;
}; };
struct NodeDescriptorBase { struct NodeDescriptorBase {
std::vector<Socket> m_inputs; std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs; std::vector<Socket> m_outputs;
@@ -417,7 +418,7 @@ struct NodeDescriptorBase {
return socket->GetValue<T>(); return socket->GetValue<T>();
} }
virtual void UpdateFlags(){}; virtual void UpdateFlags() {};
protected: protected:
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) { Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
@@ -430,7 +431,8 @@ struct NodeDescriptorBase {
return nullptr; return nullptr;
} }
const Socket* FindSocket(const char* name, const std::vector<Socket>& sockets) const { const Socket* FindSocket(const char* name, const std::vector<Socket>& sockets)
const {
for (int i = 0, n = sockets.size(); i < n; i++) { for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) { if (sockets[i].m_name == name) {
return &sockets[i]; return &sockets[i];
+421 -390
View File
@@ -7,15 +7,23 @@
#include <sstream> #include <sstream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h" #include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "AnimGraphBlendTreeResource.h" #include "AnimGraphResource.h"
#include "SkinnedMesh.h" #include "SkinnedMesh.h"
#include "imgui.h" #include "imgui.h"
#include "imnodes.h" #include "imnodes.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
static AnimGraphBlendTreeResource sGraphGresource = struct EditorState {
AnimGraphBlendTreeResource(); AnimGraphResource* rootGraphResource = nullptr;
static bool sGraphLoadedThisFrame = false;
std::vector<AnimGraphResource*> hierarchyStack;
size_t hierarchyStackIndex = 0;
bool isGraphLoadedThisFrame = false;
ImVec2 mousePopupStart = {};
};
static EditorState sEditorState;
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) { ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
switch (socket_type) { switch (socket_type) {
@@ -38,44 +46,41 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
return ImNodesPinShape_Quad; return ImNodesPinShape_Quad;
} }
int GetNodeInputSocketId(int node_index, int input_socket_index) { bool NodeSocketEditor(Socket& socket) {
return node_index * 1000 + input_socket_index; bool modified = false;
}
int GetNodeOutputSocketId(int node_index, int output_socket_index) {
return node_index * 1000 + 100 + output_socket_index;
}
void NodeSocketEditor(Socket& socket) {
int mode_current = static_cast<int>(socket.m_type); int mode_current = static_cast<int>(socket.m_type);
ImGui::InputText("Name", &socket.m_name); if (ImGui::InputText("Name", &socket.m_name)) {
modified = true;
}
if (ImGui::Combo( if (ImGui::Combo(
"Type", "Type",
&mode_current, &mode_current,
SocketTypeNames, SocketTypeNames,
sizeof(SocketTypeNames) / sizeof(char*))) { sizeof(SocketTypeNames) / sizeof(char*))) {
socket.m_type = static_cast<SocketType>(mode_current); socket.m_type = static_cast<SocketType>(mode_current);
modified = true;
} }
return modified;
} }
void RemoveConnectionsForSocket( void RemoveBlendTreeConnectionsForSocket(
AnimGraphBlendTreeResource& graph_resource, BlendTreeResource& blend_tree_resource,
AnimNodeResource& node_resource, AnimNodeResource* node_resource,
Socket& socket) { Socket& socket) {
std::vector<AnimGraphConnectionResource>::iterator iter = const BlendTreeConnectionResource* connection =
graph_resource.m_connections.begin(); blend_tree_resource.FindConnectionForSocket(node_resource, socket.m_name);
while (connection != nullptr) {
blend_tree_resource.DisconnectSockets(
blend_tree_resource.GetNode(connection->source_node_index),
connection->source_socket_name,
blend_tree_resource.GetNode(connection->target_node_index),
connection->target_socket_name);
while (iter != graph_resource.m_connections.end()) { connection = blend_tree_resource.FindConnectionForSocket(
// TODO adjust for refactor node_resource,
assert(false); socket.m_name);
// AnimGraphConnectionResource& connection = *iter;
// if (connection.m_source_node == &node_resource
// && connection.m_source_socket == &socket) {
// iter = sGraphGresource.m_connections.erase(iter);
// } else {
// iter++;
// }
} }
} }
@@ -160,24 +165,31 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
} }
void AnimGraphEditorRenderSidebar( void AnimGraphEditorRenderSidebar(
AnimGraphBlendTreeResource& graph_resource, BlendTreeResource& blend_tree_resource,
AnimNodeResource& node_resource) { AnimNodeResource* node_resource) {
ImGui::Text("[%s]", node_resource.m_node_type_name.c_str()); ImGui::Text(
"[%s (%2.2f, %2.2f)]",
node_resource->m_node_type_name.c_str(),
node_resource->m_position[0],
node_resource->m_position[1]);
char node_name_buffer[256]; char node_name_buffer[256];
memset(node_name_buffer, 0, sizeof(node_name_buffer)); memset(node_name_buffer, 0, sizeof(node_name_buffer));
strncpy( strncpy(
node_name_buffer, node_name_buffer,
node_resource.m_name.c_str(), node_resource->m_name.c_str(),
std::min(node_resource.m_name.size(), sizeof(node_name_buffer))); std::min(node_resource->m_name.size(), sizeof(node_name_buffer)));
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) { if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
node_resource.m_name = node_name_buffer; node_resource->m_name = node_name_buffer;
} }
int num_properties = node_resource.m_socket_accessor->m_properties.size(); int num_properties = 0;
if (node_resource->m_socket_accessor != nullptr) {
num_properties = node_resource->m_socket_accessor->m_properties.size();
}
for (int i = 0; i < num_properties; i++) { for (int i = 0; i < num_properties; i++) {
Socket& property = node_resource.m_socket_accessor->m_properties[i]; Socket& property = node_resource->m_socket_accessor->m_properties[i];
if (property.m_type == SocketType::SocketTypeInt) { if (property.m_type == SocketType::SocketTypeInt) {
ImGui::InputInt( ImGui::InputInt(
property.m_name.c_str(), property.m_name.c_str(),
@@ -212,223 +224,360 @@ void AnimGraphEditorRenderSidebar(
} }
} }
if (&node_resource == &graph_resource.getGraphOutputNode()) { if (node_resource == blend_tree_resource.GetGraphOutputNode()) {
ImGui::Text("Outputs"); ImGui::Text("Outputs");
// Graph outputs are the inputs of the output node! // Graph outputs are the inputs of the output node!
std::vector<Socket>& outputs = node_resource.m_socket_accessor->m_inputs; std::vector<Socket>& outputs = node_resource->m_socket_accessor->m_inputs;
std::vector<Socket>::iterator iter = outputs.begin(); std::vector<Socket>::iterator iter = outputs.begin();
while (iter != outputs.end()) { while (iter != outputs.end()) {
Socket& output = *iter; Socket& output = *iter;
ImGui::PushID(&output); ImGui::PushID(&output);
NodeSocketEditor(output); if (NodeSocketEditor(output)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = outputs;
}
if (ImGui::Button("X")) { if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, output); RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
output);
iter = outputs.erase(iter); iter = outputs.erase(iter);
} else { } else {
iter++; iter++;
} }
ImGui::PopID(); ImGui::PopID();
} }
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
"GraphFloatOutput");
}
} }
if (&node_resource == &graph_resource.getGraphInputNode()) { if (node_resource == blend_tree_resource.GetGraphInputNode()) {
ImGui::Text("Inputs"); ImGui::Text("Inputs");
// Graph inputs are the outputs of the input node! // Graph inputs are the outputs of the input node!
std::vector<Socket>& inputs = node_resource.m_socket_accessor->m_outputs; std::vector<Socket>& inputs = node_resource->m_socket_accessor->m_outputs;
std::vector<Socket>::iterator iter = inputs.begin(); std::vector<Socket>::iterator iter = inputs.begin();
while (iter != inputs.end()) { while (iter != inputs.end()) {
Socket& input = *iter; Socket& input = *iter;
ImGui::PushID(&input); ImGui::PushID(&input);
NodeSocketEditor(input); if (NodeSocketEditor(input)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = inputs;
}
if (ImGui::Button("X")) { if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, input); RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
input);
iter = inputs.erase(iter); iter = inputs.erase(iter);
} else { } else {
iter++; iter++;
} }
ImGui::PopID(); ImGui::PopID();
} }
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeInputSocket<float>(
"GraphFloatInput");
}
} }
} }
void AnimGraphEditorClear() {
if (ax::NodeEditor::GetCurrentEditor() != nullptr) {
ax::NodeEditor::ClearSelection();
}
if (sEditorState.rootGraphResource) {
delete sEditorState.rootGraphResource->m_socket_accessor;
}
delete sEditorState.rootGraphResource;
sEditorState.rootGraphResource = new AnimGraphResource();
sEditorState.rootGraphResource->m_name = "Root";
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
sEditorState.rootGraphResource->m_socket_accessor = new NodeDescriptorBase;
sEditorState.hierarchyStack.clear();
sEditorState.hierarchyStack.push_back(sEditorState.rootGraphResource);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] =
sEditorState.hierarchyStack.back();
sEditorState.hierarchyStackIndex = 0;
}
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
sGraphGresource.saveToFile("editor_graph.json");
}
if (ImGui::Button("Load")) {
sGraphGresource.loadFromFile("editor_graph.json");
sGraphLoadedThisFrame = true;
// for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
// const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
// ImNodes::SetNodeGridSpacePos(
// i,
// ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
// }
}
if (ImGui::Button("Clear")) {
sGraphGresource.clear();
}
char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy(
graph_name_buffer,
sGraphGresource.m_name.c_str(),
sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
sGraphGresource.m_name = graph_name_buffer;
}
ImGui::EndMenuBar();
//
// Node editor canvas
//
ax::NodeEditor::SetCurrentEditor(context); ax::NodeEditor::SetCurrentEditor(context);
ax::NodeEditor::Begin("Graph Editor");
#if 1 //
for (size_t node_id = 0, n = sGraphGresource.m_nodes.size(); node_id < n; // Menu bar
node_id++) { //
AnimNodeResource& node_resource = sGraphGresource.m_nodes[node_id];
if (node_id == 0 || node_id == 1) {
// continue;
}
if (sGraphLoadedThisFrame) {
ax::NodeEditor::SetNodePosition(
node_id,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
ax::NodeEditor::BeginNode(node_id);
ImGui::Text("%s", node_resource.m_node_type_name.c_str());
// Inputs
std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ax::NodeEditor::BeginPin(
GetNodeInputSocketId(static_cast<int>(node_id), static_cast<int>(j)),
ax::NodeEditor::PinKind::Input);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
// Outputs
std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j];
ax::NodeEditor::BeginPin(
GetNodeOutputSocketId(static_cast<int>(node_id), static_cast<int>(j)),
ax::NodeEditor::PinKind::Output);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
ax::NodeEditor::EndNode();
}
int link_id = 0;
for (size_t connection_id = 0, n = sGraphGresource.m_connections.size(); connection_id < n;
connection_id++) {
const AnimGraphConnectionResource& connection_resource = sGraphGresource.m_connections[connection_id];
const AnimNodeResource& source_node_resource = sGraphGresource.m_nodes[connection_resource.source_node_index];
int source_socket_index = source_node_resource.m_socket_accessor->GetOutputIndex(connection_resource.source_socket_name.c_str());
const AnimNodeResource& target_node_resource = sGraphGresource.m_nodes[connection_resource.target_node_index];
int target_socket_index = target_node_resource.m_socket_accessor->GetInputIndex(connection_resource.target_socket_name.c_str());
int source_socket_id = GetNodeOutputSocketId(static_cast<int>(connection_resource.source_node_index), source_socket_index);
int target_socket_id = GetNodeInputSocketId(static_cast<int>(connection_resource.target_node_index), target_socket_index);
ax::NodeEditor::Link(link_id++, source_socket_id, target_socket_id);
}
#endif
#if 1
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
if (input_pin_id && output_pin_id) {
if (ax::NodeEditor::AcceptNewItem()) {
}
}
}
}
ax::NodeEditor::EndCreate();
#endif
ax::NodeEditor::End();
sGraphLoadedThisFrame = false;
ax::NodeEditor::SetCurrentEditor(nullptr);
}
void LegacyAnimGraphEditorUpdate() {
ImGui::BeginMenuBar(); ImGui::BeginMenuBar();
if (ImGui::Button("Save")) { if (ImGui::Button("Save")) {
sGraphGresource.saveToFile("editor_graph.json"); sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
} }
if (ImGui::Button("Load")) { if (ImGui::Button("Load")) {
sGraphGresource.loadFromFile("editor_graph.json"); AnimGraphEditorClear();
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) { sEditorState.isGraphLoadedThisFrame = true;
const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::SetNodeGridSpacePos(
i,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
} }
if (ImGui::Button("Clear")) { if (ImGui::Button("Clear")) {
sGraphGresource.clear(); AnimGraphEditorClear();
}
if (ImGui::Button("Content")) {
ax::NodeEditor::NavigateToContent();
} }
char graph_name_buffer[256]; char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer)); memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy( strncpy(
graph_name_buffer, graph_name_buffer,
sGraphGresource.m_name.c_str(), sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_name.c_str(),
sizeof(graph_name_buffer)); sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) { if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
sGraphGresource.m_name = graph_name_buffer; sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]->m_name =
graph_name_buffer;
} }
ImGui::EndMenuBar(); ImGui::EndMenuBar();
//
// Breadcrumb navigation
//
for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) {
AnimGraphResource* graph_resource =
dynamic_cast<AnimGraphResource*>(sEditorState.hierarchyStack[i]);
ImGui::PushID(graph_resource);
bool highlight_button = i == sEditorState.hierarchyStackIndex;
if (highlight_button) {
ImGui::PushStyleColor(
ImGuiCol_Button,
(ImVec4)ImColor::HSV(1. / 7.0f, 0.6f, 0.6f));
}
if (ImGui::Button(graph_resource->m_name.c_str())) {
sEditorState.hierarchyStackIndex = i;
}
if (highlight_button) {
ImGui::PopStyleColor(1);
}
ImGui::PopID();
if (i < n - 1) {
ImGui::SameLine();
}
}
ImGui::Columns(2); ImGui::Columns(2);
// //
// Node editor canvas // Node editor canvas
// //
ImNodes::BeginNodeEditor(); ax::NodeEditor::Begin("Graph Editor");
for (size_t node_index = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumNodes();
node_index < n;
node_index++) {
AnimNodeResource* node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(node_index);
ax::NodeEditor::NodeId node_id(node_resource);
if (sEditorState.isGraphLoadedThisFrame) {
ax::NodeEditor::SetNodePosition(
node_id,
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
}
ax::NodeEditor::BeginNode(node_id);
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
// Inputs
std::vector<Socket> node_inputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSockets(node_resource);
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Input);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
// Outputs
std::vector<Socket> node_outputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSockets(node_resource);
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Output);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
ax::NodeEditor::EndNode();
ImVec2 node_position = ax::NodeEditor::GetNodePosition(node_id);
node_resource->m_position[0] = node_position.x;
node_resource->m_position[1] = node_position.y;
}
int link_id = 0;
for (size_t connection_id = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumConnections();
connection_id < n;
connection_id++) {
const BlendTreeConnectionResource* connection_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetConnection(connection_id);
const AnimNodeResource* source_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->source_node_index);
int source_socket_index =
source_node_resource->m_socket_accessor->GetOutputIndex(
connection_resource->source_socket_name.c_str());
const AnimNodeResource* target_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->target_node_index);
int target_socket_index =
target_node_resource->m_socket_accessor->GetInputIndex(
connection_resource->target_socket_name.c_str());
int source_socket_pin_id = NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(connection_resource->source_node_index),
source_socket_index);
int target_socket_pin_id = NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(connection_resource->target_node_index),
target_socket_index);
ax::NodeEditor::Link(
ax::NodeEditor::LinkId(connection_resource),
source_socket_pin_id,
target_socket_pin_id);
}
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
int source_node_index;
int source_node_socket_index;
const AnimNodeResource* source_node = nullptr;
const Socket* source_socket = nullptr;
if (input_pin_id) {
OutputPinIdToNodeIndexAndSocketIndex(
input_pin_id.Get(),
&source_node_index,
&source_node_socket_index);
source_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(source_node_index);
if (source_node->m_socket_accessor->m_outputs.size()
< source_node_socket_index) {
source_node_socket_index = -1;
} else {
source_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSocketByIndex(
source_node,
source_node_socket_index);
}
}
int target_node_index;
int target_node_socket_index;
const AnimNodeResource* target_node = nullptr;
const Socket* target_socket = nullptr;
if (output_pin_id) {
InputPinIdToNodeIndexAndSocketIndex(
output_pin_id.Get(),
&target_node_index,
&target_node_socket_index);
target_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(target_node_index);
if (target_node->m_socket_accessor->m_inputs.size()
< target_node_socket_index) {
target_node_socket_index = -1;
} else {
target_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSocketByIndex(
target_node,
target_node_socket_index);
}
}
if (input_pin_id && output_pin_id) {
if (source_socket == nullptr || target_socket == nullptr
|| !sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.IsConnectionValid(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name)) {
ax::NodeEditor::RejectNewItem();
} else if (ax::NodeEditor::AcceptNewItem()) {
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.ConnectSockets(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name);
}
}
}
}
ax::NodeEditor::EndCreate();
// Popup menu // Popup menu
{ {
const bool open_popup = const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
&& ImNodes::IsEditorHovered()
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); if (open_popup && ImGui::IsWindowHovered()) {
if (!ImGui::IsAnyItemHovered() && open_popup) { ax::NodeEditor::Suspend();
ImGui::OpenPopup("add node"); ImGui::OpenPopup("add node");
ax::NodeEditor::Resume();
sEditorState.mousePopupStart = ImGui::GetMousePos();
} }
ax::NodeEditor::Suspend();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (ImGui::BeginPopup("add node")) { if (ImGui::BeginPopup("add node")) {
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
std::string node_type_name = ""; std::string node_type_name = "";
if (ImGui::MenuItem("AnimSampler")) { if (ImGui::MenuItem("AnimSampler")) {
node_type_name = "AnimSampler"; node_type_name = "AnimSampler";
@@ -458,226 +607,108 @@ void LegacyAnimGraphEditorUpdate() {
node_type_name = "ConstScalarNode"; node_type_name = "ConstScalarNode";
} }
if (node_type_name != "") { if (ImGui::MenuItem("BlendTree")) {
AnimNodeResource node_resource = node_type_name = "BlendTree";
}
if (!node_type_name.empty()) {
AnimNodeResource* node_resource =
AnimNodeResourceFactory(node_type_name); AnimNodeResourceFactory(node_type_name);
size_t node_id = sGraphGresource.m_nodes.size(); ax::NodeEditor::SetNodePosition(
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos()); ax::NodeEditor::NodeId(node_resource),
sGraphGresource.m_nodes.push_back(node_resource); sEditorState.mousePopupStart);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.AddNode(node_resource);
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
ImGui::PopStyleVar();
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding); ax::NodeEditor::Resume();
} }
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) { ax::NodeEditor::End();
AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::BeginNode(i);
ImGui::PushItemWidth(110.0f);
// Header
ImNodes::BeginNodeTitleBar();
if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
ImGui::TextUnformatted("Graph Outputs");
} else if (&node_resource == &sGraphGresource.getGraphInputNode()) {
ImGui::TextUnformatted("Graph Inputs");
} else {
ImGui::TextUnformatted(node_resource.m_node_type_name.c_str());
}
ImNodes::EndNodeTitleBar();
// Inputs
std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ImColor socket_color = ImColor(255, 255, 255, 255);
if (socket.m_flags & SocketFlagAffectsTime) {
socket_color = ImColor(255, 128, 128, 255);
}
ImNodes::BeginInputAttribute(
GenerateInputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
socket_color);
ImGui::TextUnformatted(socket.m_name.c_str());
bool socket_connected =
sGraphGresource.isSocketConnected(node_resource, socket.m_name);
if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
ImGui::SameLine();
float socket_value = socket.m_value.float_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::DragFloat("##hidelabel", &socket_value, 0.01f)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
if (!socket_connected && (socket.m_type == SocketType::SocketTypeInt)) {
ImGui::SameLine();
int socket_value = socket.m_value.int_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::InputInt("##hidelabel", &socket_value, 1)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Outputs
const std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
const Socket& socket = node_outputs[j];
ImNodes::BeginOutputAttribute(
GenerateOutputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
ImColor(255, 255, 255, 255));
ImGui::TextUnformatted(socket.m_name.c_str());
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Graph output node
if (i == 0) {
if (ImGui::Button("+Output")) {
AnimNodeResource& graph_output_node =
sGraphGresource.getGraphOutputNode();
static float bla = 0.f;
std::string socket_name = "Output";
socket_name += std::to_string(
graph_output_node.m_socket_accessor->m_inputs.size());
graph_output_node.m_socket_accessor->RegisterInput<float>(
socket_name.c_str(),
nullptr);
}
} else if (i == 1) {
if (ImGui::Button("+Input")) {
AnimNodeResource& graph_input_node =
sGraphGresource.getGraphInputNode();
static float bla = 0.f;
std::string socket_name = "Input";
socket_name += std::to_string(
graph_input_node.m_socket_accessor->m_outputs.size());
graph_input_node.m_socket_accessor->RegisterOutput<float>(
socket_name.c_str(),
nullptr);
}
}
// Save state in node resource
ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
node_resource.m_position[0] = node_pos[0];
node_resource.m_position[1] = node_pos[1];
ImGui::PopItemWidth();
ImNodes::EndNode();
// Ensure flags such as SocketFlagAffectsTime are properly set.
node_resource.m_socket_accessor->UpdateFlags();
}
for (size_t i = 0, n = sGraphGresource.m_connections.size(); i < n; i++) {
const AnimGraphConnectionResource& connection =
sGraphGresource.m_connections[i];
int start_attr, end_attr;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[connection.source_node_index];
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
connection.source_socket_name.c_str());
const AnimNodeResource& target_node =
sGraphGresource.m_nodes[connection.target_node_index];
int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
connection.target_socket_name.c_str());
start_attr = GenerateOutputAttributeId(
connection.source_node_index,
source_socket_index);
end_attr = GenerateInputAttributeId(
connection.target_node_index,
target_socket_index);
ImNodes::Link(i, start_attr, end_attr);
}
ImNodes::EndNodeEditor();
// Handle newly created links.
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
int node_start_id;
int node_start_output_index;
SplitOutputAttributeId(
start_attr,
&node_start_id,
&node_start_output_index);
int node_end_id;
int node_end_input_index;
SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);
AnimGraphConnectionResource connection;
connection.source_node_index = node_start_id;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[node_start_id];
connection.source_socket_name =
source_node.m_socket_accessor->m_outputs[node_start_output_index]
.m_name;
connection.target_node_index = node_end_id;
const AnimNodeResource& target_node = sGraphGresource.m_nodes[node_end_id];
connection.target_socket_name =
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
sGraphGresource.m_connections.push_back(connection);
}
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
std::cerr << "Delete key!" << std::endl;
}
// Handle link detachements.
int link_id = 0;
if (ImNodes::IsLinkDestroyed(&link_id)) {
sGraphGresource.m_connections.erase(
sGraphGresource.m_connections.begin() + link_id);
}
int selected_nodes[ImNodes::NumSelectedNodes()];
ImNodes::GetSelectedNodes(selected_nodes);
// //
// Sidebar // Sidebar
// //
ImGui::NextColumn(); ImGui::NextColumn();
if (ImNodes::NumSelectedNodes() == 1) { if (ax::NodeEditor::GetSelectedObjectCount() > 0) {
if (selected_nodes[0] < sGraphGresource.m_nodes.size()) { ax::NodeEditor::NodeId selected_node_id = 0;
AnimNodeResource& selected_node =
sGraphGresource.m_nodes[selected_nodes[0]]; ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1);
AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
if (selected_node_id.Get() != 0) {
AnimGraphEditorRenderSidebar(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource,
selected_node_id.AsPointer<AnimNodeResource>());
} }
} }
ImGui::Columns(1); ImGui::Columns(1);
// Clear flag, however it may be re-set further down when handling double
// clicking into subgraphs.
sEditorState.isGraphLoadedThisFrame = false;
//
// Handle double click into subgraphs
//
ax::NodeEditor::NodeId double_clicked_node_id =
ax::NodeEditor::GetDoubleClickedNode();
if (!double_clicked_node_id.Invalid) {
AnimNodeResource* clicked_node_resource =
double_clicked_node_id.AsPointer<AnimNodeResource>();
if (clicked_node_resource != nullptr
&& clicked_node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* clicked_graph_resource =
dynamic_cast<AnimGraphResource*>(clicked_node_resource);
assert(clicked_graph_resource != nullptr);
if (sEditorState.hierarchyStack.size()
> sEditorState.hierarchyStackIndex + 1
&& sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex + 1]
== clicked_graph_resource) {
sEditorState.hierarchyStackIndex++;
} else {
sEditorState.hierarchyStack.resize(
sEditorState.hierarchyStackIndex + 1);
sEditorState.hierarchyStack.push_back(clicked_graph_resource);
sEditorState.hierarchyStackIndex++;
}
sEditorState.isGraphLoadedThisFrame = true;
ax::NodeEditor::ClearSelection();
}
}
ax::NodeEditor::LinkId hovered_link = ax::NodeEditor::GetHoveredLink();
if (!hovered_link.Invalid) {
BlendTreeConnectionResource* connection_resource =
hovered_link.AsPointer<BlendTreeConnectionResource>();
if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
BlendTreeResource* blend_tree_resource =
&sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource;
blend_tree_resource->DisconnectSockets(
blend_tree_resource->GetNode(connection_resource->source_node_index),
connection_resource->source_socket_name,
blend_tree_resource->GetNode(connection_resource->target_node_index),
connection_resource->target_socket_name);
ax::NodeEditor::DeleteLink(hovered_link);
}
}
ax::NodeEditor::SetCurrentEditor(nullptr);
} }
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) { void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
sGraphGresource.createInstance(anim_graph); sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
} }
+33 -5
View File
@@ -5,13 +5,13 @@
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H #ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
#define ANIMTESTBED_ANIMGRAPHEDITOR_H #define ANIMTESTBED_ANIMGRAPHEDITOR_H
#include "AnimGraph.h"
namespace ax::NodeEditor { namespace ax::NodeEditor {
struct EditorContext; struct EditorContext;
} // namespace ax::NodeEditor } // namespace ax::NodeEditor
struct SkinnedMesh; struct SkinnedMesh;
struct AnimGraphBlendTree;
struct SyncTrack;
inline int GenerateInputAttributeId(int node_id, int input_index) { inline int GenerateInputAttributeId(int node_id, int input_index) {
return ((input_index + 1) << 14) + node_id; return ((input_index + 1) << 14) + node_id;
@@ -33,14 +33,42 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
*output_index = (attribute_id >> 23) - 1; *output_index = (attribute_id >> 23) - 1;
} }
inline int NodeIndexAndSocketIndexToInputPinId(
int node_index,
int input_socket_index) {
return node_index * 1000 + input_socket_index;
}
inline int NodeIndexAndSocketIndexToOutputPinId(
int node_index,
int output_socket_index) {
return node_index * 1000 + 500 + output_socket_index;
}
inline void InputPinIdToNodeIndexAndSocketIndex(
unsigned long input_pin_id,
int* node_index,
int* socket_index) {
*socket_index = input_pin_id % 1000;
*node_index = (input_pin_id - *socket_index) / 1000;
}
inline void OutputPinIdToNodeIndexAndSocketIndex(
unsigned long output_pin_id,
int* node_index,
int* socket_index) {
*socket_index = ((output_pin_id - 500) % 1000);
*node_index = (output_pin_id - *socket_index) / 1000;
}
void SyncTrackEditor(SyncTrack* sync_track); void SyncTrackEditor(SyncTrack* sync_track);
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh); void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
void AnimGraphEditorClear();
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context); void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
void LegacyAnimGraphEditorUpdate(); void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph);
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H #endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
+39 -14
View File
@@ -3,13 +3,13 @@
// //
#include "AnimGraphNodes.h" #include "AnimGraphNodes.h"
#include "AnimGraphBlendTree.h"
#include "ozz/base/log.h" #include "AnimGraphBlendTree.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/base/io/archive.h" #include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h" #include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
AnimNode* AnimNodeFactory(const std::string& name) { AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result; AnimNode* result;
@@ -24,7 +24,7 @@ AnimNode* AnimNodeFactory(const std::string& name) {
} else if (name == "BlendTree") { } else if (name == "BlendTree") {
result = new AnimGraphBlendTree; result = new AnimGraphBlendTree;
} else if (name == "BlendTreeSockets") { } else if (name == "BlendTreeSockets") {
result = new BlendTreeNode; result = new BlendTreeSocketNode;
} else if (name == "MathAddNode") { } else if (name == "MathAddNode") {
result = new MathAddNode; result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") { } else if (name == "MathFloatToVec3Node") {
@@ -42,12 +42,39 @@ AnimNode* AnimNodeFactory(const std::string& name) {
return nullptr; return nullptr;
} }
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
void Blend2Node::Evaluate(AnimGraphContext& context) { void Blend2Node::Evaluate(AnimGraphContext& context) {
assert (i_input0 != nullptr); assert(i_input0 != nullptr);
assert (i_input1 != nullptr); assert(i_input1 != nullptr);
assert (i_blend_weight != nullptr); assert(i_blend_weight != nullptr);
assert (o_output != nullptr); assert(o_output != nullptr);
// perform blend // perform blend
ozz::animation::BlendingJob::Layer layers[2]; ozz::animation::BlendingJob::Layer layers[2];
@@ -71,12 +98,10 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
// //
// AnimSamplerNode // AnimSamplerNode
// //
AnimSamplerNode::~AnimSamplerNode() noexcept { AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; }
m_animation = nullptr;
}
bool AnimSamplerNode::Init(AnimGraphContext& context) { bool AnimSamplerNode::Init(AnimGraphContext& context) {
assert (m_animation == nullptr); assert(m_animation == nullptr);
assert(!m_filename.empty()); assert(!m_filename.empty());
AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter; AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter;
@@ -103,14 +128,14 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
context.m_animation_map[m_filename] = m_animation; context.m_animation_map[m_filename] = m_animation;
} }
assert (context.m_skeleton != nullptr); assert(context.m_skeleton != nullptr);
m_sampling_context.Resize(context.m_skeleton->num_joints()); m_sampling_context.Resize(context.m_skeleton->num_joints());
return true; return true;
} }
void AnimSamplerNode::Evaluate(AnimGraphContext& context) { void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
assert (o_output != nullptr); assert(o_output != nullptr);
ozz::animation::SamplingJob sampling_job; ozz::animation::SamplingJob sampling_job;
sampling_job.animation = m_animation; sampling_job.animation = m_animation;
+19 -59
View File
@@ -7,23 +7,13 @@
#include <vector> #include <vector>
#include "AnimNode.h"
#include "AnimGraphData.h" #include "AnimGraphData.h"
#include "AnimNode.h"
#include "SyncTrack.h" #include "SyncTrack.h"
#include "ozz/animation/runtime/sampling_job.h" #include "ozz/animation/runtime/sampling_job.h"
struct AnimNode; struct AnimNode;
//
// BlendTreeNode
//
struct BlendTreeNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeNode* node_) {}
};
// //
// Blend2Node // Blend2Node
// //
@@ -34,26 +24,27 @@ struct Blend2Node : public AnimNode {
float* i_blend_weight = nullptr; float* i_blend_weight = nullptr;
bool m_sync_blend = false; bool m_sync_blend = false;
virtual void MarkActiveInputs() override { void MarkActiveInputs(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) override {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node == nullptr) { if (input_node == nullptr) {
continue; continue;
} }
if (input.m_target_socket.m_name == "Input0" && *i_blend_weight < 0.999) { if (input.m_target_socket_name == "Input0" && *i_blend_weight < 0.999) {
input_node->m_state = AnimNodeEvalState::Activated; input_node->m_state = AnimNodeEvalState::Activated;
continue; continue;
} }
if (input.m_target_socket.m_name == "Input1" && *i_blend_weight > 0.001) { if (input.m_target_socket_name == "Input1" && *i_blend_weight > 0.001) {
input_node->m_state = AnimNodeEvalState::Activated; input_node->m_state = AnimNodeEvalState::Activated;
continue; continue;
} }
} }
} }
virtual void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
}; };
template <> template <>
@@ -80,7 +71,6 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
} }
}; };
// //
// SpeedScaleNode // SpeedScaleNode
// //
@@ -96,8 +86,8 @@ struct SpeedScaleNode : public AnimNode {
} }
void Evaluate(AnimGraphContext& context) override { void Evaluate(AnimGraphContext& context) override {
assert (i_input != nullptr); assert(i_input != nullptr);
assert (o_output != nullptr); assert(o_output != nullptr);
o_output->m_local_matrices = i_input->m_local_matrices; o_output->m_local_matrices = i_input->m_local_matrices;
}; };
@@ -116,7 +106,6 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
} }
}; };
// //
// AnimSamplerNode // AnimSamplerNode
// //
@@ -126,8 +115,8 @@ struct AnimSamplerNode : public AnimNode {
ozz::animation::SamplingJob::Context m_sampling_context; ozz::animation::SamplingJob::Context m_sampling_context;
ozz::animation::Animation* m_animation = nullptr; ozz::animation::Animation* m_animation = nullptr;
virtual ~AnimSamplerNode(); ~AnimSamplerNode() noexcept override;
virtual bool Init(AnimGraphContext& context) override; bool Init(AnimGraphContext& context) override;
void UpdateTime(float time_last, float time_now) override { void UpdateTime(float time_last, float time_now) override {
m_time_last = time_last; m_time_last = time_last;
m_time_now = time_now; m_time_now = time_now;
@@ -172,7 +161,6 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
} }
}; };
// //
// ConstScalarNode // ConstScalarNode
// //
@@ -180,9 +168,7 @@ struct ConstScalarNode : public AnimNode {
float* o_value = nullptr; float* o_value = nullptr;
float value = 0.f; float value = 0.f;
virtual void Evaluate(AnimGraphContext& context){ virtual void Evaluate(AnimGraphContext& context) { *o_value = value; };
*o_value = value;
};
}; };
template <> template <>
@@ -193,7 +179,6 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
} }
}; };
// //
// MathAddNode // MathAddNode
// //
@@ -203,8 +188,8 @@ struct MathAddNode : public AnimNode {
float* o_output = nullptr; float* o_output = nullptr;
void Evaluate(AnimGraphContext& context) override { void Evaluate(AnimGraphContext& context) override {
assert (i_input0 != nullptr); assert(i_input0 != nullptr);
assert (i_input1 != nullptr); assert(i_input1 != nullptr);
*o_output = *i_input0 + *i_input1; *o_output = *i_input0 + *i_input1;
} }
@@ -229,9 +214,9 @@ struct MathFloatToVec3Node : public AnimNode {
Vec3* o_output = nullptr; Vec3* o_output = nullptr;
void Evaluate(AnimGraphContext& context) override { void Evaluate(AnimGraphContext& context) override {
assert (i_input0 != nullptr); assert(i_input0 != nullptr);
assert (i_input1 != nullptr); assert(i_input1 != nullptr);
assert (i_input2 != nullptr); assert(i_input2 != nullptr);
o_output->v[0] = *i_input0; o_output->v[0] = *i_input0;
o_output->v[1] = *i_input1; o_output->v[1] = *i_input1;
@@ -249,35 +234,10 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
} }
}; };
AnimNode* AnimNodeFactory(const std::string& name); AnimNode* AnimNodeFactory(const std::string& name);
static inline NodeDescriptorBase* AnimNodeDescriptorFactory( NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name, const std::string& node_type_name,
AnimNode* node) { AnimNode* node);
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
#endif //ANIMTESTBED_ANIMGRAPHNODES_H #endif //ANIMTESTBED_ANIMGRAPHNODES_H
+611 -120
View File
@@ -1,3 +1,5 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
// //
// Created by martin on 17.03.24. // Created by martin on 17.03.24.
// //
@@ -8,15 +10,17 @@
#include <fstream> #include <fstream>
#include "3rdparty/json/json.hpp" #include "3rdparty/json/json.hpp"
#include "AnimGraphBlendTree.h" #include "AnimGraphBlendTree.h"
#include "AnimGraphNodes.h" #include "AnimGraphNodes.h"
using json = nlohmann::json; using json = nlohmann::json;
// forward declarations // forward declarations
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource); static json sAnimGraphResourceBlendTreeToJson(
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource); const AnimGraphResource& anim_graph_resource);
static bool sAnimGraphResourceBlendTreeFromJson(
const json& json_data,
AnimGraphResource* result_graph_resource);
// //
// Socket <-> json // Socket <-> json
@@ -143,7 +147,6 @@ json sAnimGraphNodeToJson(
} }
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
} }
for (const auto& socket : node->m_socket_accessor->m_inputs) { for (const auto& socket : node->m_socket_accessor->m_inputs) {
@@ -175,11 +178,11 @@ json sAnimGraphNodeToJson(
AnimNodeResource* sAnimGraphNodeFromJson( AnimNodeResource* sAnimGraphNodeFromJson(
const json& json_node, const json& json_node,
size_t node_index) { size_t node_index) {
std::string node_type = json_node["node_type"]; std::string node_type = json_node["node_type"];
if (node_type == "BlendTree") { if (node_type == "BlendTree") {
AnimGraphResource* result = new AnimGraphResource(); AnimGraphResource* result = new AnimGraphResource();
result->m_socket_accessor = new NodeDescriptorBase;
sAnimGraphResourceBlendTreeFromJson(json_node, result); sAnimGraphResourceBlendTreeFromJson(json_node, result);
return result; return result;
} }
@@ -220,16 +223,16 @@ AnimNodeResource* sAnimGraphNodeFromJson(
// //
// AnimGraphConnectionResource <-> Json // AnimGraphConnectionResource <-> Json
// //
json sAnimGraphConnectionToJson(const BlendTreeConnectionResource& connection) { json sAnimGraphConnectionToJson(const BlendTreeConnectionResource* connection) {
json result; json result;
result["type"] = "AnimGraphConnectionResource"; result["type"] = "AnimGraphConnectionResource";
result["source_node_index"] = connection.source_node_index; result["source_node_index"] = connection->source_node_index;
result["source_socket_name"] = connection.source_socket_name; result["source_socket_name"] = connection->source_socket_name;
result["target_node_index"] = connection.target_node_index; result["target_node_index"] = connection->target_node_index;
result["target_socket_name"] = connection.target_socket_name; result["target_socket_name"] = connection->target_socket_name;
return result; return result;
} }
@@ -247,44 +250,48 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson(
return connection; return connection;
} }
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource) { static json sAnimGraphResourceBlendTreeToJson(
const AnimGraphResource& anim_graph_resource) {
json result; json result;
result["name"] = anim_graph_resource.m_name; result["name"] = anim_graph_resource.m_name;
result["type"] = "AnimNodeResource"; result["type"] = "AnimNodeResource";
result["node_type"] = "BlendTree"; result["node_type"] = "BlendTree";
result["position"][0] = anim_graph_resource.m_position[0];
result["position"][1] = anim_graph_resource.m_position[1];
const BlendTreeResource& blend_tree_resource = anim_graph_resource.m_blend_tree_resource; const BlendTreeResource& blend_tree_resource =
anim_graph_resource.m_blend_tree_resource;
for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) { for (size_t i = 0; i < blend_tree_resource.GetNumNodes(); i++) {
const AnimNodeResource* node = blend_tree_resource.m_nodes[i]; const AnimNodeResource* node = blend_tree_resource.GetNode(i);
if (node->m_node_type_name == "BlendTree") { if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource = dynamic_cast<const AnimGraphResource*>(node); const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource); result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource);
} else { } else {
result["nodes"][i] = result["nodes"][i] =
sAnimGraphNodeToJson(node, i, blend_tree_resource.m_connections); sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections());
} }
} }
for (size_t i = 0; i < blend_tree_resource.m_connections.size(); i++) { for (size_t i = 0; i < blend_tree_resource.GetNumConnections(); i++) {
const BlendTreeConnectionResource& connection = const BlendTreeConnectionResource* connection =
blend_tree_resource.m_connections[i]; blend_tree_resource.GetConnection(i);
result["connections"][i] = sAnimGraphConnectionToJson(connection); result["connections"][i] = sAnimGraphConnectionToJson(connection);
} }
// Graph inputs and outputs // Graph inputs and outputs
{ {
const AnimNodeResource* graph_output_node = const AnimNodeResource* graph_output_node = blend_tree_resource.GetNode(0);
blend_tree_resource.m_nodes[0];
const std::vector<Socket> graph_inputs = const std::vector<Socket> graph_inputs =
graph_output_node->m_socket_accessor->m_inputs; graph_output_node->m_socket_accessor->m_inputs;
for (size_t i = 0; i < graph_inputs.size(); i++) { for (size_t i = 0; i < graph_inputs.size(); i++) {
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]); result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
} }
const AnimNodeResource* graph_input_node = blend_tree_resource.m_nodes[1]; const AnimNodeResource* graph_input_node = blend_tree_resource.GetNode(1);
const std::vector<Socket> graph_outputs = const std::vector<Socket> graph_outputs =
graph_input_node->m_socket_accessor->m_outputs; graph_input_node->m_socket_accessor->m_outputs;
for (size_t i = 0; i < graph_outputs.size(); i++) { for (size_t i = 0; i < graph_outputs.size(); i++) {
@@ -294,12 +301,17 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap
return result; return result;
} }
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource) { static bool sAnimGraphResourceBlendTreeFromJson(
BlendTreeResource& blend_tree_resource = result_graph_resource->m_blend_tree_resource; const json& json_data,
AnimGraphResource* result_graph_resource) {
BlendTreeResource& blend_tree_resource =
result_graph_resource->m_blend_tree_resource;
result_graph_resource->m_graph_type_name = "BlendTree"; result_graph_resource->m_graph_type_name = "BlendTree";
result_graph_resource->m_node_type_name = "BlendTree"; result_graph_resource->m_node_type_name = "BlendTree";
result_graph_resource->m_name = json_data["name"]; result_graph_resource->m_name = json_data["name"];
result_graph_resource->m_position[0] = json_data["position"][0];
result_graph_resource->m_position[1] = json_data["position"][1];
// Load nodes // Load nodes
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
@@ -312,52 +324,67 @@ static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraph
} }
AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i); AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i);
blend_tree_resource.m_nodes.push_back(node); blend_tree_resource.AddNode(node);
} }
// Graph outputs // Graph outputs
const json& graph_outputs = json_data["nodes"][0]["inputs"]; if (json_data["nodes"][0].contains("inputs")) {
for (const auto& graph_output : graph_outputs) { const json& graph_outputs = json_data["nodes"][0]["inputs"];
AnimNodeResource* graph_node = blend_tree_resource.m_nodes[0]; for (const auto& graph_output : graph_outputs) {
graph_node->m_socket_accessor->m_inputs.push_back( result_graph_resource->RegisterBlendTreeOutputSocket(
sJsonToSocket(graph_output)); sJsonToSocket(graph_output));
}
} }
// Graph inputs (optional) // Graph inputs (optional)
if (json_data["nodes"][1].contains("outputs")) { if (json_data["nodes"][1].contains("outputs")) {
const json& graph_inputs = json_data["nodes"][1]["outputs"]; const json& graph_inputs = json_data["nodes"][1]["outputs"];
for (const auto& graph_input : graph_inputs) { for (const auto& graph_input : graph_inputs) {
AnimNodeResource* graph_node = blend_tree_resource.m_nodes[1]; result_graph_resource->RegisterBlendTreeInputSocket(
graph_node->m_socket_accessor->m_outputs.push_back(
sJsonToSocket(graph_input)); sJsonToSocket(graph_input));
} }
} }
// Load connections // Load connections
for (const auto& json_connection : json_data["connections"]) { if (json_data.contains("connections")) {
if (json_connection["type"] != "AnimGraphConnectionResource") { for (const auto& json_connection : json_data["connections"]) {
std::cerr << "Invalid json object. Expected type " if (json_connection["type"] != "AnimGraphConnectionResource") {
"'AnimGraphConnectionResource' " std::cerr << "Invalid json object. Expected type "
"but got '" "'AnimGraphConnectionResource' "
<< json_connection["type"] << "'." << std::endl; "but got '"
return false; << json_connection["type"] << "'." << std::endl;
} return false;
}
BlendTreeConnectionResource connection = BlendTreeConnectionResource connection =
sAnimGraphConnectionFromJson(json_connection); sAnimGraphConnectionFromJson(json_connection);
blend_tree_resource.m_connections.push_back(connection);
blend_tree_resource.ConnectSockets(
blend_tree_resource.GetNode(connection.source_node_index),
connection.source_socket_name,
blend_tree_resource.GetNode(connection.target_node_index),
connection.target_socket_name);
}
} }
return true; return true;
} }
bool BlendTreeResource::ConnectSockets ( bool BlendTreeResource::ConnectSockets(
const AnimNodeResource* source_node, const AnimNodeResource* source_node,
const std::string& source_socket_name, const std::string& source_socket_name,
const AnimNodeResource* target_node, const AnimNodeResource* target_node,
const std::string& target_socket_name) { const std::string& target_socket_name) {
size_t source_node_index = GetNodeIndex(source_node); if (!IsConnectionValid(
size_t target_node_index = GetNodeIndex(target_node); source_node,
source_socket_name,
target_node,
target_socket_name)) {
return false;
}
int source_node_index = GetNodeIndex(source_node);
int target_node_index = GetNodeIndex(target_node);
if (source_node_index >= m_nodes.size() if (source_node_index >= m_nodes.size()
|| target_node_index >= m_nodes.size()) { || target_node_index >= m_nodes.size()) {
@@ -365,29 +392,20 @@ bool BlendTreeResource::ConnectSockets (
return false; return false;
} }
Socket* source_socket; Socket* source_socket = GetNodeOutputSocket(source_node, source_socket_name);
Socket* target_socket; Socket* target_socket = GetNodeInputSocket(target_node, target_socket_name);
if (target_node->m_node_type_name == "BlendTree") { if (source_socket == nullptr) {
const AnimGraphResource* target_graph_resource = dynamic_cast<const AnimGraphResource*>(target_node); std::cerr << "Cannot connect nodes: could not find source socket '"
AnimNodeResource* graph_output_node = target_graph_resource->m_blend_tree_resource.GetGraphInputNode(); << source_socket_name << "'." << std::endl;
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(target_socket_name.c_str());
} else {
target_socket =
target_node->m_socket_accessor->GetInputSocket(target_socket_name.c_str());
} }
if (source_node->m_node_type_name == "BlendTree") { if (target_socket == nullptr) {
const AnimGraphResource* source_graph_resource = dynamic_cast<const AnimGraphResource*>(source_node); std::cerr << "Cannot connect nodes: could not find target socket '"
AnimNodeResource* graph_output_node = source_graph_resource->m_blend_tree_resource.GetGraphOutputNode(); << target_socket_name << "'." << std::endl;
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(source_socket_name.c_str());
} else {
source_socket =
source_node->m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
} }
if (source_socket == nullptr || target_socket == nullptr) { if (target_socket == nullptr || source_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
return false; return false;
} }
@@ -398,10 +416,352 @@ bool BlendTreeResource::ConnectSockets (
connection.target_socket_name = target_socket_name; connection.target_socket_name = target_socket_name;
m_connections.push_back(connection); m_connections.push_back(connection);
m_node_input_connection_indices[target_node_index].emplace_back(
m_connections.size() - 1);
UpdateTreeTopologyInfo();
return true; return true;
} }
bool BlendTreeResource::DisconnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name) {
int source_node_index = GetNodeIndex(source_node);
int target_node_index = GetNodeIndex(target_node);
if (source_node_index < 0 || target_node_index < 0) {
return false;
}
int connection_index = -1;
for (size_t i = 0, n = m_connections.size(); i < n; i++) {
if (m_connections[i]
== BlendTreeConnectionResource{
source_node_index,
source_socket_name,
target_node_index,
target_socket_name}) {
connection_index = i;
break;
}
}
if (connection_index == -1) {
std::cerr << "Error: cannot disconnect sockets as connection is not found!"
<< std::endl;
return false;
}
// remove connection
m_connections.erase(m_connections.begin() + connection_index);
// remove the input connection of the target node
std::vector<size_t>& target_input_connections =
m_node_input_connection_indices[target_node_index];
std::vector<size_t>::iterator end_iterator = std::remove(
target_input_connections.begin(),
target_input_connections.end(),
connection_index);
target_input_connections.erase(end_iterator);
// Decrement all node input connection indices that are after the connection
// we have removed above.
for (size_t node_index = 0, n = m_nodes.size(); node_index < n;
node_index++) {
std::vector<size_t>& node_input_connections =
m_node_input_connection_indices[node_index];
for (size_t& node_connection_index : node_input_connections) {
if (node_connection_index > connection_index) {
node_connection_index--;
}
}
}
UpdateTreeTopologyInfo();
return true;
}
Socket* BlendTreeResource::GetNodeOutputSocket(
const AnimNodeResource* node,
const std::string& output_socket_name) const {
Socket* output_socket = nullptr;
if (node->m_socket_accessor) {
output_socket =
node->m_socket_accessor->GetOutputSocket(output_socket_name.c_str());
}
if (output_socket == nullptr && node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_socket =
blend_tree_resource.GetGraphOutputNode()
->m_socket_accessor->GetInputSocket(output_socket_name.c_str());
}
return output_socket;
}
const Socket* BlendTreeResource::GetNodeOutputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_output_index) const {
const std::vector<Socket>* output_sockets = nullptr;
if (node->m_socket_accessor) {
output_sockets = &node->m_socket_accessor->m_outputs;
} else if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_sockets =
&blend_tree_resource.GetGraphOutputNode()->m_socket_accessor->m_outputs;
}
if (output_sockets != nullptr
&& output_sockets->size() > socket_output_index) {
return &output_sockets->operator[](socket_output_index);
}
return nullptr;
}
Socket* BlendTreeResource::GetNodeInputSocket(
const AnimNodeResource* node,
const std::string& input_socket_name) const {
Socket* input_socket = nullptr;
if (node->m_socket_accessor) {
input_socket =
node->m_socket_accessor->GetInputSocket(input_socket_name.c_str());
}
if (input_socket == nullptr && node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
input_socket =
blend_tree_resource.GetGraphInputNode()
->m_socket_accessor->GetOutputSocket(input_socket_name.c_str());
}
return input_socket;
}
const Socket* BlendTreeResource::GetNodeInputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_input_index) const {
const std::vector<Socket>* output_sockets = nullptr;
if (node->m_socket_accessor) {
output_sockets = &node->m_socket_accessor->m_inputs;
} else if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
output_sockets =
&blend_tree_resource.GetGraphOutputNode()->m_socket_accessor->m_outputs;
}
if (output_sockets != nullptr
&& output_sockets->size() > socket_input_index) {
return &output_sockets->operator[](socket_input_index);
}
return nullptr;
}
std::vector<Socket> BlendTreeResource::GetNodeOutputSockets(
const AnimNodeResource* node) const {
if (node->m_socket_accessor) {
return node->m_socket_accessor->m_outputs;
}
if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
return blend_tree_resource.GetGraphOutputNode()
->m_socket_accessor->m_inputs;
}
return std::vector<Socket>();
}
std::vector<Socket> BlendTreeResource::GetNodeInputSockets(
const AnimNodeResource* node) const {
if (node->m_socket_accessor) {
return node->m_socket_accessor->m_inputs;
}
if (node->m_node_type_name == "BlendTree") {
const AnimGraphResource* graph_resource =
dynamic_cast<const AnimGraphResource*>(node);
const BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
return blend_tree_resource.GetGraphInputNode()
->m_socket_accessor->m_outputs;
}
return std::vector<Socket>();
}
bool BlendTreeResource::IsConnectionValid(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name) const {
// Check self connection
if (source_node == target_node) {
return false;
}
// Check for loops
int source_node_index = GetNodeIndex(source_node);
int target_node_index = GetNodeIndex(target_node);
if (std::find(
m_node_inputs_subtree[source_node_index].cbegin(),
m_node_inputs_subtree[source_node_index].cend(),
target_node_index)
!= m_node_inputs_subtree[source_node_index].end()) {
return false;
}
const Socket* source_socket =
GetNodeOutputSocket(source_node, source_socket_name);
const Socket* target_socket =
GetNodeInputSocket(target_node, target_socket_name);
if (source_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find source socket '"
<< source_socket_name << "'." << std::endl;
}
if (target_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find target socket '"
<< target_socket_name << "'." << std::endl;
}
if (target_socket == nullptr || source_socket == nullptr) {
return false;
}
// Check socket types
if (source_socket->m_type != target_socket->m_type) {
return false;
}
return true;
}
const BlendTreeConnectionResource* BlendTreeResource::FindConnectionForSocket(
const AnimNodeResource* node,
const std::string& socket_name) const {
int node_index = GetNodeIndex(node);
std::vector<BlendTreeConnectionResource>::const_iterator connection_iter =
std::find_if(
m_connections.begin(),
m_connections.end(),
[node_index,
socket_name](const BlendTreeConnectionResource& connection) {
if ((connection.source_node_index == node_index
&& connection.source_socket_name == socket_name)
|| (connection.target_node_index == node_index
&& connection.target_socket_name == socket_name)) {
return true;
}
return false;
});
if (connection_iter != m_connections.end()) {
return &*connection_iter;
}
return nullptr;
}
void BlendTreeResource::UpdateTreeTopologyInfo() {
// TODO: Updating eval order and subtrees may get slow with many nodes. An
// iterative approach would scale better. But let's leave that optimization
// for a later time.
UpdateNodeEvalOrder();
UpdateNodeSubtrees();
}
void BlendTreeResource::UpdateNodeEvalOrderRecursive(const size_t node_index) {
const std::vector<size_t>& node_input_connection_indices =
m_node_input_connection_indices[node_index];
for (size_t i = 0, n = node_input_connection_indices.size(); i < n; i++) {
const BlendTreeConnectionResource& connection_resource =
m_connections[node_input_connection_indices[i]];
if (connection_resource.source_node_index == 1) {
continue;
}
UpdateNodeEvalOrderRecursive(connection_resource.source_node_index);
}
if (node_index != 0 && node_index != 1) {
// In case we have multiple output connections from the node we here
// ensure that use the node evaluation that is the furthest away from
// the output.
std::vector<size_t>::iterator find_iter = std::find(
m_node_eval_order.begin(),
m_node_eval_order.end(),
node_index);
if (find_iter != m_node_eval_order.end()) {
m_node_eval_order.erase(find_iter);
}
m_node_eval_order.push_back(node_index);
}
}
void BlendTreeResource::UpdateNodeSubtrees() {
for (size_t eval_index = 0, num_eval_nodes = m_node_eval_order.size();
eval_index < num_eval_nodes;
eval_index++) {
size_t node_index = m_node_eval_order[eval_index];
m_node_inputs_subtree[node_index].clear();
const std::vector<size_t>& node_input_connection_indices =
m_node_input_connection_indices[node_index];
for (size_t i = 0, n = node_input_connection_indices.size(); i < n; i++) {
const BlendTreeConnectionResource& connection_resource =
m_connections[node_input_connection_indices[i]];
m_node_inputs_subtree[node_index].emplace_back(
connection_resource.source_node_index);
m_node_inputs_subtree[node_index].insert(
m_node_inputs_subtree[node_index].end(),
m_node_inputs_subtree[connection_resource.source_node_index].cbegin(),
m_node_inputs_subtree[connection_resource.source_node_index].cend());
}
}
}
bool AnimGraphResource::LoadFromFile(const char* filename) { bool AnimGraphResource::LoadFromFile(const char* filename) {
Clear();
std::ifstream input_file; std::ifstream input_file;
input_file.open(filename); input_file.open(filename);
std::stringstream buffer; std::stringstream buffer;
@@ -443,7 +803,8 @@ bool AnimGraphResource::SaveToFile(const char* filename) const {
return SaveStateMachineResourceToFile(filename); return SaveStateMachineResourceToFile(filename);
} }
std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "." << std::endl; std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "."
<< std::endl;
return false; return false;
} }
@@ -452,8 +813,7 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
const char* filename) const { const char* filename) const {
json result; json result;
result = sAnimGraphResourceBlendTreeToJson( result = sAnimGraphResourceBlendTreeToJson(*this);
*this);
std::ofstream output_file; std::ofstream output_file;
output_file.open(filename); output_file.open(filename);
@@ -466,14 +826,20 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
void AnimGraphResource::CreateBlendTreeInstance( void AnimGraphResource::CreateBlendTreeInstance(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
if (m_node_type_name != "BlendTree") { if (m_node_type_name != "BlendTree") {
std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '" std::cerr
<< m_graph_type_name << "'." << std::endl; << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
<< m_graph_type_name << "'." << std::endl;
return; return;
} }
result.m_name = m_name;
NodeSocketDataOffsetMap node_offset_map;
CreateBlendTreeRuntimeNodeInstances(result); CreateBlendTreeRuntimeNodeInstances(result);
PrepareBlendTreeIOData(result); PrepareBlendTreeIOData(result, node_offset_map);
SetRuntimeNodeProperties(result); SetRuntimeNodeProperties(result);
CreateBlendTreeConnectionInstances(result, node_offset_map);
result.UpdateOrderedNodes(); result.UpdateOrderedNodes();
result.ResetNodeStates(); result.ResetNodeStates();
@@ -481,18 +847,24 @@ void AnimGraphResource::CreateBlendTreeInstance(
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances( void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
for (auto node_resource : m_blend_tree_resource.m_nodes) { for (const AnimNodeResource* node_resource :
m_blend_tree_resource.GetNodes()) {
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name); AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
if (node_resource->m_node_type_name == "BlendTree") { if (node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* embedded_blend_tree_resource = dynamic_cast<AnimGraphResource*>(node_resource); const AnimGraphResource* embedded_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(node_resource);
assert(embedded_blend_tree_resource != nullptr); assert(embedded_blend_tree_resource != nullptr);
AnimGraphBlendTree* embedded_blend_tree = dynamic_cast<AnimGraphBlendTree*>(node); AnimGraphBlendTree* embedded_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(node);
assert(embedded_blend_tree != nullptr); assert(embedded_blend_tree != nullptr);
embedded_blend_tree_resource->CreateBlendTreeInstance(*embedded_blend_tree); embedded_blend_tree_resource->CreateBlendTreeInstance(
embedded_blend_tree_resource->m_socket_accessor->m_inputs = embedded_blend_tree->m_node_descriptor->m_outputs; *embedded_blend_tree);
embedded_blend_tree_resource->m_socket_accessor->m_outputs = embedded_blend_tree->m_node_descriptor->m_inputs; embedded_blend_tree_resource->m_socket_accessor->m_inputs =
embedded_blend_tree->m_node_descriptor->m_outputs;
embedded_blend_tree_resource->m_socket_accessor->m_outputs =
embedded_blend_tree->m_node_descriptor->m_inputs;
} }
node->m_name = node_resource->m_name; node->m_name = node_resource->m_name;
@@ -506,20 +878,21 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
} }
void AnimGraphResource::PrepareBlendTreeIOData( void AnimGraphResource::PrepareBlendTreeIOData(
AnimGraphBlendTree& instance) const { AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const {
instance.m_node_descriptor = instance.m_node_descriptor =
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]); AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
instance.m_node_descriptor->m_outputs = instance.m_node_descriptor->m_outputs =
m_blend_tree_resource.m_nodes[1]->m_socket_accessor->m_outputs; m_blend_tree_resource.GetNode(1)->m_socket_accessor->m_outputs;
instance.m_node_descriptor->m_inputs = instance.m_node_descriptor->m_inputs =
m_blend_tree_resource.m_nodes[0]->m_socket_accessor->m_inputs; m_blend_tree_resource.GetNode(0)->m_socket_accessor->m_inputs;
// //
// graph inputs // graph inputs
// //
int input_block_size = 0; int input_block_size = 0;
std::vector<Socket>& graph_inputs = instance.getGraphInputs(); std::vector<Socket>& graph_inputs = instance.GetGraphInputs();
for (int i = 0; i < graph_inputs.size(); i++) { for (int i = 0; i < graph_inputs.size(); i++) {
input_block_size += sizeof(void*); input_block_size += sizeof(void*);
} }
@@ -542,7 +915,7 @@ void AnimGraphResource::PrepareBlendTreeIOData(
// graph outputs // graph outputs
// //
int output_block_size = 0; int output_block_size = 0;
std::vector<Socket>& graph_outputs = instance.getGraphOutputs(); std::vector<Socket>& graph_outputs = instance.GetGraphOutputs();
for (int i = 0; i < graph_outputs.size(); i++) { for (int i = 0; i < graph_outputs.size(); i++) {
output_block_size += sizeof(void*); output_block_size += sizeof(void*);
} }
@@ -559,35 +932,49 @@ void AnimGraphResource::PrepareBlendTreeIOData(
output_block_offset += sizeof(void*); output_block_offset += sizeof(void*);
} }
// connections: make source and target sockets point to the same address in the connection data storage. //
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once. // connecton data storage
//
size_t connection_data_storage_size = 0; size_t connection_data_storage_size = 0;
for (const auto& connection : m_blend_tree_resource.m_connections) { for (const BlendTreeConnectionResource& connection :
m_blend_tree_resource.GetConnections()) {
const AnimNodeResource* source_node = const AnimNodeResource* source_node =
m_blend_tree_resource.m_nodes[connection.source_node_index]; m_blend_tree_resource.GetNode(connection.source_node_index);
Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket( Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket(
connection.source_socket_name.c_str()); connection.source_socket_name.c_str());
connection_data_storage_size += source_socket->m_type_size;
NodeSocketPair source_socket_pair{source_node, source_socket->m_name};
if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) {
node_offset_map.insert(
{source_socket_pair, connection_data_storage_size});
connection_data_storage_size += source_socket->m_type_size;
}
} }
if (connection_data_storage_size > 0) { if (connection_data_storage_size > 0) {
instance.m_connection_data_storage = new char[connection_data_storage_size]; instance.m_connection_data_storage = new char[connection_data_storage_size];
memset(instance.m_connection_data_storage, 0, connection_data_storage_size); memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
} }
}
void AnimGraphResource::CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const {
std::vector<NodeDescriptorBase*> instance_node_descriptors( std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_blend_tree_resource.m_nodes.size(), m_blend_tree_resource.GetNumNodes(),
nullptr); nullptr);
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) {
instance_node_descriptors[i] = AnimNodeDescriptorFactory( instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_blend_tree_resource.m_nodes[i]->m_node_type_name, m_blend_tree_resource.GetNode(i)->m_node_type_name,
instance.m_nodes[i]); instance.m_nodes[i]);
if (i > 1 && m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") { if (i > 1
&& m_blend_tree_resource.GetNode(i)->m_node_type_name == "BlendTree") {
instance_node_descriptors[i]->m_inputs = instance_node_descriptors[i]->m_inputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs; m_blend_tree_resource.GetNode(i)->m_socket_accessor->m_inputs;
instance_node_descriptors[i]->m_outputs = instance_node_descriptors[i]->m_outputs =
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_outputs; m_blend_tree_resource.GetNode(i)->m_socket_accessor->m_outputs;
} }
} }
@@ -595,8 +982,8 @@ void AnimGraphResource::PrepareBlendTreeIOData(
instance_node_descriptors[1]->m_outputs = instance_node_descriptors[1]->m_outputs =
instance.m_node_descriptor->m_outputs; instance.m_node_descriptor->m_outputs;
size_t connection_data_offset = 0; for (const BlendTreeConnectionResource& connection :
for (const auto& connection : m_blend_tree_resource.m_connections) { m_blend_tree_resource.GetConnections()) {
NodeDescriptorBase* source_node_descriptor = NodeDescriptorBase* source_node_descriptor =
instance_node_descriptors[connection.source_node_index]; instance_node_descriptors[connection.source_node_index];
NodeDescriptorBase* target_node_descriptor = NodeDescriptorBase* target_node_descriptor =
@@ -611,30 +998,131 @@ void AnimGraphResource::PrepareBlendTreeIOData(
connection.target_socket_name.c_str()); connection.target_socket_name.c_str());
AnimGraphConnection instance_connection; AnimGraphConnection instance_connection;
if (source_node->m_node_type_name == "BlendTree") {
// For embedded subgraphs we have to ensure two things:
// 1. The parent graph target node must activate the source node within
// the embedded subgraph.
// 2. The parent graph target node must activate the AnimNode of the
// parent graph which contains the embedded subgraph. Otherwise, the
// embedded graph does not get evaluated.
// For each case we have to add a connection here. First we insert the
// default connection within the graph (i.e. for case 2). Then we alter
// the source node such that it points to the node within the embedded
// subgraph.
AnimGraphConnection embedded_graph_activation_connection;
embedded_graph_activation_connection.m_source_node = source_node;
embedded_graph_activation_connection.m_source_socket_name =
source_socket->m_name;
embedded_graph_activation_connection.m_target_node = target_node;
embedded_graph_activation_connection.m_target_socket_name =
target_socket->m_name;
embedded_graph_activation_connection.m_socket = *target_socket;
instance.m_node_input_connections[connection.target_node_index].push_back(
embedded_graph_activation_connection);
instance.m_node_output_connections[connection.source_node_index]
.push_back(embedded_graph_activation_connection);
const AnimGraphResource* source_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(
m_blend_tree_resource.GetNode(connection.source_node_index));
AnimGraphBlendTree* source_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(source_node);
size_t source_blend_tree_output_node_index =
source_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForOutputSocket(connection.source_socket_name);
source_node =
source_blend_tree->m_nodes[source_blend_tree_output_node_index];
instance_connection.m_crosses_hierarchy = true;
} else if (target_node->m_node_type_name == "BlendTree") {
// When a connection points to an embedded blend tree we have to ensure
// that the embedded node knows about its connection partner in the parent
// tree. This allows the embedded node to properly activate the node in
// the parent graph.
const AnimGraphResource* target_blend_tree_resource =
dynamic_cast<const AnimGraphResource*>(
m_blend_tree_resource.GetNode(connection.target_node_index));
AnimGraphBlendTree* target_blend_tree =
dynamic_cast<AnimGraphBlendTree*>(target_node);
size_t target_blend_tree_output_node_index =
target_blend_tree_resource->m_blend_tree_resource
.GetNodeIndexForInputSocket(connection.target_socket_name);
target_node =
target_blend_tree->m_nodes[target_blend_tree_output_node_index];
// Make sure that the embedded node input activates the parent graph node.
std::vector<AnimGraphConnection>& embeded_target_node_inputs =
target_blend_tree
->m_node_input_connections[target_blend_tree_output_node_index];
for (size_t j = 0; j < embeded_target_node_inputs.size(); j++) {
AnimGraphConnection& embedded_target_connection =
embeded_target_node_inputs[j];
if (embedded_target_connection.m_source_socket_name
== target_socket->m_name) {
embedded_target_connection.m_source_node = source_node;
embedded_target_connection.m_crosses_hierarchy = true;
// In addition: make sure we expose the embedded socket to the
// connection in the parent blend tree. That way
// parent_tree.SetValue<>() correctly propagates the value to the
// embedded node socket.
target_socket = &embedded_target_connection.m_socket;
}
}
}
instance_connection.m_source_node = source_node; instance_connection.m_source_node = source_node;
instance_connection.m_source_socket = *source_socket; instance_connection.m_source_socket_name = source_socket->m_name;
instance_connection.m_target_node = target_node; instance_connection.m_target_node = target_node;
instance_connection.m_target_socket = *target_socket; instance_connection.m_target_socket_name = target_socket->m_name;
instance_connection.m_socket = *target_socket;
size_t socket_data_offset = 0;
if (connection.source_node_index == 1) {
instance_connection.m_socket = *target_socket;
} else {
if (connection.target_node_index == 0) {
instance_connection.m_socket = *source_socket;
}
NodeSocketPair node_socket_pair{
m_blend_tree_resource.GetNode(connection.source_node_index),
source_socket->m_name};
NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter =
node_offset_map.find(node_socket_pair);
if (socket_data_offset_iter != node_offset_map.end()) {
socket_data_offset = socket_data_offset_iter->second;
} else {
std::cerr << "Error: could not find data offset for node '"
<< source_node->m_name << "' and socket '"
<< source_socket->m_name << "'." << std::endl;
assert(false);
abort();
}
*target_socket->m_reference.ptr_ptr =
&instance.m_connection_data_storage[socket_data_offset];
*source_socket->m_reference.ptr_ptr =
&instance.m_connection_data_storage[socket_data_offset];
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
instance.m_animdata_blocks.push_back(
(AnimData*)(&instance
.m_connection_data_storage[socket_data_offset]));
}
}
instance.m_node_input_connections[connection.target_node_index].push_back( instance.m_node_input_connections[connection.target_node_index].push_back(
instance_connection); instance_connection);
instance.m_node_output_connections[connection.source_node_index].push_back( instance.m_node_output_connections[connection.source_node_index].push_back(
instance_connection); instance_connection);
source_node_descriptor->SetOutputUnchecked(
connection.source_socket_name.c_str(),
&instance.m_connection_data_storage[connection_data_offset]);
target_node_descriptor->SetInputUnchecked(
connection.target_socket_name.c_str(),
&instance.m_connection_data_storage[connection_data_offset]);
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
instance.m_animdata_blocks.push_back(
(AnimData*)(&instance
.m_connection_data_storage[connection_data_offset]));
}
connection_data_offset += source_socket->m_type_size;
} }
// //
@@ -675,18 +1163,19 @@ void AnimGraphResource::PrepareBlendTreeIOData(
const_input_buffer_offset += i->m_type_size; const_input_buffer_offset += i->m_type_size;
} }
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) { for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) {
delete instance_node_descriptors[i]; delete instance_node_descriptors[i];
} }
} }
void AnimGraphResource::SetRuntimeNodeProperties( void AnimGraphResource::SetRuntimeNodeProperties(
AnimGraphBlendTree& result) const { AnimGraphBlendTree& result) const {
for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) { for (int i = 2; i < m_blend_tree_resource.GetNumNodes(); i++) {
const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i]; const AnimNodeResource* node_resource = m_blend_tree_resource.GetNode(i);
NodeDescriptorBase* node_instance_accessor = NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
AnimNodeDescriptorFactory(node_resource->m_node_type_name, result.m_nodes[i]); node_resource->m_node_type_name,
result.m_nodes[i]);
std::vector<Socket>& resource_properties = std::vector<Socket>& resource_properties =
node_resource->m_socket_accessor->m_properties; node_resource->m_socket_accessor->m_properties;
@@ -741,8 +1230,10 @@ bool AnimGraphResource::SaveStateMachineResourceToFile(
return false; return false;
} }
bool AnimGraphResource::LoadStateMachineResourceFromJson(nlohmann::json const& json_data) { bool AnimGraphResource::LoadStateMachineResourceFromJson(
nlohmann::json const& json_data) {
assert(false && "Not yet implemented"); assert(false && "Not yet implemented");
return false; return false;
} }
#pragma clang diagnostic pop
+246 -22
View File
@@ -5,10 +5,8 @@
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H #ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H #define ANIMTESTBED_ANIMGRAPHRESOURCE_H
#include "AnimGraph.h"
#include "AnimGraphNodes.h"
#include "3rdparty/json/json.hpp" #include "3rdparty/json/json.hpp"
#include "AnimGraphNodes.h"
struct AnimGraphBlendTree; struct AnimGraphBlendTree;
struct AnimGraphStateMachine; struct AnimGraphStateMachine;
@@ -23,26 +21,38 @@ struct AnimNodeResource {
float m_position[2] = {0.f, 0.f}; float m_position[2] = {0.f, 0.f};
}; };
static inline AnimNodeResource* AnimNodeResourceFactory(const std::string& node_type_name); static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name);
struct BlendTreeConnectionResource { struct BlendTreeConnectionResource {
size_t source_node_index = -1; int source_node_index = -1;
std::string source_socket_name; std::string source_socket_name;
size_t target_node_index = -1; int target_node_index = -1;
std::string target_socket_name; std::string target_socket_name;
bool operator==(const BlendTreeConnectionResource& other) const {
return (
source_node_index == other.source_node_index
&& target_node_index == other.target_node_index
&& source_socket_name == other.source_socket_name
&& target_socket_name == other.target_socket_name);
}
}; };
struct BlendTreeResource { struct BlendTreeResource {
std::vector<AnimNodeResource*> m_nodes; std::vector<std::vector<size_t> > m_node_input_connection_indices;
std::vector<BlendTreeConnectionResource> m_connections; std::vector<std::vector<size_t> > m_node_inputs_subtree;
~BlendTreeResource() { CleanupNodes(); ~BlendTreeResource() { CleanupNodes(); }
}
void Reset() { void Reset() {
CleanupNodes(); CleanupNodes();
m_connections.clear(); m_connections.clear();
m_node_input_connection_indices.clear();
m_node_inputs_subtree.clear();
} }
void CleanupNodes() { void CleanupNodes() {
@@ -56,43 +66,133 @@ struct BlendTreeResource {
} }
void InitGraphConnectors() { void InitGraphConnectors() {
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets")); AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
m_nodes[0]->m_name = "Outputs"; AnimNodeResource* output_node = GetGraphOutputNode();
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets")); output_node->m_name = "Outputs";
m_nodes[1]->m_name = "Inputs";
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* input_node = GetGraphInputNode();
output_node->m_name = "Inputs";
} }
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { return m_nodes[0]; } [[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const { return m_nodes[1]; } return m_nodes[0];
}
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const {
return m_nodes[1];
}
size_t GetNodeIndex(const AnimNodeResource* node_resource) const { int GetNodeIndex(const AnimNodeResource* node_resource) const {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (m_nodes[i] == node_resource) { if (m_nodes[i] == node_resource) {
return i; return i;
} }
} }
std::cerr << "Error: could not find node index for node resource "
<< node_resource << std::endl;
return -1; return -1;
} }
bool ConnectSockets ( [[maybe_unused]] size_t AddNode(AnimNodeResource* node_resource) {
m_nodes.push_back(node_resource);
m_node_input_connection_indices.emplace_back();
m_node_inputs_subtree.emplace_back();
return m_nodes.size() - 1;
}
[[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); }
[[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; }
[[nodiscard]] const AnimNodeResource* GetNode(size_t i) const {
return m_nodes[i];
}
[[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const {
return m_nodes;
}
[[nodiscard]] size_t GetNumConnections() const {
return m_connections.size();
}
[[nodiscard]] BlendTreeConnectionResource* GetConnection(size_t i) {
return &m_connections[i];
}
[[nodiscard]] const BlendTreeConnectionResource* GetConnection(
size_t i) const {
return &m_connections[i];
}
[[nodiscard]] const std::vector<BlendTreeConnectionResource>& GetConnections()
const {
return m_connections;
}
Socket* GetNodeOutputSocket(
const AnimNodeResource* node,
const std::string& output_socket_name) const;
const Socket* GetNodeOutputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_output_index) const;
Socket* GetNodeInputSocket(
const AnimNodeResource* node,
const std::string& input_socket_name) const;
const Socket* GetNodeInputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_input_index) const;
std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const;
std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const;
bool ConnectSockets(
const AnimNodeResource* source_node, const AnimNodeResource* source_node,
const std::string& source_socket_name, const std::string& source_socket_name,
const AnimNodeResource* target_node, const AnimNodeResource* target_node,
const std::string& target_socket_name); const std::string& target_socket_name);
bool DisconnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name);
bool IsConnectionValid(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name) const;
const BlendTreeConnectionResource* FindConnectionForSocket(
const AnimNodeResource* node,
const std::string& socket_name) const;
bool IsSocketConnected(
const AnimNodeResource* node,
const std::string& socket_name) const {
const BlendTreeConnectionResource* connection =
FindConnectionForSocket(node, socket_name);
return connection != nullptr;
}
std::vector<Socket*> GetConstantNodeInputs( std::vector<Socket*> GetConstantNodeInputs(
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const { std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
std::vector<Socket*> result; std::vector<Socket*> result;
for (size_t i = 0; i < m_nodes.size(); i++) { for (size_t i = 0; i < m_nodes.size(); i++) {
for (size_t j = 0, num_inputs = instance_node_descriptors[i]->m_inputs.size(); for (size_t j = 0,
num_inputs = instance_node_descriptors[i]->m_inputs.size();
j < num_inputs; j < num_inputs;
j++) { j++) {
Socket& input = instance_node_descriptors[i]->m_inputs[j]; Socket& input = instance_node_descriptors[i]->m_inputs[j];
if (*input.m_reference.ptr_ptr == nullptr) { if (*input.m_reference.ptr_ptr == nullptr) {
memcpy(&input.m_value, &m_nodes[i]->m_socket_accessor->m_inputs[j].m_value, sizeof(Socket::SocketValue)); memcpy(
&input.m_value,
&m_nodes[i]->m_socket_accessor->m_inputs[j].m_value,
sizeof(Socket::SocketValue));
result.push_back(&input); result.push_back(&input);
} }
} }
@@ -100,6 +200,54 @@ struct BlendTreeResource {
return result; return result;
} }
size_t GetNodeIndexForOutputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.target_node_index == 0
&& connection.target_socket_name == socket_name) {
return connection.source_node_index;
}
}
std::cerr << "Error: could not find a node connected to output '"
<< socket_name << "'." << std::endl;
return -1;
}
size_t GetNodeIndexForInputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.source_node_index == 1
&& connection.source_socket_name == socket_name) {
return connection.target_node_index;
}
}
std::cerr << "Error: could not find a node connected to input '"
<< socket_name << "'." << std::endl;
return -1;
}
void UpdateTreeTopologyInfo();
[[nodiscard]] const std::vector<size_t>& GetNodeEvalOrder() const {
return m_node_eval_order;
}
private:
void UpdateNodeEvalOrder() {
m_node_eval_order.clear();
UpdateNodeEvalOrderRecursive(0);
}
void UpdateNodeEvalOrderRecursive(size_t node_index);
void UpdateNodeSubtrees();
std::vector<AnimNodeResource*> m_nodes;
std::vector<BlendTreeConnectionResource> m_connections;
std::vector<size_t> m_node_eval_order;
}; };
struct StateMachineTransitionResources { struct StateMachineTransitionResources {
@@ -114,23 +262,99 @@ struct StateMachineResource {
std::vector<StateMachineTransitionResources> m_transitions; std::vector<StateMachineTransitionResources> m_transitions;
}; };
struct AnimGraphResource: AnimNodeResource { struct AnimGraphResource : AnimNodeResource {
std::string m_graph_type_name; std::string m_graph_type_name;
BlendTreeResource m_blend_tree_resource; BlendTreeResource m_blend_tree_resource;
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketPair;
typedef std::map<NodeSocketPair, int> NodeSocketDataOffsetMap;
StateMachineResource m_state_machine_resource; StateMachineResource m_state_machine_resource;
void Clear() { m_blend_tree_resource.Reset(); }
bool SaveToFile(const char* filename) const; bool SaveToFile(const char* filename) const;
bool LoadFromFile(const char* filename); bool LoadFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const; void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
template <typename T>
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeInputSocket(socket);
}
bool RegisterBlendTreeInputSocket(const Socket& socket) {
AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode();
std::vector<Socket> input_sockets =
input_node->m_socket_accessor->m_outputs;
std::vector<Socket>::const_iterator iter = std::find_if(
input_sockets.begin(),
input_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != input_sockets.end()) {
std::cerr << "Error: cannot register input socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
input_node->m_socket_accessor->m_outputs.push_back(socket);
m_socket_accessor->m_inputs = input_node->m_socket_accessor->m_outputs;
return true;
}
template <typename T>
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeOutputSocket(socket);
}
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
std::vector<Socket> output_sockets =
output_node->m_socket_accessor->m_inputs;
std::vector<Socket>::const_iterator iter = std::find_if(
output_sockets.begin(),
output_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != output_sockets.end()) {
return false;
}
output_node->m_socket_accessor->m_inputs.push_back(socket);
m_socket_accessor->m_outputs = output_node->m_socket_accessor->m_inputs;
return true;
}
void CreateStateMachineInstance(AnimGraphStateMachine& result) const; void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
private: private:
// BlendTree // BlendTree
bool SaveBlendTreeResourceToFile(const char* filename) const; bool SaveBlendTreeResourceToFile(const char* filename) const;
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const; void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const; void PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const; void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
bool SaveStateMachineResourceToFile(const char* filename) const; bool SaveStateMachineResourceToFile(const char* filename) const;
+10 -7
View File
@@ -5,21 +5,24 @@
#include "AnimGraphStateMachine.h" #include "AnimGraphStateMachine.h"
bool AnimGraphStateMachine::Init(AnimGraphContext& context) { bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
return false;
} }
void AnimGraphStateMachine::MarkActiveInputs() { void AnimGraphStateMachine::MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
} }
void AnimGraphStateMachine::CalcSyncTrack() { void AnimGraphStateMachine::CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
} }
void AnimGraphStateMachine::UpdateTime(float time_last, float time_now) { void AnimGraphStateMachine::UpdateTime(float time_last, float time_now) {
assert(false && !"Not yet implemented!");
} }
void AnimGraphStateMachine::Evaluate(AnimGraphContext& context) { void AnimGraphStateMachine::Evaluate(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
} }
+2 -2
View File
@@ -25,8 +25,8 @@ struct AnimGraphStateMachine : public AnimNode {
Transition* m_active_transition = nullptr; Transition* m_active_transition = nullptr;
bool Init(AnimGraphContext& context); bool Init(AnimGraphContext& context);
void MarkActiveInputs() override; void MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack() override; void CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override; void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override; void Evaluate(AnimGraphContext& context) override;
}; };
+15 -9
View File
@@ -8,8 +8,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "SyncTrack.h"
#include "AnimGraphData.h" #include "AnimGraphData.h"
#include "SyncTrack.h"
struct AnimNode; struct AnimNode;
@@ -25,32 +25,38 @@ enum class AnimNodeEvalState {
struct AnimNode { struct AnimNode {
std::string m_name; std::string m_name;
std::string m_node_type_name; std::string m_node_type_name;
int m_tick_number = 0;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined; AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
float m_time_now = 0.f; float m_time_now = 0.f;
float m_time_last = 0.f; float m_time_last = 0.f;
SyncTrack m_sync_track; SyncTrack m_sync_track;
std::vector<AnimGraphConnection> m_inputs;
virtual ~AnimNode() = default; virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) { return true; }; virtual bool Init(AnimGraphContext& context) {
m_time_now = 0.f;
m_time_last = 0.f;
return true;
};
virtual void MarkActiveInputs() { virtual void MarkActiveInputs(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) { if (input_node != nullptr) {
input_node->m_tick_number = m_tick_number;
input_node->m_state = AnimNodeEvalState::Activated; input_node->m_state = AnimNodeEvalState::Activated;
} }
} }
} }
virtual void CalcSyncTrack() { virtual void CalcSyncTrack(
for (const auto & input : m_inputs) { const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node; AnimNode* input_node = input.m_source_node;
if (input_node != nullptr if (input_node != nullptr
&& input.m_source_socket.m_type == SocketType::SocketTypeAnimation && input.m_socket.m_type == SocketType::SocketTypeAnimation
&& input_node->m_state != AnimNodeEvalState::Deactivated) { && input_node->m_state != AnimNodeEvalState::Deactivated) {
m_sync_track = input_node->m_sync_track; m_sync_track = input_node->m_sync_track;
return; return;
+10441
View File
File diff suppressed because it is too large Load Diff
+66 -99
View File
@@ -16,13 +16,17 @@
#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_NONE
#include <iostream> #include <iostream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "3rdparty/json/json.hpp" #include "3rdparty/json/json.hpp"
#include "AnimGraph/AnimGraphBlendTree.h"
#include "AnimGraph/AnimGraphData.h"
#include "Camera.h" #include "Camera.h"
#include "GLFW/glfw3.h" #include "GLFW/glfw3.h"
#include "SkinnedMesh.h" #include "SkinnedMesh.h"
#include "SkinnedMeshRenderer.h"
#include "SkinnedMeshResource.h" #include "SkinnedMeshResource.h"
#include "embedded_fonts.h"
#include "src/AnimGraph/AnimGraphEditor.h" #include "src/AnimGraph/AnimGraphEditor.h"
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
const int Width = 1024; const int Width = 1024;
const int Height = 768; const int Height = 768;
@@ -49,7 +53,6 @@ static void draw_imgui(ImDrawData*);
#include <cmath> // fmodf #include <cmath> // fmodf
#include <fstream> #include <fstream>
#include "SkinnedMeshRenderer.h"
#include "ozz/animation/runtime/animation.h" #include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/sampling_job.h" #include "ozz/animation/runtime/sampling_job.h"
#include "ozz/animation/runtime/skeleton.h" #include "ozz/animation/runtime/skeleton.h"
@@ -156,14 +159,10 @@ struct Viewport {
this->pass = sg_make_pass(&offscreen_pass_desc); this->pass = sg_make_pass(&offscreen_pass_desc);
sg_pipeline_desc gl_pipeline_desc = { sg_pipeline_desc gl_pipeline_desc = {
.depth = { .depth = {.compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true},
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.cull_mode = SG_CULLMODE_BACK, .cull_mode = SG_CULLMODE_BACK,
.sample_count = cMSAASampleCount .sample_count = cMSAASampleCount};
}; // this->pip = sg_make_pipeline(gl_pipeline_desc);
// this->pip = sg_make_pipeline(gl_pipeline_desc);
} }
}; };
@@ -171,13 +170,6 @@ struct ApplicationConfig {
int window_position[2] = {100, 30}; int window_position[2] = {100, 30};
int window_size[2] = {1000, 600}; int window_size[2] = {1000, 600};
struct LegacyGraphEditor {
bool visible = false;
int position[2] = {20, 20};
int size[2] = {800, 500};
};
LegacyGraphEditor legacy_graph_editor;
struct GraphEditor { struct GraphEditor {
bool visible = false; bool visible = false;
int position[2] = {20, 20}; int position[2] = {20, 20};
@@ -223,12 +215,6 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) {
j["main_window"]["size"][0] = config.window_size[0]; j["main_window"]["size"][0] = config.window_size[0];
j["main_window"]["size"][1] = config.window_size[1]; j["main_window"]["size"][1] = config.window_size[1];
j["legacy_graph_editor"]["visible"] = config.legacy_graph_editor.visible;
j["legacy_graph_editor"]["position"][0] = config.legacy_graph_editor.position[0];
j["legacy_graph_editor"]["position"][1] = config.legacy_graph_editor.position[1];
j["legacy_graph_editor"]["size"][0] = config.legacy_graph_editor.size[0];
j["legacy_graph_editor"]["size"][1] = config.legacy_graph_editor.size[1];
j["graph_editor"]["visible"] = config.graph_editor.visible; j["graph_editor"]["visible"] = config.graph_editor.visible;
j["graph_editor"]["position"][0] = config.graph_editor.position[0]; j["graph_editor"]["position"][0] = config.graph_editor.position[0];
j["graph_editor"]["position"][1] = config.graph_editor.position[1]; j["graph_editor"]["position"][1] = config.graph_editor.position[1];
@@ -280,24 +266,6 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
} }
} }
if (j.contains("legacy_graph_editor")) {
if (j["legacy_graph_editor"].contains("visible")) {
config.legacy_graph_editor.visible = j["legacy_graph_editor"]["visible"];
}
if (j["legacy_graph_editor"].contains("position")
and j["legacy_graph_editor"]["position"].size() == 2) {
config.legacy_graph_editor.position[0] = j["legacy_graph_editor"]["position"].at(0);
config.legacy_graph_editor.position[1] = j["legacy_graph_editor"]["position"].at(1);
}
if (j["legacy_graph_editor"].contains("size")
and j["legacy_graph_editor"]["size"].size() == 2) {
config.legacy_graph_editor.size[0] = j["legacy_graph_editor"]["size"].at(0);
config.legacy_graph_editor.size[1] = j["legacy_graph_editor"]["size"].at(1);
}
}
if (j.contains("graph_editor")) { if (j.contains("graph_editor")) {
if (j["graph_editor"].contains("visible")) { if (j["graph_editor"].contains("visible")) {
config.graph_editor.visible = j["graph_editor"]["visible"]; config.graph_editor.visible = j["graph_editor"]["visible"];
@@ -529,7 +497,9 @@ int main() {
// setup sokol_gfx and sokol_time // setup sokol_gfx and sokol_time
stm_setup(); stm_setup();
sg_desc desc = {.logger = {.func = slog_func}, .context {.sample_count = cMSAASampleCount}}; sg_desc desc = {
.logger = {.func = slog_func},
.context{.sample_count = cMSAASampleCount}};
sg_setup(&desc); sg_setup(&desc);
assert(sg_isvalid()); assert(sg_isvalid());
@@ -550,11 +520,12 @@ int main() {
skinned_mesh_resource.createInstance(skinned_mesh); skinned_mesh_resource.createInstance(skinned_mesh);
skinned_mesh.SetCurrentAnimation(0); skinned_mesh.SetCurrentAnimation(0);
AnimGraph anim_graph; AnimGraphBlendTree anim_graph;
AnimGraphContext anim_graph_context; AnimGraphContext anim_graph_context;
AnimData anim_graph_output; AnimData anim_graph_output;
anim_graph_output.m_local_matrices.resize( anim_graph_output.m_local_matrices.resize(
skinned_mesh.m_skeleton.num_soa_joints()); skinned_mesh.m_skeleton.num_soa_joints());
AnimGraphEditorClear();
state.time.factor = 1.0f; state.time.factor = 1.0f;
@@ -566,7 +537,20 @@ int main() {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.IniFilename = "ATPImgui.ini"; io.IniFilename = "ATPImgui.ini";
io.Fonts->AddFontDefault();
//io.Fonts->AddFontDefault();
ImFontConfig font_config;
font_config.OversampleH = 4;
font_config.OversampleV = 4;
font_config.GlyphExtraSpacing.x = 1.0f;
io.Fonts->AddFontFromMemoryCompressedTTF(
// roboto_medium_ttf_compressed_data,
// roboto_medium_ttf_compressed_size,
droid_sans_ttf_compressed_data,
droid_sans_ttf_compressed_size,
14,
&font_config);
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
@@ -673,7 +657,8 @@ int main() {
// Graph Editor // Graph Editor
gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json"; gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json";
gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2; gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2;
gApplicationConfig.graph_editor.context = ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config); gApplicationConfig.graph_editor.context =
ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
// draw loop // draw loop
while (!glfwWindowShouldClose(w)) { while (!glfwWindowShouldClose(w)) {
@@ -795,9 +780,6 @@ int main() {
&gApplicationConfig.animation_player_widget.visible); &gApplicationConfig.animation_player_widget.visible);
ImGui::Separator(); ImGui::Separator();
ImGui::Checkbox(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible);
ImGui::Checkbox( ImGui::Checkbox(
"ImGui Demo", "ImGui Demo",
@@ -811,12 +793,12 @@ int main() {
AnimGraphEditorGetRuntimeGraph(anim_graph); AnimGraphEditorGetRuntimeGraph(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton; anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
anim_graph.init(anim_graph_context); anim_graph.Init(anim_graph_context);
// For simplicity use first animation data output // For simplicity use first animation data output
const std::vector<Socket>& graph_output_sockets = const std::vector<Socket>& graph_output_sockets =
anim_graph.getGraphOutputs(); anim_graph.GetGraphOutputs();
for (const auto & output : graph_output_sockets) { for (const auto& output : graph_output_sockets) {
if (output.m_type == SocketType::SocketTypeAnimation) { if (output.m_type == SocketType::SocketTypeAnimation) {
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output); anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
} }
@@ -845,7 +827,8 @@ int main() {
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible); ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) { if (ImGui::IsWindowHovered()
&& ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
if (gControlMode == ControlMode::ControlModeNone) { if (gControlMode == ControlMode::ControlModeNone) {
gControlMode = ControlMode::ControlModeFPS; gControlMode = ControlMode::ControlModeFPS;
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]); Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
@@ -854,8 +837,10 @@ int main() {
} }
ImVec2 viewport_widget_size = ImGui::GetWindowSize(); ImVec2 viewport_widget_size = ImGui::GetWindowSize();
gApplicationConfig.viewport_widget.size[0] = static_cast<int>(viewport_widget_size.x); gApplicationConfig.viewport_widget.size[0] =
gApplicationConfig.viewport_widget.size[1] = static_cast<int>(viewport_widget_size.y); static_cast<int>(viewport_widget_size.x);
gApplicationConfig.viewport_widget.size[1] =
static_cast<int>(viewport_widget_size.y);
ImGui::Text( ImGui::Text(
"Viewport size: %d, %d", "Viewport size: %d, %d",
@@ -869,12 +854,16 @@ int main() {
if (static_cast<float>(current_size[0]) != content_size[0] if (static_cast<float>(current_size[0]) != content_size[0]
|| static_cast<float>(current_size[1]) != content_size[1] || static_cast<float>(current_size[1]) != content_size[1]
|| offscreen_viewport.pass.id == 0) { || offscreen_viewport.pass.id == 0) {
offscreen_viewport.Resize(static_cast<int>(content_size[0]), static_cast<int>(content_size[1])); offscreen_viewport.Resize(
static_cast<int>(content_size[0]),
static_cast<int>(content_size[1]));
} }
ImGui::Image( ImGui::Image(
(ImTextureID)(uintptr_t)offscreen_viewport.color_image.id, (ImTextureID)(uintptr_t)offscreen_viewport.color_image.id,
ImVec2(static_cast<float>(offscreen_viewport.size[0]), static_cast<float>(offscreen_viewport.size[1])), ImVec2(
static_cast<float>(offscreen_viewport.size[0]),
static_cast<float>(offscreen_viewport.size[1])),
ImVec2(0.0f, 1.0f), ImVec2(0.0f, 1.0f),
ImVec2(1.0f, 0.0f)); ImVec2(1.0f, 0.0f));
@@ -897,13 +886,17 @@ int main() {
if (gApplicationConfig.skinned_mesh_widget.visible) { if (gApplicationConfig.skinned_mesh_widget.visible) {
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2( ImVec2(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[0]), static_cast<float>(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[1])), gApplicationConfig.skinned_mesh_widget.position[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.position[1])),
ImGuiCond_FirstUseEver); ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2( ImVec2(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[0]), static_cast<float>(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[1])), gApplicationConfig.skinned_mesh_widget.size[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.size[1])),
ImGuiCond_FirstUseEver); ImGuiCond_FirstUseEver);
ImGui::Begin( ImGui::Begin(
@@ -930,13 +923,17 @@ int main() {
if (gApplicationConfig.animation_player_widget.visible) { if (gApplicationConfig.animation_player_widget.visible) {
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2( ImVec2(
static_cast<float>(gApplicationConfig.animation_player_widget.position[0]), static_cast<float>(
static_cast<float>(gApplicationConfig.animation_player_widget.position[1])), gApplicationConfig.animation_player_widget.position[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.position[1])),
ImGuiCond_FirstUseEver); ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2( ImVec2(
static_cast<float>(gApplicationConfig.animation_player_widget.size[0]), static_cast<float>(
static_cast<float>(gApplicationConfig.animation_player_widget.size[1])), gApplicationConfig.animation_player_widget.size[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.size[1])),
ImGuiCond_FirstUseEver); ImGuiCond_FirstUseEver);
ImGui::Begin( ImGui::Begin(
@@ -1015,8 +1012,8 @@ int main() {
if (state.ozz.animation != nullptr) { if (state.ozz.animation != nullptr) {
state.ozz.sampling_job.animation = state.ozz.animation; state.ozz.sampling_job.animation = state.ozz.animation;
state.ozz.sampling_job.ratio = state.ozz.sampling_job.ratio = static_cast<float>(state.time.absolute)
static_cast<float>(state.time.absolute) / state.ozz.animation->duration(); / state.ozz.animation->duration();
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context; state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
state.ozz.sampling_job.output = state.ozz.sampling_job.output =
ozz::make_span(skinned_mesh.m_local_matrices); ozz::make_span(skinned_mesh.m_local_matrices);
@@ -1031,9 +1028,10 @@ int main() {
if (state.time.use_graph && !anim_graph.m_nodes.empty() if (state.time.use_graph && !anim_graph.m_nodes.empty()
&& state.time.anim_update_time > 0.) { && state.time.anim_update_time > 0.) {
anim_graph.markActiveNodes(); // TODO: update for new API after embedding refactor
anim_graph.updateTime(state.time.anim_update_time); // anim_graph.MarkActiveNodes();
anim_graph.evaluate(anim_graph_context); // anim_graph.UpdateTime()pdateTime(state.time.anim_update_time);
// anim_graph.evaluate(anim_graph_context);
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices; skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
skinned_mesh.CalcModelMatrices(); skinned_mesh.CalcModelMatrices();
@@ -1080,37 +1078,6 @@ int main() {
ImGui::End(); ImGui::End();
} }
// Legacy Animation Graph Editor
if (gApplicationConfig.legacy_graph_editor.visible) {
ImGui::SetNextWindowPos(
ImVec2(
gApplicationConfig.legacy_graph_editor.position[0],
gApplicationConfig.legacy_graph_editor.position[1]),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
gApplicationConfig.legacy_graph_editor.size[0],
gApplicationConfig.legacy_graph_editor.size[1]),
ImGuiCond_FirstUseEver);
ImGui::Begin(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible,
ImGuiWindowFlags_MenuBar);
ImVec2 graph_editor_position = ImGui::GetWindowPos();
gApplicationConfig.legacy_graph_editor.position[0] = graph_editor_position.x;
gApplicationConfig.legacy_graph_editor.position[1] = graph_editor_position.y;
ImVec2 graph_editor_size = ImGui::GetWindowSize();
gApplicationConfig.legacy_graph_editor.size[0] = graph_editor_size.x;
gApplicationConfig.legacy_graph_editor.size[1] = graph_editor_size.y;
LegacyAnimGraphEditorUpdate();
ImGui::End();
}
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow() // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
if (gApplicationConfig.show_imgui_demo_window) { if (gApplicationConfig.show_imgui_demo_window) {
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
+40
View File
@@ -0,0 +1,40 @@
#include "AnimGraph/AnimGraphEditor.h"
#include "catch.hpp"
TEST_CASE("Node Socket To InputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToInputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
InputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
TEST_CASE("Node Socket To OutputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToOutputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
OutputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
File diff suppressed because it is too large Load Diff