Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8ef7924d2 | ||
|
|
0a45497de9 | ||
|
|
7c8b44247b | ||
|
|
abf44a875a | ||
|
|
42303d5f47 | ||
|
|
3d55b748e6 | ||
|
|
91607baa9d | ||
|
|
411aa5ef20 | ||
|
|
9168dec9f9 | ||
|
|
9dd10e8f27 | ||
|
|
08283d9bcf | ||
|
|
e38c0b4934 | ||
|
|
a1931185d8 |
+13
-12
@@ -43,23 +43,11 @@ set(ThirdPartyIncludeDeps
|
|||||||
|
|
||||||
# Shared code by main executable and tests
|
# Shared code by main executable and tests
|
||||||
add_library(AnimTestbedCode OBJECT
|
add_library(AnimTestbedCode OBJECT
|
||||||
src/Camera.c
|
|
||||||
src/SkinnedMesh.cc
|
|
||||||
src/SkinnedMesh.h
|
|
||||||
src/SkinnedMeshResource.cc
|
|
||||||
src/SkinnedMeshResource.h
|
|
||||||
src/SyncTrack.cc
|
src/SyncTrack.cc
|
||||||
src/SyncTrack.h
|
src/SyncTrack.h
|
||||||
src/ozzutils.cc
|
src/ozzutils.cc
|
||||||
3rdparty/imgui/imgui.cpp
|
|
||||||
3rdparty/imgui/imgui_draw.cpp
|
|
||||||
3rdparty/imgui/imgui_widgets.cpp
|
|
||||||
3rdparty/imgui/misc/cpp/imgui_stdlib.cpp
|
|
||||||
3rdparty/imnodes/imnodes.cpp
|
|
||||||
src/AnimGraph/AnimGraphResource.cc
|
src/AnimGraph/AnimGraphResource.cc
|
||||||
src/AnimGraph/AnimGraphResource.h
|
src/AnimGraph/AnimGraphResource.h
|
||||||
src/AnimGraph/AnimGraphEditor.cc
|
|
||||||
src/AnimGraph/AnimGraphEditor.h
|
|
||||||
src/AnimGraph/AnimGraph.cc
|
src/AnimGraph/AnimGraph.cc
|
||||||
src/AnimGraph/AnimGraph.h
|
src/AnimGraph/AnimGraph.h
|
||||||
src/AnimGraph/AnimGraphNodes.cc
|
src/AnimGraph/AnimGraphNodes.cc
|
||||||
@@ -87,7 +75,19 @@ target_include_directories(
|
|||||||
target_sources(AnimTestbed PRIVATE
|
target_sources(AnimTestbed PRIVATE
|
||||||
src/main.cc
|
src/main.cc
|
||||||
src/SkinnedMeshRenderer.cc
|
src/SkinnedMeshRenderer.cc
|
||||||
|
src/AnimGraph/AnimGraphEditor.cc
|
||||||
|
src/AnimGraph/AnimGraphEditor.h
|
||||||
|
src/Camera.c
|
||||||
|
src/SkinnedMesh.cc
|
||||||
|
src/SkinnedMesh.h
|
||||||
|
src/SkinnedMeshResource.cc
|
||||||
|
src/SkinnedMeshResource.h
|
||||||
3rdparty/glfw/deps/glad_gl.c
|
3rdparty/glfw/deps/glad_gl.c
|
||||||
|
3rdparty/imgui/imgui.cpp
|
||||||
|
3rdparty/imgui/imgui_draw.cpp
|
||||||
|
3rdparty/imgui/imgui_widgets.cpp
|
||||||
|
3rdparty/imgui/misc/cpp/imgui_stdlib.cpp
|
||||||
|
3rdparty/imnodes/imnodes.cpp
|
||||||
3rdparty/imgui/imgui_demo.cpp
|
3rdparty/imgui/imgui_demo.cpp
|
||||||
3rdparty/imgui/backends/imgui_impl_glfw.cpp
|
3rdparty/imgui/backends/imgui_impl_glfw.cpp
|
||||||
3rdparty/imgui/backends/imgui_impl_opengl3.cpp
|
3rdparty/imgui/backends/imgui_impl_opengl3.cpp
|
||||||
@@ -108,6 +108,7 @@ set(ozz_offline_test_objs
|
|||||||
target_sources(runtests PRIVATE
|
target_sources(runtests PRIVATE
|
||||||
tests/AnimGraphResourceTests.cc
|
tests/AnimGraphResourceTests.cc
|
||||||
tests/AnimGraphEvalTests.cc
|
tests/AnimGraphEvalTests.cc
|
||||||
|
tests/NodeDescriptorTests.cc
|
||||||
tests/SyncTrackTests.cc
|
tests/SyncTrackTests.cc
|
||||||
tests/main.cc
|
tests/main.cc
|
||||||
${ozz_offline_test_objs}
|
${ozz_offline_test_objs}
|
||||||
|
|||||||
+19
-106
@@ -4,25 +4,21 @@
|
|||||||
|
|
||||||
#include "AnimGraph.h"
|
#include "AnimGraph.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
bool AnimGraph::init(AnimGraphContext& context) {
|
bool AnimGraph::init(AnimGraphContext& context) {
|
||||||
|
context.m_graph = this;
|
||||||
|
|
||||||
for (size_t i = 2; i < m_nodes.size(); i++) {
|
for (size_t i = 2; i < m_nodes.size(); i++) {
|
||||||
if (!m_nodes[i]->Init(context)) {
|
if (!m_nodes[i]->Init(context)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AnimGraphConnection>& graph_outputs = m_node_input_connections[0];
|
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
|
||||||
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
|
int num_soa_joints = context.m_skeleton->num_soa_joints();
|
||||||
AnimGraphConnection& connection = graph_outputs[i];
|
m_animdata_blocks[i]->m_local_matrices.resize(num_soa_joints);
|
||||||
if (connection.m_target_socket.m_type == SocketType::SocketTypeAnimation) {
|
|
||||||
AnimData* graph_anim_output =
|
|
||||||
static_cast<AnimData*>(connection.m_target_socket.m_reference.ptr);
|
|
||||||
assert(graph_anim_output != nullptr);
|
|
||||||
graph_anim_output->m_local_matrices.resize(
|
|
||||||
context.m_skeleton->num_soa_joints());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -35,11 +31,11 @@ void AnimGraph::updateOrderedNodes() {
|
|||||||
|
|
||||||
void AnimGraph::updateOrderedNodesRecursive(int node_index) {
|
void AnimGraph::updateOrderedNodesRecursive(int node_index) {
|
||||||
AnimNode* node = m_nodes[node_index];
|
AnimNode* node = m_nodes[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++) {
|
||||||
int input_node_index =
|
int input_node_index =
|
||||||
getAnimNodeIndex(node_input_connections[i].m_source_node);
|
getAnimNodeIndex(node_input_connections.at(i).m_source_node);
|
||||||
|
|
||||||
if (input_node_index == 1) {
|
if (input_node_index == 1) {
|
||||||
continue;
|
continue;
|
||||||
@@ -49,6 +45,17 @@ void AnimGraph::updateOrderedNodesRecursive(int node_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node_index != 0) {
|
if (node_index != 0) {
|
||||||
|
// 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<AnimNode*>::iterator find_iter = std::find(
|
||||||
|
m_eval_ordered_nodes.begin(),
|
||||||
|
m_eval_ordered_nodes.end(),
|
||||||
|
node);
|
||||||
|
if (find_iter != m_eval_ordered_nodes.end()) {
|
||||||
|
m_eval_ordered_nodes.erase(find_iter);
|
||||||
|
}
|
||||||
|
|
||||||
m_eval_ordered_nodes.push_back(node);
|
m_eval_ordered_nodes.push_back(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,93 +97,6 @@ void AnimGraph::markActiveNodes() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraph::prepareNodeEval(
|
|
||||||
AnimGraphContext& graph_context,
|
|
||||||
size_t node_index) {
|
|
||||||
for (size_t i = 0, n = m_node_output_connections[node_index].size(); i < n;
|
|
||||||
i++) {
|
|
||||||
AnimGraphConnection& output_connection =
|
|
||||||
m_node_output_connections[node_index][i];
|
|
||||||
if (output_connection.m_source_socket.m_type
|
|
||||||
!= SocketType::SocketTypeAnimation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert (*output_connection.m_source_socket.m_reference.ptr_ptr == nullptr);
|
|
||||||
|
|
||||||
(*output_connection.m_source_socket.m_reference.ptr_ptr) =
|
|
||||||
m_anim_data_allocator.allocate(graph_context.m_skeleton);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n;
|
|
||||||
i++) {
|
|
||||||
AnimGraphConnection& input_connection =
|
|
||||||
m_node_input_connections[node_index][i];
|
|
||||||
if (input_connection.m_source_socket.m_type
|
|
||||||
!= SocketType::SocketTypeAnimation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*input_connection.m_target_socket.m_reference.ptr_ptr) =
|
|
||||||
(*input_connection.m_source_socket.m_reference.ptr_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraph::finishNodeEval(size_t node_index) {
|
|
||||||
for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n;
|
|
||||||
i++) {
|
|
||||||
AnimGraphConnection& input_connection =
|
|
||||||
m_node_input_connections[node_index][i];
|
|
||||||
if (input_connection.m_source_socket.m_type
|
|
||||||
!= SocketType::SocketTypeAnimation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_anim_data_allocator.free(static_cast<AnimData*>(
|
|
||||||
*input_connection.m_source_socket.m_reference.ptr_ptr));
|
|
||||||
(*input_connection.m_source_socket.m_reference.ptr_ptr) = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraph::evalInputNode() {
|
|
||||||
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
|
|
||||||
AnimGraphConnection& graph_input_connection =
|
|
||||||
m_node_output_connections[1][i];
|
|
||||||
|
|
||||||
if (graph_input_connection.m_source_socket.m_type
|
|
||||||
!= SocketType::SocketTypeAnimation) {
|
|
||||||
memcpy(
|
|
||||||
*graph_input_connection.m_target_socket.m_reference.ptr_ptr,
|
|
||||||
graph_input_connection.m_source_socket.m_reference.ptr,
|
|
||||||
sizeof(void*));
|
|
||||||
printf("bla");
|
|
||||||
} else {
|
|
||||||
// TODO: how to deal with anim data outputs?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraph::evalOutputNode() {
|
|
||||||
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
|
|
||||||
AnimGraphConnection& graph_output_connection =
|
|
||||||
m_node_input_connections[0][i];
|
|
||||||
|
|
||||||
if (graph_output_connection.m_source_socket.m_type
|
|
||||||
!= SocketType::SocketTypeAnimation) {
|
|
||||||
memcpy(
|
|
||||||
graph_output_connection.m_target_socket.m_reference.ptr,
|
|
||||||
graph_output_connection.m_source_socket.m_reference.ptr,
|
|
||||||
graph_output_connection.m_target_socket.m_type_size);
|
|
||||||
} else {
|
|
||||||
AnimData* source_data = static_cast<AnimData*>(
|
|
||||||
*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
|
|
||||||
AnimData* target_data = static_cast<AnimData*>(
|
|
||||||
graph_output_connection.m_target_socket.m_reference.ptr);
|
|
||||||
target_data->m_local_matrices = source_data->m_local_matrices;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraph::evalSyncTracks() {
|
void AnimGraph::evalSyncTracks() {
|
||||||
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
|
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
|
||||||
AnimNode* node = m_eval_ordered_nodes[i];
|
AnimNode* node = m_eval_ordered_nodes[i];
|
||||||
@@ -233,15 +153,8 @@ void AnimGraph::evaluate(AnimGraphContext& context) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareNodeEval(context, node->m_index);
|
|
||||||
|
|
||||||
node->Evaluate(context);
|
node->Evaluate(context);
|
||||||
|
|
||||||
finishNodeEval(node->m_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
evalOutputNode();
|
|
||||||
finishNodeEval(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket* AnimGraph::getInputSocket(const std::string& name) {
|
Socket* AnimGraph::getInputSocket(const std::string& name) {
|
||||||
|
|||||||
+107
-27
@@ -18,43 +18,43 @@ struct AnimGraph {
|
|||||||
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;
|
||||||
NodeSocketAccessorBase* m_socket_accessor;
|
std::vector<AnimData*> m_animdata_blocks;
|
||||||
|
NodeDescriptorBase* m_node_descriptor;
|
||||||
char* m_input_buffer = nullptr;
|
char* m_input_buffer = nullptr;
|
||||||
char* m_output_buffer = nullptr;
|
char* m_output_buffer = nullptr;
|
||||||
|
char* m_connection_data_storage = nullptr;
|
||||||
|
char* m_const_node_inputs = nullptr;
|
||||||
|
|
||||||
std::vector<Socket>& getGraphOutputs() { return m_socket_accessor->m_inputs; }
|
std::vector<Socket>& getGraphOutputs() { return m_node_descriptor->m_inputs; }
|
||||||
std::vector<Socket>& getGraphInputs() { return m_socket_accessor->m_outputs; }
|
std::vector<Socket>& getGraphInputs() { return m_node_descriptor->m_outputs; }
|
||||||
|
|
||||||
AnimDataAllocator m_anim_data_allocator;
|
AnimDataAllocator m_anim_data_allocator;
|
||||||
|
|
||||||
~AnimGraph() {
|
~AnimGraph() { dealloc(); }
|
||||||
std::vector<AnimGraphConnection>& graph_outputs = m_node_input_connections[0];
|
|
||||||
|
|
||||||
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
|
bool init(AnimGraphContext& context);
|
||||||
AnimGraphConnection& connection = graph_outputs[i];
|
void dealloc() {
|
||||||
if (connection.m_target_socket.m_type == SocketType::SocketTypeAnimation) {
|
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
|
||||||
AnimData* graph_anim_output =
|
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
|
||||||
static_cast<AnimData*>(connection.m_target_socket.m_reference.ptr);
|
}
|
||||||
assert(graph_anim_output != nullptr);
|
m_animdata_blocks.clear();
|
||||||
|
|
||||||
// we have to explicitly call the destructor as the AnimData* was
|
m_node_input_connections.clear();
|
||||||
// initialized using a placement new operator.
|
m_node_output_connections.clear();
|
||||||
graph_anim_output->m_local_matrices.vector::~vector();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] m_input_buffer;
|
delete[] m_input_buffer;
|
||||||
delete[] m_output_buffer;
|
delete[] m_output_buffer;
|
||||||
|
delete[] m_connection_data_storage;
|
||||||
|
delete[] m_const_node_inputs;
|
||||||
|
|
||||||
for (int i = 0; i < m_nodes.size(); i++) {
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
delete m_nodes[i];
|
delete m_nodes[i];
|
||||||
}
|
}
|
||||||
|
m_nodes.clear();
|
||||||
|
|
||||||
delete m_socket_accessor;
|
delete m_node_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init(AnimGraphContext& context);
|
|
||||||
|
|
||||||
void updateOrderedNodes();
|
void updateOrderedNodes();
|
||||||
void updateOrderedNodesRecursive(int node_index);
|
void updateOrderedNodesRecursive(int node_index);
|
||||||
void markActiveNodes();
|
void markActiveNodes();
|
||||||
@@ -62,15 +62,10 @@ struct AnimGraph {
|
|||||||
return node->m_state != AnimNodeEvalState::Deactivated;
|
return node->m_state != AnimNodeEvalState::Deactivated;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evalInputNode();
|
|
||||||
void prepareNodeEval(AnimGraphContext& graph_context, size_t node_index);
|
|
||||||
void finishNodeEval(size_t node_index);
|
|
||||||
void evalOutputNode();
|
|
||||||
|
|
||||||
void evalSyncTracks();
|
void evalSyncTracks();
|
||||||
void updateTime(float dt);
|
void updateTime(float dt);
|
||||||
void evaluate(AnimGraphContext& context);
|
void evaluate(AnimGraphContext& context);
|
||||||
void reset() {
|
void resetNodeStates() {
|
||||||
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
||||||
m_nodes[i]->m_time_now = 0.f;
|
m_nodes[i]->m_time_now = 0.f;
|
||||||
m_nodes[i]->m_time_last = 0.f;
|
m_nodes[i]->m_time_last = 0.f;
|
||||||
@@ -84,6 +79,91 @@ struct AnimGraph {
|
|||||||
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;
|
||||||
|
|
||||||
|
/** Sets the address that is used for the specified AnimGraph input Socket.
|
||||||
|
*
|
||||||
|
* @tparam T Type of the Socket.
|
||||||
|
* @param name Name of the Socket.
|
||||||
|
* @param value_ptr Pointer where the input is fetched during evaluation.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void SetInput(const char* name, T* value_ptr) {
|
||||||
|
m_node_descriptor->SetOutput(name, value_ptr);
|
||||||
|
|
||||||
|
for (int i = 0; i < m_node_output_connections[1].size(); i++) {
|
||||||
|
const AnimGraphConnection& graph_input_connection =
|
||||||
|
m_node_output_connections[1][i];
|
||||||
|
|
||||||
|
if (graph_input_connection.m_source_socket.m_name == name) {
|
||||||
|
*graph_input_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the address that is used for the specified AnimGraph output Socket.
|
||||||
|
*
|
||||||
|
* @tparam T Type of the Socket.
|
||||||
|
* @param name Name of the Socket.
|
||||||
|
* @param value_ptr Pointer where the graph output output is written to at the end of evaluation.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void SetOutput(const char* name, T* value_ptr) {
|
||||||
|
m_node_descriptor->SetInput(name, value_ptr);
|
||||||
|
|
||||||
|
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
|
||||||
|
const AnimGraphConnection& graph_output_connection =
|
||||||
|
m_node_input_connections[0][i];
|
||||||
|
|
||||||
|
if (graph_output_connection.m_target_socket.m_name == name) {
|
||||||
|
if (graph_output_connection.m_source_node == m_nodes[1]
|
||||||
|
&& graph_output_connection.m_target_node == m_nodes[0]) {
|
||||||
|
std::cerr << "Error: cannot set output for direct graph input to graph "
|
||||||
|
"output connections. Use GetOutptPtr for output instead!"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*graph_output_connection.m_source_socket.m_reference.ptr_ptr =
|
||||||
|
value_ptr;
|
||||||
|
|
||||||
|
// Make sure all other output connections of this pin use the same output pointer
|
||||||
|
int source_node_index = getAnimNodeIndex(graph_output_connection.m_source_node);
|
||||||
|
for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) {
|
||||||
|
const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j];
|
||||||
|
if (source_output_connection.m_target_node == m_nodes[0]) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the address that is used for the specified AnimGraph output Socket.
|
||||||
|
*
|
||||||
|
* This function is needed for connections that directly connect an AnimGraph
|
||||||
|
* input Socket to an output Socket of the same AnimGraph.
|
||||||
|
*
|
||||||
|
* @tparam T Type of the Socket.
|
||||||
|
* @param name Name of the Socket.
|
||||||
|
* @return Address that is used for the specified AnimGraph output Socket.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
T* GetOutputPtr(const char* name) {
|
||||||
|
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
|
||||||
|
const AnimGraphConnection& graph_output_connection =
|
||||||
|
m_node_input_connections[0][i];
|
||||||
|
if (graph_output_connection.m_target_socket.m_name == name) {
|
||||||
|
return static_cast<float*>(*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void* getInputPtr(const std::string& name) const {
|
void* getInputPtr(const std::string& name) const {
|
||||||
const Socket* input_socket = getInputSocket(name);
|
const Socket* input_socket = getInputSocket(name);
|
||||||
if (input_socket != nullptr) {
|
if (input_socket != nullptr) {
|
||||||
@@ -102,7 +182,6 @@ struct AnimGraph {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int getNodeEvalOrderIndex(const AnimNode* node) {
|
int getNodeEvalOrderIndex(const AnimNode* node) {
|
||||||
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
|
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
|
||||||
if (m_eval_ordered_nodes[i] == node) {
|
if (m_eval_ordered_nodes[i] == node) {
|
||||||
@@ -117,7 +196,8 @@ struct AnimGraph {
|
|||||||
const std::string& input_name) const {
|
const std::string& input_name) const {
|
||||||
assert(node_index < m_nodes.size());
|
assert(node_index < m_nodes.size());
|
||||||
|
|
||||||
const std::vector<AnimGraphConnection>& input_connection = m_node_input_connections[node_index];
|
const std::vector<AnimGraphConnection>& input_connection =
|
||||||
|
m_node_input_connections[node_index];
|
||||||
for (size_t i = 0, n = input_connection.size(); i < n; i++) {
|
for (size_t i = 0, n = input_connection.size(); i < n; i++) {
|
||||||
if (input_connection[i].m_target_socket.m_name == input_name) {
|
if (input_connection[i].m_target_socket.m_name == input_name) {
|
||||||
return input_connection[i].m_source_node;
|
return input_connection[i].m_source_node;
|
||||||
|
|||||||
+295
-287
@@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
#include "SyncTrack.h"
|
||||||
#include "ozz/animation/runtime/animation.h"
|
#include "ozz/animation/runtime/animation.h"
|
||||||
@@ -29,6 +29,10 @@ struct AnimData {
|
|||||||
ozz::vector<ozz::math::SoaTransform> m_local_matrices;
|
ozz::vector<ozz::math::SoaTransform> m_local_matrices;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnimDataRef {
|
||||||
|
AnimData* ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
struct AnimDataAllocator {
|
struct AnimDataAllocator {
|
||||||
struct AnimDataList {
|
struct AnimDataList {
|
||||||
AnimData* m_anim_data = nullptr;
|
AnimData* m_anim_data = nullptr;
|
||||||
@@ -76,16 +80,15 @@ struct AnimDataAllocator {
|
|||||||
|
|
||||||
void free(AnimData* anim_data) {
|
void free(AnimData* anim_data) {
|
||||||
#ifdef ANIM_DATA_ALLOCATOR_DEBUG
|
#ifdef ANIM_DATA_ALLOCATOR_DEBUG
|
||||||
std::cout << "Storing buffer with size " << anim_data->m_local_matrices.size()
|
std::cout << "Storing buffer with size "
|
||||||
<< " " << anim_data << std::endl;
|
<< anim_data->m_local_matrices.size() << " " << anim_data
|
||||||
|
<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_anim_data_list.push_front(anim_data);
|
m_anim_data_list.push_front(anim_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() {
|
size_t size() { return m_anim_data_list.size(); }
|
||||||
return m_anim_data_list.size();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimGraphContext {
|
struct AnimGraphContext {
|
||||||
@@ -105,13 +108,32 @@ struct AnimGraphContext {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef float Vec3[3];
|
union Vec3 {
|
||||||
typedef float Quat[4];
|
struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
};
|
||||||
|
|
||||||
|
float v[3] = {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
union Quat {
|
||||||
|
struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float w;
|
||||||
|
};
|
||||||
|
|
||||||
|
float v[4] = {0};
|
||||||
|
};
|
||||||
|
|
||||||
enum class SocketType {
|
enum class SocketType {
|
||||||
SocketTypeUndefined = 0,
|
SocketTypeUndefined = 0,
|
||||||
SocketTypeBool,
|
SocketTypeBool,
|
||||||
SocketTypeAnimation,
|
SocketTypeAnimation,
|
||||||
|
SocketTypeInt,
|
||||||
SocketTypeFloat,
|
SocketTypeFloat,
|
||||||
SocketTypeVec3,
|
SocketTypeVec3,
|
||||||
SocketTypeQuat,
|
SocketTypeQuat,
|
||||||
@@ -122,80 +144,279 @@ enum class SocketType {
|
|||||||
constexpr size_t cSocketStringValueMaxLength = 256;
|
constexpr size_t cSocketStringValueMaxLength = 256;
|
||||||
|
|
||||||
static const char* SocketTypeNames[] =
|
static const char* SocketTypeNames[] =
|
||||||
{"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"};
|
{"", "Bool", "Animation", "Int", "Float", "Vec3", "Quat", "String"};
|
||||||
|
|
||||||
enum SocketFlags { SocketFlagAffectsTime = 1 };
|
enum SocketFlags { SocketFlagNone = 0, SocketFlagAffectsTime = 1 };
|
||||||
|
|
||||||
struct Socket {
|
struct Socket {
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
SocketType m_type = SocketType::SocketTypeUndefined;
|
SocketType m_type = SocketType::SocketTypeUndefined;
|
||||||
union SocketValue {
|
union SocketValue {
|
||||||
bool flag;
|
bool flag;
|
||||||
|
int int_value;
|
||||||
float float_value;
|
float float_value;
|
||||||
float vec3[3];
|
Vec3 vec3;
|
||||||
float quat[4];
|
Quat quat;
|
||||||
char str[cSocketStringValueMaxLength];
|
|
||||||
};
|
};
|
||||||
SocketValue m_value = {0};
|
SocketValue m_value = {0};
|
||||||
|
std::string m_value_string;
|
||||||
union SocketReference {
|
union SocketReference {
|
||||||
void* ptr;
|
void* ptr;
|
||||||
void** ptr_ptr;
|
void** ptr_ptr;
|
||||||
};
|
};
|
||||||
SocketReference m_reference = {0};
|
SocketReference m_reference = {0};
|
||||||
int m_flags = 0;
|
SocketFlags m_flags = SocketFlagNone;
|
||||||
size_t m_type_size = 0;
|
size_t m_type_size = 0;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void SetValue(const T value) {
|
||||||
|
if constexpr (std::is_same<T, bool>::value) {
|
||||||
|
m_value.flag = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, int>::value) {
|
||||||
|
m_value.int_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, float>::value) {
|
||||||
|
m_value.float_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Vec3>::value) {
|
||||||
|
m_value.vec3 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Quat>::value) {
|
||||||
|
m_value.quat = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, std::string>::value) {
|
||||||
|
m_value_string = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T GetValue() const {
|
||||||
|
if constexpr (std::is_same<T, bool>::value) {
|
||||||
|
return m_value.flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, int>::value) {
|
||||||
|
return m_value.int_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, float>::value) {
|
||||||
|
return m_value.float_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Vec3>::value) {
|
||||||
|
return m_value.vec3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Quat>::value) {
|
||||||
|
return m_value.quat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, std::string>::value) {
|
||||||
|
return m_value_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return T();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NodeSocketAccessorBase {
|
template <typename T>
|
||||||
std::vector<Socket> m_properties;
|
SocketType GetSocketType() {
|
||||||
|
if constexpr (std::is_same<T, bool>::value) {
|
||||||
|
return SocketType::SocketTypeBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, AnimData>::value) {
|
||||||
|
return SocketType::SocketTypeAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, int>::value) {
|
||||||
|
return SocketType::SocketTypeInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, float>::value) {
|
||||||
|
return SocketType::SocketTypeFloat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Vec3>::value) {
|
||||||
|
return SocketType::SocketTypeVec3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, Quat>::value) {
|
||||||
|
return SocketType::SocketTypeQuat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_same<T, std::string>::value) {
|
||||||
|
return SocketType::SocketTypeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false && "This should not be reachable");
|
||||||
|
abort();
|
||||||
|
return SocketType::SocketTypeUndefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeDescriptorBase {
|
||||||
std::vector<Socket> m_inputs;
|
std::vector<Socket> m_inputs;
|
||||||
std::vector<Socket> m_outputs;
|
std::vector<Socket> m_outputs;
|
||||||
|
std::vector<Socket> m_properties;
|
||||||
|
|
||||||
NodeSocketAccessorBase() {}
|
template <typename T>
|
||||||
virtual ~NodeSocketAccessorBase() {}
|
bool RegisterInput(
|
||||||
|
const char* name,
|
||||||
|
T** value_ptr_ptr,
|
||||||
|
SocketFlags flags = SocketFlags::SocketFlagNone) {
|
||||||
|
return RegisterSocket(name, value_ptr_ptr, m_inputs, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool RegisterOutput(
|
||||||
|
const char* name,
|
||||||
|
T** value_ptr_ptr,
|
||||||
|
SocketFlags flags = SocketFlags::SocketFlagNone) {
|
||||||
|
return RegisterSocket(name, value_ptr_ptr, m_outputs, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool RegisterProperty(
|
||||||
|
const char* name,
|
||||||
|
T* value_ptr,
|
||||||
|
SocketFlags flags = SocketFlags::SocketFlagNone) {
|
||||||
|
for (int i = 0; i < m_properties.size(); i++) {
|
||||||
|
if (m_properties[i].m_name == name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket socket;
|
||||||
|
socket.m_name = name;
|
||||||
|
socket.m_type = GetSocketType<T>();
|
||||||
|
socket.m_reference.ptr = static_cast<void*>(value_ptr);
|
||||||
|
socket.m_flags = flags;
|
||||||
|
socket.m_type_size = sizeof(T);
|
||||||
|
|
||||||
|
m_properties.push_back(socket);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* GetInput(const char* name) {
|
||||||
|
Socket* socket = FindSocket(name, m_inputs);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
return *socket->m_reference.ptr_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void SetInput(const char* name, T* value_ptr) {
|
||||||
|
Socket* socket = FindSocket(name, m_inputs);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
*socket->m_reference.ptr_ptr = value_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void SetInputValue(const char* name, T value) {
|
||||||
|
Socket* socket = FindSocket(name, m_inputs);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
socket->SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetInputUnchecked(const char* name, void* value_ptr) {
|
||||||
|
Socket* socket = FindSocket(name, m_inputs);
|
||||||
|
*socket->m_reference.ptr_ptr = value_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* GetInputSocket(const char* name) {
|
||||||
|
return FindSocket(name, m_inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetInputIndex(const char* name) {
|
||||||
|
return FindSocketIndex(name, m_inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void SetOutput(const char* name, T* value_ptr) {
|
||||||
|
Socket* socket = FindSocket(name, m_outputs);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
*socket->m_reference.ptr_ptr = value_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOutputUnchecked(const char* name, void* value_ptr) {
|
||||||
|
Socket* socket = FindSocket(name, m_outputs);
|
||||||
|
*socket->m_reference.ptr_ptr = value_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* GetOutputSocket(const char* name) {
|
||||||
|
return FindSocket(name, m_outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetOutputIndex(const char* name) {
|
||||||
|
return FindSocketIndex(name, m_outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets value of an AnimNode Socket.
|
||||||
|
*
|
||||||
|
* @note Should only be used when the NodeDescriptor is associated with an AnimNode instance.
|
||||||
|
*
|
||||||
|
* @tparam T can be any AnimGraph data type.
|
||||||
|
* @param Socket name
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void SetProperty(const char* name, const T& value) {
|
||||||
|
Socket* socket = FindSocket(name, m_properties);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
*static_cast<T*>(socket->m_reference.ptr) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets value of an AnimNodeResource Socket.
|
||||||
|
*
|
||||||
|
* @note Should only be used when the NodeDescriptor is associated with an AnimNodeResource instance. For AnimNode instances use Socket::SetProperty().
|
||||||
|
*
|
||||||
|
* @tparam T can be any AnimGraph data type.
|
||||||
|
* @param Socket name
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void SetPropertyValue(const char* name, const T& value) {
|
||||||
|
Socket* socket = FindSocket(name, m_properties);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
socket->SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetProperty(const char* name) {
|
||||||
|
Socket* socket = FindSocket(name, m_properties);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
return *static_cast<T*>(socket->m_reference.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T GetPropertyValue(const char* name) {
|
||||||
|
Socket* socket = FindSocket(name, m_properties);
|
||||||
|
assert(GetSocketType<T>() == socket->m_type);
|
||||||
|
return socket->GetValue<T>();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void UpdateFlags(){};
|
virtual void UpdateFlags(){};
|
||||||
|
|
||||||
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
|
protected:
|
||||||
Socket* result = nullptr;
|
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
|
||||||
for (size_t 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) {
|
||||||
result = &sockets[i];
|
return &sockets[i];
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Socket* FindSocket(
|
int FindSocketIndex(const char* name, std::vector<Socket>& sockets) {
|
||||||
const std::vector<Socket>& sockets,
|
for (int i = 0, n = sockets.size(); i < n; i++) {
|
||||||
const std::string& name) const {
|
|
||||||
const Socket* result = nullptr;
|
|
||||||
for (size_t i = 0, n = sockets.size(); i < n; i++) {
|
|
||||||
if (sockets[i].m_name == name) {
|
|
||||||
result = &sockets[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketType GetSocketType(
|
|
||||||
const std::vector<Socket>& sockets,
|
|
||||||
const std::string& name) {
|
|
||||||
const Socket* socket = FindSocket(sockets, name);
|
|
||||||
if (socket == nullptr) {
|
|
||||||
return SocketType::SocketTypeUndefined;
|
|
||||||
}
|
|
||||||
return socket->m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetSocketIndex(
|
|
||||||
const std::vector<Socket>& sockets,
|
|
||||||
const std::string& name) const {
|
|
||||||
for (size_t i = 0, n = sockets.size(); i < n; i++) {
|
|
||||||
if (sockets[i].m_name == name) {
|
if (sockets[i].m_name == name) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -204,254 +425,41 @@ struct NodeSocketAccessorBase {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T GetSocketValue(
|
|
||||||
const std::vector<Socket>& sockets,
|
|
||||||
const std::string& name,
|
|
||||||
T default_value) {
|
|
||||||
const Socket* socket = FindSocket(sockets, name);
|
|
||||||
if (socket == nullptr) {
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *static_cast<T*>(socket->m_reference.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void SetSocketReferenceValue(Socket* socket, T value) {
|
|
||||||
std::cerr << "Could not find template specialization for socket type "
|
|
||||||
<< static_cast<int>(socket->m_type) << " ("
|
|
||||||
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")."
|
|
||||||
<< std::endl;
|
|
||||||
// *static_cast<T*>(socket->m_value.ptr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void SetSocketValue(Socket* socket, T value) {
|
|
||||||
std::cerr << "Could not find template specialization for socket type "
|
|
||||||
<< static_cast<int>(socket->m_type) << " ("
|
|
||||||
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")."
|
|
||||||
<< std::endl;
|
|
||||||
// *static_cast<T*>(socket->m_value.ptr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool RegisterSocket(
|
bool RegisterSocket(
|
||||||
|
const char* name,
|
||||||
|
T** value_ptr_ptr,
|
||||||
std::vector<Socket>& sockets,
|
std::vector<Socket>& sockets,
|
||||||
const std::string& name,
|
SocketFlags flags) {
|
||||||
T* value_ptr,
|
for (int i = 0; i < sockets.size(); i++) {
|
||||||
int flags = 0) {
|
if (sockets[i].m_name == name) {
|
||||||
Socket* socket = FindSocket(sockets, name);
|
|
||||||
if (socket != nullptr) {
|
|
||||||
std::cerr << "Socket " << name << " already registered." << std::endl;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockets.push_back(Socket());
|
|
||||||
socket = &sockets[sockets.size() - 1];
|
|
||||||
socket->m_name = name;
|
|
||||||
socket->m_type_size = sizeof(T);
|
|
||||||
socket->m_flags = flags;
|
|
||||||
|
|
||||||
if constexpr (std::is_same<T, float>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeFloat;
|
|
||||||
} else if constexpr (std::is_same<T, bool>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeBool;
|
|
||||||
} else if constexpr (std::is_same<T, Vec3>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeVec3;
|
|
||||||
} else if constexpr (std::is_same<T, Quat>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeQuat;
|
|
||||||
} else if constexpr (std::is_same<T, AnimData>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeAnimation;
|
|
||||||
} else if constexpr (std::is_same<T, std::string>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeString;
|
|
||||||
} else if constexpr (std::is_same<T, float*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeFloat;
|
|
||||||
} else if constexpr (std::is_same<T, bool*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeBool;
|
|
||||||
} else if constexpr (std::is_same<T, Vec3*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeVec3;
|
|
||||||
} else if constexpr (std::is_same<T, Quat*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeQuat;
|
|
||||||
} else if constexpr (std::is_same<T, AnimData*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeAnimation;
|
|
||||||
} else if constexpr (std::is_same<T, std::string*>::value) {
|
|
||||||
socket->m_type = SocketType::SocketTypeString;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Cannot register socket, invalid type." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
socket->m_reference.ptr = value_ptr;
|
Socket socket;
|
||||||
|
socket.m_name = name;
|
||||||
|
socket.m_type = GetSocketType<T>();
|
||||||
|
socket.m_reference.ptr_ptr = (void**)(value_ptr_ptr);
|
||||||
|
socket.m_type_size = sizeof(T);
|
||||||
|
socket.m_flags = flags;
|
||||||
|
|
||||||
|
sockets.push_back(socket);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool RegisterProperty(const std::string& name, T* value) {
|
|
||||||
return RegisterSocket(m_properties, name, value);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
void SetPropertyReferenceValue(const std::string& name, T value) {
|
|
||||||
Socket* socket = FindSocket(m_properties, name);
|
|
||||||
SetSocketReferenceValue<T>(socket, value);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
void SetPropertyValue(const std::string& name, T value) {
|
|
||||||
Socket* socket = FindSocket(m_properties, name);
|
|
||||||
SetSocketValue<T>(socket, value);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
T GetProperty(const std::string& name, T default_value) {
|
|
||||||
return GetSocketValue(m_properties, name, default_value);
|
|
||||||
}
|
|
||||||
SocketType GetPropertyType(const std::string& name) {
|
|
||||||
return GetSocketType(m_properties, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool RegisterInput(const std::string& name, T* value, int flags = 0) {
|
|
||||||
return RegisterSocket(m_inputs, name, value, flags);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
T* GetInput(const std::string& name, T* value) {
|
|
||||||
return GetSocketValue(m_inputs, name, value);
|
|
||||||
}
|
|
||||||
Socket* FindInputSocket(const std::string& name) {
|
|
||||||
return FindSocket(m_inputs, name);
|
|
||||||
}
|
|
||||||
SocketType GetInputType(const std::string& name) {
|
|
||||||
return GetSocketType(m_inputs, name);
|
|
||||||
}
|
|
||||||
size_t GetInputIndex(const std::string& name) {
|
|
||||||
return GetSocketIndex(m_inputs, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool RegisterOutput(const std::string& name, T* value, int flags = 0) {
|
|
||||||
return RegisterSocket(m_outputs, name, value, flags);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
bool RegisterOutput(const std::string& name, T** value, int flags = 0) {
|
|
||||||
return RegisterSocket(m_outputs, name, value, flags);
|
|
||||||
}
|
|
||||||
SocketType GetOutputType(const std::string& name) {
|
|
||||||
return GetSocketType(m_outputs, name);
|
|
||||||
}
|
|
||||||
Socket* FindOutputSocket(const std::string& name) {
|
|
||||||
return FindSocket(m_outputs, name);
|
|
||||||
}
|
|
||||||
size_t GetOutputIndex(const std::string& name) {
|
|
||||||
return GetSocketIndex(m_outputs, name);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
template <typename T>
|
||||||
// SetSocketReferenceValue<> specializations
|
struct NodeDescriptor : public NodeDescriptorBase {
|
||||||
//
|
virtual ~NodeDescriptor() {}
|
||||||
template <>
|
};
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const bool&>(
|
|
||||||
Socket* socket,
|
|
||||||
const bool& value) {
|
|
||||||
*static_cast<bool*>(socket->m_reference.ptr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
struct AnimNode;
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const float&>(
|
|
||||||
Socket* socket,
|
|
||||||
const float& value) {
|
|
||||||
*static_cast<float*>(socket->m_reference.ptr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const Vec3&>(
|
|
||||||
Socket* socket,
|
|
||||||
const Vec3& value) {
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[0] = value[0];
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[1] = value[1];
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[2] = value[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const Quat&>(
|
|
||||||
Socket* socket,
|
|
||||||
const Quat& value) {
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[0] = value[0];
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[1] = value[1];
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[2] = value[2];
|
|
||||||
static_cast<float*>(socket->m_reference.ptr)[3] = value[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string&>(
|
|
||||||
Socket* socket,
|
|
||||||
const std::string& value) {
|
|
||||||
*static_cast<std::string*>(socket->m_reference.ptr) = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const char*>(
|
|
||||||
Socket* socket,
|
|
||||||
const char* value) {
|
|
||||||
std::string value_string(value);
|
|
||||||
SetSocketReferenceValue<const std::string&>(socket, value_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// SetSocketValue<> specializations
|
|
||||||
//
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const bool&>(
|
|
||||||
Socket* socket,
|
|
||||||
const bool& value) {
|
|
||||||
socket->m_value.flag = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const float&>(
|
|
||||||
Socket* socket,
|
|
||||||
const float& value) {
|
|
||||||
socket->m_value.float_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const Vec3&>(
|
|
||||||
Socket* socket,
|
|
||||||
const Vec3& value) {
|
|
||||||
socket->m_value.vec3[0] = value[0];
|
|
||||||
socket->m_value.vec3[1] = value[1];
|
|
||||||
socket->m_value.vec3[2] = value[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const Quat&>(
|
|
||||||
Socket* socket,
|
|
||||||
const Quat& value) {
|
|
||||||
socket->m_value.quat[0] = value[0];
|
|
||||||
socket->m_value.quat[1] = value[1];
|
|
||||||
socket->m_value.quat[2] = value[2];
|
|
||||||
socket->m_value.quat[3] = value[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const std::string&>(
|
|
||||||
Socket* socket,
|
|
||||||
const std::string& value) {
|
|
||||||
constexpr size_t string_max_length = sizeof(socket->m_value.str) - 1;
|
|
||||||
strncpy(socket->m_value.str, value.data(), string_max_length);
|
|
||||||
socket->m_value.str
|
|
||||||
[value.size() > string_max_length ? string_max_length : value.size()] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void NodeSocketAccessorBase::SetSocketValue<const char*>(
|
|
||||||
Socket* socket,
|
|
||||||
const char* value) {
|
|
||||||
SetSocketValue<const std::string&>(socket, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct NodeSocketAccessor : public NodeSocketAccessorBase {
|
NodeDescriptorBase* CreateNodeDescriptor(AnimNode* node) {
|
||||||
virtual ~NodeSocketAccessor() {}
|
return new NodeDescriptor<T>(dynamic_cast<T*>(node));
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHDATA_H
|
#endif //ANIMTESTBED_ANIMGRAPHDATA_H
|
||||||
|
|||||||
@@ -4,15 +4,22 @@
|
|||||||
|
|
||||||
#include "AnimGraphEditor.h"
|
#include "AnimGraphEditor.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "AnimGraphResource.h"
|
#include "AnimGraphResource.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 AnimGraphResource sGraphGresource = AnimGraphResource();
|
||||||
|
|
||||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||||
switch (socket_type) {
|
switch (socket_type) {
|
||||||
case SocketType::SocketTypeAnimation:
|
case SocketType::SocketTypeAnimation:
|
||||||
return ImNodesPinShape_QuadFilled;
|
return ImNodesPinShape_QuadFilled;
|
||||||
|
case SocketType::SocketTypeInt:
|
||||||
|
return ImNodesPinShape_CircleFilled;
|
||||||
case SocketType::SocketTypeFloat:
|
case SocketType::SocketTypeFloat:
|
||||||
return ImNodesPinShape_CircleFilled;
|
return ImNodesPinShape_CircleFilled;
|
||||||
case SocketType::SocketTypeVec3:
|
case SocketType::SocketTypeVec3:
|
||||||
@@ -54,13 +61,93 @@ void RemoveConnectionsForSocket(
|
|||||||
// AnimGraphConnectionResource& connection = *iter;
|
// AnimGraphConnectionResource& connection = *iter;
|
||||||
// if (connection.m_source_node == &node_resource
|
// if (connection.m_source_node == &node_resource
|
||||||
// && connection.m_source_socket == &socket) {
|
// && connection.m_source_socket == &socket) {
|
||||||
// iter = graph_resource.m_connections.erase(iter);
|
// iter = sGraphGresource.m_connections.erase(iter);
|
||||||
// } else {
|
// } else {
|
||||||
// iter++;
|
// iter++;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncTrackEditor(SyncTrack* sync_track) {
|
||||||
|
ImGui::SliderFloat("duration", &sync_track->m_duration, 0.001f, 10.f);
|
||||||
|
|
||||||
|
ImGui::Text("Marker");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%d", sync_track->m_num_intervals);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("+")) {
|
||||||
|
if (sync_track->m_num_intervals < cSyncTrackMaxIntervals) {
|
||||||
|
sync_track->m_num_intervals++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("-")) {
|
||||||
|
if (sync_track->m_num_intervals > 0) {
|
||||||
|
sync_track->m_num_intervals--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Marker:");
|
||||||
|
for (int i = 0; i < sync_track->m_num_intervals; i++) {
|
||||||
|
ImGui::Text("%2d:", i);
|
||||||
|
ImGui::SameLine();
|
||||||
|
std::ostringstream marker_stream;
|
||||||
|
marker_stream << i;
|
||||||
|
ImGui::SliderFloat(
|
||||||
|
marker_stream.str().c_str(),
|
||||||
|
&sync_track->m_sync_markers[i],
|
||||||
|
0.f,
|
||||||
|
1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Update Intervals")) {
|
||||||
|
sync_track->CalcIntervals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
||||||
|
if (ImGui::TreeNode("Bones")) {
|
||||||
|
for (int i = 0; i < skinned_mesh->m_skeleton.num_joints(); i++) {
|
||||||
|
ImGui::Text("%s", skinned_mesh->m_skeleton.joint_names()[i]);
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Animations");
|
||||||
|
|
||||||
|
const char* items[255] = {0};
|
||||||
|
static int selected = -1;
|
||||||
|
for (int i = 0; i < skinned_mesh->m_animations.size(); i++) {
|
||||||
|
items[i] = skinned_mesh->m_animation_names[i].c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Combo(
|
||||||
|
"Animation",
|
||||||
|
&selected,
|
||||||
|
items,
|
||||||
|
skinned_mesh->m_animations.size());
|
||||||
|
|
||||||
|
ImGui::Text("Sync Track");
|
||||||
|
if (selected >= 0 && selected < skinned_mesh->m_animations.size()) {
|
||||||
|
SyncTrackEditor(&skinned_mesh->m_animation_sync_track[selected]);
|
||||||
|
skinned_mesh->m_override_anim = selected;
|
||||||
|
|
||||||
|
ImGui::Checkbox("Override Animation", &skinned_mesh->m_sync_track_override);
|
||||||
|
if (skinned_mesh->m_sync_track_override) {
|
||||||
|
ImGui::SliderFloat("Ratio", &skinned_mesh->m_override_ratio, 0.f, 1.f);
|
||||||
|
|
||||||
|
ozz::animation::SamplingJob sampling_job;
|
||||||
|
sampling_job.animation = skinned_mesh->m_animations[selected];
|
||||||
|
sampling_job.context = &skinned_mesh->m_sampling_context;
|
||||||
|
sampling_job.ratio = skinned_mesh->m_override_ratio;
|
||||||
|
sampling_job.output = make_span(skinned_mesh->m_local_matrices);
|
||||||
|
if (!sampling_job.Run()) {
|
||||||
|
ozz::log::Err() << "Error sampling animation." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AnimGraphEditorRenderSidebar(
|
void AnimGraphEditorRenderSidebar(
|
||||||
AnimGraphResource& graph_resource,
|
AnimGraphResource& graph_resource,
|
||||||
AnimNodeResource& node_resource) {
|
AnimNodeResource& node_resource) {
|
||||||
@@ -80,30 +167,38 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
int num_properties = node_resource.m_socket_accessor->m_properties.size();
|
int 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::SocketTypeFloat) {
|
if (property.m_type == SocketType::SocketTypeInt) {
|
||||||
|
ImGui::InputInt(
|
||||||
|
property.m_name.c_str(),
|
||||||
|
reinterpret_cast<int*>(&property.m_value.int_value),
|
||||||
|
1);
|
||||||
|
} else if (property.m_type == SocketType::SocketTypeFloat) {
|
||||||
ImGui::SliderFloat(
|
ImGui::SliderFloat(
|
||||||
property.m_name.c_str(),
|
property.m_name.c_str(),
|
||||||
reinterpret_cast<float*>(property.m_reference.ptr),
|
reinterpret_cast<float*>(&property.m_value.float_value),
|
||||||
-100.f,
|
-100.f,
|
||||||
100.f);
|
100.f);
|
||||||
} else if (property.m_type == SocketType::SocketTypeBool) {
|
} else if (property.m_type == SocketType::SocketTypeBool) {
|
||||||
ImGui::Checkbox(
|
bool flag_value = property.GetValue<bool>();
|
||||||
|
if (ImGui::Checkbox(
|
||||||
property.m_name.c_str(),
|
property.m_name.c_str(),
|
||||||
reinterpret_cast<bool*>(property.m_reference.ptr));
|
&flag_value)) {
|
||||||
|
property.SetValue(flag_value);
|
||||||
|
}
|
||||||
} else if (property.m_type == SocketType::SocketTypeString) {
|
} else if (property.m_type == SocketType::SocketTypeString) {
|
||||||
std::string* property_string =
|
char string_buf[1024];
|
||||||
reinterpret_cast<std::string*>(property.m_reference.ptr);
|
memset(string_buf, '\0', sizeof(string_buf));
|
||||||
char string_buf[256];
|
memcpy(
|
||||||
memset(string_buf, 0, sizeof(string_buf));
|
|
||||||
strncpy(
|
|
||||||
string_buf,
|
string_buf,
|
||||||
property_string->c_str(),
|
property.m_value_string.c_str(),
|
||||||
std::min(property_string->size(), sizeof(string_buf)));
|
std::min(
|
||||||
|
static_cast<size_t>(1024),
|
||||||
|
property.m_value_string.size() + 1));
|
||||||
if (ImGui::InputText(
|
if (ImGui::InputText(
|
||||||
property.m_name.c_str(),
|
property.m_name.c_str(),
|
||||||
string_buf,
|
string_buf,
|
||||||
sizeof(string_buf))) {
|
sizeof(string_buf))) {
|
||||||
(*property_string) = string_buf;
|
property.m_value_string = string_buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,33 +247,31 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorUpdate() {
|
void AnimGraphEditorUpdate() {
|
||||||
static AnimGraphResource graph_resource = AnimGraphResource();
|
|
||||||
|
|
||||||
ImGui::BeginMenuBar();
|
ImGui::BeginMenuBar();
|
||||||
if (ImGui::Button("Save")) {
|
if (ImGui::Button("Save")) {
|
||||||
graph_resource.saveToFile("editor_graph.json");
|
sGraphGresource.saveToFile("editor_graph.json");
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Load")) {
|
if (ImGui::Button("Load")) {
|
||||||
graph_resource.loadFromFile("editor_graph.json");
|
sGraphGresource.loadFromFile("editor_graph.json");
|
||||||
|
|
||||||
for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) {
|
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
|
||||||
const AnimNodeResource& node_resource = graph_resource.m_nodes[i];
|
const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
|
||||||
ImNodes::SetNodeGridSpacePos(
|
ImNodes::SetNodeGridSpacePos(
|
||||||
i,
|
i,
|
||||||
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
|
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Clear")) {
|
if (ImGui::Button("Clear")) {
|
||||||
graph_resource.clear();
|
sGraphGresource.clear();
|
||||||
}
|
}
|
||||||
char graph_name_buffer[256];
|
char graph_name_buffer[256];
|
||||||
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
||||||
strncpy(
|
strncpy(
|
||||||
graph_name_buffer,
|
graph_name_buffer,
|
||||||
graph_resource.m_name.c_str(),
|
sGraphGresource.m_name.c_str(),
|
||||||
sizeof(graph_name_buffer));
|
sizeof(graph_name_buffer));
|
||||||
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
|
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
|
||||||
graph_resource.m_name = graph_name_buffer;
|
sGraphGresource.m_name = graph_name_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndMenuBar();
|
ImGui::EndMenuBar();
|
||||||
@@ -217,6 +310,10 @@ void AnimGraphEditorUpdate() {
|
|||||||
node_type_name = "SpeedScale";
|
node_type_name = "SpeedScale";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("LockTranslationNode")) {
|
||||||
|
node_type_name = "LockTranslationNode";
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::MenuItem("MathAddNode")) {
|
if (ImGui::MenuItem("MathAddNode")) {
|
||||||
node_type_name = "MathAddNode";
|
node_type_name = "MathAddNode";
|
||||||
}
|
}
|
||||||
@@ -225,12 +322,16 @@ void AnimGraphEditorUpdate() {
|
|||||||
node_type_name = "MathFloatToVec3Node";
|
node_type_name = "MathFloatToVec3Node";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("ConstScalarNode")) {
|
||||||
|
node_type_name = "ConstScalarNode";
|
||||||
|
}
|
||||||
|
|
||||||
if (node_type_name != "") {
|
if (node_type_name != "") {
|
||||||
AnimNodeResource node_resource =
|
AnimNodeResource node_resource =
|
||||||
AnimNodeResourceFactory(node_type_name);
|
AnimNodeResourceFactory(node_type_name);
|
||||||
size_t node_id = graph_resource.m_nodes.size();
|
size_t node_id = sGraphGresource.m_nodes.size();
|
||||||
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
|
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
|
||||||
graph_resource.m_nodes.push_back(node_resource);
|
sGraphGresource.m_nodes.push_back(node_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@@ -239,17 +340,17 @@ void AnimGraphEditorUpdate() {
|
|||||||
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
|
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) {
|
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
|
||||||
AnimNodeResource& node_resource = graph_resource.m_nodes[i];
|
AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
|
||||||
|
|
||||||
ImNodes::BeginNode(i);
|
ImNodes::BeginNode(i);
|
||||||
ImGui::PushItemWidth(110.0f);
|
ImGui::PushItemWidth(110.0f);
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
ImNodes::BeginNodeTitleBar();
|
ImNodes::BeginNodeTitleBar();
|
||||||
if (&node_resource == &graph_resource.getGraphOutputNode()) {
|
if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
|
||||||
ImGui::TextUnformatted("Graph Outputs");
|
ImGui::TextUnformatted("Graph Outputs");
|
||||||
} else if (&node_resource == &graph_resource.getGraphInputNode()) {
|
} else if (&node_resource == &sGraphGresource.getGraphInputNode()) {
|
||||||
ImGui::TextUnformatted("Graph Inputs");
|
ImGui::TextUnformatted("Graph Inputs");
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextUnformatted(node_resource.m_type_name.c_str());
|
ImGui::TextUnformatted(node_resource.m_type_name.c_str());
|
||||||
@@ -257,10 +358,10 @@ void AnimGraphEditorUpdate() {
|
|||||||
ImNodes::EndNodeTitleBar();
|
ImNodes::EndNodeTitleBar();
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
const std::vector<Socket>& node_inputs =
|
std::vector<Socket>& node_inputs =
|
||||||
node_resource.m_socket_accessor->m_inputs;
|
node_resource.m_socket_accessor->m_inputs;
|
||||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
||||||
const Socket& socket = node_inputs[j];
|
Socket& socket = node_inputs[j];
|
||||||
|
|
||||||
ImColor socket_color = ImColor(255, 255, 255, 255);
|
ImColor socket_color = ImColor(255, 255, 255, 255);
|
||||||
if (socket.m_flags & SocketFlagAffectsTime) {
|
if (socket.m_flags & SocketFlagAffectsTime) {
|
||||||
@@ -273,13 +374,27 @@ void AnimGraphEditorUpdate() {
|
|||||||
socket_color);
|
socket_color);
|
||||||
ImGui::TextUnformatted(socket.m_name.c_str());
|
ImGui::TextUnformatted(socket.m_name.c_str());
|
||||||
|
|
||||||
bool socket_connected = graph_resource.isSocketConnected(node_resource, socket.m_name);
|
bool socket_connected =
|
||||||
if (!socket_connected &&
|
sGraphGresource.isSocketConnected(node_resource, socket.m_name);
|
||||||
(socket.m_type == SocketType::SocketTypeFloat)) {
|
if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
float socket_value = 0.f;
|
float socket_value = socket.m_value.float_value;
|
||||||
ImGui::PushItemWidth(100.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
|
ImGui::PushItemWidth(
|
||||||
ImGui::DragFloat("##hidelabel", &socket_value, 0.01f);
|
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();
|
ImGui::PopItemWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,26 +422,27 @@ void AnimGraphEditorUpdate() {
|
|||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
if (ImGui::Button("+Output")) {
|
if (ImGui::Button("+Output")) {
|
||||||
AnimNodeResource& graph_output_node =
|
AnimNodeResource& graph_output_node =
|
||||||
graph_resource.getGraphOutputNode();
|
sGraphGresource.getGraphOutputNode();
|
||||||
|
|
||||||
static float bla = 0.f;
|
static float bla = 0.f;
|
||||||
std::string socket_name = "Output";
|
std::string socket_name = "Output";
|
||||||
socket_name += std::to_string(
|
socket_name += std::to_string(
|
||||||
graph_output_node.m_socket_accessor->m_inputs.size());
|
graph_output_node.m_socket_accessor->m_inputs.size());
|
||||||
graph_output_node.m_socket_accessor->RegisterInput<float>(
|
graph_output_node.m_socket_accessor->RegisterInput<float>(
|
||||||
socket_name,
|
socket_name.c_str(),
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
if (ImGui::Button("+Input")) {
|
if (ImGui::Button("+Input")) {
|
||||||
AnimNodeResource& graph_input_node = graph_resource.getGraphInputNode();
|
AnimNodeResource& graph_input_node =
|
||||||
|
sGraphGresource.getGraphInputNode();
|
||||||
|
|
||||||
static float bla = 0.f;
|
static float bla = 0.f;
|
||||||
std::string socket_name = "Input";
|
std::string socket_name = "Input";
|
||||||
socket_name += std::to_string(
|
socket_name += std::to_string(
|
||||||
graph_input_node.m_socket_accessor->m_outputs.size());
|
graph_input_node.m_socket_accessor->m_outputs.size());
|
||||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
||||||
socket_name,
|
socket_name.c_str(),
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,20 +459,20 @@ void AnimGraphEditorUpdate() {
|
|||||||
node_resource.m_socket_accessor->UpdateFlags();
|
node_resource.m_socket_accessor->UpdateFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0, n = graph_resource.m_connections.size(); i < n; i++) {
|
for (size_t i = 0, n = sGraphGresource.m_connections.size(); i < n; i++) {
|
||||||
const AnimGraphConnectionResource& connection =
|
const AnimGraphConnectionResource& connection =
|
||||||
graph_resource.m_connections[i];
|
sGraphGresource.m_connections[i];
|
||||||
int start_attr, end_attr;
|
int start_attr, end_attr;
|
||||||
|
|
||||||
const AnimNodeResource& source_node =
|
const AnimNodeResource& source_node =
|
||||||
graph_resource.m_nodes[connection.source_node_index];
|
sGraphGresource.m_nodes[connection.source_node_index];
|
||||||
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
|
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
|
||||||
connection.source_socket_name);
|
connection.source_socket_name.c_str());
|
||||||
|
|
||||||
const AnimNodeResource& target_node =
|
const AnimNodeResource& target_node =
|
||||||
graph_resource.m_nodes[connection.target_node_index];
|
sGraphGresource.m_nodes[connection.target_node_index];
|
||||||
int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
|
int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
|
||||||
connection.target_socket_name);
|
connection.target_socket_name.c_str());
|
||||||
|
|
||||||
start_attr = GenerateOutputAttributeId(
|
start_attr = GenerateOutputAttributeId(
|
||||||
connection.source_node_index,
|
connection.source_node_index,
|
||||||
@@ -386,24 +502,29 @@ void AnimGraphEditorUpdate() {
|
|||||||
|
|
||||||
AnimGraphConnectionResource connection;
|
AnimGraphConnectionResource connection;
|
||||||
connection.source_node_index = node_start_id;
|
connection.source_node_index = node_start_id;
|
||||||
const AnimNodeResource& source_node = graph_resource.m_nodes[node_start_id];
|
const AnimNodeResource& source_node =
|
||||||
|
sGraphGresource.m_nodes[node_start_id];
|
||||||
connection.source_socket_name =
|
connection.source_socket_name =
|
||||||
source_node.m_socket_accessor->m_outputs[node_start_output_index]
|
source_node.m_socket_accessor->m_outputs[node_start_output_index]
|
||||||
.m_name;
|
.m_name;
|
||||||
|
|
||||||
connection.target_node_index = node_end_id;
|
connection.target_node_index = node_end_id;
|
||||||
const AnimNodeResource& target_node = graph_resource.m_nodes[node_end_id];
|
const AnimNodeResource& target_node = sGraphGresource.m_nodes[node_end_id];
|
||||||
connection.target_socket_name =
|
connection.target_socket_name =
|
||||||
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
|
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
|
||||||
|
|
||||||
graph_resource.m_connections.push_back(connection);
|
sGraphGresource.m_connections.push_back(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
|
||||||
|
std::cerr << "Delete key!" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle link detachements.
|
// Handle link detachements.
|
||||||
int link_id = 0;
|
int link_id = 0;
|
||||||
if (ImNodes::IsLinkDestroyed(&link_id)) {
|
if (ImNodes::IsLinkDestroyed(&link_id)) {
|
||||||
graph_resource.m_connections.erase(
|
sGraphGresource.m_connections.erase(
|
||||||
graph_resource.m_connections.begin() + link_id);
|
sGraphGresource.m_connections.begin() + link_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int selected_nodes[ImNodes::NumSelectedNodes()];
|
int selected_nodes[ImNodes::NumSelectedNodes()];
|
||||||
@@ -415,12 +536,16 @@ void AnimGraphEditorUpdate() {
|
|||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
|
||||||
if (ImNodes::NumSelectedNodes() == 1) {
|
if (ImNodes::NumSelectedNodes() == 1) {
|
||||||
if (selected_nodes[0] < graph_resource.m_nodes.size()) {
|
if (selected_nodes[0] < sGraphGresource.m_nodes.size()) {
|
||||||
AnimNodeResource& selected_node =
|
AnimNodeResource& selected_node =
|
||||||
graph_resource.m_nodes[selected_nodes[0]];
|
sGraphGresource.m_nodes[selected_nodes[0]];
|
||||||
AnimGraphEditorRenderSidebar(graph_resource, selected_node);
|
AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Columns(1);
|
ImGui::Columns(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) {
|
||||||
|
sGraphGresource.createInstance(anim_graph);
|
||||||
|
}
|
||||||
@@ -5,6 +5,10 @@
|
|||||||
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
|
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
|
||||||
#define ANIMTESTBED_ANIMGRAPHEDITOR_H
|
#define ANIMTESTBED_ANIMGRAPHEDITOR_H
|
||||||
|
|
||||||
|
#include "AnimGraph.h"
|
||||||
|
|
||||||
|
struct SkinnedMesh;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -25,6 +29,12 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
|
|||||||
*output_index = (attribute_id >> 23) - 1;
|
*output_index = (attribute_id >> 23) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncTrackEditor(SyncTrack* sync_track);
|
||||||
|
|
||||||
|
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
|
||||||
|
|
||||||
void AnimGraphEditorUpdate();
|
void AnimGraphEditorUpdate();
|
||||||
|
|
||||||
|
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
|
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
archive >> *m_animation;
|
||||||
|
|
||||||
context.m_animation_map[m_filename] = m_animation;
|
context.m_animation_map[m_filename] = m_animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +82,40 @@ void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
|
|||||||
ozz::animation::SamplingJob sampling_job;
|
ozz::animation::SamplingJob sampling_job;
|
||||||
sampling_job.animation = m_animation;
|
sampling_job.animation = m_animation;
|
||||||
sampling_job.context = &m_sampling_context;
|
sampling_job.context = &m_sampling_context;
|
||||||
sampling_job.ratio = m_time_now;
|
sampling_job.ratio = fmodf(m_time_now, m_animation->duration());
|
||||||
sampling_job.output = make_span(o_output->m_local_matrices);
|
sampling_job.output = make_span(o_output->m_local_matrices);
|
||||||
|
|
||||||
if (!sampling_job.Run()) {
|
if (!sampling_job.Run()) {
|
||||||
ozz::log::Err() << "Error sampling animation." << std::endl;
|
ozz::log::Err() << "Error sampling animation." << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LockTranslationNode::Evaluate(AnimGraphContext& context) {
|
||||||
|
o_output->m_local_matrices = i_input->m_local_matrices;
|
||||||
|
ozz::math::SoaFloat3 translation =
|
||||||
|
o_output->m_local_matrices[m_locked_bone_index].translation;
|
||||||
|
float x[4];
|
||||||
|
float y[4];
|
||||||
|
float z[4];
|
||||||
|
_mm_store_ps(x, translation.x);
|
||||||
|
_mm_store_ps(y, translation.y);
|
||||||
|
_mm_store_ps(z, translation.z);
|
||||||
|
|
||||||
|
if (m_lock_x) {
|
||||||
|
x[0] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_lock_y) {
|
||||||
|
y[0] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_lock_z) {
|
||||||
|
z[0] = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
translation.x = _mm_load_ps(x);
|
||||||
|
translation.y = _mm_load_ps(y);
|
||||||
|
translation.z = _mm_load_ps(z);
|
||||||
|
|
||||||
|
o_output->m_local_matrices[m_locked_bone_index].translation = translation;
|
||||||
|
}
|
||||||
@@ -86,8 +86,8 @@ struct AnimNode {
|
|||||||
struct BlendTreeNode : public AnimNode {};
|
struct BlendTreeNode : public AnimNode {};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<BlendTreeNode> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {}
|
NodeDescriptor(BlendTreeNode* node_) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -123,33 +123,30 @@ struct Blend2Node : public AnimNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {
|
NodeDescriptor(Blend2Node* node) {
|
||||||
Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
|
|
||||||
RegisterInput("Input0", &node->i_input0);
|
RegisterInput("Input0", &node->i_input0);
|
||||||
RegisterInput("Input1", &node->i_input1);
|
RegisterInput("Input1", &node->i_input1);
|
||||||
RegisterInput(
|
RegisterInput("Weight", &node->i_blend_weight);
|
||||||
"Weight",
|
|
||||||
&node->i_blend_weight,
|
|
||||||
SocketFlags::SocketFlagAffectsTime);
|
|
||||||
|
|
||||||
RegisterOutput("Output", &node->o_output);
|
RegisterOutput("Output", &node->o_output);
|
||||||
|
|
||||||
RegisterProperty("Sync", &node->m_sync_blend);
|
RegisterProperty("Sync", &node->m_sync_blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void UpdateFlags() override {
|
void UpdateFlags() override {
|
||||||
Socket* weight_input_socket = FindSocket(m_inputs, "Weight");
|
Socket* weight_input_socket = FindSocket("Weight", m_inputs);
|
||||||
assert(weight_input_socket != nullptr);
|
assert(weight_input_socket != nullptr);
|
||||||
|
|
||||||
if (GetProperty<bool>("Sync", false) == true) {
|
if (GetProperty<bool>("Sync") == true) {
|
||||||
weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
|
weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
|
||||||
} else {
|
} else {
|
||||||
weight_input_socket->m_flags = 0;
|
weight_input_socket->m_flags = SocketFlags::SocketFlagNone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SpeedScaleNode
|
// SpeedScaleNode
|
||||||
//
|
//
|
||||||
@@ -159,8 +156,8 @@ struct SpeedScaleNode : public AnimNode {
|
|||||||
float* i_speed_scale = nullptr;
|
float* i_speed_scale = nullptr;
|
||||||
|
|
||||||
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 = m_time_now;
|
||||||
m_time_now = time_last + (time_now - time_last) * (*i_speed_scale);
|
m_time_now = m_time_last + (time_now - time_last) * (*i_speed_scale);
|
||||||
m_state = AnimNodeEvalState::TimeUpdated;
|
m_state = AnimNodeEvalState::TimeUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,9 +170,8 @@ struct SpeedScaleNode : public AnimNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {
|
NodeDescriptor(SpeedScaleNode* node) {
|
||||||
SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
|
|
||||||
RegisterInput(
|
RegisterInput(
|
||||||
"SpeedScale",
|
"SpeedScale",
|
||||||
&node->i_speed_scale,
|
&node->i_speed_scale,
|
||||||
@@ -186,6 +182,7 @@ struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// AnimSamplerNode
|
// AnimSamplerNode
|
||||||
//
|
//
|
||||||
@@ -197,46 +194,97 @@ struct AnimSamplerNode : public AnimNode {
|
|||||||
|
|
||||||
virtual ~AnimSamplerNode();
|
virtual ~AnimSamplerNode();
|
||||||
virtual bool Init(AnimGraphContext& context) override;
|
virtual bool Init(AnimGraphContext& context) override;
|
||||||
|
void UpdateTime(float time_last, float time_now) override {
|
||||||
|
m_time_last = time_last;
|
||||||
|
m_time_now = time_now;
|
||||||
|
m_state = AnimNodeEvalState::TimeUpdated;
|
||||||
|
}
|
||||||
virtual void Evaluate(AnimGraphContext& context) override;
|
virtual void Evaluate(AnimGraphContext& context) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<AnimSamplerNode> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<AnimSamplerNode> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {
|
NodeDescriptor(AnimSamplerNode* node) {
|
||||||
AnimSamplerNode* node = dynamic_cast<AnimSamplerNode*>(node_);
|
|
||||||
RegisterOutput("Output", &node->o_output);
|
RegisterOutput("Output", &node->o_output);
|
||||||
|
|
||||||
RegisterProperty("Filename", &node->m_filename);
|
RegisterProperty("Filename", &node->m_filename);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// LockTranslationNode
|
||||||
|
//
|
||||||
|
struct LockTranslationNode : public AnimNode {
|
||||||
|
AnimData* i_input = nullptr;
|
||||||
|
AnimData* o_output = nullptr;
|
||||||
|
int m_locked_bone_index;
|
||||||
|
bool m_lock_x;
|
||||||
|
bool m_lock_y;
|
||||||
|
bool m_lock_z;
|
||||||
|
|
||||||
|
virtual void Evaluate(AnimGraphContext& context) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
|
||||||
|
NodeDescriptor(LockTranslationNode* node) {
|
||||||
|
RegisterInput("Input", &node->i_input);
|
||||||
|
RegisterOutput("Output", &node->o_output);
|
||||||
|
|
||||||
|
RegisterProperty("BoneIndex", &node->m_locked_bone_index);
|
||||||
|
RegisterProperty("LockAxisX", &node->m_lock_x);
|
||||||
|
RegisterProperty("LockAxisY", &node->m_lock_y);
|
||||||
|
RegisterProperty("LockAxisZ", &node->m_lock_z);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ConstScalarNode
|
||||||
|
//
|
||||||
|
struct ConstScalarNode : public AnimNode {
|
||||||
|
float* o_value = nullptr;
|
||||||
|
float value = 0.f;
|
||||||
|
|
||||||
|
virtual void Evaluate(AnimGraphContext& context){
|
||||||
|
*o_value = value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
|
||||||
|
NodeDescriptor(ConstScalarNode* node) {
|
||||||
|
RegisterOutput("ScalarOutput", &node->o_value);
|
||||||
|
RegisterProperty("ScalarValue", &node->value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MathAddNode
|
// MathAddNode
|
||||||
//
|
//
|
||||||
struct MathAddNode : public AnimNode {
|
struct MathAddNode : public AnimNode {
|
||||||
float* i_input0 = nullptr;
|
float* i_input0 = nullptr;
|
||||||
float* i_input1 = nullptr;
|
float* i_input1 = nullptr;
|
||||||
float o_output = 0.f;
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<MathAddNode> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<MathAddNode> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {
|
NodeDescriptor(MathAddNode* node) {
|
||||||
MathAddNode* node = dynamic_cast<MathAddNode*>(node_);
|
|
||||||
RegisterInput("Input0", &node->i_input0);
|
RegisterInput("Input0", &node->i_input0);
|
||||||
RegisterInput("Input1", &node->i_input1);
|
RegisterInput("Input1", &node->i_input1);
|
||||||
RegisterOutput("Output", &node->o_output);
|
RegisterOutput("Output", &node->o_output);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MathFloatToVec3Node
|
// MathFloatToVec3Node
|
||||||
//
|
//
|
||||||
@@ -244,23 +292,22 @@ struct MathFloatToVec3Node : public AnimNode {
|
|||||||
float* i_input0 = nullptr;
|
float* i_input0 = nullptr;
|
||||||
float* i_input1 = nullptr;
|
float* i_input1 = nullptr;
|
||||||
float* i_input2 = nullptr;
|
float* i_input2 = nullptr;
|
||||||
Vec3 o_output = {0.f, 0.f, 0.f};
|
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[0] = *i_input0;
|
o_output->v[0] = *i_input0;
|
||||||
o_output[1] = *i_input1;
|
o_output->v[1] = *i_input1;
|
||||||
o_output[2] = *i_input2;
|
o_output->v[2] = *i_input2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct NodeSocketAccessor<MathFloatToVec3Node> : public NodeSocketAccessorBase {
|
struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
|
||||||
NodeSocketAccessor(AnimNode* node_) {
|
NodeDescriptor(MathFloatToVec3Node* node) {
|
||||||
MathFloatToVec3Node* node = dynamic_cast<MathFloatToVec3Node*>(node_);
|
|
||||||
RegisterInput("Input0", &node->i_input0);
|
RegisterInput("Input0", &node->i_input0);
|
||||||
RegisterInput("Input1", &node->i_input1);
|
RegisterInput("Input1", &node->i_input1);
|
||||||
RegisterInput("Input2", &node->i_input2);
|
RegisterInput("Input2", &node->i_input2);
|
||||||
@@ -268,6 +315,7 @@ struct NodeSocketAccessor<MathFloatToVec3Node> : public NodeSocketAccessorBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
||||||
AnimNode* result;
|
AnimNode* result;
|
||||||
if (name == "Blend2") {
|
if (name == "Blend2") {
|
||||||
@@ -276,12 +324,16 @@ static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
|||||||
result = new SpeedScaleNode;
|
result = new SpeedScaleNode;
|
||||||
} else if (name == "AnimSampler") {
|
} else if (name == "AnimSampler") {
|
||||||
result = new AnimSamplerNode;
|
result = new AnimSamplerNode;
|
||||||
|
} else if (name == "LockTranslationNode") {
|
||||||
|
result = new LockTranslationNode;
|
||||||
} else if (name == "BlendTree") {
|
} else if (name == "BlendTree") {
|
||||||
result = new BlendTreeNode;
|
result = new BlendTreeNode;
|
||||||
} else if (name == "MathAddNode") {
|
} else if (name == "MathAddNode") {
|
||||||
result = new MathAddNode;
|
result = new MathAddNode;
|
||||||
} else if (name == "MathFloatToVec3Node") {
|
} else if (name == "MathFloatToVec3Node") {
|
||||||
result = new MathFloatToVec3Node;
|
result = new MathFloatToVec3Node;
|
||||||
|
} else if (name == "ConstScalarNode") {
|
||||||
|
result = new ConstScalarNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != nullptr) {
|
if (result != nullptr) {
|
||||||
@@ -293,21 +345,25 @@ static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline NodeSocketAccessorBase* AnimNodeAccessorFactory(
|
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
|
||||||
const std::string& node_type_name,
|
const std::string& node_type_name,
|
||||||
AnimNode* node) {
|
AnimNode* node) {
|
||||||
if (node_type_name == "Blend2") {
|
if (node_type_name == "Blend2") {
|
||||||
return new NodeSocketAccessor<Blend2Node>(node);
|
return CreateNodeDescriptor<Blend2Node>(node);
|
||||||
} else if (node_type_name == "SpeedScale") {
|
} else if (node_type_name == "SpeedScale") {
|
||||||
return new NodeSocketAccessor<SpeedScaleNode>(node);
|
return CreateNodeDescriptor<SpeedScaleNode>(node);
|
||||||
} else if (node_type_name == "AnimSampler") {
|
} else if (node_type_name == "AnimSampler") {
|
||||||
return new NodeSocketAccessor<AnimSamplerNode>(node);
|
return CreateNodeDescriptor<AnimSamplerNode>(node);
|
||||||
|
} else if (node_type_name == "LockTranslationNode") {
|
||||||
|
return CreateNodeDescriptor<LockTranslationNode>(node);
|
||||||
} else if (node_type_name == "BlendTree") {
|
} else if (node_type_name == "BlendTree") {
|
||||||
return new NodeSocketAccessor<BlendTreeNode>(node);
|
return CreateNodeDescriptor<BlendTreeNode>(node);
|
||||||
} else if (node_type_name == "MathAddNode") {
|
} else if (node_type_name == "MathAddNode") {
|
||||||
return new NodeSocketAccessor<MathAddNode>(node);
|
return CreateNodeDescriptor<MathAddNode>(node);
|
||||||
} else if (node_type_name == "MathFloatToVec3Node") {
|
} else if (node_type_name == "MathFloatToVec3Node") {
|
||||||
return new NodeSocketAccessor<MathFloatToVec3Node>(node);
|
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
|
||||||
|
} else if (node_type_name == "ConstScalarNode") {
|
||||||
|
return CreateNodeDescriptor<ConstScalarNode>(node);
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Invalid node type name " << node_type_name << "."
|
std::cerr << "Invalid node type name " << node_type_name << "."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|||||||
+241
-176
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "AnimGraphResource.h"
|
#include "AnimGraphResource.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
@@ -27,23 +28,26 @@ json sSocketToJson(const Socket& socket) {
|
|||||||
result["name"] = socket.m_name;
|
result["name"] = socket.m_name;
|
||||||
result["type"] = sSocketTypeToStr(socket.m_type);
|
result["type"] = sSocketTypeToStr(socket.m_type);
|
||||||
|
|
||||||
if (socket.m_reference.ptr != nullptr) {
|
if (socket.m_type == SocketType::SocketTypeString
|
||||||
|
&& socket.m_value_string.size() > 0) {
|
||||||
|
result["value"] = socket.m_value_string;
|
||||||
|
} else if (socket.m_value.flag) {
|
||||||
if (socket.m_type == SocketType::SocketTypeBool) {
|
if (socket.m_type == SocketType::SocketTypeBool) {
|
||||||
result["value"] = socket.m_value.flag;
|
result["value"] = socket.m_value.flag;
|
||||||
} else if (socket.m_type == SocketType::SocketTypeAnimation) {
|
} 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) {
|
} else if (socket.m_type == SocketType::SocketTypeFloat) {
|
||||||
result["value"] = socket.m_value.float_value;
|
result["value"] = socket.m_value.float_value;
|
||||||
} else if (socket.m_type == SocketType::SocketTypeVec3) {
|
} else if (socket.m_type == SocketType::SocketTypeVec3) {
|
||||||
result["value"][0] = socket.m_value.vec3[0];
|
result["value"][0] = socket.m_value.vec3.v[0];
|
||||||
result["value"][1] = socket.m_value.vec3[1];
|
result["value"][1] = socket.m_value.vec3.v[1];
|
||||||
result["value"][2] = socket.m_value.vec3[2];
|
result["value"][2] = socket.m_value.vec3.v[2];
|
||||||
} else if (socket.m_type == SocketType::SocketTypeQuat) {
|
} else if (socket.m_type == SocketType::SocketTypeQuat) {
|
||||||
result["value"][0] = socket.m_value.quat[0];
|
result["value"][0] = socket.m_value.quat.v[0];
|
||||||
result["value"][1] = socket.m_value.quat[1];
|
result["value"][1] = socket.m_value.quat.v[1];
|
||||||
result["value"][2] = socket.m_value.quat[2];
|
result["value"][2] = socket.m_value.quat.v[2];
|
||||||
result["value"][3] = socket.m_value.quat[3];
|
result["value"][3] = socket.m_value.quat.v[3];
|
||||||
} else if (socket.m_type == SocketType::SocketTypeString) {
|
|
||||||
result["value"] = std::string(socket.m_value.str);
|
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
|
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
|
||||||
<< "'." << std::endl;
|
<< "'." << std::endl;
|
||||||
@@ -59,25 +63,52 @@ Socket sJsonToSocket(const json& json_data) {
|
|||||||
result.m_name = json_data["name"];
|
result.m_name = json_data["name"];
|
||||||
|
|
||||||
std::string type_string = json_data["type"];
|
std::string type_string = json_data["type"];
|
||||||
|
bool have_value = json_data.contains("value");
|
||||||
|
|
||||||
if (type_string == "Bool") {
|
if (type_string == "Bool") {
|
||||||
result.m_type = SocketType::SocketTypeBool;
|
result.m_type = SocketType::SocketTypeBool;
|
||||||
result.m_type_size = sizeof(bool);
|
result.m_type_size = sizeof(bool);
|
||||||
|
if (have_value) {
|
||||||
|
result.m_value.flag = json_data["value"];
|
||||||
|
}
|
||||||
} else if (type_string == "Animation") {
|
} else if (type_string == "Animation") {
|
||||||
result.m_type = SocketType::SocketTypeAnimation;
|
result.m_type = SocketType::SocketTypeAnimation;
|
||||||
result.m_type_size = sizeof(AnimData);
|
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") {
|
} else if (type_string == "Float") {
|
||||||
result.m_type = SocketType::SocketTypeFloat;
|
result.m_type = SocketType::SocketTypeFloat;
|
||||||
result.m_type_size = sizeof(float);
|
result.m_type_size = sizeof(float);
|
||||||
|
if (have_value) {
|
||||||
|
result.m_value.float_value = json_data["value"];
|
||||||
|
}
|
||||||
} else if (type_string == "Vec3") {
|
} else if (type_string == "Vec3") {
|
||||||
result.m_type = SocketType::SocketTypeVec3;
|
result.m_type = SocketType::SocketTypeVec3;
|
||||||
result.m_type_size = sizeof(Vec3);
|
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") {
|
} else if (type_string == "Quat") {
|
||||||
result.m_type = SocketType::SocketTypeQuat;
|
result.m_type = SocketType::SocketTypeQuat;
|
||||||
result.m_type_size = sizeof(Quat);
|
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") {
|
} else if (type_string == "String") {
|
||||||
result.m_type = SocketType::SocketTypeString;
|
result.m_type = SocketType::SocketTypeString;
|
||||||
result.m_type_size = sizeof(std::string);
|
result.m_type_size = sizeof(std::string);
|
||||||
|
if (have_value) {
|
||||||
|
result.m_value_string = json_data["value"];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Invalid socket type '" << type_string << "'." << std::endl;
|
std::cerr << "Invalid socket type '" << type_string << "'." << std::endl;
|
||||||
}
|
}
|
||||||
@@ -88,7 +119,10 @@ Socket sJsonToSocket(const json& json_data) {
|
|||||||
//
|
//
|
||||||
// AnimGraphNode <-> json
|
// AnimGraphNode <-> json
|
||||||
//
|
//
|
||||||
json sAnimGraphNodeToJson(const AnimNodeResource& node) {
|
json sAnimGraphNodeToJson(
|
||||||
|
const AnimNodeResource& node,
|
||||||
|
int node_index,
|
||||||
|
const std::vector<AnimGraphConnectionResource>& connections) {
|
||||||
json result;
|
json result;
|
||||||
|
|
||||||
result["name"] = node.m_name;
|
result["name"] = node.m_name;
|
||||||
@@ -99,6 +133,27 @@ json sAnimGraphNodeToJson(const AnimNodeResource& node) {
|
|||||||
result["position"][j] = node.m_position[j];
|
result["position"][j] = node.m_position[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0, n = node.m_socket_accessor->m_inputs.size(); j < n; j++) {
|
||||||
|
const Socket& socket = node.m_socket_accessor->m_inputs[j];
|
||||||
|
|
||||||
|
if (socket.m_type == SocketType::SocketTypeAnimation) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool socket_connected = false;
|
||||||
|
for (size_t k = 0, m = connections.size(); k < m; k++) {
|
||||||
|
if (connections[k].source_node_index == node_index
|
||||||
|
&& connections[k].source_socket_name == socket.m_name) {
|
||||||
|
socket_connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!socket_connected) {
|
||||||
|
result["inputs"].push_back(sSocketToJson(socket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t j = 0, n = node.m_socket_accessor->m_properties.size(); j < n;
|
for (size_t j = 0, n = node.m_socket_accessor->m_properties.size(); j < n;
|
||||||
j++) {
|
j++) {
|
||||||
Socket& property = node.m_socket_accessor->m_properties[j];
|
Socket& property = node.m_socket_accessor->m_properties[j];
|
||||||
@@ -108,7 +163,7 @@ json sAnimGraphNodeToJson(const AnimNodeResource& node) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
|
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node, int node_index) {
|
||||||
AnimNodeResource result;
|
AnimNodeResource result;
|
||||||
|
|
||||||
result.m_name = json_node["name"];
|
result.m_name = json_node["name"];
|
||||||
@@ -118,51 +173,26 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
|
|||||||
|
|
||||||
result.m_anim_node = AnimNodeFactory(result.m_type_name);
|
result.m_anim_node = AnimNodeFactory(result.m_type_name);
|
||||||
result.m_socket_accessor =
|
result.m_socket_accessor =
|
||||||
AnimNodeAccessorFactory(result.m_type_name, result.m_anim_node);
|
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
|
||||||
|
|
||||||
for (size_t j = 0, n = result.m_socket_accessor->m_properties.size(); j < n;
|
for (size_t j = 0, n = result.m_socket_accessor->m_properties.size(); j < n;
|
||||||
j++) {
|
j++) {
|
||||||
Socket& property = result.m_socket_accessor->m_properties[j];
|
Socket& property = result.m_socket_accessor->m_properties[j];
|
||||||
json json_property = json_node["properties"][property.m_name];
|
property = sJsonToSocket(json_node["properties"][property.m_name]);
|
||||||
|
}
|
||||||
|
|
||||||
if (sSocketTypeToStr(property.m_type) == json_property["type"]) {
|
if (node_index != 0 && node_index != 1 && json_node.contains("inputs")) {
|
||||||
if (property.m_type == SocketType::SocketTypeBool) {
|
for (size_t j = 0, n = json_node["inputs"].size(); j < n; j++) {
|
||||||
property.m_value.flag = json_property["value"];
|
assert(json_node["inputs"][j].contains("name"));
|
||||||
} else if (property.m_type == SocketType::SocketTypeAnimation) {
|
std::string input_name = json_node["inputs"][j]["name"];
|
||||||
} else if (property.m_type == SocketType::SocketTypeFloat) {
|
Socket* input_socket =
|
||||||
property.m_value.float_value = json_property["value"];
|
result.m_socket_accessor->GetInputSocket(input_name.c_str());
|
||||||
} else if (property.m_type == SocketType::SocketTypeVec3) {
|
if (input_socket == nullptr) {
|
||||||
property.m_value.vec3[0] = json_property["value"][0];
|
std::cerr << "Could not find input socket with name " << input_name
|
||||||
property.m_value.vec3[1] = json_property["value"][1];
|
<< " for node type " << result.m_type_name << std::endl;
|
||||||
property.m_value.vec3[2] = json_property["value"][2];
|
abort();
|
||||||
} else if (property.m_type == SocketType::SocketTypeQuat) {
|
|
||||||
Quat* property_quat = reinterpret_cast<Quat*>(property.m_reference.ptr);
|
|
||||||
property.m_value.quat[0] = json_property["value"][0];
|
|
||||||
property.m_value.quat[1] = json_property["value"][1];
|
|
||||||
property.m_value.quat[2] = json_property["value"][2];
|
|
||||||
property.m_value.quat[3] = json_property["value"][3];
|
|
||||||
} else if (property.m_type == SocketType::SocketTypeString) {
|
|
||||||
std::string value_str = json_property["value"];
|
|
||||||
size_t string_length = value_str.size();
|
|
||||||
constexpr size_t string_max_length = sizeof(property.m_value.str) - 1;
|
|
||||||
if (string_length > string_max_length) {
|
|
||||||
std::cerr << "Warning: string '" << value_str
|
|
||||||
<< "' too long, truncating to " << string_max_length
|
|
||||||
<< " bytes." << std::endl;
|
|
||||||
string_length = string_max_length;
|
|
||||||
}
|
}
|
||||||
memcpy(property.m_value.str, value_str.data(), string_length);
|
*input_socket = sJsonToSocket(json_node["inputs"][j]);
|
||||||
property.m_value.str[string_length] = 0;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Invalid type for property '" << property.m_name
|
|
||||||
<< "'. Cannot parse json to type '"
|
|
||||||
<< static_cast<int>(property.m_type) << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "Invalid type for property '" << property.m_name
|
|
||||||
<< "': expected " << sSocketTypeToStr(property.m_type)
|
|
||||||
<< " but got " << json_property["type"] << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +266,7 @@ bool AnimGraphResource::saveToFile(const char* filename) const {
|
|||||||
|
|
||||||
for (size_t i = 0; i < m_nodes.size(); i++) {
|
for (size_t i = 0; i < m_nodes.size(); i++) {
|
||||||
const AnimNodeResource& node = m_nodes[i];
|
const AnimNodeResource& node = m_nodes[i];
|
||||||
result["nodes"][i] = sAnimGraphNodeToJson(node);
|
result["nodes"][i] = sAnimGraphNodeToJson(node, i, m_connections);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < m_connections.size(); i++) {
|
for (size_t i = 0; i < m_connections.size(); i++) {
|
||||||
@@ -263,7 +293,7 @@ bool AnimGraphResource::saveToFile(const char* filename) const {
|
|||||||
|
|
||||||
std::ofstream output_file;
|
std::ofstream output_file;
|
||||||
output_file.open(filename);
|
output_file.open(filename);
|
||||||
output_file << to_string(result) << std::endl;
|
output_file << result.dump(4, ' ') << std::endl;
|
||||||
output_file.close();
|
output_file.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -293,7 +323,7 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
|
|||||||
m_name = json_data["name"];
|
m_name = json_data["name"];
|
||||||
|
|
||||||
// Load nodes
|
// Load nodes
|
||||||
for (size_t i = 0; i < json_data["nodes"].size(); i++) {
|
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
||||||
const json& json_node = json_data["nodes"][i];
|
const json& json_node = json_data["nodes"][i];
|
||||||
if (json_node["type"] != "AnimNodeResource") {
|
if (json_node["type"] != "AnimNodeResource") {
|
||||||
std::cerr
|
std::cerr
|
||||||
@@ -302,20 +332,20 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimNodeResource node = sAnimGraphNodeFromJson(json_node);
|
AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i);
|
||||||
m_nodes.push_back(node);
|
m_nodes.push_back(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup graph inputs and outputs
|
// Setup graph inputs and outputs
|
||||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
||||||
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
|
||||||
AnimNodeResource& graph_node = m_nodes[0];
|
AnimNodeResource& graph_node = m_nodes[0];
|
||||||
graph_node.m_socket_accessor->m_inputs.push_back(
|
graph_node.m_socket_accessor->m_inputs.push_back(
|
||||||
sJsonToSocket(graph_outputs[i]));
|
sJsonToSocket(graph_outputs[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
||||||
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
for (size_t i = 0, n = graph_inputs.size(); i < n; i++) {
|
||||||
AnimNodeResource& graph_node = m_nodes[1];
|
AnimNodeResource& graph_node = m_nodes[1];
|
||||||
graph_node.m_socket_accessor->m_outputs.push_back(
|
graph_node.m_socket_accessor->m_outputs.push_back(
|
||||||
sJsonToSocket(graph_inputs[i]));
|
sJsonToSocket(graph_inputs[i]));
|
||||||
@@ -340,18 +370,13 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimGraph AnimGraphResource::createInstance() const {
|
void AnimGraphResource::createInstance(AnimGraph& result) const {
|
||||||
AnimGraph result;
|
|
||||||
|
|
||||||
createRuntimeNodeInstances(result);
|
createRuntimeNodeInstances(result);
|
||||||
prepareGraphIOData(result);
|
prepareGraphIOData(result);
|
||||||
connectRuntimeNodes(result);
|
|
||||||
setRuntimeNodeProperties(result);
|
setRuntimeNodeProperties(result);
|
||||||
|
|
||||||
result.updateOrderedNodes();
|
result.updateOrderedNodes();
|
||||||
result.reset();
|
result.resetNodeStates();
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
||||||
@@ -372,153 +397,166 @@ void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
|
void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
|
||||||
instance.m_socket_accessor =
|
instance.m_node_descriptor =
|
||||||
AnimNodeAccessorFactory("BlendTree", instance.m_nodes[0]);
|
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
||||||
instance.m_socket_accessor->m_outputs =
|
instance.m_node_descriptor->m_outputs =
|
||||||
m_nodes[1].m_socket_accessor->m_outputs;
|
m_nodes[1].m_socket_accessor->m_outputs;
|
||||||
instance.m_socket_accessor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
|
instance.m_node_descriptor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
|
||||||
|
|
||||||
// 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*);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input_block_size > 0) {
|
||||||
instance.m_input_buffer = new char[input_block_size];
|
instance.m_input_buffer = new char[input_block_size];
|
||||||
memset(instance.m_input_buffer, 0, input_block_size);
|
memset(instance.m_input_buffer, 0, input_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
int input_block_offset = 0;
|
int input_block_offset = 0;
|
||||||
for (int i = 0; i < graph_inputs.size(); i++) {
|
for (int i = 0; i < graph_inputs.size(); i++) {
|
||||||
graph_inputs[i].m_reference.ptr =
|
graph_inputs[i].m_reference.ptr =
|
||||||
(void*)&instance.m_input_buffer[input_block_offset];
|
(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*);
|
input_block_offset += sizeof(void*);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 += graph_outputs[i].m_type_size;
|
output_block_size += sizeof(void*);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (output_block_size > 0) {
|
||||||
instance.m_output_buffer = new char[output_block_size];
|
instance.m_output_buffer = new char[output_block_size];
|
||||||
memset(instance.m_output_buffer, 0, output_block_size);
|
memset(instance.m_output_buffer, 0, output_block_size);
|
||||||
|
}
|
||||||
|
|
||||||
int output_block_offset = 0;
|
int output_block_offset = 0;
|
||||||
for (int i = 0; i < graph_outputs.size(); i++) {
|
for (int i = 0; i < graph_outputs.size(); i++) {
|
||||||
graph_outputs[i].m_reference.ptr =
|
instance.m_node_descriptor->m_inputs[i].m_reference.ptr =
|
||||||
&instance.m_output_buffer[output_block_offset];
|
&instance.m_output_buffer[output_block_offset];
|
||||||
output_block_offset += graph_outputs[i].m_type_size;
|
output_block_offset += sizeof(void*);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
|
// 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.
|
||||||
|
int connection_data_storage_size = 0;
|
||||||
for (int i = 0; i < m_connections.size(); i++) {
|
for (int i = 0; i < m_connections.size(); i++) {
|
||||||
const AnimGraphConnectionResource& connection = m_connections[i];
|
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||||
std::string source_node_type = "";
|
const AnimNodeResource& source_node = m_nodes[connection.source_node_index];
|
||||||
std::string target_node_type = "";
|
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
|
||||||
AnimNode* source_node = nullptr;
|
connection.source_socket_name.c_str());
|
||||||
AnimNode* target_node = nullptr;
|
connection_data_storage_size += source_socket->m_type_size;
|
||||||
NodeSocketAccessorBase* source_node_accessor = nullptr;
|
|
||||||
NodeSocketAccessorBase* target_node_accessor = nullptr;
|
|
||||||
SocketType source_type;
|
|
||||||
SocketType target_type;
|
|
||||||
size_t source_socket_index = -1;
|
|
||||||
size_t target_socket_index = -1;
|
|
||||||
|
|
||||||
if (connection.source_node_index < 0
|
|
||||||
|| connection.source_node_index >= m_nodes.size()) {
|
|
||||||
std::cerr << "Could not find source node index." << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
source_node = instance.m_nodes[connection.source_node_index];
|
if (connection_data_storage_size > 0) {
|
||||||
source_node_type = source_node->m_node_type_name;
|
instance.m_connection_data_storage = new char[connection_data_storage_size];
|
||||||
if (connection.source_node_index == 1) {
|
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
|
||||||
source_node_accessor = instance.m_socket_accessor;
|
|
||||||
} else {
|
|
||||||
source_node_accessor =
|
|
||||||
AnimNodeAccessorFactory(source_node_type, source_node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection.target_node_index < 0
|
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
||||||
|| connection.target_node_index >= m_nodes.size()) {
|
m_nodes.size(),
|
||||||
std::cerr << "Could not find source node index." << std::endl;
|
nullptr);
|
||||||
continue;
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
|
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||||
|
m_nodes[i].m_type_name.c_str(),
|
||||||
|
instance.m_nodes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
target_node = instance.m_nodes[connection.target_node_index];
|
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;
|
||||||
target_node_type = target_node->m_node_type_name;
|
instance_node_descriptors[1]->m_outputs =
|
||||||
if (connection.target_node_index == 0) {
|
instance.m_node_descriptor->m_outputs;
|
||||||
target_node_accessor = instance.m_socket_accessor;
|
|
||||||
} else {
|
int connection_data_offset = 0;
|
||||||
target_node_accessor =
|
for (int i = 0; i < m_connections.size(); i++) {
|
||||||
AnimNodeAccessorFactory(target_node_type, target_node);
|
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||||
|
|
||||||
|
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]));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(source_node != nullptr);
|
connection_data_offset += source_socket->m_type_size;
|
||||||
assert(target_node != nullptr);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Map resource node sockets to graph instance node sockets
|
|
||||||
//
|
|
||||||
source_socket_index =
|
|
||||||
source_node_accessor->GetOutputIndex(connection.source_socket_name);
|
|
||||||
if (source_socket_index == -1) {
|
|
||||||
std::cerr << "Invalid source socket " << connection.source_socket_name
|
|
||||||
<< " for node " << source_node->m_name << "." << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Socket* source_socket =
|
|
||||||
&source_node_accessor->m_outputs[source_socket_index];
|
|
||||||
|
|
||||||
target_socket_index =
|
|
||||||
target_node_accessor->GetInputIndex(connection.target_socket_name);
|
|
||||||
if (target_socket_index == -1) {
|
|
||||||
std::cerr << "Invalid target socket " << connection.target_socket_name
|
|
||||||
<< " for node " << target_node->m_name << "." << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Socket* target_socket =
|
|
||||||
&target_node_accessor->m_inputs[target_socket_index];
|
|
||||||
|
|
||||||
if (source_socket->m_type != target_socket->m_type) {
|
|
||||||
std::cerr << "Cannot connect sockets: invalid types!" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Wire up outputs to inputs.
|
// const node inputs
|
||||||
//
|
//
|
||||||
// Skip animation connections and connections to the output node as the
|
std::vector<Socket*> const_inputs =
|
||||||
// pointers are already set up in AnimGraphResource::prepareGraphIOData().
|
getConstNodeInputs(instance, instance_node_descriptors);
|
||||||
if (target_socket->m_type != SocketType::SocketTypeAnimation
|
int const_node_inputs_buffer_size = 0;
|
||||||
&& connection.target_node_index != 0) {
|
for (int i = 0, n = const_inputs.size(); i < n; i++) {
|
||||||
(*target_socket->m_reference.ptr_ptr) = source_socket->m_reference.ptr;
|
if (const_inputs[i]->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_inputs[i]->m_type_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t target_node_index = target_node->m_index;
|
if (const_node_inputs_buffer_size > 0) {
|
||||||
|
instance.m_const_node_inputs = new char[const_node_inputs_buffer_size];
|
||||||
// Register the runtime connection
|
memset(instance.m_const_node_inputs, '\0', const_node_inputs_buffer_size);
|
||||||
AnimGraphConnection runtime_connection = {
|
|
||||||
source_node,
|
|
||||||
*source_socket,
|
|
||||||
target_node,
|
|
||||||
*target_socket};
|
|
||||||
|
|
||||||
std::vector<AnimGraphConnection>& target_input_connections =
|
|
||||||
instance.m_node_input_connections[target_node_index];
|
|
||||||
target_input_connections.push_back(runtime_connection);
|
|
||||||
|
|
||||||
std::vector<AnimGraphConnection>& source_output_connections =
|
|
||||||
instance.m_node_output_connections[source_node->m_index];
|
|
||||||
source_output_connections.push_back(runtime_connection);
|
|
||||||
|
|
||||||
if (target_node_accessor != instance.m_socket_accessor) {
|
|
||||||
delete target_node_accessor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_node_accessor != instance.m_socket_accessor) {
|
int const_input_buffer_offset = 0;
|
||||||
delete source_node_accessor;
|
for (int i = 0, n = const_inputs.size(); i < n; i++) {
|
||||||
|
Socket* const_input = const_inputs[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, const_inputs[i]->m_type_size);
|
||||||
|
|
||||||
|
const_input_buffer_offset += const_inputs[i]->m_type_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
|
delete instance_node_descriptors[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,8 +564,9 @@ void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
|||||||
for (int i = 2; i < m_nodes.size(); i++) {
|
for (int i = 2; i < m_nodes.size(); i++) {
|
||||||
const AnimNodeResource& node_resource = m_nodes[i];
|
const AnimNodeResource& node_resource = m_nodes[i];
|
||||||
|
|
||||||
NodeSocketAccessorBase* node_instance_accessor =
|
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
||||||
AnimNodeAccessorFactory(node_resource.m_type_name, instance.m_nodes[i]);
|
node_resource.m_type_name,
|
||||||
|
instance.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;
|
||||||
@@ -537,29 +576,34 @@ void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
|||||||
|
|
||||||
switch (property.m_type) {
|
switch (property.m_type) {
|
||||||
case SocketType::SocketTypeBool:
|
case SocketType::SocketTypeBool:
|
||||||
node_instance_accessor->SetPropertyReferenceValue<const bool&>(
|
node_instance_accessor->SetProperty(
|
||||||
name,
|
name.c_str(),
|
||||||
property.m_value.flag);
|
property.m_value.flag);
|
||||||
break;
|
break;
|
||||||
|
case SocketType::SocketTypeInt:
|
||||||
|
node_instance_accessor->SetProperty(
|
||||||
|
name.c_str(),
|
||||||
|
property.m_value.int_value);
|
||||||
|
break;
|
||||||
case SocketType::SocketTypeFloat:
|
case SocketType::SocketTypeFloat:
|
||||||
node_instance_accessor->SetPropertyReferenceValue(
|
node_instance_accessor->SetProperty(
|
||||||
name,
|
name.c_str(),
|
||||||
property.m_value.float_value);
|
property.m_value.float_value);
|
||||||
break;
|
break;
|
||||||
case SocketType::SocketTypeVec3:
|
case SocketType::SocketTypeVec3:
|
||||||
node_instance_accessor->SetPropertyReferenceValue(
|
node_instance_accessor->SetProperty<Vec3>(
|
||||||
name,
|
name.c_str(),
|
||||||
property.m_value.vec3);
|
property.m_value.vec3);
|
||||||
break;
|
break;
|
||||||
case SocketType::SocketTypeQuat:
|
case SocketType::SocketTypeQuat:
|
||||||
node_instance_accessor->SetPropertyReferenceValue(
|
node_instance_accessor->SetProperty(
|
||||||
name,
|
name.c_str(),
|
||||||
property.m_value.quat);
|
property.m_value.quat);
|
||||||
break;
|
break;
|
||||||
case SocketType::SocketTypeString:
|
case SocketType::SocketTypeString:
|
||||||
node_instance_accessor->SetPropertyReferenceValue(
|
node_instance_accessor->SetProperty(
|
||||||
name,
|
name.c_str(),
|
||||||
property.m_value.str);
|
property.m_value_string);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
std::cerr << "Invalid socket type "
|
std::cerr << "Invalid socket type "
|
||||||
@@ -570,3 +614,24 @@ void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
|||||||
delete node_instance_accessor;
|
delete node_instance_accessor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Socket*> AnimGraphResource::getConstNodeInputs(
|
||||||
|
AnimGraph& instance,
|
||||||
|
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
||||||
|
std::vector<Socket*> result;
|
||||||
|
|
||||||
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
|
for (int 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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct AnimNodeResource {
|
|||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_type_name;
|
std::string m_type_name;
|
||||||
AnimNode* m_anim_node = nullptr;
|
AnimNode* m_anim_node = nullptr;
|
||||||
NodeSocketAccessorBase* m_socket_accessor = nullptr;
|
NodeDescriptorBase* m_socket_accessor = nullptr;
|
||||||
float m_position[2] = {0.f, 0.f};
|
float m_position[2] = {0.f, 0.f};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ static inline AnimNodeResource AnimNodeResourceFactory(
|
|||||||
result.m_type_name = node_type_name;
|
result.m_type_name = node_type_name;
|
||||||
result.m_anim_node = AnimNodeFactory(node_type_name);
|
result.m_anim_node = AnimNodeFactory(node_type_name);
|
||||||
result.m_socket_accessor =
|
result.m_socket_accessor =
|
||||||
AnimNodeAccessorFactory(node_type_name, result.m_anim_node);
|
AnimNodeDescriptorFactory(node_type_name.c_str(), result.m_anim_node);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +104,9 @@ struct AnimGraphResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Socket* source_socket =
|
Socket* source_socket =
|
||||||
source_node.m_socket_accessor->FindOutputSocket(source_socket_name);
|
source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
|
||||||
Socket* target_socket =
|
Socket* target_socket =
|
||||||
target_node.m_socket_accessor->FindInputSocket(target_socket_name);
|
target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str());
|
||||||
|
|
||||||
if (source_socket == nullptr || target_socket == nullptr) {
|
if (source_socket == nullptr || target_socket == nullptr) {
|
||||||
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
|
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
|
||||||
@@ -140,12 +140,13 @@ struct AnimGraphResource {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimGraph createInstance() const;
|
void createInstance(AnimGraph& result) const;
|
||||||
|
|
||||||
void createRuntimeNodeInstances(AnimGraph& instance) const;
|
void createRuntimeNodeInstances(AnimGraph& instance) const;
|
||||||
void prepareGraphIOData(AnimGraph& instance) const;
|
void prepareGraphIOData(AnimGraph& instance) const;
|
||||||
void connectRuntimeNodes(AnimGraph& instance) const;
|
void connectRuntimeNodes(AnimGraph& instance) const;
|
||||||
void setRuntimeNodeProperties(AnimGraph& instance) const;
|
void setRuntimeNodeProperties(AnimGraph& instance) const;
|
||||||
|
std::vector<Socket*> getConstNodeInputs(AnimGraph& instance, std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
|
|||||||
@@ -99,41 +99,3 @@ void SkinnedMesh::CalcModelMatrices() {
|
|||||||
|
|
||||||
void SkinnedMesh::DrawSkeleton() {}
|
void SkinnedMesh::DrawSkeleton() {}
|
||||||
|
|
||||||
void SkinnedMesh::DrawDebugUi() {
|
|
||||||
if (ImGui::TreeNode("Bones")) {
|
|
||||||
for (int i = 0; i < m_skeleton.num_joints(); i++) {
|
|
||||||
ImGui::Text("%s", m_skeleton.joint_names()[i]);
|
|
||||||
}
|
|
||||||
ImGui::TreePop();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("Animations");
|
|
||||||
|
|
||||||
const char* items[255] = {0};
|
|
||||||
static int selected = -1;
|
|
||||||
for (int i = 0; i < m_animations.size(); i++) {
|
|
||||||
items[i] = m_animation_names[i].c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Combo("Animation", &selected, items, m_animations.size());
|
|
||||||
|
|
||||||
ImGui::Text("Sync Track");
|
|
||||||
if (selected >= 0 && selected < m_animations.size()) {
|
|
||||||
m_animation_sync_track[selected].DrawDebugUi();
|
|
||||||
m_override_anim = selected;
|
|
||||||
|
|
||||||
ImGui::Checkbox("Override Animation", &m_sync_track_override);
|
|
||||||
if (m_sync_track_override) {
|
|
||||||
ImGui::SliderFloat("Ratio", &m_override_ratio, 0.f, 1.f);
|
|
||||||
|
|
||||||
ozz::animation::SamplingJob sampling_job;
|
|
||||||
sampling_job.animation = m_animations[selected];
|
|
||||||
sampling_job.context = &m_sampling_context;
|
|
||||||
sampling_job.ratio = m_override_ratio;
|
|
||||||
sampling_job.output = make_span(m_local_matrices);
|
|
||||||
if (!sampling_job.Run()) {
|
|
||||||
ozz::log::Err() << "Error sampling animation." << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,7 +40,6 @@ struct SkinnedMesh {
|
|||||||
void DrawSkeleton();
|
void DrawSkeleton();
|
||||||
void DrawJoint(int joint_index, int parent_joint_index);
|
void DrawJoint(int joint_index, int parent_joint_index);
|
||||||
|
|
||||||
void DrawDebugUi();
|
|
||||||
// void DrawSkinnedMesh();
|
// void DrawSkinnedMesh();
|
||||||
|
|
||||||
ozz::vector<ozz::animation::Animation*> m_animations;
|
ozz::vector<ozz::animation::Animation*> m_animations;
|
||||||
|
|||||||
@@ -7,40 +7,3 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
void SyncTrack::DrawDebugUi() {
|
|
||||||
ImGui::SliderFloat("duration", &m_duration, 0.001f, 10.f);
|
|
||||||
|
|
||||||
ImGui::Text("Marker");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Text("%d", m_num_intervals);
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("+")) {
|
|
||||||
if (m_num_intervals < cSyncTrackMaxIntervals) {
|
|
||||||
m_num_intervals ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("-")) {
|
|
||||||
if (m_num_intervals > 0) {
|
|
||||||
m_num_intervals --;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("Marker:");
|
|
||||||
for (int i = 0; i < m_num_intervals; i++) {
|
|
||||||
ImGui::Text("%2d:", i);
|
|
||||||
ImGui::SameLine();
|
|
||||||
std::ostringstream marker_stream;
|
|
||||||
marker_stream << i;
|
|
||||||
ImGui::SliderFloat(
|
|
||||||
marker_stream.str().c_str(),
|
|
||||||
&m_sync_markers[i],
|
|
||||||
0.f,
|
|
||||||
1.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button ("Update Intervals")) {
|
|
||||||
CalcIntervals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -150,8 +150,6 @@ struct SyncTrack {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawDebugUi();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //ANIMTESTBED_SYNCTRACK_H
|
#endif //ANIMTESTBED_SYNCTRACK_H
|
||||||
|
|||||||
+77
-10
@@ -65,7 +65,7 @@ static struct {
|
|||||||
struct {
|
struct {
|
||||||
ozz::animation::Animation* animation = nullptr;
|
ozz::animation::Animation* animation = nullptr;
|
||||||
ozz::animation::SamplingJob sampling_job;
|
ozz::animation::SamplingJob sampling_job;
|
||||||
ozz::animation::SamplingJob::Context* m_sampling_context = nullptr;
|
ozz::vector<ozz::math::SoaTransform> local_matrices;
|
||||||
} ozz;
|
} ozz;
|
||||||
sg_pass_action pass_action;
|
sg_pass_action pass_action;
|
||||||
Camera camera;
|
Camera camera;
|
||||||
@@ -76,12 +76,14 @@ static struct {
|
|||||||
} loaded;
|
} loaded;
|
||||||
struct {
|
struct {
|
||||||
double frame;
|
double frame;
|
||||||
|
double anim_update_time;
|
||||||
float absolute;
|
float absolute;
|
||||||
uint64_t laptime;
|
uint64_t laptime;
|
||||||
float factor;
|
float factor;
|
||||||
float anim_ratio;
|
float anim_ratio;
|
||||||
bool anim_ratio_ui_override;
|
bool anim_ratio_ui_override;
|
||||||
bool paused;
|
bool paused;
|
||||||
|
bool use_graph = false;
|
||||||
} time;
|
} time;
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
@@ -311,7 +313,7 @@ int main() {
|
|||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
|
||||||
glfwWindowHint(GLFW_SAMPLES, 16);
|
glfwWindowHint(GLFW_SAMPLES, 16);
|
||||||
GLFWwindow* w = glfwCreateWindow(Width, Height, "AnimTestbed", 0, 0);
|
GLFWwindow* w = glfwCreateWindow(Width, Height, "ATP Editor", 0, 0);
|
||||||
glfwMakeContextCurrent(w);
|
glfwMakeContextCurrent(w);
|
||||||
glfwSwapInterval(1);
|
glfwSwapInterval(1);
|
||||||
|
|
||||||
@@ -378,9 +380,13 @@ int main() {
|
|||||||
|
|
||||||
SkinnedMesh skinned_mesh;
|
SkinnedMesh skinned_mesh;
|
||||||
skinned_mesh_resource.createInstance(skinned_mesh);
|
skinned_mesh_resource.createInstance(skinned_mesh);
|
||||||
|
|
||||||
skinned_mesh.SetCurrentAnimation(0);
|
skinned_mesh.SetCurrentAnimation(0);
|
||||||
|
|
||||||
|
AnimGraph anim_graph;
|
||||||
|
AnimGraphContext anim_graph_context;
|
||||||
|
AnimData anim_graph_output;
|
||||||
|
anim_graph_output.m_local_matrices.resize(skinned_mesh.m_skeleton.num_soa_joints());
|
||||||
|
|
||||||
state.time.factor = 1.0f;
|
state.time.factor = 1.0f;
|
||||||
|
|
||||||
Camera_Init(&state.camera);
|
Camera_Init(&state.camera);
|
||||||
@@ -502,16 +508,19 @@ int main() {
|
|||||||
&gApplicationConfig.window_size[0],
|
&gApplicationConfig.window_size[0],
|
||||||
&gApplicationConfig.window_size[1]);
|
&gApplicationConfig.window_size[1]);
|
||||||
|
|
||||||
if (!state.time.paused) {
|
|
||||||
state.time.frame = stm_sec(
|
state.time.frame = stm_sec(
|
||||||
stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime)));
|
stm_round_to_common_refresh_rate(stm_laptime(&state.time.laptime)));
|
||||||
|
|
||||||
|
if (!state.time.paused) {
|
||||||
|
state.time.anim_update_time = state.time.frame;
|
||||||
state.time.absolute += state.time.frame * state.time.factor;
|
state.time.absolute += state.time.frame * state.time.factor;
|
||||||
|
} else {
|
||||||
|
state.time.anim_update_time = 0.;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.ozz.animation != nullptr) {
|
if (state.ozz.animation != nullptr) {
|
||||||
state.time.absolute = fmodf (state.time.absolute, state.ozz.animation->duration());
|
state.time.absolute = fmodf (state.time.absolute, state.ozz.animation->duration());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int cur_width, cur_height;
|
int cur_width, cur_height;
|
||||||
glfwGetFramebufferSize(w, &cur_width, &cur_height);
|
glfwGetFramebufferSize(w, &cur_width, &cur_height);
|
||||||
@@ -611,6 +620,23 @@ int main() {
|
|||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Update Runtime Graph")) {
|
||||||
|
anim_graph.dealloc();
|
||||||
|
|
||||||
|
AnimGraphEditorGetRuntimeGraph(anim_graph);
|
||||||
|
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
|
||||||
|
anim_graph.init(anim_graph_context);
|
||||||
|
|
||||||
|
// For simplicity use first animation data output
|
||||||
|
const std::vector<Socket>& graph_output_sockets = anim_graph.getGraphOutputs();
|
||||||
|
for (int i = 0; i < graph_output_sockets.size(); i++) {
|
||||||
|
const Socket& output = graph_output_sockets[i];
|
||||||
|
if (output.m_type == SocketType::SocketTypeAnimation) {
|
||||||
|
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,14 +674,14 @@ int main() {
|
|||||||
gApplicationConfig.skinned_mesh_widget.size[0] = skinned_mesh_widget_size.x;
|
gApplicationConfig.skinned_mesh_widget.size[0] = skinned_mesh_widget_size.x;
|
||||||
gApplicationConfig.skinned_mesh_widget.size[1] = skinned_mesh_widget_size.y;
|
gApplicationConfig.skinned_mesh_widget.size[1] = skinned_mesh_widget_size.y;
|
||||||
|
|
||||||
skinned_mesh.DrawDebugUi();
|
SkinnedMeshWidget(&skinned_mesh);
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gApplicationConfig.animation_player_widget.visible) {
|
if (gApplicationConfig.animation_player_widget.visible) {
|
||||||
ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.skinned_mesh_widget.position[1]), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowPos(ImVec2(gApplicationConfig.animation_player_widget.position[0], gApplicationConfig.animation_player_widget.position[1]), ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.skinned_mesh_widget.size[1]), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(ImVec2(gApplicationConfig.animation_player_widget.size[0], gApplicationConfig.animation_player_widget.size[1]), ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible);
|
ImGui::Begin("Animation Player", &gApplicationConfig.animation_player_widget.visible);
|
||||||
|
|
||||||
@@ -667,6 +693,13 @@ int main() {
|
|||||||
gApplicationConfig.animation_player_widget.size[0] = animation_player_widget_size.x;
|
gApplicationConfig.animation_player_widget.size[0] = animation_player_widget_size.x;
|
||||||
gApplicationConfig.animation_player_widget.size[1] = animation_player_widget_size.y;
|
gApplicationConfig.animation_player_widget.size[1] = animation_player_widget_size.y;
|
||||||
|
|
||||||
|
if (anim_graph.m_nodes.size() > 0) {
|
||||||
|
ImGui::Checkbox("Use Graph", &state.time.use_graph);
|
||||||
|
} else {
|
||||||
|
state.time.use_graph = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.time.use_graph) {
|
||||||
ImGui::Text("Animation");
|
ImGui::Text("Animation");
|
||||||
|
|
||||||
const char* items[255] = {0};
|
const char* items[255] = {0};
|
||||||
@@ -675,9 +708,14 @@ int main() {
|
|||||||
items[i] = skinned_mesh.m_animation_names[i].c_str();
|
items[i] = skinned_mesh.m_animation_names[i].c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Combo("Animation", &selected, items, skinned_mesh.m_animations.size())) {
|
if (ImGui::Combo(
|
||||||
|
"Animation",
|
||||||
|
&selected,
|
||||||
|
items,
|
||||||
|
skinned_mesh.m_animations.size())) {
|
||||||
state.ozz.animation = skinned_mesh.m_animations[selected];
|
state.ozz.animation = skinned_mesh.m_animations[selected];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.time.paused) {
|
if (state.time.paused) {
|
||||||
if (ImGui::Button("Play")) {
|
if (ImGui::Button("Play")) {
|
||||||
@@ -689,7 +727,15 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Step")) {
|
||||||
|
state.time.anim_update_time = 1. / 30.f;
|
||||||
|
state.time.absolute += state.time.anim_update_time;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.ozz.animation != nullptr) {
|
if (state.ozz.animation != nullptr) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::SliderFloat(
|
ImGui::SliderFloat(
|
||||||
"Time",
|
"Time",
|
||||||
&state.time.absolute,
|
&state.time.absolute,
|
||||||
@@ -702,8 +748,29 @@ int main() {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.ozz.animation != nullptr) {
|
||||||
|
state.ozz.sampling_job.animation = state.ozz.animation;
|
||||||
|
state.ozz.sampling_job.ratio =
|
||||||
|
state.time.absolute / state.ozz.animation->duration();
|
||||||
|
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
|
||||||
|
state.ozz.sampling_job.output = ozz::make_span(skinned_mesh.m_local_matrices);
|
||||||
|
|
||||||
|
if(!state.ozz.sampling_job.Run()) {
|
||||||
|
ozz::log::Err() << "Error sampling animation." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: add AnimGraph to calculate pose
|
// TODO: add AnimGraph to calculate pose
|
||||||
// skinned_mesh.CalcModelMatrices();
|
skinned_mesh.CalcModelMatrices();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.time.use_graph && anim_graph.m_nodes.size() > 0 && state.time.anim_update_time > 0.) {
|
||||||
|
anim_graph.markActiveNodes();
|
||||||
|
anim_graph.updateTime(state.time.anim_update_time);
|
||||||
|
anim_graph.evaluate(anim_graph_context);
|
||||||
|
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
|
||||||
|
|
||||||
|
skinned_mesh.CalcModelMatrices();
|
||||||
|
}
|
||||||
|
|
||||||
sgl_defaults();
|
sgl_defaults();
|
||||||
sgl_matrix_mode_projection();
|
sgl_matrix_mode_projection();
|
||||||
|
|||||||
+12
-14
@@ -153,11 +153,11 @@ TEST_CASE_METHOD(
|
|||||||
|
|
||||||
// Setup nodes
|
// Setup nodes
|
||||||
AnimNodeResource& trans_x_node = graph_resource.m_nodes[trans_x_node_index];
|
AnimNodeResource& trans_x_node = graph_resource.m_nodes[trans_x_node_index];
|
||||||
trans_x_node.m_socket_accessor->SetPropertyValue("Filename", "trans_x");
|
trans_x_node.m_socket_accessor->SetPropertyValue("Filename", std::string("trans_x"));
|
||||||
trans_x_node.m_name = "trans_x";
|
trans_x_node.m_name = "trans_x";
|
||||||
|
|
||||||
AnimNodeResource& trans_y_node = graph_resource.m_nodes[trans_y_node_index];
|
AnimNodeResource& trans_y_node = graph_resource.m_nodes[trans_y_node_index];
|
||||||
trans_y_node.m_socket_accessor->SetPropertyValue("Filename", "trans_y");
|
trans_y_node.m_socket_accessor->SetPropertyValue("Filename", std::string("trans_y"));
|
||||||
trans_y_node.m_name = "trans_y";
|
trans_y_node.m_name = "trans_y";
|
||||||
|
|
||||||
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
||||||
@@ -190,22 +190,20 @@ TEST_CASE_METHOD(
|
|||||||
graph_context.m_animation_map["trans_y"] = animation_translate_y.get();
|
graph_context.m_animation_map["trans_y"] = animation_translate_y.get();
|
||||||
|
|
||||||
// Instantiate graph
|
// Instantiate graph
|
||||||
AnimGraph graph = graph_resource.createInstance();
|
AnimGraph graph;
|
||||||
graph_context.m_graph = &graph;
|
graph_resource.createInstance(graph);
|
||||||
graph.init(graph_context);
|
graph.init(graph_context);
|
||||||
|
|
||||||
// Get runtime graph inputs and outputs
|
// Get runtime graph inputs and outputs
|
||||||
float* graph_float_input = nullptr;
|
float graph_float_input = 0.f;
|
||||||
graph_float_input =
|
graph.SetInput("GraphFloatInput", &graph_float_input);
|
||||||
static_cast<float*>(graph.getInputPtr("GraphFloatInput"));
|
|
||||||
|
|
||||||
Socket* anim_output_socket =
|
AnimData graph_anim_output;
|
||||||
graph.getOutputSocket("GraphOutput");
|
graph_anim_output.m_local_matrices.resize(skeleton->num_joints());
|
||||||
|
graph.SetOutput("GraphOutput", &graph_anim_output);
|
||||||
AnimData* graph_anim_output = static_cast<AnimData*>(graph.getOutputPtr("GraphOutput"));
|
|
||||||
|
|
||||||
// Evaluate graph
|
// Evaluate graph
|
||||||
*graph_float_input = 0.1f;
|
graph_float_input = 0.1f;
|
||||||
|
|
||||||
graph.markActiveNodes();
|
graph.markActiveNodes();
|
||||||
CHECK(graph.m_nodes[trans_x_node_index]->m_state == AnimNodeEvalState::Activated);
|
CHECK(graph.m_nodes[trans_x_node_index]->m_state == AnimNodeEvalState::Activated);
|
||||||
@@ -215,6 +213,6 @@ TEST_CASE_METHOD(
|
|||||||
graph.updateTime(0.5f);
|
graph.updateTime(0.5f);
|
||||||
graph.evaluate(graph_context);
|
graph.evaluate(graph_context);
|
||||||
|
|
||||||
CHECK(graph_anim_output->m_local_matrices[0].translation.x[0] == Approx(0.5).margin(0.1));
|
CHECK(graph_anim_output.m_local_matrices[0].translation.x[0] == Approx(0.5).margin(0.1));
|
||||||
CHECK(graph_anim_output->m_local_matrices[0].translation.y[0] == Approx(0.05).margin(0.01));
|
CHECK(graph_anim_output.m_local_matrices[0].translation.y[0] == Approx(0.05).margin(0.01));
|
||||||
}
|
}
|
||||||
+200
-422
@@ -2,14 +2,13 @@
|
|||||||
// Created by martin on 04.02.22.
|
// Created by martin on 04.02.22.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "ozz/base/io/archive.h"
|
|
||||||
#include "ozz/base/io/stream.h"
|
|
||||||
#include "ozz/base/log.h"
|
|
||||||
|
|
||||||
#include "AnimGraph/AnimGraph.h"
|
#include "AnimGraph/AnimGraph.h"
|
||||||
#include "AnimGraph/AnimGraphEditor.h"
|
#include "AnimGraph/AnimGraphEditor.h"
|
||||||
#include "AnimGraph/AnimGraphResource.h"
|
#include "AnimGraph/AnimGraphResource.h"
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "ozz/base/io/archive.h"
|
||||||
|
#include "ozz/base/io/stream.h"
|
||||||
|
#include "ozz/base/log.h"
|
||||||
|
|
||||||
bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) {
|
bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) {
|
||||||
assert(filename);
|
assert(filename);
|
||||||
@@ -32,7 +31,147 @@ bool load_skeleton (ozz::animation::Skeleton& skeleton, const char* filename) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") {
|
||||||
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
|
graph_resource.clear();
|
||||||
|
graph_resource.m_name = "AnimSamplerGraph";
|
||||||
|
|
||||||
|
// Prepare graph inputs and outputs
|
||||||
|
size_t walk_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
|
|
||||||
|
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
||||||
|
walk_node.m_name = "WalkAnim";
|
||||||
|
walk_node.m_socket_accessor->SetPropertyValue(
|
||||||
|
"Filename",
|
||||||
|
std::string("data/walk.anim.ozz"));
|
||||||
|
|
||||||
|
AnimNodeResource& graph_node = graph_resource.m_nodes[0];
|
||||||
|
graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
|
||||||
|
|
||||||
|
graph_resource.connectSockets(
|
||||||
|
walk_node,
|
||||||
|
"Output",
|
||||||
|
graph_resource.getGraphOutputNode(),
|
||||||
|
"GraphOutput");
|
||||||
|
|
||||||
|
graph_resource.saveToFile("AnimSamplerGraph.animgraph.json");
|
||||||
|
AnimGraphResource graph_resource_loaded;
|
||||||
|
graph_resource_loaded.loadFromFile("AnimSamplerGraph.animgraph.json");
|
||||||
|
|
||||||
|
AnimGraph graph;
|
||||||
|
graph_resource_loaded.createInstance(graph);
|
||||||
|
AnimGraphContext graph_context;
|
||||||
|
|
||||||
|
ozz::animation::Skeleton skeleton;
|
||||||
|
REQUIRE(load_skeleton(skeleton, "data/skeleton.ozz"));
|
||||||
|
graph_context.m_skeleton = &skeleton;
|
||||||
|
|
||||||
|
REQUIRE(graph.init(graph_context));
|
||||||
|
|
||||||
|
REQUIRE(graph.m_nodes.size() == 3);
|
||||||
|
REQUIRE(graph.m_nodes[0]->m_node_type_name == "BlendTree");
|
||||||
|
REQUIRE(graph.m_nodes[1]->m_node_type_name == "BlendTree");
|
||||||
|
REQUIRE(graph.m_nodes[2]->m_node_type_name == "AnimSampler");
|
||||||
|
|
||||||
|
// connections within the graph
|
||||||
|
AnimSamplerNode* anim_sampler_walk =
|
||||||
|
dynamic_cast<AnimSamplerNode*>(graph.m_nodes[2]);
|
||||||
|
|
||||||
|
BlendTreeNode* graph_output_node =
|
||||||
|
dynamic_cast<BlendTreeNode*>(graph.m_nodes[0]);
|
||||||
|
|
||||||
|
// check node input dependencies
|
||||||
|
size_t anim_sampler_index = anim_sampler_walk->m_index;
|
||||||
|
|
||||||
|
REQUIRE(graph.m_node_output_connections[anim_sampler_index].size() == 1);
|
||||||
|
CHECK(
|
||||||
|
graph.m_node_output_connections[anim_sampler_index][0].m_target_node
|
||||||
|
== graph_output_node);
|
||||||
|
|
||||||
|
// Ensure animation sampler nodes use the correct files
|
||||||
|
REQUIRE(anim_sampler_walk->m_filename == "data/walk.anim.ozz");
|
||||||
|
REQUIRE(anim_sampler_walk->m_animation != nullptr);
|
||||||
|
|
||||||
|
// Ensure that outputs are properly propagated.
|
||||||
|
AnimData output;
|
||||||
|
output.m_local_matrices.resize(skeleton.num_soa_joints());
|
||||||
|
graph.SetOutput("GraphOutput", &output);
|
||||||
|
REQUIRE(anim_sampler_walk->o_output == &output);
|
||||||
|
|
||||||
|
WHEN("Emulating Graph Evaluation") {
|
||||||
|
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||||
|
anim_sampler_walk->Evaluate(graph_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
graph_context.freeAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks that node const inputs are properly set.
|
||||||
|
*/
|
||||||
|
TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
||||||
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
|
graph_resource.clear();
|
||||||
|
graph_resource.m_name = "AnimSamplerSpeedScaleGraph";
|
||||||
|
|
||||||
|
// Prepare graph inputs and outputs
|
||||||
|
size_t walk_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
|
|
||||||
|
size_t speed_scale_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("SpeedScale"));
|
||||||
|
|
||||||
|
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
||||||
|
walk_node.m_name = "WalkAnim";
|
||||||
|
walk_node.m_socket_accessor->SetPropertyValue(
|
||||||
|
"Filename",
|
||||||
|
std::string("data/walk.anim.ozz"));
|
||||||
|
|
||||||
|
AnimNodeResource& speed_scale_node =
|
||||||
|
graph_resource.m_nodes[speed_scale_node_index];
|
||||||
|
speed_scale_node.m_name = "SpeedScale";
|
||||||
|
float speed_scale_value = 1.35f;
|
||||||
|
speed_scale_node.m_socket_accessor->SetInputValue(
|
||||||
|
"SpeedScale",
|
||||||
|
speed_scale_value);
|
||||||
|
|
||||||
|
AnimNodeResource& graph_node = graph_resource.m_nodes[0];
|
||||||
|
graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
|
||||||
|
|
||||||
|
graph_resource.connectSockets(walk_node, "Output", speed_scale_node, "Input");
|
||||||
|
|
||||||
|
graph_resource.connectSockets(
|
||||||
|
speed_scale_node,
|
||||||
|
"Output",
|
||||||
|
graph_resource.getGraphOutputNode(),
|
||||||
|
"GraphOutput");
|
||||||
|
|
||||||
|
graph_resource.saveToFile("AnimSamplerSpeedScaleGraph.animgraph.json");
|
||||||
|
AnimGraphResource graph_resource_loaded;
|
||||||
|
graph_resource_loaded.loadFromFile(
|
||||||
|
"AnimSamplerSpeedScaleGraph.animgraph.json");
|
||||||
|
|
||||||
|
Socket* speed_scale_resource_loaded_input =
|
||||||
|
graph_resource_loaded.m_nodes[speed_scale_node_index]
|
||||||
|
.m_socket_accessor->GetInputSocket("SpeedScale");
|
||||||
|
REQUIRE(speed_scale_resource_loaded_input != nullptr);
|
||||||
|
|
||||||
|
REQUIRE_THAT(
|
||||||
|
speed_scale_resource_loaded_input->m_value.float_value,
|
||||||
|
Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
|
||||||
|
|
||||||
|
AnimGraph graph;
|
||||||
|
graph_resource_loaded.createInstance(graph);
|
||||||
|
|
||||||
|
REQUIRE_THAT(*dynamic_cast<SpeedScaleNode*>(graph.m_nodes[speed_scale_node_index])->i_speed_scale,
|
||||||
|
Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Blend2Graph", "[AnimGraphResource]") {
|
||||||
AnimGraphResource graph_resource;
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
graph_resource.clear();
|
graph_resource.clear();
|
||||||
@@ -48,9 +187,13 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||||||
|
|
||||||
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
||||||
walk_node.m_name = "WalkAnim";
|
walk_node.m_name = "WalkAnim";
|
||||||
walk_node.m_socket_accessor->SetPropertyValue("Filename", "data/walk.anim.ozz");
|
walk_node.m_socket_accessor->SetPropertyValue(
|
||||||
|
"Filename",
|
||||||
|
std::string("data/walk.anim.ozz"));
|
||||||
AnimNodeResource& run_node = graph_resource.m_nodes[run_node_index];
|
AnimNodeResource& run_node = graph_resource.m_nodes[run_node_index];
|
||||||
run_node.m_socket_accessor->SetPropertyValue("Filename", "data/run.anim.ozz");
|
run_node.m_socket_accessor->SetPropertyValue(
|
||||||
|
"Filename",
|
||||||
|
std::string("data/run.anim.ozz"));
|
||||||
run_node.m_name = "RunAnim";
|
run_node.m_name = "RunAnim";
|
||||||
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
||||||
blend_node.m_name = "BlendWalkRun";
|
blend_node.m_name = "BlendWalkRun";
|
||||||
@@ -70,13 +213,13 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||||||
graph_resource.getGraphOutputNode(),
|
graph_resource.getGraphOutputNode(),
|
||||||
"GraphOutput");
|
"GraphOutput");
|
||||||
|
|
||||||
graph_resource.saveToFile("WalkGraph.animgraph.json");
|
graph_resource.saveToFile("Blend2Graph.animgraph.json");
|
||||||
AnimGraphResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.loadFromFile("WalkGraph.animgraph.json");
|
graph_resource_loaded.loadFromFile("Blend2Graph.animgraph.json");
|
||||||
|
|
||||||
AnimGraph graph = graph_resource_loaded.createInstance();
|
AnimGraph graph;
|
||||||
|
graph_resource_loaded.createInstance(graph);
|
||||||
AnimGraphContext graph_context;
|
AnimGraphContext graph_context;
|
||||||
graph_context.m_graph = &graph;
|
|
||||||
|
|
||||||
ozz::animation::Skeleton skeleton;
|
ozz::animation::Skeleton skeleton;
|
||||||
REQUIRE(load_skeleton(skeleton, "data/skeleton.ozz"));
|
REQUIRE(load_skeleton(skeleton, "data/skeleton.ozz"));
|
||||||
@@ -130,43 +273,19 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||||||
|
|
||||||
WHEN("Emulating Graph Evaluation") {
|
WHEN("Emulating Graph Evaluation") {
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||||
graph.prepareNodeEval(graph_context, walk_node_index);
|
|
||||||
graph.finishNodeEval(walk_node_index);
|
|
||||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 1);
|
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
|
||||||
|
|
||||||
graph.prepareNodeEval(graph_context, run_node_index);
|
|
||||||
graph.finishNodeEval(run_node_index);
|
|
||||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 2);
|
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
|
||||||
|
|
||||||
graph.prepareNodeEval(graph_context, blend_node_index);
|
|
||||||
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
|
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
|
||||||
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
|
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
|
||||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
|
||||||
|
|
||||||
graph.finishNodeEval(blend_node_index);
|
|
||||||
CHECK(anim_sampler_walk->o_output == nullptr);
|
|
||||||
CHECK(anim_sampler_run->o_output == nullptr);
|
|
||||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 2);
|
|
||||||
|
|
||||||
// Evaluate output node.
|
|
||||||
graph.evalOutputNode();
|
|
||||||
graph.finishNodeEval(0);
|
|
||||||
|
|
||||||
|
|
||||||
const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
|
const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
|
||||||
AnimData* graph_output =
|
AnimData* graph_output =
|
||||||
static_cast<AnimData*>(graph_output_socket->m_reference.ptr);
|
static_cast<AnimData*>(*graph_output_socket->m_reference.ptr_ptr);
|
||||||
|
|
||||||
CHECK(graph_output->m_local_matrices.size() == graph_context.m_skeleton->num_soa_joints());
|
CHECK(
|
||||||
|
graph_output->m_local_matrices.size()
|
||||||
|
== graph_context.m_skeleton->num_soa_joints());
|
||||||
|
|
||||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
CHECK(
|
||||||
CHECK(graph.m_anim_data_allocator.size() == 3);
|
blend2_instance->o_output == *graph_output_socket->m_reference.ptr_ptr);
|
||||||
|
|
||||||
CHECK(blend2_instance->o_output == nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
graph_context.freeAnimations();
|
graph_context.freeAnimations();
|
||||||
@@ -269,51 +388,52 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
== graph_loaded_input_node.m_socket_accessor->m_outputs.size());
|
== graph_loaded_input_node.m_socket_accessor->m_outputs.size());
|
||||||
|
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
graph_loaded_input_node.m_socket_accessor->FindOutputSocket(
|
graph_loaded_input_node.m_socket_accessor->GetOutputSocket(
|
||||||
"GraphFloatInput")
|
"GraphFloatInput")
|
||||||
!= nullptr);
|
!= nullptr);
|
||||||
|
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
graph_loaded_output_node.m_socket_accessor->FindInputSocket(
|
graph_loaded_output_node.m_socket_accessor->GetInputSocket(
|
||||||
"GraphFloatOutput")
|
"GraphFloatOutput")
|
||||||
!= nullptr);
|
!= nullptr);
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
graph_loaded_output_node.m_socket_accessor->FindInputSocket(
|
graph_loaded_output_node.m_socket_accessor->GetInputSocket(
|
||||||
"GraphVec3Output")
|
"GraphVec3Output")
|
||||||
!= nullptr);
|
!= nullptr);
|
||||||
|
|
||||||
WHEN("Instantiating an AnimGraph") {
|
WHEN("Instantiating an AnimGraph") {
|
||||||
AnimGraph anim_graph = graph_resource_loaded.createInstance();
|
AnimGraph anim_graph;
|
||||||
|
graph_resource_loaded.createInstance(anim_graph);
|
||||||
|
|
||||||
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
|
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
anim_graph.getInputPtr("GraphFloatInput")
|
anim_graph.getInputPtr("GraphFloatInput")
|
||||||
== anim_graph.m_input_buffer);
|
== anim_graph.m_input_buffer);
|
||||||
|
|
||||||
float* graph_float_input = nullptr;
|
float graph_float_input = 123.456f;
|
||||||
graph_float_input =
|
anim_graph.SetInput("GraphFloatInput", &graph_float_input);
|
||||||
static_cast<float*>(anim_graph.getInputPtr("GraphFloatInput"));
|
|
||||||
|
|
||||||
*graph_float_input = 123.456f;
|
|
||||||
|
|
||||||
AND_WHEN("Evaluating Graph") {
|
AND_WHEN("Evaluating Graph") {
|
||||||
AnimGraphContext context;
|
AnimGraphContext context;
|
||||||
context.m_graph = &anim_graph;
|
context.m_graph = &anim_graph;
|
||||||
|
|
||||||
|
anim_graph.init(context);
|
||||||
|
|
||||||
|
// GraphFloatOutput is directly connected to GraphFloatInput therefore
|
||||||
|
// we need to get the pointer here.
|
||||||
|
float* graph_float_ptr = nullptr;
|
||||||
|
graph_float_ptr = anim_graph.GetOutputPtr<float>("GraphFloatOutput");
|
||||||
|
Vec3 graph_vec3_output;
|
||||||
|
anim_graph.SetOutput("GraphVec3Output", &graph_vec3_output);
|
||||||
|
|
||||||
anim_graph.updateTime(0.f);
|
anim_graph.updateTime(0.f);
|
||||||
anim_graph.evaluate(context);
|
anim_graph.evaluate(context);
|
||||||
|
|
||||||
Socket* float_output_socket =
|
|
||||||
anim_graph.getOutputSocket("GraphFloatOutput");
|
|
||||||
Socket* vec3_output_socket =
|
|
||||||
anim_graph.getOutputSocket("GraphVec3Output");
|
|
||||||
Vec3& vec3_output =
|
|
||||||
*static_cast<Vec3*>(vec3_output_socket->m_reference.ptr);
|
|
||||||
|
|
||||||
THEN("output vector components equal the graph input vaulues") {
|
THEN("output vector components equal the graph input vaulues") {
|
||||||
CHECK(vec3_output[0] == *graph_float_input);
|
CHECK(graph_float_ptr == &graph_float_input);
|
||||||
CHECK(vec3_output[1] == *graph_float_input);
|
CHECK(graph_vec3_output.v[0] == graph_float_input);
|
||||||
CHECK(vec3_output[2] == *graph_float_input);
|
CHECK(graph_vec3_output.v[1] == graph_float_input);
|
||||||
|
CHECK(graph_vec3_output.v[2] == graph_float_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.freeAnimations();
|
context.freeAnimations();
|
||||||
@@ -418,48 +538,41 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
graph_resource_loaded.m_nodes[1];
|
graph_resource_loaded.m_nodes[1];
|
||||||
|
|
||||||
WHEN("Instantiating an AnimGraph") {
|
WHEN("Instantiating an AnimGraph") {
|
||||||
AnimGraph anim_graph = graph_resource_loaded.createInstance();
|
AnimGraph anim_graph;
|
||||||
|
graph_resource_loaded.createInstance(anim_graph);
|
||||||
|
|
||||||
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
|
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
anim_graph.getInputPtr("GraphFloatInput")
|
anim_graph.getInputPtr("GraphFloatInput")
|
||||||
== anim_graph.m_input_buffer);
|
== anim_graph.m_input_buffer);
|
||||||
|
|
||||||
float* graph_float_input = nullptr;
|
float graph_float_input = 123.456f;
|
||||||
graph_float_input =
|
anim_graph.SetInput("GraphFloatInput", &graph_float_input);
|
||||||
static_cast<float*>(anim_graph.getInputPtr("GraphFloatInput"));
|
|
||||||
|
|
||||||
*graph_float_input = 123.456f;
|
|
||||||
|
|
||||||
AND_WHEN("Evaluating Graph") {
|
AND_WHEN("Evaluating Graph") {
|
||||||
AnimGraphContext context;
|
AnimGraphContext context;
|
||||||
context.m_graph = &anim_graph;
|
context.m_graph = &anim_graph;
|
||||||
|
|
||||||
|
// float0 output is directly connected to the graph input, therefore
|
||||||
|
// we have to get a ptr to the input data here.
|
||||||
|
float* float0_output_ptr = nullptr;
|
||||||
|
float float1_output = -1.f;
|
||||||
|
float float2_output = -1.f;
|
||||||
|
|
||||||
|
float0_output_ptr = anim_graph.GetOutputPtr<float>("GraphFloat0Output");
|
||||||
|
|
||||||
|
anim_graph.SetOutput("GraphFloat1Output", &float1_output);
|
||||||
|
anim_graph.SetOutput("GraphFloat2Output", &float2_output);
|
||||||
|
|
||||||
anim_graph.updateTime(0.f);
|
anim_graph.updateTime(0.f);
|
||||||
anim_graph.evaluate(context);
|
anim_graph.evaluate(context);
|
||||||
|
|
||||||
Socket* float0_output_socket =
|
|
||||||
anim_graph.getOutputSocket("GraphFloat0Output");
|
|
||||||
Socket* float1_output_socket =
|
|
||||||
anim_graph.getOutputSocket("GraphFloat1Output");
|
|
||||||
Socket* float2_output_socket =
|
|
||||||
anim_graph.getOutputSocket("GraphFloat2Output");
|
|
||||||
|
|
||||||
REQUIRE(float0_output_socket != nullptr);
|
|
||||||
REQUIRE(float1_output_socket != nullptr);
|
|
||||||
REQUIRE(float2_output_socket != nullptr);
|
|
||||||
|
|
||||||
float& float0_output =
|
|
||||||
*static_cast<float*>(float0_output_socket->m_reference.ptr);
|
|
||||||
float& float1_output =
|
|
||||||
*static_cast<float*>(float1_output_socket->m_reference.ptr);
|
|
||||||
float& float2_output =
|
|
||||||
*static_cast<float*>(float2_output_socket->m_reference.ptr);
|
|
||||||
|
|
||||||
THEN("output vector components equal the graph input vaulues") {
|
THEN("output vector components equal the graph input vaulues") {
|
||||||
CHECK(float0_output == Approx(*graph_float_input));
|
CHECK(*float0_output_ptr == Approx(graph_float_input));
|
||||||
CHECK(float1_output == Approx(*graph_float_input * 2.));
|
CHECK(float1_output == Approx(graph_float_input * 2.f));
|
||||||
CHECK(float2_output == Approx(*graph_float_input * 3.));
|
REQUIRE_THAT(
|
||||||
|
float2_output,
|
||||||
|
Catch::Matchers::WithinAbs(graph_float_input * 3.f, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
context.freeAnimations();
|
context.freeAnimations();
|
||||||
@@ -467,338 +580,3 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
WHEN("Connecting input to output and instantiating the graph") {
|
|
||||||
AnimNodeResource& graph_output_node = graph_resource_origin.m_nodes[0];
|
|
||||||
AnimNodeResource& graph_input_node = graph_resource_origin.m_nodes[1];
|
|
||||||
|
|
||||||
REQUIRE(graph_resource_origin.connectSockets(
|
|
||||||
graph_input_node,
|
|
||||||
"GraphAnimInput",
|
|
||||||
graph_output_node,
|
|
||||||
"GraphOutput"));
|
|
||||||
|
|
||||||
AnimGraph anim_graph = graph_resource_origin.createInstance();
|
|
||||||
|
|
||||||
void* graph_anim_input_ptr = anim_graph.getInput("GraphAnimInput");
|
|
||||||
void* graph_output_ptr = anim_graph.getOutput("GraphOutput");
|
|
||||||
|
|
||||||
REQUIRE(graph_anim_input_ptr == graph_output_ptr);
|
|
||||||
REQUIRE(graph_output_ptr == anim_graph.m_output_buffer);
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.getInput("GraphAnimInput")
|
|
||||||
== anim_graph.getOutput("GraphOutput"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
|
||||||
AnimGraphResource graph_resource;
|
|
||||||
|
|
||||||
graph_resource.clear();
|
|
||||||
graph_resource.m_name = "TestGraphInputOutputConnectivity";
|
|
||||||
|
|
||||||
AnimNodeResource& graph_output_node = graph_resource.m_nodes[0];
|
|
||||||
graph_output_node.m_socket_accessor->RegisterInput<float>(
|
|
||||||
"GraphFloatOutput",
|
|
||||||
nullptr);
|
|
||||||
graph_output_node.m_socket_accessor->RegisterInput<AnimData>(
|
|
||||||
"GraphAnimOutput",
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
AnimNodeResource& graph_input_node = graph_resource.m_nodes[1];
|
|
||||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
|
||||||
"GraphFloatInput",
|
|
||||||
nullptr);
|
|
||||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
|
||||||
"SpeedScaleInput",
|
|
||||||
nullptr);
|
|
||||||
graph_input_node.m_socket_accessor->RegisterOutput<AnimData>(
|
|
||||||
"GraphAnimInput0",
|
|
||||||
nullptr);
|
|
||||||
graph_input_node.m_socket_accessor->RegisterOutput<AnimData>(
|
|
||||||
"GraphAnimInput1",
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
WHEN("Connecting float input with float output") {
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphFloatInput",
|
|
||||||
graph_resource.getGraphOutputNode(),
|
|
||||||
"GraphFloatOutput"));
|
|
||||||
|
|
||||||
AnimGraph anim_graph = graph_resource.createInstance();
|
|
||||||
|
|
||||||
THEN("Writing to the input pointer changes the value of the output.") {
|
|
||||||
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
|
|
||||||
REQUIRE(float_input_ptr != nullptr);
|
|
||||||
*float_input_ptr = 23.123f;
|
|
||||||
|
|
||||||
float* float_output_ptr =
|
|
||||||
(float*)anim_graph.getOutput("GraphFloatOutput");
|
|
||||||
REQUIRE(float_output_ptr != nullptr);
|
|
||||||
CHECK(*float_output_ptr == Approx(23.123f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Connecting adding a Blend2 node") {
|
|
||||||
size_t blend2_node_index =
|
|
||||||
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
|
||||||
AnimNodeResource& blend2_node_resource =
|
|
||||||
graph_resource.m_nodes[blend2_node_index];
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphFloatInput",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Weight"));
|
|
||||||
|
|
||||||
THEN("Connected float input points to the blend weight.") {
|
|
||||||
AnimGraph anim_graph = graph_resource.createInstance();
|
|
||||||
|
|
||||||
Blend2Node* blend2_node =
|
|
||||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
*anim_graph.m_socket_accessor->m_outputs[0].m_reference.ptr_ptr
|
|
||||||
== blend2_node->i_blend_weight);
|
|
||||||
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
|
|
||||||
REQUIRE(float_input_ptr == blend2_node->i_blend_weight);
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN(
|
|
||||||
"Connecting AnimData inputs to blend2 node and blend2 output to graph "
|
|
||||||
"output.") {
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphAnimInput0",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Input0"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphAnimInput1",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Input1"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
blend2_node_resource,
|
|
||||||
"Output",
|
|
||||||
graph_resource.getGraphOutputNode(),
|
|
||||||
"GraphAnimOutput"));
|
|
||||||
|
|
||||||
THEN(
|
|
||||||
"AnimData from output gets blended and result is written to "
|
|
||||||
"Output.") {
|
|
||||||
AnimGraph anim_graph = graph_resource.createInstance();
|
|
||||||
|
|
||||||
Blend2Node* blend2_node =
|
|
||||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
|
||||||
|
|
||||||
AnimData* graph_input0 =
|
|
||||||
(AnimData*)anim_graph.getInput("GraphAnimInput0");
|
|
||||||
REQUIRE(graph_input0 == blend2_node->i_input0);
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.m_nodes[1]
|
|
||||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
|
||||||
|
|
||||||
AnimData* graph_input1 =
|
|
||||||
(AnimData*)anim_graph.getInput("GraphAnimInput1");
|
|
||||||
REQUIRE(graph_input1 == blend2_node->i_input1);
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.m_nodes[1]
|
|
||||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
|
||||||
|
|
||||||
AnimData* graph_output =
|
|
||||||
(AnimData*)anim_graph.getOutput("GraphAnimOutput");
|
|
||||||
REQUIRE(graph_output == blend2_node->o_output);
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.m_nodes[blend2_node_index]
|
|
||||||
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Adding AnimSampler Nodes") {
|
|
||||||
size_t blend2_node_index =
|
|
||||||
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
|
||||||
size_t sampler_node_index =
|
|
||||||
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
|
||||||
size_t speed_scale_node_index =
|
|
||||||
graph_resource.addNode(AnimNodeResourceFactory("SpeedScale"));
|
|
||||||
|
|
||||||
AnimNodeResource& blend2_node_resource =
|
|
||||||
graph_resource.m_nodes[blend2_node_index];
|
|
||||||
AnimNodeResource& sampler_node_resource =
|
|
||||||
graph_resource.m_nodes[sampler_node_index];
|
|
||||||
AnimNodeResource& speed_scale_node_resource =
|
|
||||||
graph_resource.m_nodes[speed_scale_node_index];
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphFloatInput",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Weight"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"SpeedScaleInput",
|
|
||||||
speed_scale_node_resource,
|
|
||||||
"SpeedScale"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
graph_resource.getGraphInputNode(),
|
|
||||||
"GraphAnimInput0",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Input0"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
sampler_node_resource,
|
|
||||||
"Output",
|
|
||||||
speed_scale_node_resource,
|
|
||||||
"Input"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
speed_scale_node_resource,
|
|
||||||
"Output",
|
|
||||||
blend2_node_resource,
|
|
||||||
"Input1"));
|
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
|
||||||
blend2_node_resource,
|
|
||||||
"Output",
|
|
||||||
graph_resource.getGraphOutputNode(),
|
|
||||||
"GraphAnimOutput"));
|
|
||||||
|
|
||||||
THEN("Data flow and node ordering must be correct.") {
|
|
||||||
AnimGraph anim_graph = graph_resource.createInstance();
|
|
||||||
Blend2Node* blend2_node =
|
|
||||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
|
||||||
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
|
||||||
anim_graph.m_nodes[speed_scale_node_index]);
|
|
||||||
AnimSamplerNode* sampler_node = dynamic_cast<AnimSamplerNode*>(
|
|
||||||
anim_graph.m_nodes[sampler_node_index]);
|
|
||||||
|
|
||||||
//
|
|
||||||
// check connectivity
|
|
||||||
//
|
|
||||||
AnimData* graph_input0 =
|
|
||||||
(AnimData*)anim_graph.getInput("GraphAnimInput0");
|
|
||||||
REQUIRE(graph_input0 == blend2_node->i_input0);
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.m_nodes[1]
|
|
||||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
|
||||||
|
|
||||||
AnimData* graph_input1 =
|
|
||||||
(AnimData*)anim_graph.getInput("GraphAnimInput1");
|
|
||||||
REQUIRE(graph_input1 == nullptr);
|
|
||||||
|
|
||||||
REQUIRE(sampler_node->o_output == speed_scale_node->i_input);
|
|
||||||
REQUIRE(
|
|
||||||
sampler_node
|
|
||||||
== anim_graph.getAnimNodeForInput(speed_scale_node_index, "Input"));
|
|
||||||
|
|
||||||
REQUIRE(speed_scale_node->o_output == blend2_node->i_input1);
|
|
||||||
REQUIRE(
|
|
||||||
speed_scale_node
|
|
||||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
|
||||||
|
|
||||||
AnimData* graph_output =
|
|
||||||
(AnimData*)anim_graph.getOutput("GraphAnimOutput");
|
|
||||||
REQUIRE(graph_output == blend2_node->o_output);
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.m_nodes[blend2_node_index]
|
|
||||||
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
|
||||||
|
|
||||||
//
|
|
||||||
// check ordering
|
|
||||||
//
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.getNodeEvalOrderIndex(blend2_node)
|
|
||||||
< anim_graph.getNodeEvalOrderIndex(sampler_node));
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.getNodeEvalOrderIndex(blend2_node)
|
|
||||||
< anim_graph.getNodeEvalOrderIndex(speed_scale_node));
|
|
||||||
REQUIRE(
|
|
||||||
anim_graph.getNodeEvalOrderIndex(speed_scale_node)
|
|
||||||
< anim_graph.getNodeEvalOrderIndex(sampler_node));
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Instantiating graph") {
|
|
||||||
AnimGraph anim_graph = graph_resource.createInstance();
|
|
||||||
float* blend_weight_input =
|
|
||||||
reinterpret_cast<float*>(anim_graph.getInput("GraphFloatInput"));
|
|
||||||
|
|
||||||
Blend2Node* blend2_node =
|
|
||||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
|
||||||
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
|
||||||
anim_graph.m_nodes[speed_scale_node_index]);
|
|
||||||
AnimSamplerNode* sampler_node = dynamic_cast<AnimSamplerNode*>(
|
|
||||||
anim_graph.m_nodes[sampler_node_index]);
|
|
||||||
|
|
||||||
WHEN("Setting weight to 0. and marking nodes active.") {
|
|
||||||
*blend_weight_input = 0.;
|
|
||||||
anim_graph.markActiveNodes();
|
|
||||||
|
|
||||||
THEN("Speed scale and sampler node are inactive") {
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == false);
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Setting weight to 0. and marking nodes active") {
|
|
||||||
*blend_weight_input = 0.1;
|
|
||||||
anim_graph.markActiveNodes();
|
|
||||||
|
|
||||||
THEN("Speed scale and sampler nodes are active") {
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == true);
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Setting weight to 1. and marking nodes active") {
|
|
||||||
*blend_weight_input = 1.0;
|
|
||||||
anim_graph.markActiveNodes();
|
|
||||||
|
|
||||||
THEN("Speed scale and sampler nodes are active") {
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == true);
|
|
||||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Updating time with dt = 0.3f and speed scale = 1.0f") {
|
|
||||||
float* speed_scale_input =
|
|
||||||
reinterpret_cast<float*>(anim_graph.getInput("SpeedScaleInput"));
|
|
||||||
|
|
||||||
*blend_weight_input = 0.1;
|
|
||||||
*speed_scale_input = 1.0f;
|
|
||||||
|
|
||||||
anim_graph.markActiveNodes();
|
|
||||||
anim_graph.updateTime(0.3f);
|
|
||||||
|
|
||||||
THEN ("Anim sampler node time now must be 0.3f") {
|
|
||||||
REQUIRE(sampler_node->m_time_now == Approx(0.3f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WHEN("Updating time with dt = 0.3f and speed scale = 1.3f") {
|
|
||||||
float* speed_scale_input =
|
|
||||||
reinterpret_cast<float*>(anim_graph.getInput("SpeedScaleInput"));
|
|
||||||
|
|
||||||
*blend_weight_input = 0.1;
|
|
||||||
*speed_scale_input = 1.3f;
|
|
||||||
|
|
||||||
anim_graph.markActiveNodes();
|
|
||||||
anim_graph.updateTime(0.3f);
|
|
||||||
|
|
||||||
THEN ("Anim sampler node time now must be 0.39f") {
|
|
||||||
REQUIRE(sampler_node->m_time_now == Approx(0.39f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 04.02.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AnimGraph/AnimGraphData.h"
|
||||||
|
#include "AnimGraph/AnimGraphNodes.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("Descriptor Access", "[NodeDescriptorTests]") {
|
||||||
|
Blend2Node blend2Node;
|
||||||
|
NodeDescriptor<Blend2Node> blend2Descriptor (&blend2Node);
|
||||||
|
CHECK(blend2Descriptor.m_inputs.size() == 3);
|
||||||
|
CHECK(*blend2Descriptor.m_inputs[0].m_reference.ptr_ptr == blend2Node.i_input0);
|
||||||
|
CHECK(*blend2Descriptor.m_inputs[1].m_reference.ptr_ptr == blend2Node.i_input1);
|
||||||
|
CHECK(*blend2Descriptor.m_inputs[2].m_reference.ptr_ptr == blend2Node.i_blend_weight);
|
||||||
|
|
||||||
|
CHECK(blend2Descriptor.m_inputs[0].m_type_size == sizeof(AnimData));
|
||||||
|
CHECK(blend2Descriptor.m_inputs[2].m_type_size == 4);
|
||||||
|
|
||||||
|
CHECK(blend2Descriptor.m_outputs.size() == 1);
|
||||||
|
CHECK(*blend2Descriptor.m_outputs[0].m_reference.ptr_ptr == blend2Node.o_output);
|
||||||
|
|
||||||
|
CHECK(blend2Descriptor.m_properties.size() == 1);
|
||||||
|
CHECK(blend2Descriptor.m_properties[0].m_reference.ptr == &blend2Node.m_sync_blend);
|
||||||
|
|
||||||
|
// Check we can properly update inputs
|
||||||
|
CHECK(blend2Node.i_input0 == nullptr);
|
||||||
|
AnimData some_anim_data;
|
||||||
|
blend2Descriptor.SetInput("Input0", &some_anim_data);
|
||||||
|
CHECK(blend2Node.i_input0 == &some_anim_data);
|
||||||
|
|
||||||
|
// Check we properly can set properties
|
||||||
|
CHECK(blend2Node.m_sync_blend == false);
|
||||||
|
CHECK(blend2Descriptor.GetProperty<bool>("Sync") == false);
|
||||||
|
blend2Descriptor.SetProperty<bool>("Sync", true);
|
||||||
|
CHECK(blend2Node.m_sync_blend == true);
|
||||||
|
CHECK(blend2Descriptor.GetProperty<bool>("Sync") == true);
|
||||||
|
|
||||||
|
// Check that flags are properly set.
|
||||||
|
CHECK(blend2Node.m_sync_blend == true);
|
||||||
|
blend2Descriptor.UpdateFlags();
|
||||||
|
Socket* weight_input_socket = blend2Descriptor.GetInputSocket("Weight");
|
||||||
|
CHECK(weight_input_socket != nullptr);
|
||||||
|
CHECK(weight_input_socket->m_flags & SocketFlagAffectsTime == SocketFlagAffectsTime);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user