Author SHA1 Message Date
Martin Felis e8ef7924d2 Fixed Blend2Node::UpdateFlags(). 2023-04-03 19:31:09 +02:00
Martin Felis 0a45497de9 Added LockTranslationNode. 2023-04-02 21:40:49 +02:00
Martin Felis 7c8b44247b Fixed SpeedScale node not properly propagating time. 2023-04-02 21:24:12 +02:00
Martin Felis abf44a875a Added support for const node inputs. 2023-04-02 16:26:24 +02:00
Martin Felis 42303d5f47 Store the input values of nodes if they are non-zero. 2023-04-01 22:53:53 +02:00
Martin Felis 3d55b748e6 Refactored anim graph data usage and evaluation.
- Refactored NodeSocketAccessor to NodeDescriptor.
- Connections are wired up during AnimGraph instantiation.
  - Output and input sockets point to the same memory location.
  - No re-wiring needed during evaluation.
  - AnimGraph are pre-allocated (refactoring for less memory usage postponed).
- Evaluation of AnimGraph now possible from the editor.
2023-04-01 14:16:20 +02:00
Martin Felis 91607baa9d Initial step for connectivity refactor.
Instead of wiring up pointers with prepareNodeEval() and finishNodeEval() use for each connection a single memory block where outputs and inputs point to.
2023-03-30 23:50:07 +02:00
Martin Felis 411aa5ef20 Better split between runtime library and editor. 2023-03-30 18:11:54 +02:00
17 changed files with 997 additions and 1260 deletions
+12 -12
View File
@@ -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
+17 -106
View File
@@ -4,6 +4,7 @@
#include "AnimGraph.h" #include "AnimGraph.h"
#include <algorithm>
#include <cstring> #include <cstring>
bool AnimGraph::init(AnimGraphContext& context) { bool AnimGraph::init(AnimGraphContext& context) {
@@ -15,16 +16,9 @@ bool AnimGraph::init(AnimGraphContext& context) {
} }
} }
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;
@@ -37,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;
@@ -51,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);
} }
} }
@@ -92,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];
@@ -235,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) {
+98 -45
View File
@@ -18,12 +18,15 @@ 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;
@@ -31,55 +34,25 @@ struct AnimGraph {
bool init(AnimGraphContext& context); bool init(AnimGraphContext& context);
void dealloc() { void dealloc() {
if (m_node_input_connections.size() > 0) { for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
std::vector<AnimGraphConnection>& graph_outputs = m_animdata_blocks[i]->m_local_matrices.vector::~vector();
m_node_input_connections[0];
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
AnimGraphConnection& connection = graph_outputs[i];
if (connection.m_target_socket.m_type
== SocketType::SocketTypeAnimation) {
AnimData* graph_anim_output = static_cast<AnimData*>(
connection.m_target_socket.m_reference.ptr);
assert(graph_anim_output != nullptr);
// we have to explicitly call the destructor as the AnimData* was
// initialized using a placement new operator.
graph_anim_output->m_local_matrices.vector::~vector();
}
}
} }
m_animdata_blocks.clear();
m_node_input_connections.clear(); m_node_input_connections.clear();
if (m_node_output_connections.size() > 0) {
std::vector<AnimGraphConnection>& graph_inputs =
m_node_output_connections[0];
for (size_t i = 0, n = graph_inputs.size(); i < n; i++) {
AnimGraphConnection& connection = graph_inputs[i];
if (connection.m_target_socket.m_type
== SocketType::SocketTypeAnimation) {
AnimData* graph_anim_output = static_cast<AnimData*>(
connection.m_target_socket.m_reference.ptr);
assert(graph_anim_output != nullptr);
// we have to explicitly call the destructor as the AnimData* was
// initialized using a placement new operator.
graph_anim_output->m_local_matrices.vector::~vector();
}
}
}
m_node_output_connections.clear(); m_node_output_connections.clear();
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(); m_nodes.clear();
delete m_socket_accessor; delete m_node_descriptor;
} }
void updateOrderedNodes(); void updateOrderedNodes();
@@ -89,11 +62,6 @@ 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);
@@ -111,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) {
+172 -329
View File
@@ -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;
@@ -104,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,
@@ -121,7 +144,7 @@ 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 { SocketFlagNone = 0, SocketFlagAffectsTime = 1 }; enum SocketFlags { SocketFlagNone = 0, SocketFlagAffectsTime = 1 };
@@ -130,12 +153,13 @@ struct Socket {
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;
std::string* string_ptr;
}; };
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;
@@ -143,6 +167,62 @@ struct Socket {
SocketReference m_reference = {0}; SocketReference m_reference = {0};
SocketFlags m_flags = SocketFlagNone; 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();
}
}; };
template <typename T> template <typename T>
@@ -155,6 +235,10 @@ SocketType GetSocketType() {
return SocketType::SocketTypeAnimation; return SocketType::SocketTypeAnimation;
} }
if constexpr (std::is_same<T, int>::value) {
return SocketType::SocketTypeInt;
}
if constexpr (std::is_same<T, float>::value) { if constexpr (std::is_same<T, float>::value) {
return SocketType::SocketTypeFloat; return SocketType::SocketTypeFloat;
} }
@@ -177,9 +261,9 @@ SocketType GetSocketType() {
} }
struct NodeDescriptorBase { struct NodeDescriptorBase {
std::vector<Socket> m_properties;
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;
template <typename T> template <typename T>
bool RegisterInput( bool RegisterInput(
@@ -234,10 +318,54 @@ struct NodeDescriptorBase {
*socket->m_reference.ptr_ptr = value_ptr; *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) { Socket* GetInputSocket(const char* name) {
return FindSocket(name, m_inputs); 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> template <typename T>
void SetProperty(const char* name, const T& value) { void SetProperty(const char* name, const T& value) {
Socket* socket = FindSocket(name, m_properties); Socket* socket = FindSocket(name, m_properties);
@@ -245,6 +373,21 @@ struct NodeDescriptorBase {
*static_cast<T*>(socket->m_reference.ptr) = value; *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> template <typename T>
const T& GetProperty(const char* name) { const T& GetProperty(const char* name) {
Socket* socket = FindSocket(name, m_properties); Socket* socket = FindSocket(name, m_properties);
@@ -252,6 +395,15 @@ struct NodeDescriptorBase {
return *static_cast<T*>(socket->m_reference.ptr); 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(){};
protected: protected:
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) { Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
for (int i = 0, n = sockets.size(); i < n; i++) { for (int i = 0, n = sockets.size(); i < n; i++) {
@@ -263,7 +415,15 @@ struct NodeDescriptorBase {
return nullptr; return nullptr;
} }
virtual void UpdateFlags(){}; int FindSocketIndex(const char* name, std::vector<Socket>& sockets) {
for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
return i;
}
}
return -1;
}
template <typename T> template <typename T>
bool RegisterSocket( bool RegisterSocket(
@@ -295,328 +455,11 @@ struct NodeDescriptor : public NodeDescriptorBase {
virtual ~NodeDescriptor() {} virtual ~NodeDescriptor() {}
}; };
struct NodeSocketAccessorBase { struct AnimNode;
std::vector<Socket> m_properties;
std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs;
NodeSocketAccessorBase() {}
virtual ~NodeSocketAccessorBase() {}
virtual void UpdateFlags(){};
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
Socket* result = nullptr;
for (size_t i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
result = &sockets[i];
break;
}
}
return result;
}
const Socket* FindSocket(
const std::vector<Socket>& sockets,
const std::string& name) const {
const Socket* result = nullptr;
for (size_t i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
result = &sockets[i];
break;
}
}
return result;
}
SocketType GetSocketType(
const std::vector<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) {
return i;
}
}
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>
bool RegisterSocket(
std::vector<Socket>& sockets,
const std::string& name,
T* value_ptr,
SocketFlags flags = SocketFlagNone) {
Socket* socket = FindSocket(sockets, name);
if (socket != nullptr) {
std::cerr << "Socket " << name << " already registered." << std::endl;
return false;
}
sockets.push_back(Socket());
socket = &sockets[sockets.size() - 1];
socket->m_name = name;
socket->m_type_size = sizeof(T);
socket->m_flags = flags;
if constexpr (std::is_same<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;
socket->m_value.string_ptr = value_ptr;
socket->m_reference.ptr = value_ptr;
return true;
} 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;
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,
SocketFlags flags = SocketFlagNone) {
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,
SocketFlags flags = SocketFlagNone) {
return RegisterSocket(m_outputs, name, value, flags);
}
template <typename T>
bool RegisterOutput(
const std::string& name,
T** value,
SocketFlags flags = SocketFlagNone) {
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);
}
};
//
// SetSocketReferenceValue<> specializations
//
template <>
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const bool&>(
Socket* socket,
const bool& value) {
*static_cast<bool*>(socket->m_reference.ptr) = value;
}
template <>
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) {
socket->m_value.string_ptr = const_cast<std::string*>(value);
}
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<float>(
Socket* socket,
float value) {
*static_cast<float*>(socket->m_reference.ptr) = 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<std::string>(
Socket* socket,
std::string value) {
*socket->m_value.string_ptr = value;
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const char*>(
Socket* socket,
const char* value) {
SetSocketValue<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
+132 -16
View File
@@ -4,7 +4,10 @@
#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"
@@ -15,6 +18,8 @@ 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:
@@ -63,6 +68,86 @@ void RemoveConnectionsForSocket(
} }
} }
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) {
@@ -82,24 +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_value.float_value), 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) {
char string_buf[1024]; char string_buf[1024];
memcpy (string_buf, property.m_value.string_ptr->c_str(), property.m_value.string_ptr->size() + 1); memset(string_buf, '\0', sizeof(string_buf));
memcpy(
string_buf,
property.m_value_string.c_str(),
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.m_value.string_ptr = string_buf; property.m_value_string = string_buf;
} }
} }
} }
@@ -211,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";
} }
@@ -255,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,12 +376,25 @@ void AnimGraphEditorUpdate() {
bool socket_connected = bool socket_connected =
sGraphGresource.isSocketConnected(node_resource, socket.m_name); sGraphGresource.isSocketConnected(node_resource, socket.m_name);
if (!socket_connected && if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
(socket.m_type == SocketType::SocketTypeFloat)) {
ImGui::SameLine(); ImGui::SameLine();
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();
} }
@@ -313,7 +429,7 @@ void AnimGraphEditorUpdate() {
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) {
@@ -326,7 +442,7 @@ void AnimGraphEditorUpdate() {
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);
} }
} }
@@ -351,12 +467,12 @@ void AnimGraphEditorUpdate() {
const AnimNodeResource& source_node = const AnimNodeResource& source_node =
sGraphGresource.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 =
sGraphGresource.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,
+6
View File
@@ -7,6 +7,8 @@
#include "AnimGraph.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;
} }
@@ -27,6 +29,10 @@ 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); void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
+31 -1
View File
@@ -82,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;
}
+64 -62
View File
@@ -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_) {}
}; };
// //
@@ -122,34 +122,6 @@ struct Blend2Node : public AnimNode {
virtual void Evaluate(AnimGraphContext& context) override; virtual void Evaluate(AnimGraphContext& context) override;
}; };
template <>
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
NodeSocketAccessor(AnimNode* node_) {
Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
RegisterInput("Input0", &node->i_input0);
RegisterInput("Input1", &node->i_input1);
RegisterInput(
"Weight",
&node->i_blend_weight,
SocketFlags::SocketFlagAffectsTime);
RegisterOutput("Output", &node->o_output);
RegisterProperty("Sync", &node->m_sync_blend);
}
virtual void UpdateFlags() override {
Socket* weight_input_socket = FindSocket(m_inputs, "Weight");
assert(weight_input_socket != nullptr);
if (GetProperty<bool>("Sync", false) == true) {
weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
} else {
weight_input_socket->m_flags = SocketFlags::SocketFlagNone;
}
}
};
template <> template <>
struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase { struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
NodeDescriptor(Blend2Node* node) { NodeDescriptor(Blend2Node* node) {
@@ -184,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;
} }
@@ -198,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,
@@ -211,6 +182,7 @@ struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
} }
}; };
// //
// AnimSamplerNode // AnimSamplerNode
// //
@@ -224,22 +196,49 @@ struct AnimSamplerNode : public AnimNode {
virtual bool Init(AnimGraphContext& context) override; virtual bool Init(AnimGraphContext& context) override;
void UpdateTime(float time_last, float time_now) override { void UpdateTime(float time_last, float time_now) override {
m_time_last = time_last; m_time_last = time_last;
m_time_now = fmodf(time_last + (time_now - time_last), m_animation->duration()); m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated; 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 // ConstScalarNode
// //
@@ -253,34 +252,33 @@ struct ConstScalarNode : public AnimNode {
}; };
template <> template <>
struct NodeSocketAccessor<ConstScalarNode> : public NodeSocketAccessorBase { struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
NodeSocketAccessor(AnimNode* node_) { NodeDescriptor(ConstScalarNode* node) {
ConstScalarNode* node = dynamic_cast<ConstScalarNode*>(node_);
RegisterOutput("ScalarOutput", &node->o_value); RegisterOutput("ScalarOutput", &node->o_value);
RegisterProperty("ScalarValue", &node->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);
@@ -294,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);
@@ -318,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") {
@@ -326,6 +324,8 @@ 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") {
@@ -345,23 +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") { } else if (node_type_name == "ConstScalarNode") {
return new NodeSocketAccessor<ConstScalarNode>(node); 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;
+239 -166
View File
@@ -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"] = *socket.m_value.string_ptr;
} else { } else {
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type) std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
<< "'." << std::endl; << "'." << std::endl;
@@ -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,41 +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) {
*(property.m_value.string_ptr) = json_property["value"].get<std::string>();
} 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 { *input_socket = sJsonToSocket(json_node["inputs"][j]);
std::cerr << "Invalid type for property '" << property.m_name
<< "': expected " << sSocketTypeToStr(property.m_type)
<< " but got " << json_property["type"] << std::endl;
} }
} }
@@ -226,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++) {
@@ -283,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
@@ -292,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]));
@@ -333,7 +373,6 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
void AnimGraphResource::createInstance(AnimGraph& result) const { void AnimGraphResource::createInstance(AnimGraph& result) const {
createRuntimeNodeInstances(result); createRuntimeNodeInstances(result);
prepareGraphIOData(result); prepareGraphIOData(result);
connectRuntimeNodes(result);
setRuntimeNodeProperties(result); setRuntimeNodeProperties(result);
result.updateOrderedNodes(); result.updateOrderedNodes();
@@ -358,13 +397,15 @@ 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++) {
@@ -380,14 +421,18 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
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) { if (output_block_size > 0) {
@@ -397,120 +442,121 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
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 if (connection_data_storage_size > 0) {
|| connection.source_node_index >= m_nodes.size()) { instance.m_connection_data_storage = new char[connection_data_storage_size];
std::cerr << "Could not find source node index." << std::endl; memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
continue; }
std::vector<NodeDescriptorBase*> instance_node_descriptors(
m_nodes.size(),
nullptr);
for (int i = 0; i < m_nodes.size(); i++) {
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
m_nodes[i].m_type_name.c_str(),
instance.m_nodes[i]);
}
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;
instance_node_descriptors[1]->m_outputs =
instance.m_node_descriptor->m_outputs;
int connection_data_offset = 0;
for (int i = 0; i < m_connections.size(); i++) {
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]));
} }
source_node = instance.m_nodes[connection.source_node_index]; connection_data_offset += source_socket->m_type_size;
source_node_type = source_node->m_node_type_name; }
if (connection.source_node_index == 1) {
source_node_accessor = instance.m_socket_accessor; //
} else { // const node inputs
source_node_accessor = //
AnimNodeAccessorFactory(source_node_type, source_node); std::vector<Socket*> const_inputs =
getConstNodeInputs(instance, instance_node_descriptors);
int const_node_inputs_buffer_size = 0;
for (int i = 0, n = const_inputs.size(); i < n; i++) {
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;
}
if (connection.target_node_index < 0 if (const_node_inputs_buffer_size > 0) {
|| connection.target_node_index >= m_nodes.size()) { instance.m_const_node_inputs = new char[const_node_inputs_buffer_size];
std::cerr << "Could not find source node index." << std::endl; memset(instance.m_const_node_inputs, '\0', const_node_inputs_buffer_size);
continue; }
}
target_node = instance.m_nodes[connection.target_node_index]; int const_input_buffer_offset = 0;
target_node_type = target_node->m_node_type_name; for (int i = 0, n = const_inputs.size(); i < n; i++) {
if (connection.target_node_index == 0) { Socket* const_input = const_inputs[i];
target_node_accessor = instance.m_socket_accessor;
} else {
target_node_accessor =
AnimNodeAccessorFactory(target_node_type, target_node);
}
assert(source_node != nullptr); // TODO: implement string const node input support
assert(target_node != nullptr); assert(const_input->m_type != SocketType::SocketTypeString);
// *const_input->m_reference.ptr_ptr =
// Map resource node sockets to graph instance node sockets &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);
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 = const_input_buffer_offset += const_inputs[i]->m_type_size;
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) { for (int i = 0; i < m_nodes.size(); i++) {
std::cerr << "Cannot connect sockets: invalid types!" << std::endl; delete instance_node_descriptors[i];
}
//
// Wire up outputs to inputs.
//
// Skip animation connections and connections to the output node as the
// pointers are already set up in AnimGraphResource::prepareGraphIOData().
if (target_socket->m_type != SocketType::SocketTypeAnimation
&& connection.target_node_index != 0) {
(*target_socket->m_reference.ptr_ptr) = source_socket->m_reference.ptr;
}
size_t target_node_index = target_node->m_index;
// Register the runtime connection
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) {
delete source_node_accessor;
}
} }
} }
@@ -518,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;
@@ -529,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->SetPropertyValue( 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->SetPropertyValue( node_instance_accessor->SetProperty(
name, name.c_str(),
*property.m_value.string_ptr); property.m_value_string);
break; break;
default: default:
std::cerr << "Invalid socket type " std::cerr << "Invalid socket type "
@@ -562,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;
}
+5 -4
View File
@@ -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;
@@ -146,6 +146,7 @@ struct AnimGraphResource {
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
-38
View File
@@ -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;
}
}
}
}
-1
View File
@@ -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;
-37
View File
@@ -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();
}
}
-2
View File
@@ -150,8 +150,6 @@ struct SyncTrack {
return result; return result;
} }
void DrawDebugUi();
}; };
#endif //ANIMTESTBED_SYNCTRACK_H #endif //ANIMTESTBED_SYNCTRACK_H
+14 -8
View File
@@ -76,6 +76,7 @@ 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;
@@ -383,7 +384,8 @@ int main() {
AnimGraph anim_graph; AnimGraph anim_graph;
AnimGraphContext anim_graph_context; AnimGraphContext anim_graph_context;
AnimData* anim_graph_output = nullptr; 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;
@@ -510,7 +512,10 @@ int main() {
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) { 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) {
@@ -617,7 +622,6 @@ int main() {
if (ImGui::Button("Update Runtime Graph")) { if (ImGui::Button("Update Runtime Graph")) {
anim_graph.dealloc(); anim_graph.dealloc();
anim_graph_output = nullptr;
AnimGraphEditorGetRuntimeGraph(anim_graph); AnimGraphEditorGetRuntimeGraph(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton; anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
@@ -628,7 +632,7 @@ int main() {
for (int i = 0; i < graph_output_sockets.size(); i++) { for (int i = 0; i < graph_output_sockets.size(); i++) {
const Socket& output = graph_output_sockets[i]; const Socket& output = graph_output_sockets[i];
if (output.m_type == SocketType::SocketTypeAnimation) { if (output.m_type == SocketType::SocketTypeAnimation) {
anim_graph_output = static_cast<AnimData*>(output.m_reference.ptr); anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
} }
} }
} }
@@ -670,7 +674,7 @@ 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();
} }
@@ -725,7 +729,8 @@ int main() {
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Step")) { if (ImGui::Button("Step")) {
state.time.absolute += state.time.frame; 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) {
@@ -758,10 +763,11 @@ int main() {
skinned_mesh.CalcModelMatrices(); skinned_mesh.CalcModelMatrices();
} }
if (state.time.use_graph && anim_graph.m_nodes.size() > 0) { if (state.time.use_graph && anim_graph.m_nodes.size() > 0 && state.time.anim_update_time > 0.) {
anim_graph.updateTime(state.time.frame); anim_graph.markActiveNodes();
anim_graph.updateTime(state.time.anim_update_time);
anim_graph.evaluate(anim_graph_context); anim_graph.evaluate(anim_graph_context);
skinned_mesh.m_local_matrices = anim_graph_output->m_local_matrices; skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
skinned_mesh.CalcModelMatrices(); skinned_mesh.CalcModelMatrices();
} }
+10 -12
View File
@@ -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];
@@ -195,17 +195,15 @@ TEST_CASE_METHOD(
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));
} }
+195 -419
View File
@@ -2,16 +2,15 @@
// 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);
ozz::io::File file(filename, "rb"); ozz::io::File file(filename, "rb");
if (!file.opened()) { if (!file.opened()) {
@@ -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,9 +213,9 @@ 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; AnimGraph graph;
graph_resource_loaded.createInstance(graph); graph_resource_loaded.createInstance(graph);
@@ -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,16 +388,16 @@ 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);
@@ -291,30 +410,30 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
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();
@@ -427,41 +546,33 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
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();
@@ -469,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 sGraphGresource;
sGraphGresource.clear();
sGraphGresource.m_name = "TestGraphInputOutputConnectivity";
AnimNodeResource& graph_output_node = sGraphGresource.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 = sGraphGresource.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(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphFloatInput",
sGraphGresource.getGraphOutputNode(),
"GraphFloatOutput"));
AnimGraph anim_graph = sGraphGresource.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 =
sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
AnimNodeResource& blend2_node_resource =
sGraphGresource.m_nodes[blend2_node_index];
REQUIRE(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphFloatInput",
blend2_node_resource,
"Weight"));
THEN("Connected float input points to the blend weight.") {
AnimGraph anim_graph = sGraphGresource.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(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphAnimInput0",
blend2_node_resource,
"Input0"));
REQUIRE(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphAnimInput1",
blend2_node_resource,
"Input1"));
REQUIRE(sGraphGresource.connectSockets(
blend2_node_resource,
"Output",
sGraphGresource.getGraphOutputNode(),
"GraphAnimOutput"));
THEN(
"AnimData from output gets blended and result is written to "
"Output.") {
AnimGraph anim_graph = sGraphGresource.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 =
sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
size_t sampler_node_index =
sGraphGresource.addNode(AnimNodeResourceFactory("AnimSampler"));
size_t speed_scale_node_index =
sGraphGresource.addNode(AnimNodeResourceFactory("SpeedScale"));
AnimNodeResource& blend2_node_resource =
sGraphGresource.m_nodes[blend2_node_index];
AnimNodeResource& sampler_node_resource =
sGraphGresource.m_nodes[sampler_node_index];
AnimNodeResource& speed_scale_node_resource =
sGraphGresource.m_nodes[speed_scale_node_index];
REQUIRE(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphFloatInput",
blend2_node_resource,
"Weight"));
REQUIRE(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"SpeedScaleInput",
speed_scale_node_resource,
"SpeedScale"));
REQUIRE(sGraphGresource.connectSockets(
sGraphGresource.getGraphInputNode(),
"GraphAnimInput0",
blend2_node_resource,
"Input0"));
REQUIRE(sGraphGresource.connectSockets(
sampler_node_resource,
"Output",
speed_scale_node_resource,
"Input"));
REQUIRE(sGraphGresource.connectSockets(
speed_scale_node_resource,
"Output",
blend2_node_resource,
"Input1"));
REQUIRE(sGraphGresource.connectSockets(
blend2_node_resource,
"Output",
sGraphGresource.getGraphOutputNode(),
"GraphAnimOutput"));
THEN("Data flow and node ordering must be correct.") {
AnimGraph anim_graph = sGraphGresource.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 = sGraphGresource.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));
}
}
}
}
}
*/