Working on graph evaluations. WIP.
parent
b518220576
commit
2da07ef961
|
@ -13,6 +13,18 @@ bool AnimGraph::init(AnimGraphContext& context) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<AnimGraphConnection>& graph_outputs = 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);
|
||||
graph_anim_output->m_local_matrices.resize(
|
||||
context.m_skeleton->num_soa_joints());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -56,7 +68,7 @@ void AnimGraph::markActiveNodes() {
|
|||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
|
||||
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; i--) {
|
||||
AnimNode* node = m_eval_ordered_nodes[i];
|
||||
if (checkIsNodeActive(node)) {
|
||||
int node_index = node->m_index;
|
||||
|
@ -78,7 +90,9 @@ void AnimGraph::markActiveNodes() {
|
|||
}
|
||||
}
|
||||
|
||||
void AnimGraph::prepareNodeEval(size_t node_index) {
|
||||
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 =
|
||||
|
@ -88,9 +102,10 @@ void AnimGraph::prepareNodeEval(size_t node_index) {
|
|||
continue;
|
||||
}
|
||||
|
||||
assert (*output_connection.m_source_socket.m_reference.ptr_ptr == nullptr);
|
||||
|
||||
(*output_connection.m_source_socket.m_reference.ptr_ptr) =
|
||||
m_anim_data_work_buffer.peek();
|
||||
m_anim_data_work_buffer.pop();
|
||||
m_anim_data_work_buffer.allocate(graph_context);
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n;
|
||||
|
@ -117,13 +132,12 @@ void AnimGraph::finishNodeEval(size_t node_index) {
|
|||
continue;
|
||||
}
|
||||
|
||||
m_anim_data_work_buffer.push(
|
||||
static_cast<AnimData*>(input_connection.m_source_socket.m_reference.ptr));
|
||||
m_anim_data_work_buffer.free(static_cast<AnimData*>(
|
||||
input_connection.m_source_socket.m_reference.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 =
|
||||
|
@ -154,7 +168,11 @@ void AnimGraph::evalOutputNode() {
|
|||
graph_output_connection.m_source_socket.m_reference.ptr,
|
||||
graph_output_connection.m_target_socket.m_type_size);
|
||||
} else {
|
||||
// TODO: how to deal with anim data outputs?
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,14 +226,6 @@ void AnimGraph::updateTime(float dt) {
|
|||
}
|
||||
|
||||
void AnimGraph::evaluate(AnimGraphContext& context) {
|
||||
constexpr int eval_stack_size = 5;
|
||||
int eval_stack_index = eval_stack_size;
|
||||
AnimData eval_buffers[eval_stack_size];
|
||||
AnimData* eval_stack[eval_stack_size];
|
||||
for (size_t i = 0; i < eval_stack_size; i++) {
|
||||
eval_stack[i] = &eval_buffers[i];
|
||||
}
|
||||
|
||||
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
|
||||
AnimNode* node = m_eval_ordered_nodes[i];
|
||||
|
||||
|
@ -223,7 +233,7 @@ void AnimGraph::evaluate(AnimGraphContext& context) {
|
|||
continue;
|
||||
}
|
||||
|
||||
prepareNodeEval(node->m_index);
|
||||
prepareNodeEval(context, node->m_index);
|
||||
|
||||
node->Evaluate(context);
|
||||
|
||||
|
|
|
@ -9,30 +9,57 @@
|
|||
#include "AnimGraphNodes.h"
|
||||
|
||||
struct AnimDataWorkBuffer {
|
||||
std::vector<AnimData> m_eval_anim_data;
|
||||
std::vector<AnimData*> m_available_data;
|
||||
struct AnimDataList {
|
||||
AnimData* m_anim_data = nullptr;
|
||||
AnimDataList* next = nullptr;
|
||||
};
|
||||
|
||||
AnimDataWorkBuffer(size_t stack_size) {
|
||||
m_eval_anim_data.resize(stack_size);
|
||||
m_available_data.resize(stack_size);
|
||||
AnimDataList* m_anim_data_list = nullptr;
|
||||
size_t m_num_allocations = 0;
|
||||
|
||||
for (size_t i = 0; i < stack_size; i++) {
|
||||
m_available_data[i] = &m_eval_anim_data[i];
|
||||
~AnimDataWorkBuffer() {
|
||||
AnimDataList* list_node = m_anim_data_list;
|
||||
while (list_node != nullptr) {
|
||||
AnimDataList* current_node = list_node;
|
||||
list_node = list_node->next;
|
||||
//delete current_node->m_anim_data;
|
||||
current_node->m_anim_data = nullptr;
|
||||
delete current_node;
|
||||
}
|
||||
}
|
||||
|
||||
void push(AnimData* anim_data) {
|
||||
assert (m_available_data.size() < m_eval_anim_data.size());
|
||||
m_available_data.push_back(anim_data);
|
||||
AnimData* allocate(AnimGraphContext& graph_context) {
|
||||
if (m_anim_data_list == nullptr) {
|
||||
AnimData* result = new AnimData();
|
||||
result->m_local_matrices.resize(graph_context.m_skeleton->num_soa_joints());
|
||||
m_num_allocations++;
|
||||
return result;
|
||||
}
|
||||
|
||||
AnimData* result = m_anim_data_list->m_anim_data;
|
||||
m_anim_data_list = m_anim_data_list->next;
|
||||
delete m_anim_data_list;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pop() {
|
||||
assert (m_available_data.size() > 0);
|
||||
m_available_data.pop_back();
|
||||
void free(AnimData* anim_data) {
|
||||
AnimDataList* list_node = new AnimDataList;
|
||||
list_node->next = m_anim_data_list;
|
||||
list_node->m_anim_data = anim_data;
|
||||
m_anim_data_list = list_node;
|
||||
}
|
||||
|
||||
AnimData* peek() {
|
||||
return m_available_data.back();
|
||||
size_t size() {
|
||||
size_t result = 0;
|
||||
|
||||
AnimDataList* node = m_anim_data_list;
|
||||
while (node != nullptr) {
|
||||
result ++;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -53,7 +80,7 @@ struct AnimGraph {
|
|||
std::vector<Socket>& getGraphOutputs() { return m_socket_accessor->m_inputs; }
|
||||
std::vector<Socket>& getGraphInputs() { return m_socket_accessor->m_outputs; }
|
||||
|
||||
AnimDataWorkBuffer m_anim_data_work_buffer = AnimDataWorkBuffer(5);
|
||||
AnimDataWorkBuffer m_anim_data_work_buffer;
|
||||
|
||||
~AnimGraph() {
|
||||
delete[] m_input_buffer;
|
||||
|
@ -76,7 +103,7 @@ struct AnimGraph {
|
|||
}
|
||||
|
||||
void evalInputNode();
|
||||
void prepareNodeEval(size_t node_index);
|
||||
void prepareNodeEval(AnimGraphContext& graph_context, size_t node_index);
|
||||
void finishNodeEval(size_t node_index);
|
||||
void evalOutputNode();
|
||||
|
||||
|
@ -106,6 +133,16 @@ struct AnimGraph {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void* getOutputPtr(const std::string& name) const {
|
||||
const Socket* input_socket = getOutputSocket(name);
|
||||
if (input_socket != nullptr) {
|
||||
return input_socket->m_reference.ptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
int getNodeEvalOrderIndex(const AnimNode* node) {
|
||||
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
|
||||
if (m_eval_ordered_nodes[i] == node) {
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "SyncTrack.h"
|
||||
#include "ozz/base/containers/vector.h"
|
||||
#include <ozz/base/maths/soa_transform.h>
|
||||
#include "ozz/animation/runtime/skeleton.h"
|
||||
#include "ozz/animation/runtime/animation.h"
|
||||
|
||||
//
|
||||
// Data types
|
||||
|
@ -24,6 +26,18 @@ struct AnimGraph;
|
|||
struct AnimGraphContext {
|
||||
AnimGraph* m_graph = nullptr;
|
||||
ozz::animation::Skeleton* m_skeleton = nullptr;
|
||||
|
||||
typedef std::map<std::string, ozz::animation::Animation*> AnimationFileMap;
|
||||
AnimationFileMap m_animation_map;
|
||||
|
||||
void freeAnimations() {
|
||||
AnimationFileMap::iterator animation_map_iter = m_animation_map.begin();
|
||||
|
||||
while (animation_map_iter != m_animation_map.end()) {
|
||||
delete animation_map_iter->second;
|
||||
animation_map_iter++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimData {
|
||||
|
@ -66,7 +80,7 @@ struct Socket {
|
|||
void* ptr;
|
||||
void** ptr_ptr;
|
||||
};
|
||||
SocketReference m_reference;
|
||||
SocketReference m_reference = { 0 };
|
||||
int m_flags = 0;
|
||||
size_t m_type_size = 0;
|
||||
};
|
||||
|
|
|
@ -33,39 +33,43 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
|
|||
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;
|
||||
|
||||
AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter;
|
||||
animation_map_iter = context.m_animation_map.find(m_filename);
|
||||
if (animation_map_iter != context.m_animation_map.end()) {
|
||||
m_animation = animation_map_iter->second;
|
||||
} else {
|
||||
m_animation = new ozz::animation::Animation();
|
||||
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;
|
||||
}
|
||||
|
||||
context.m_animation_map[m_filename] = m_animation;
|
||||
}
|
||||
|
||||
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);
|
||||
m_sampling_cache.Resize(context.m_skeleton->num_joints());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -486,8 +486,9 @@ void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
|
|||
//
|
||||
// Wire up outputs to inputs.
|
||||
//
|
||||
(*target_socket->m_reference.ptr_ptr) = source_socket->m_reference.ptr;
|
||||
// (*source_socket->m_reference.ptr_ptr) = target_socket->m_reference.ptr;
|
||||
if (target_socket->m_type != SocketType::SocketTypeAnimation) {
|
||||
(*target_socket->m_reference.ptr_ptr) = source_socket->m_reference.ptr;
|
||||
}
|
||||
|
||||
size_t target_node_index = target_node->m_index;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ struct SimpleAnimFixture {
|
|||
bone0_translations.push_back(translation_key);
|
||||
|
||||
translation_key.time = 1.f;
|
||||
translation_key.value = ozz::math::Float3(1.f, 6.f, 9.f);
|
||||
translation_key.value = ozz::math::Float3(1.f, 0.f, 9.f);
|
||||
bone0_translations.push_back(translation_key);
|
||||
|
||||
bone0_track.translations = bone0_translations;
|
||||
|
@ -119,3 +119,85 @@ TEST_CASE_METHOD(
|
|||
CHECK(
|
||||
sampled_translation.z[0] == Approx(translation_key.value.z).margin(0.01));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(
|
||||
SimpleAnimFixture,
|
||||
"AnimGraphSimpleEval",
|
||||
"[AnimGraphEvalTests]") {
|
||||
AnimGraphResource graph_resource;
|
||||
|
||||
// Add nodes
|
||||
size_t trans_x_node_index =
|
||||
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||
size_t trans_y_node_index =
|
||||
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||
size_t blend_node_index =
|
||||
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
||||
|
||||
// Setup nodes
|
||||
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_name = "trans_x";
|
||||
|
||||
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_name = "trans_y";
|
||||
|
||||
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
||||
blend_node.m_name = "BlendWalkRun";
|
||||
|
||||
// Setup graph outputs and inputs
|
||||
AnimNodeResource& graph_output_node = graph_resource.getGraphOutputNode();
|
||||
graph_output_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
|
||||
|
||||
AnimNodeResource& graph_input_node =
|
||||
graph_resource.getGraphInputNode();
|
||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
||||
"GraphFloatInput",
|
||||
nullptr);
|
||||
|
||||
// Wire up nodes
|
||||
graph_resource.connectSockets(trans_x_node, "Output", blend_node, "Input0");
|
||||
graph_resource.connectSockets(trans_y_node, "Output", blend_node, "Input1");
|
||||
graph_resource.connectSockets(
|
||||
blend_node,
|
||||
"Output",
|
||||
graph_resource.getGraphOutputNode(),
|
||||
"GraphOutput");
|
||||
REQUIRE(graph_resource.connectSockets(graph_input_node, "GraphFloatInput", blend_node, "Weight"));
|
||||
|
||||
// Prepare animation maps
|
||||
AnimGraphContext graph_context;
|
||||
graph_context.m_skeleton = skeleton.get();
|
||||
graph_context.m_animation_map["trans_x"] = animation_translate_x.get();
|
||||
graph_context.m_animation_map["trans_y"] = animation_translate_y.get();
|
||||
|
||||
// Instantiate graph
|
||||
AnimGraph graph = graph_resource.createInstance();
|
||||
graph_context.m_graph = &graph;
|
||||
graph.init(graph_context);
|
||||
|
||||
// Get runtime graph inputs and outputs
|
||||
float* graph_float_input = nullptr;
|
||||
graph_float_input =
|
||||
static_cast<float*>(graph.getInputPtr("GraphFloatInput"));
|
||||
|
||||
Socket* anim_output_socket =
|
||||
graph.getOutputSocket("GraphOutput");
|
||||
|
||||
AnimData* graph_anim_output = static_cast<AnimData*>(graph.getOutputPtr("GraphOutput"));
|
||||
|
||||
// Evaluate graph
|
||||
*graph_float_input = 0.1f;
|
||||
|
||||
graph.markActiveNodes();
|
||||
CHECK(graph.m_nodes[trans_x_node_index]->m_state == AnimNodeEvalState::Activated);
|
||||
CHECK(graph.m_nodes[trans_y_node_index]->m_state == AnimNodeEvalState::Activated);
|
||||
CHECK(graph.m_nodes[blend_node_index]->m_state == AnimNodeEvalState::Activated);
|
||||
|
||||
graph.updateTime(0.5f);
|
||||
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.y[0] == Approx(0.05).margin(0.01));
|
||||
}
|
|
@ -129,23 +129,31 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||
REQUIRE(anim_sampler_run->m_animation != nullptr);
|
||||
|
||||
WHEN("Emulating Graph Evaluation") {
|
||||
CHECK(graph.m_anim_data_work_buffer.m_available_data.size() == 5);
|
||||
graph.prepareNodeEval(walk_node_index);
|
||||
CHECK(graph.m_anim_data_work_buffer.size() == 0);
|
||||
graph.prepareNodeEval(graph_context, 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);
|
||||
CHECK(graph.m_anim_data_work_buffer.m_num_allocations == 1);
|
||||
CHECK(graph.m_anim_data_work_buffer.size() == 0);
|
||||
|
||||
graph.prepareNodeEval(graph_context, 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(graph.m_anim_data_work_buffer.m_num_allocations == 2);
|
||||
CHECK(graph.m_anim_data_work_buffer.size() == 0);
|
||||
|
||||
graph.prepareNodeEval(graph_context, 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);
|
||||
CHECK(graph.m_anim_data_work_buffer.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_work_buffer.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_work_buffer.m_available_data.size() == 4);
|
||||
CHECK(graph.m_anim_data_work_buffer.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_work_buffer.size() == 2);
|
||||
|
||||
graph.prepareNodeEval(0);
|
||||
// TODO: rethink output node evaluation
|
||||
graph.prepareNodeEval(graph_context, 0);
|
||||
const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
|
||||
CHECK(blend2_instance->o_output == (*graph_output_socket->m_reference.ptr_ptr));
|
||||
AnimData* graph_output =
|
||||
|
@ -153,8 +161,11 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||
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);
|
||||
CHECK(graph.m_anim_data_work_buffer.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_work_buffer.size() == 3);
|
||||
}
|
||||
|
||||
graph_context.freeAnimations();
|
||||
}
|
||||
|
||||
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
||||
|
@ -282,7 +293,8 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||
*graph_float_input = 123.456f;
|
||||
|
||||
AND_WHEN("Evaluating Graph") {
|
||||
AnimGraphContext context = {&anim_graph, nullptr};
|
||||
AnimGraphContext context;
|
||||
context.m_graph = &anim_graph;
|
||||
|
||||
anim_graph.updateTime(0.f);
|
||||
anim_graph.evaluate(context);
|
||||
|
@ -299,6 +311,8 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||
CHECK(vec3_output[1] == *graph_float_input);
|
||||
CHECK(vec3_output[2] == *graph_float_input);
|
||||
}
|
||||
|
||||
context.freeAnimations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +428,8 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||
*graph_float_input = 123.456f;
|
||||
|
||||
AND_WHEN("Evaluating Graph") {
|
||||
AnimGraphContext context = {&anim_graph, nullptr};
|
||||
AnimGraphContext context;
|
||||
context.m_graph = &anim_graph;
|
||||
|
||||
anim_graph.updateTime(0.f);
|
||||
anim_graph.evaluate(context);
|
||||
|
@ -442,6 +457,8 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||
CHECK(float1_output == Approx(*graph_float_input * 2.));
|
||||
CHECK(float2_output == Approx(*graph_float_input * 3.));
|
||||
}
|
||||
|
||||
context.freeAnimations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue