Compare commits

..

No commits in common. "eeef635c64a57a2825ea285302d05e6ef73b4061" and "e7314e34770be0bf001569c7c0fb502791eacdaa" have entirely different histories.

9 changed files with 165 additions and 583 deletions

View File

@ -6,16 +6,6 @@
#include <cstring> #include <cstring>
bool AnimGraph::init(AnimGraphContext& context) {
for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) {
return false;
}
}
return true;
}
void AnimGraph::updateOrderedNodes() { void AnimGraph::updateOrderedNodes() {
m_eval_ordered_nodes.clear(); m_eval_ordered_nodes.clear();
updateOrderedNodesRecursive(0); updateOrderedNodesRecursive(0);
@ -79,6 +69,8 @@ void AnimGraph::markActiveNodes() {
} }
void AnimGraph::prepareNodeEval(size_t node_index) { void AnimGraph::prepareNodeEval(size_t node_index) {
AnimNode* node = m_nodes[node_index];
for (size_t i = 0, n = m_node_output_connections[node_index].size(); i < n; for (size_t i = 0, n = m_node_output_connections[node_index].size(); i < n;
i++) { i++) {
AnimGraphConnection& output_connection = AnimGraphConnection& output_connection =
@ -88,7 +80,7 @@ void AnimGraph::prepareNodeEval(size_t node_index) {
continue; continue;
} }
(*output_connection.m_source_socket.m_reference.ptr_ptr) = (*output_connection.m_source_socket.m_value.ptr_ptr) =
m_anim_data_work_buffer.peek(); m_anim_data_work_buffer.peek();
m_anim_data_work_buffer.pop(); m_anim_data_work_buffer.pop();
} }
@ -102,12 +94,14 @@ void AnimGraph::prepareNodeEval(size_t node_index) {
continue; continue;
} }
(*input_connection.m_target_socket.m_reference.ptr_ptr) = (*input_connection.m_target_socket.m_value.ptr_ptr) =
(*input_connection.m_source_socket.m_reference.ptr_ptr); (*input_connection.m_source_socket.m_value.ptr_ptr);
} }
} }
void AnimGraph::finishNodeEval(size_t node_index) { void AnimGraph::finishNodeEval(size_t node_index) {
AnimNode* node = m_nodes[node_index];
for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n; for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n;
i++) { i++) {
AnimGraphConnection& input_connection = AnimGraphConnection& input_connection =
@ -118,8 +112,8 @@ void AnimGraph::finishNodeEval(size_t node_index) {
} }
m_anim_data_work_buffer.push( m_anim_data_work_buffer.push(
static_cast<AnimData*>(input_connection.m_source_socket.m_reference.ptr)); static_cast<AnimData*>(input_connection.m_source_socket.m_value.ptr));
(*input_connection.m_source_socket.m_reference.ptr_ptr) = nullptr; (*input_connection.m_source_socket.m_value.ptr_ptr) = nullptr;
} }
} }
@ -132,8 +126,8 @@ void AnimGraph::evalInputNode() {
if (graph_input_connection.m_source_socket.m_type if (graph_input_connection.m_source_socket.m_type
!= SocketType::SocketTypeAnimation) { != SocketType::SocketTypeAnimation) {
memcpy( memcpy(
*graph_input_connection.m_target_socket.m_reference.ptr_ptr, *graph_input_connection.m_target_socket.m_value.ptr_ptr,
graph_input_connection.m_source_socket.m_reference.ptr, graph_input_connection.m_source_socket.m_value.ptr,
sizeof(void*)); sizeof(void*));
printf("bla"); printf("bla");
} else { } else {
@ -150,8 +144,8 @@ void AnimGraph::evalOutputNode() {
if (graph_output_connection.m_source_socket.m_type if (graph_output_connection.m_source_socket.m_type
!= SocketType::SocketTypeAnimation) { != SocketType::SocketTypeAnimation) {
memcpy( memcpy(
graph_output_connection.m_target_socket.m_reference.ptr, graph_output_connection.m_target_socket.m_value.ptr,
graph_output_connection.m_source_socket.m_reference.ptr, graph_output_connection.m_source_socket.m_value.ptr,
graph_output_connection.m_target_socket.m_type_size); graph_output_connection.m_target_socket.m_type_size);
} else { } else {
// TODO: how to deal with anim data outputs? // TODO: how to deal with anim data outputs?
@ -172,7 +166,7 @@ void AnimGraph::evalSyncTracks() {
} }
void AnimGraph::updateTime(float dt) { void AnimGraph::updateTime(float dt) {
const std::vector<AnimGraphConnection>& graph_output_inputs = const std::vector<AnimGraphConnection> graph_output_inputs =
m_node_input_connections[0]; m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) { for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
AnimNode* node = graph_output_inputs[i].m_source_node; AnimNode* node = graph_output_inputs[i].m_source_node;
@ -188,11 +182,11 @@ void AnimGraph::updateTime(float dt) {
} }
int node_index = node->m_index; int node_index = node->m_index;
const std::vector<AnimGraphConnection> node_input_connections =
m_node_input_connections[node_index];
float node_time_now = node->m_time_now; float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last; float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) { for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
AnimNode* input_node = node_input_connections[i].m_source_node; AnimNode* input_node = node_input_connections[i].m_source_node;
@ -207,7 +201,7 @@ void AnimGraph::updateTime(float dt) {
} }
} }
void AnimGraph::evaluate(AnimGraphContext& context) { void AnimGraph::evaluate() {
constexpr int eval_stack_size = 5; constexpr int eval_stack_size = 5;
int eval_stack_index = eval_stack_size; int eval_stack_index = eval_stack_size;
AnimData eval_buffers[eval_stack_size]; AnimData eval_buffers[eval_stack_size];
@ -225,12 +219,10 @@ void AnimGraph::evaluate(AnimGraphContext& context) {
prepareNodeEval(node->m_index); prepareNodeEval(node->m_index);
node->Evaluate(context); node->Evaluate();
finishNodeEval(node->m_index); finishNodeEval(node->m_index);
} }
evalOutputNode();
} }
Socket* AnimGraph::getInputSocket(const std::string& name) { Socket* AnimGraph::getInputSocket(const std::string& name) {

View File

@ -66,8 +66,6 @@ struct AnimGraph {
delete m_socket_accessor; delete m_socket_accessor;
} }
bool init(AnimGraphContext& context);
void updateOrderedNodes(); void updateOrderedNodes();
void updateOrderedNodesRecursive(int node_index); void updateOrderedNodesRecursive(int node_index);
void markActiveNodes(); void markActiveNodes();
@ -75,6 +73,7 @@ struct AnimGraph {
return node->m_state != AnimNodeEvalState::Deactivated; return node->m_state != AnimNodeEvalState::Deactivated;
} }
void evalInputNode(); void evalInputNode();
void prepareNodeEval(size_t node_index); void prepareNodeEval(size_t node_index);
void finishNodeEval(size_t node_index); void finishNodeEval(size_t node_index);
@ -82,7 +81,7 @@ struct AnimGraph {
void evalSyncTracks(); void evalSyncTracks();
void updateTime(float dt); void updateTime(float dt);
void evaluate(AnimGraphContext& context); void evaluate();
void reset() { void reset() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f; m_nodes[i]->m_time_now = 0.f;
@ -100,7 +99,7 @@ struct AnimGraph {
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) {
return input_socket->m_reference.ptr; return input_socket->m_value.ptr;
} }
return nullptr; return nullptr;

View File

@ -5,29 +5,18 @@
#ifndef ANIMTESTBED_ANIMGRAPHDATA_H #ifndef ANIMTESTBED_ANIMGRAPHDATA_H
#define ANIMTESTBED_ANIMGRAPHDATA_H #define ANIMTESTBED_ANIMGRAPHDATA_H
#include <cstring>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iostream>
#include "SyncTrack.h" #include "SyncTrack.h"
#include "ozz/base/containers/vector.h"
#include <ozz/base/maths/soa_transform.h>
#include "ozz/animation/runtime/skeleton.h"
// //
// Data types // Data types
// //
struct AnimGraph;
struct AnimGraphContext {
AnimGraph* m_graph = nullptr;
ozz::animation::Skeleton* m_skeleton = nullptr;
};
struct AnimData { struct AnimData {
ozz::vector<ozz::math::SoaTransform> m_local_matrices; float m_bone_transforms[16];
}; };
typedef float Vec3[3]; typedef float Vec3[3];
@ -44,8 +33,6 @@ enum class SocketType {
SocketTypeLast SocketTypeLast
}; };
constexpr size_t cSocketStringValueMaxLength = 256;
static const char* SocketTypeNames[] = static const char* SocketTypeNames[] =
{"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"}; {"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"};
@ -55,18 +42,10 @@ struct Socket {
std::string m_name; std::string m_name;
SocketType m_type = SocketType::SocketTypeUndefined; SocketType m_type = SocketType::SocketTypeUndefined;
union SocketValue { union SocketValue {
bool flag;
float float_value;
float vec3[3];
float quat[4];
char str[cSocketStringValueMaxLength];
};
SocketValue m_value = { 0 };
union SocketReference {
void* ptr; void* ptr;
void** ptr_ptr; void** ptr_ptr;
}; };
SocketReference m_reference; SocketValue m_value = {nullptr};
int m_flags = 0; int m_flags = 0;
size_t m_type_size = 0; size_t m_type_size = 0;
}; };
@ -139,25 +118,22 @@ struct NodeSocketAccessorBase {
return default_value; return default_value;
} }
return *static_cast<T*>(socket->m_reference.ptr); return *static_cast<T*>(socket->m_value.ptr);
} }
template <typename T> template <typename T>
void SetSocketReferenceValue(Socket* socket, T value) { void SetSocketValue(
std::cerr << "Could not find template specialization for socket type " const std::vector<Socket>& sockets,
<< static_cast<int>(socket->m_type) << " (" const std::string& name,
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")." const T& value) {
<< std::endl; const Socket* socket = FindSocket(sockets, name);
// *static_cast<T*>(socket->m_value.ptr) = value; if (socket == nullptr) {
} std::cerr << "Error: could not set value of socket with name " << name
<< ": no socket found." << std::endl;
return;
}
template <typename T> *static_cast<T*>(socket->m_value.ptr) = value;
void SetSocketValue(Socket* socket, T value) {
std::cerr << "Could not find template specialization for socket type "
<< static_cast<int>(socket->m_type) << " ("
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")."
<< std::endl;
// *static_cast<T*>(socket->m_value.ptr) = value;
} }
template <typename T> template <typename T>
@ -207,7 +183,7 @@ struct NodeSocketAccessorBase {
return false; return false;
} }
socket->m_reference.ptr = value_ptr; socket->m_value.ptr = value_ptr;
return true; return true;
} }
@ -216,14 +192,8 @@ struct NodeSocketAccessorBase {
return RegisterSocket(m_properties, name, value); return RegisterSocket(m_properties, name, value);
} }
template <typename T> template <typename T>
void SetPropertyReferenceValue(const std::string& name, T value) { void SetProperty(const std::string& name, const T& value) {
Socket* socket = FindSocket(m_properties, name); SetSocketValue(m_properties, name, value);
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> template <typename T>
T GetProperty(const std::string& name, T default_value) { T GetProperty(const std::string& name, T default_value) {
@ -270,90 +240,9 @@ struct NodeSocketAccessorBase {
} }
}; };
//
// 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) {
*static_cast<std::string*>(socket->m_reference.ptr) = value;
}
template <>
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const char*>(Socket* socket, const char* value) {
std::string value_string (value);
SetSocketReferenceValue<const std::string&>(socket, value_string);
}
//
// SetSocketValue<> specializations
//
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const bool&>(Socket* socket, const bool& value) {
socket->m_value.flag = value;
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const float&>(Socket* socket, const float& value) {
socket->m_value.float_value = value;
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const Vec3&>(Socket* socket, const Vec3& value) {
socket->m_value.vec3[0] = value[0];
socket->m_value.vec3[1] = value[1];
socket->m_value.vec3[2] = value[2];
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const Quat&>(Socket* socket, const Quat& value) {
socket->m_value.quat[0] = value[0];
socket->m_value.quat[1] = value[1];
socket->m_value.quat[2] = value[2];
socket->m_value.quat[3] = value[3];
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const std::string&>(Socket* socket, const std::string& value) {
constexpr size_t string_max_length = sizeof(socket->m_value.str) - 1;
strncpy(socket->m_value.str, value.data(), string_max_length);
socket->m_value.str[value.size() > string_max_length ? string_max_length : value.size() ] = 0;
}
template <>
inline void NodeSocketAccessorBase::SetSocketValue<const char*>(Socket* socket, const char* value) {
SetSocketValue<const std::string&>(socket, value);
}
template <typename T> template <typename T>
struct NodeSocketAccessor : public NodeSocketAccessorBase { struct NodeSocketAccessor : public NodeSocketAccessorBase {
virtual ~NodeSocketAccessor() {} virtual ~NodeSocketAccessor() {}
}; };
#endif //ANIMTESTBED_ANIMGRAPHDATA_H #endif //ANIMTESTBED_ANIMGRAPHDATA_H

View File

@ -83,16 +83,16 @@ void AnimGraphEditorRenderSidebar(
if (property.m_type == SocketType::SocketTypeFloat) { if (property.m_type == SocketType::SocketTypeFloat) {
ImGui::SliderFloat( ImGui::SliderFloat(
property.m_name.c_str(), property.m_name.c_str(),
reinterpret_cast<float*>(property.m_reference.ptr), reinterpret_cast<float*>(property.m_value.ptr),
-100.f, -100.f,
100.f); 100.f);
} else if (property.m_type == SocketType::SocketTypeBool) { } else if (property.m_type == SocketType::SocketTypeBool) {
ImGui::Checkbox( ImGui::Checkbox(
property.m_name.c_str(), property.m_name.c_str(),
reinterpret_cast<bool*>(property.m_reference.ptr)); reinterpret_cast<bool*>(property.m_value.ptr));
} else if (property.m_type == SocketType::SocketTypeString) { } else if (property.m_type == SocketType::SocketTypeString) {
std::string* property_string = std::string* property_string =
reinterpret_cast<std::string*>(property.m_reference.ptr); reinterpret_cast<std::string*>(property.m_value.ptr);
char string_buf[256]; char string_buf[256];
memset(string_buf, 0, sizeof(string_buf)); memset(string_buf, 0, sizeof(string_buf));
strncpy( strncpy(

View File

@ -3,83 +3,3 @@
// //
#include "AnimGraphNodes.h" #include "AnimGraphNodes.h"
#include "ozz/base/log.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
void Blend2Node::Evaluate(AnimGraphContext& context) {
assert (i_input0 != nullptr);
assert (i_input1 != nullptr);
assert (i_blend_weight != nullptr);
assert (o_output != nullptr);
// perform blend
ozz::animation::BlendingJob::Layer layers[2];
layers[0].transform = make_span(i_input0->m_local_matrices);
layers[0].weight = (1.0f - *i_blend_weight);
layers[1].transform = make_span(i_input1->m_local_matrices);
layers[1].weight = (*i_blend_weight);
ozz::animation::BlendingJob blend_job;
blend_job.threshold = ozz::animation::BlendingJob().threshold;
blend_job.layers = layers;
blend_job.bind_pose = context.m_skeleton->joint_bind_poses();
blend_job.output = make_span(o_output->m_local_matrices);
if (!blend_job.Run()) {
ozz::log::Err() << "Error blending animations." << std::endl;
}
bool m_sync_blend = false;
}
//
// AnimSamplerNode
//
AnimSamplerNode::~AnimSamplerNode() noexcept {
delete m_animation;
m_animation = nullptr;
}
bool AnimSamplerNode::Init(AnimGraphContext& context) {
assert (m_animation == nullptr);
m_animation = new ozz::animation::Animation();
assert(m_filename.size() != 0);
ozz::io::File file(m_filename.c_str(), "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open animation file " << m_filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<ozz::animation::Animation>()) {
ozz::log::Err() << "Failed to load animation instance from file "
<< m_filename << "." << std::endl;
return false;
}
assert (context.m_skeleton != nullptr);
const int num_soa_joints = context.m_skeleton->num_soa_joints();
const int num_joints = context.m_skeleton->num_joints();
m_sampling_cache.Resize(num_joints);
return true;
}
void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
assert (o_output != nullptr);
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = m_animation;
sampling_job.cache = &m_sampling_cache;
sampling_job.ratio = m_time_now;
sampling_job.output = make_span(o_output->m_local_matrices);
if (!sampling_job.Run()) {
ozz::log::Err() << "Error sampling animation." << std::endl;
}
}

View File

@ -9,7 +9,6 @@
#include "AnimGraphData.h" #include "AnimGraphData.h"
#include "SyncTrack.h" #include "SyncTrack.h"
#include "ozz/animation/runtime/sampling_job.h"
struct AnimNode; struct AnimNode;
@ -47,8 +46,6 @@ struct AnimNode {
virtual ~AnimNode(){}; virtual ~AnimNode(){};
virtual bool Init(AnimGraphContext& context) { return true; };
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) { virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) {
for (size_t i = 0, n = inputs.size(); i < n; i++) { for (size_t i = 0, n = inputs.size(); i < n; i++) {
AnimNode* input_node = inputs[i].m_source_node; AnimNode* input_node = inputs[i].m_source_node;
@ -76,7 +73,7 @@ struct AnimNode {
m_state = AnimNodeEvalState::TimeUpdated; m_state = AnimNodeEvalState::TimeUpdated;
} }
virtual void Evaluate(AnimGraphContext& context){}; virtual void Evaluate(){};
}; };
@ -119,7 +116,26 @@ struct Blend2Node : public AnimNode {
} }
} }
virtual void Evaluate(AnimGraphContext& context) override; virtual void UpdateTime(float dt, std::vector<NodeInput>& inputs) {
if (!m_sync_blend) {
m_time_now = m_time_now + dt;
}
for (size_t i = 0, n = inputs.size(); i < n; i++) {
AnimNode* input_node = inputs[i].m_node;
if (input_node == nullptr) {
continue;
}
if (input_node->m_state != AnimNodeEvalState::Deactivated) {
if (!m_sync_blend) {
input_node->m_time_now = m_time_now;
}
input_node->m_state = AnimNodeEvalState::TimeUpdated;
continue;
}
}
}
}; };
template <> template <>
@ -155,21 +171,14 @@ struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
// //
struct SpeedScaleNode : public AnimNode { struct SpeedScaleNode : public AnimNode {
AnimData* i_input = nullptr; AnimData* i_input = nullptr;
AnimData* o_output = nullptr; AnimData* i_output = nullptr;
float* i_speed_scale = nullptr; float* i_speed_scale = nullptr;
void UpdateTime(float time_last, float time_now) override { virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last; m_time_last = time_last;
m_time_now = time_last + (time_now - time_last) * (*i_speed_scale); m_time_now = time_last + (time_now - time_last) * (*i_speed_scale);
m_state = AnimNodeEvalState::TimeUpdated; m_state = AnimNodeEvalState::TimeUpdated;
} }
void Evaluate(AnimGraphContext& context) override {
assert (i_input != nullptr);
assert (o_output != nullptr);
o_output->m_local_matrices = i_input->m_local_matrices;
};
}; };
template <> template <>
@ -182,7 +191,7 @@ struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
SocketFlags::SocketFlagAffectsTime); SocketFlags::SocketFlagAffectsTime);
RegisterInput("Input", &node->i_input); RegisterInput("Input", &node->i_input);
RegisterOutput("Output", &node->o_output); RegisterOutput("Output", &node->i_output);
} }
}; };
@ -192,12 +201,6 @@ struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
struct AnimSamplerNode : public AnimNode { struct AnimSamplerNode : public AnimNode {
AnimData* o_output = nullptr; AnimData* o_output = nullptr;
std::string m_filename; std::string m_filename;
ozz::animation::SamplingCache m_sampling_cache;
ozz::animation::Animation* m_animation = nullptr;
virtual ~AnimSamplerNode();
virtual bool Init(AnimGraphContext& context) override;
virtual void Evaluate(AnimGraphContext& context) override;
}; };
template <> template <>
@ -218,7 +221,7 @@ struct MathAddNode : public AnimNode {
float* i_input1 = nullptr; float* i_input1 = nullptr;
float o_output = 0.f; float o_output = 0.f;
void Evaluate(AnimGraphContext& context) override { void Evaluate() override {
assert (i_input0 != nullptr); assert (i_input0 != nullptr);
assert (i_input1 != nullptr); assert (i_input1 != nullptr);
@ -246,7 +249,7 @@ struct MathFloatToVec3Node : public AnimNode {
float* i_input2 = nullptr; float* i_input2 = nullptr;
Vec3 o_output = {0.f, 0.f, 0.f}; Vec3 o_output = {0.f, 0.f, 0.f};
void Evaluate(AnimGraphContext& context) override { void Evaluate() 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);

View File

@ -27,23 +27,25 @@ 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_value.ptr != nullptr) {
if (socket.m_type == SocketType::SocketTypeBool) { if (socket.m_type == SocketType::SocketTypeBool) {
result["value"] = socket.m_value.flag; result["value"] = *reinterpret_cast<bool*>(socket.m_value.ptr);
} else if (socket.m_type == SocketType::SocketTypeAnimation) { } else if (socket.m_type == SocketType::SocketTypeAnimation) {
} else if (socket.m_type == SocketType::SocketTypeFloat) { } else if (socket.m_type == SocketType::SocketTypeFloat) {
result["value"] = socket.m_value.float_value; result["value"] = *reinterpret_cast<float*>(socket.m_value.ptr);
} else if (socket.m_type == SocketType::SocketTypeVec3) { } else if (socket.m_type == SocketType::SocketTypeVec3) {
result["value"][0] = socket.m_value.vec3[0]; Vec3& vec3 = *reinterpret_cast<Vec3*>(socket.m_value.ptr);
result["value"][1] = socket.m_value.vec3[1]; result["value"][0] = vec3[0];
result["value"][2] = socket.m_value.vec3[2]; result["value"][1] = vec3[1];
result["value"][2] = vec3[2];
} else if (socket.m_type == SocketType::SocketTypeQuat) { } else if (socket.m_type == SocketType::SocketTypeQuat) {
result["value"][0] = socket.m_value.quat[0]; Quat& quat = *reinterpret_cast<Quat*>(socket.m_value.ptr);
result["value"][1] = socket.m_value.quat[1]; result["value"][0] = quat[0];
result["value"][2] = socket.m_value.quat[2]; result["value"][1] = quat[1];
result["value"][3] = socket.m_value.quat[3]; result["value"][2] = quat[2];
result["value"][3] = quat[3];
} else if (socket.m_type == SocketType::SocketTypeString) { } else if (socket.m_type == SocketType::SocketTypeString) {
result["value"] = std::string(socket.m_value.str); result["value"] = *reinterpret_cast<std::string*>(socket.m_value.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;
@ -55,7 +57,7 @@ json sSocketToJson(const Socket& socket) {
Socket sJsonToSocket(const json& json_data) { Socket sJsonToSocket(const json& json_data) {
Socket result; Socket result;
result.m_type = SocketType::SocketTypeUndefined; result.m_type = SocketType::SocketTypeUndefined;
result.m_reference.ptr = nullptr; result.m_value.ptr = nullptr;
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"];
@ -127,30 +129,29 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
if (sSocketTypeToStr(property.m_type) == json_property["type"]) { if (sSocketTypeToStr(property.m_type) == json_property["type"]) {
if (property.m_type == SocketType::SocketTypeBool) { if (property.m_type == SocketType::SocketTypeBool) {
property.m_value.flag = json_property["value"]; result.m_socket_accessor->SetProperty<bool>(
property.m_name,
json_property["value"]);
} else if (property.m_type == SocketType::SocketTypeAnimation) { } else if (property.m_type == SocketType::SocketTypeAnimation) {
} else if (property.m_type == SocketType::SocketTypeFloat) { } else if (property.m_type == SocketType::SocketTypeFloat) {
property.m_value.float_value = json_property["value"]; result.m_socket_accessor->SetProperty<float>(
property.m_name,
json_property["value"]);
} else if (property.m_type == SocketType::SocketTypeVec3) { } else if (property.m_type == SocketType::SocketTypeVec3) {
property.m_value.vec3[0] = json_property["value"][0]; Vec3* property_vec3 = reinterpret_cast<Vec3*>(property.m_value.ptr);
property.m_value.vec3[1] = json_property["value"][1]; (*property_vec3)[0] = json_property["value"][0];
property.m_value.vec3[2] = json_property["value"][2]; (*property_vec3)[1] = json_property["value"][1];
(*property_vec3)[2] = json_property["value"][2];
} else if (property.m_type == SocketType::SocketTypeQuat) { } else if (property.m_type == SocketType::SocketTypeQuat) {
Quat* property_quat = reinterpret_cast<Quat*>(property.m_reference.ptr); Quat* property_quat = reinterpret_cast<Quat*>(property.m_value.ptr);
property.m_value.quat[0] = json_property["value"][0]; (*property_quat)[0] = json_property["value"][0];
property.m_value.quat[1] = json_property["value"][1]; (*property_quat)[1] = json_property["value"][1];
property.m_value.quat[2] = json_property["value"][2]; (*property_quat)[2] = json_property["value"][2];
property.m_value.quat[3] = json_property["value"][3]; (*property_quat)[3] = json_property["value"][3];
} else if (property.m_type == SocketType::SocketTypeString) { } else if (property.m_type == SocketType::SocketTypeString) {
std::string value_str = json_property["value"]; result.m_socket_accessor->SetProperty<std::string>(
size_t string_length = value_str.size(); property.m_name,
constexpr size_t string_max_length = sizeof(property.m_value.str) - 1; json_property["value"]);
if (string_length > string_max_length) {
std::cerr << "Warning: string '" << value_str << "' too long, truncating to " << string_max_length << " bytes." << std::endl;
string_length = string_max_length;
}
memcpy (property.m_value.str, value_str.data(), string_length);
property.m_value.str[string_length] = 0;
} else { } else {
std::cerr << "Invalid type for property '" << property.m_name std::cerr << "Invalid type for property '" << property.m_name
<< "'. Cannot parse json to type '" << "'. Cannot parse json to type '"
@ -344,7 +345,6 @@ AnimGraph AnimGraphResource::createInstance() const {
createRuntimeNodeInstances(result); createRuntimeNodeInstances(result);
prepareGraphIOData(result); prepareGraphIOData(result);
connectRuntimeNodes(result); connectRuntimeNodes(result);
setRuntimeNodeProperties(result);
result.updateOrderedNodes(); result.updateOrderedNodes();
result.reset(); result.reset();
@ -387,7 +387,7 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
int input_block_offset = 0; int input_block_offset = 0;
for (int i = 0; i < graph_inputs.size(); i++) { for (int i = 0; i < graph_inputs.size(); i++) {
graph_inputs[i].m_reference.ptr = graph_inputs[i].m_value.ptr =
(void*)&instance.m_input_buffer[input_block_offset]; (void*)&instance.m_input_buffer[input_block_offset];
input_block_offset += sizeof(void*); input_block_offset += sizeof(void*);
} }
@ -403,7 +403,7 @@ 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 = graph_outputs[i].m_value.ptr =
(void*)&instance.m_output_buffer[output_block_offset]; (void*)&instance.m_output_buffer[output_block_offset];
output_block_offset += graph_outputs[i].m_type_size; output_block_offset += graph_outputs[i].m_type_size;
} }
@ -486,8 +486,8 @@ void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
// //
// Wire up outputs to inputs. // Wire up outputs to inputs.
// //
(*target_socket->m_reference.ptr_ptr) = source_socket->m_reference.ptr; (*target_socket->m_value.ptr_ptr) = source_socket->m_value.ptr;
// (*source_socket->m_reference.ptr_ptr) = target_socket->m_reference.ptr; // (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr;
size_t target_node_index = target_node->m_index; size_t target_node_index = target_node->m_index;
@ -516,39 +516,3 @@ void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
} }
} }
void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
for (int i = 2; i < m_nodes.size(); i++) {
const AnimNodeResource& node_resource = m_nodes[i];
NodeSocketAccessorBase* node_instance_accessor =
AnimNodeAccessorFactory(node_resource.m_type_name, instance.m_nodes[i]);
std::vector<Socket>& resource_properties = node_resource.m_socket_accessor->m_properties;
for (size_t j = 0, n = resource_properties.size(); j < n; j++) {
const Socket& property = resource_properties[j];
const std::string& name = property.m_name;
switch (property.m_type) {
case SocketType::SocketTypeBool:
node_instance_accessor->SetPropertyReferenceValue<const bool&>(name, property.m_value.flag);
break;
case SocketType::SocketTypeFloat:
node_instance_accessor->SetPropertyReferenceValue(name, property.m_value.float_value);
break;
case SocketType::SocketTypeVec3:
node_instance_accessor->SetPropertyReferenceValue(name, property.m_value.vec3);
break;
case SocketType::SocketTypeQuat:
node_instance_accessor->SetPropertyReferenceValue(name, property.m_value.quat);
break;
case SocketType::SocketTypeString:
node_instance_accessor->SetPropertyReferenceValue(name, property.m_value.str);
break;
default:
std::cerr << "Invalid socket type " << static_cast<int>(property.m_type) << std::endl;
}
}
delete node_instance_accessor;
}
}

View File

@ -141,11 +141,9 @@ struct AnimGraphResource {
} }
AnimGraph createInstance() const; AnimGraph createInstance() const;
void createRuntimeNodeInstances(AnimGraph& instance) const; void createRuntimeNodeInstances(AnimGraph& instance) const;
void prepareGraphIOData(AnimGraph& instance) const; void prepareGraphIOData(AnimGraph& instance) const;
void connectRuntimeNodes(AnimGraph& instance) const; void connectRuntimeNodes(AnimGraph& instance) const;
void setRuntimeNodeProperties(AnimGraph& instance) const;
}; };
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H #endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H

View File

@ -2,36 +2,11 @@
// 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"
bool load_skeleton (ozz::animation::Skeleton& skeleton, const char* filename) {
assert(filename);
ozz::io::File file(filename, "rb");
if (!file.opened()) {
ozz::log::Err() << "Failed to open skeleton file " << filename << "."
<< std::endl;
return false;
}
ozz::io::IArchive archive(&file);
if (!archive.TestTag<ozz::animation::Skeleton>()) {
ozz::log::Err() << "Failed to load skeleton instance from file " << filename
<< "." << std::endl;
return false;
}
// Once the tag is validated, reading cannot fail.
archive >> skeleton;
return true;
}
TEST_CASE("BasicGraph", "[AnimGraphResource]") { TEST_CASE("BasicGraph", "[AnimGraphResource]") {
AnimGraphResource graph_resource; AnimGraphResource graph_resource;
@ -48,9 +23,7 @@ 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");
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_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";
@ -62,27 +35,30 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input0") == 0); REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input0") == 0);
REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input1") == 1); REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input1") == 1);
graph_resource.connectSockets(walk_node, "Output", blend_node, "Input0"); AnimGraphConnectionResource walk_to_blend;
graph_resource.connectSockets(run_node, "Output", blend_node, "Input1"); walk_to_blend.source_node_index = walk_node_index;
graph_resource.connectSockets( walk_to_blend.source_socket_name = "Output";
blend_node, walk_to_blend.target_node_index = blend_node_index;
"Output", walk_to_blend.target_socket_name = "Input0";
graph_resource.getGraphOutputNode(), graph_resource.m_connections.push_back(walk_to_blend);
"GraphOutput");
AnimGraphConnectionResource run_to_blend;
run_to_blend.source_node_index = run_node_index;
run_to_blend.source_socket_name = "Output";
run_to_blend.target_node_index = blend_node_index;
run_to_blend.target_socket_name = "Input1";
graph_resource.m_connections.push_back(run_to_blend);
AnimGraphConnectionResource blend_to_output;
blend_to_output.source_node_index = blend_node_index;
blend_to_output.source_socket_name = "Output";
blend_to_output.target_node_index = 0;
blend_to_output.target_socket_name = "GraphOutput";
graph_resource.m_connections.push_back(blend_to_output);
graph_resource.saveToFile("WalkGraph.animgraph.json"); graph_resource.saveToFile("WalkGraph.animgraph.json");
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.loadFromFile("WalkGraph.animgraph.json");
AnimGraph graph = graph_resource_loaded.createInstance(); AnimGraph graph = graph_resource.createInstance();
AnimGraphContext graph_context;
graph_context.m_graph = &graph;
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() == 5); REQUIRE(graph.m_nodes.size() == 5);
REQUIRE(graph.m_nodes[0]->m_node_type_name == "BlendTree"); REQUIRE(graph.m_nodes[0]->m_node_type_name == "BlendTree");
@ -121,40 +97,32 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
graph.m_node_output_connections[anim_sampler_index1][0].m_target_node graph.m_node_output_connections[anim_sampler_index1][0].m_target_node
== blend2_instance); == blend2_instance);
// Ensure animation sampler nodes use the correct files // Emulate evaluation
REQUIRE(anim_sampler_walk->m_filename == "data/walk.anim.ozz"); CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 5);
REQUIRE(anim_sampler_walk->m_animation != nullptr); graph.prepareNodeEval(walk_node_index);
graph.finishNodeEval(walk_node_index);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 4);
graph.prepareNodeEval(run_node_index);
graph.finishNodeEval(run_node_index);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 3);
graph.prepareNodeEval(blend_node_index);
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 2);
graph.finishNodeEval(blend_node_index);
CHECK(anim_sampler_walk->o_output == nullptr);
CHECK(anim_sampler_run->o_output == nullptr);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 4);
REQUIRE(anim_sampler_run->m_filename == "data/run.anim.ozz"); graph.prepareNodeEval(0);
REQUIRE(anim_sampler_run->m_animation != nullptr); const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
CHECK(blend2_instance->o_output == (*graph_output_socket->m_value.ptr_ptr));
WHEN("Emulating Graph Evaluation") { AnimData* graph_output =
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 5); static_cast<AnimData*>(*graph_output_socket->m_value.ptr_ptr);
graph.prepareNodeEval(walk_node_index); graph.finishNodeEval(0);
graph.finishNodeEval(walk_node_index); CHECK(blend2_instance->o_output == nullptr);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 4); CHECK(graph_output == (*graph_output_socket->m_value.ptr_ptr));
graph.prepareNodeEval(run_node_index); CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 5);
graph.finishNodeEval(run_node_index);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 3);
graph.prepareNodeEval(blend_node_index);
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 2);
graph.finishNodeEval(blend_node_index);
CHECK(anim_sampler_walk->o_output == nullptr);
CHECK(anim_sampler_run->o_output == nullptr);
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 4);
graph.prepareNodeEval(0);
const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
CHECK(blend2_instance->o_output == (*graph_output_socket->m_reference.ptr_ptr));
AnimData* graph_output =
static_cast<AnimData*>(*graph_output_socket->m_reference.ptr_ptr);
graph.finishNodeEval(0);
CHECK(blend2_instance->o_output == nullptr);
CHECK(graph_output == (*graph_output_socket->m_reference.ptr_ptr));
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 5);
}
} }
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") { TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
@ -182,8 +150,8 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
graph_resource_origin.clear(); graph_resource_origin.clear();
graph_resource_origin.m_name = "TestInputOutputGraph"; graph_resource_origin.m_name = "TestInputOutputGraph";
size_t float_to_vec3_node_index = graph_resource_origin.addNode( size_t float_to_vec3_node_index =
AnimNodeResourceFactory("MathFloatToVec3Node")); graph_resource_origin.addNode(AnimNodeResourceFactory("MathFloatToVec3Node"));
AnimNodeResource& graph_output_node = AnimNodeResource& graph_output_node =
graph_resource_origin.getGraphOutputNode(); graph_resource_origin.getGraphOutputNode();
@ -232,6 +200,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
graph_output_node, graph_output_node,
"GraphVec3Output")); "GraphVec3Output"));
WHEN("Saving and loading graph resource") { WHEN("Saving and loading graph resource") {
const char* filename = "ResourceSaveLoadGraphInputs.json"; const char* filename = "ResourceSaveLoadGraphInputs.json";
graph_resource_origin.saveToFile(filename); graph_resource_origin.saveToFile(filename);
@ -271,177 +240,25 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
AnimGraph anim_graph = graph_resource_loaded.createInstance(); AnimGraph anim_graph = graph_resource_loaded.createInstance();
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr); REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
REQUIRE( REQUIRE(anim_graph.getInputPtr("GraphFloatInput") == anim_graph.m_input_buffer);
anim_graph.getInputPtr("GraphFloatInput")
== anim_graph.m_input_buffer);
float* graph_float_input = nullptr; float* graph_float_input = nullptr;
graph_float_input = graph_float_input = static_cast<float*>(anim_graph.getInputPtr("GraphFloatInput"));
static_cast<float*>(anim_graph.getInputPtr("GraphFloatInput"));
*graph_float_input = 123.456f; *graph_float_input = 123.456f;
AND_WHEN("Evaluating Graph") {
AnimGraphContext context = {&anim_graph, nullptr};
anim_graph.updateTime(0.f);
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") {
CHECK(vec3_output[0] == *graph_float_input);
CHECK(vec3_output[1] == *graph_float_input);
CHECK(vec3_output[2] == *graph_float_input);
}
}
}
}
}
}
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
AnimGraphResource graph_resource_origin;
graph_resource_origin.clear();
graph_resource_origin.m_name = "TestInputOutputGraph";
size_t math_add0_node_index =
graph_resource_origin.addNode(AnimNodeResourceFactory("MathAddNode"));
size_t math_add1_node_index =
graph_resource_origin.addNode(AnimNodeResourceFactory("MathAddNode"));
AnimNodeResource& graph_output_node =
graph_resource_origin.getGraphOutputNode();
graph_output_node.m_socket_accessor->RegisterInput<float>(
"GraphFloat0Output",
nullptr);
graph_output_node.m_socket_accessor->RegisterInput<float>(
"GraphFloat1Output",
nullptr);
graph_output_node.m_socket_accessor->RegisterInput<float>(
"GraphFloat2Output",
nullptr);
AnimNodeResource& graph_input_node =
graph_resource_origin.getGraphInputNode();
graph_input_node.m_socket_accessor->RegisterOutput<float>(
"GraphFloatInput",
nullptr);
// Prepare graph inputs and outputs
AnimNodeResource& math_add0_node =
graph_resource_origin.m_nodes[math_add0_node_index];
AnimNodeResource& math_add1_node =
graph_resource_origin.m_nodes[math_add1_node_index];
// direct output
REQUIRE(graph_resource_origin.connectSockets(
graph_input_node,
"GraphFloatInput",
graph_output_node,
"GraphFloat0Output"));
// add0 node
REQUIRE(graph_resource_origin.connectSockets(
graph_input_node,
"GraphFloatInput",
math_add0_node,
"Input0"));
REQUIRE(graph_resource_origin.connectSockets(
graph_input_node,
"GraphFloatInput",
math_add0_node,
"Input1"));
REQUIRE(graph_resource_origin.connectSockets(
math_add0_node,
"Output",
graph_output_node,
"GraphFloat1Output"));
// add1 node
REQUIRE(graph_resource_origin.connectSockets(
math_add0_node,
"Output",
math_add1_node,
"Input0"));
REQUIRE(graph_resource_origin.connectSockets(
graph_input_node,
"GraphFloatInput",
math_add1_node,
"Input1"));
REQUIRE(graph_resource_origin.connectSockets(
math_add1_node,
"Output",
graph_output_node,
"GraphFloat2Output"));
WHEN("Saving and loading graph resource") {
const char* filename = "ResourceSaveLoadGraphInputs.json";
graph_resource_origin.saveToFile(filename);
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.loadFromFile(filename);
const AnimNodeResource& graph_loaded_output_node =
graph_resource_loaded.m_nodes[0];
const AnimNodeResource& graph_loaded_input_node =
graph_resource_loaded.m_nodes[1];
WHEN("Instantiating an AnimGraph") {
AnimGraph anim_graph = graph_resource_loaded.createInstance();
REQUIRE(anim_graph.getInputSocket("GraphFloatInput") != nullptr);
REQUIRE(
anim_graph.getInputPtr("GraphFloatInput")
== anim_graph.m_input_buffer);
float* graph_float_input = nullptr;
graph_float_input =
static_cast<float*>(anim_graph.getInputPtr("GraphFloatInput"));
*graph_float_input = 123.456f;
AND_WHEN("Evaluating Graph") {
AnimGraphContext context = {&anim_graph, nullptr};
anim_graph.updateTime(0.f); anim_graph.updateTime(0.f);
anim_graph.evaluate(context); anim_graph.evaluate();
Socket* float0_output_socket = anim_graph.evalOutputNode();
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); Socket* float_output_socket = anim_graph.getOutputSocket("GraphFloatOutput");
REQUIRE(float1_output_socket != nullptr); Socket* vec3_output_socket = anim_graph.getOutputSocket("GraphVec3Output");
REQUIRE(float2_output_socket != nullptr); Vec3& vec3_output = *static_cast<Vec3*>(vec3_output_socket->m_value.ptr);
float& float0_output = CHECK(vec3_output[0] == *graph_float_input);
*static_cast<float*>(float0_output_socket->m_reference.ptr); CHECK(vec3_output[1] == *graph_float_input);
float& float1_output = CHECK(vec3_output[2] == *graph_float_input);
*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") {
CHECK(float0_output == Approx(*graph_float_input));
CHECK(float1_output == Approx(*graph_float_input * 2.));
CHECK(float2_output == Approx(*graph_float_input * 3.));
}
} }
} }
} }
@ -541,7 +358,7 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]); dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
REQUIRE( REQUIRE(
*anim_graph.m_socket_accessor->m_outputs[0].m_reference.ptr_ptr *anim_graph.m_socket_accessor->m_outputs[0].m_value.ptr_ptr
== blend2_node->i_blend_weight); == blend2_node->i_blend_weight);
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput"); float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
REQUIRE(float_input_ptr == blend2_node->i_blend_weight); REQUIRE(float_input_ptr == blend2_node->i_blend_weight);
@ -679,7 +496,7 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
sampler_node sampler_node
== anim_graph.getAnimNodeForInput(speed_scale_node_index, "Input")); == anim_graph.getAnimNodeForInput(speed_scale_node_index, "Input"));
REQUIRE(speed_scale_node->o_output == blend2_node->i_input1); REQUIRE(speed_scale_node->i_output == blend2_node->i_input1);
REQUIRE( REQUIRE(
speed_scale_node speed_scale_node
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1")); == anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));