Added support of time updates for simple embedded graphs.
parent
0aebe44bd5
commit
76ea38f118
|
@ -32,8 +32,12 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
|
|||
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++) {
|
||||
if (node_input_connections[i].m_crosses_hierarchy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int input_node_index =
|
||||
GetAnimNodeIndex(node_input_connections.at(i).m_source_node);
|
||||
GetAnimNodeIndex(node_input_connections[i].m_source_node);
|
||||
|
||||
if (input_node_index == 1) {
|
||||
continue;
|
||||
|
@ -58,9 +62,11 @@ void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
|
|||
}
|
||||
}
|
||||
|
||||
void AnimGraphBlendTree::MarkActiveInputs() {
|
||||
void AnimGraphBlendTree::MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) {
|
||||
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
||||
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
|
||||
if (m_nodes[i]->m_tick_number != m_tick_number) {
|
||||
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<AnimGraphConnection>& graph_output_inputs =
|
||||
|
@ -69,15 +75,16 @@ void AnimGraphBlendTree::MarkActiveInputs() {
|
|||
const AnimGraphConnection& graph_input = graph_output_inputs[i];
|
||||
AnimNode* node = graph_input.m_source_node;
|
||||
if (node != nullptr) {
|
||||
node->m_tick_number = m_tick_number;
|
||||
node->m_state = AnimNodeEvalState::Activated;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
|
||||
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
|
||||
AnimNode* node = m_eval_ordered_nodes[i];
|
||||
if (checkIsNodeActive(node)) {
|
||||
node->MarkActiveInputs();
|
||||
if (CheckIsNodeActive(node)) {
|
||||
size_t node_index = GetAnimNodeIndex(node);
|
||||
node->MarkActiveInputs(m_node_input_connections[node_index]);
|
||||
|
||||
// Non-animation data inputs are always active.
|
||||
for (size_t j = 0, nj = m_node_input_connections[node_index].size();
|
||||
|
@ -95,14 +102,14 @@ void AnimGraphBlendTree::MarkActiveInputs() {
|
|||
}
|
||||
}
|
||||
|
||||
void AnimGraphBlendTree::CalcSyncTrack() {
|
||||
void AnimGraphBlendTree::CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) {
|
||||
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
|
||||
AnimNode* node = m_eval_ordered_nodes[i];
|
||||
if (node->m_state == AnimNodeEvalState::Deactivated) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node->CalcSyncTrack();
|
||||
node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,35 +120,21 @@ void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
|
|||
m_node_input_connections[0];
|
||||
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
|
||||
AnimNode* node = graph_output_inputs[i].m_source_node;
|
||||
if (node != nullptr) {
|
||||
node->UpdateTime(node->m_time_now, node->m_time_now + dt);
|
||||
if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) {
|
||||
node->UpdateTime(time_last, time_now);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; --i) {
|
||||
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; --i) {
|
||||
AnimNode* node = m_eval_ordered_nodes[i];
|
||||
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t node_index = GetAnimNodeIndex(node);
|
||||
float node_time_now = node->m_time_now;
|
||||
float node_time_last = node->m_time_last;
|
||||
|
||||
const std::vector<AnimGraphConnection>& node_input_connections =
|
||||
m_node_input_connections[node_index];
|
||||
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
|
||||
AnimNode* input_node = node_input_connections[i].m_source_node;
|
||||
|
||||
// Only propagate time updates via animation sockets.
|
||||
if (input_node != nullptr
|
||||
&& node_input_connections[i].m_target_socket.m_type
|
||||
== SocketType::SocketTypeAnimation
|
||||
&& input_node->m_state == AnimNodeEvalState::Activated) {
|
||||
input_node->UpdateTime(node_time_last, node_time_now);
|
||||
}
|
||||
}
|
||||
PropagateTimeToNodeInputs(node);
|
||||
}
|
||||
|
||||
m_state = AnimNodeEvalState::TimeUpdated;
|
||||
}
|
||||
|
||||
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
|
||||
|
@ -156,6 +149,27 @@ void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
|
|||
}
|
||||
}
|
||||
|
||||
void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) {
|
||||
size_t node_index = GetAnimNodeIndex(node);
|
||||
|
||||
float node_time_now = node->m_time_now;
|
||||
float node_time_last = node->m_time_last;
|
||||
|
||||
const std::vector<AnimGraphConnection>& node_input_connections =
|
||||
m_node_input_connections[node_index];
|
||||
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
|
||||
AnimNode* input_node = node_input_connections[i].m_source_node;
|
||||
|
||||
// Only propagate time updates via animation sockets.
|
||||
if (input_node != nullptr
|
||||
&& node_input_connections[i].m_target_socket.m_type
|
||||
== SocketType::SocketTypeAnimation
|
||||
&& input_node->m_state == AnimNodeEvalState::Activated) {
|
||||
input_node->UpdateTime(node_time_last, node_time_now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Socket* AnimGraphBlendTree::GetInputSocket(const std::string& name) {
|
||||
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
|
||||
AnimGraphConnection& connection = m_node_output_connections[1][i];
|
||||
|
|
|
@ -31,13 +31,19 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
|
||||
~AnimGraphBlendTree() override { dealloc(); }
|
||||
|
||||
void StartUpdateTick() { m_tick_number++; }
|
||||
|
||||
// AnimNode overrides
|
||||
bool Init(AnimGraphContext& context) override;
|
||||
void MarkActiveInputs() override;
|
||||
void CalcSyncTrack() override;
|
||||
void MarkActiveInputs(
|
||||
const std::vector<AnimGraphConnection>& input_connections) override;
|
||||
void CalcSyncTrack(
|
||||
const std::vector<AnimGraphConnection>& input_connections) override;
|
||||
void UpdateTime(float time_last, float time_now) override;
|
||||
void Evaluate(AnimGraphContext& context) override;
|
||||
|
||||
void PropagateTimeToNodeInputs(const AnimNode* node);
|
||||
|
||||
void dealloc() {
|
||||
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
|
||||
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
|
||||
|
@ -62,8 +68,8 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
|
||||
void UpdateOrderedNodes();
|
||||
void UpdateOrderedNodesRecursive(int node_index);
|
||||
bool checkIsNodeActive(AnimNode* node) {
|
||||
return node->m_state != AnimNodeEvalState::Deactivated;
|
||||
bool CheckIsNodeActive(AnimNode* node) {
|
||||
return node->m_tick_number == m_tick_number;
|
||||
}
|
||||
|
||||
void ResetNodeStates() {
|
||||
|
@ -117,9 +123,10 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
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;
|
||||
std::cerr
|
||||
<< "Error: cannot set output for direct graph input to graph "
|
||||
"output connections. Use GetOutptPtr for output instead!"
|
||||
<< std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -130,14 +137,18 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
// 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];
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +170,8 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
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 static_cast<float*>(
|
||||
*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +231,7 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
size_t GetAnimNodeIndex(AnimNode* node) {
|
||||
size_t GetAnimNodeIndex(const AnimNode* node) const {
|
||||
for (size_t i = 0; i < m_nodes.size(); i++) {
|
||||
if (m_nodes[i] == node) {
|
||||
return i;
|
||||
|
@ -230,5 +242,14 @@ struct AnimGraphBlendTree : public AnimNode {
|
|||
}
|
||||
};
|
||||
|
||||
//
|
||||
// BlendTreeSocketNode
|
||||
//
|
||||
struct BlendTreeSocketNode : public AnimNode {};
|
||||
|
||||
template <>
|
||||
struct NodeDescriptor<BlendTreeSocketNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(BlendTreeSocketNode* node_) {}
|
||||
};
|
||||
|
||||
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H
|
||||
|
|
|
@ -265,6 +265,7 @@ struct AnimGraphConnection {
|
|||
Socket m_source_socket;
|
||||
AnimNode* m_target_node = nullptr;
|
||||
Socket m_target_socket;
|
||||
bool m_crosses_hierarchy = false;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
#include <sstream>
|
||||
|
||||
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
||||
#include "AnimGraphBlendTreeResource.h"
|
||||
#include "AnimGraphResource.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "imgui.h"
|
||||
#include "imnodes.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
static AnimGraphBlendTreeResource sGraphGresource =
|
||||
AnimGraphBlendTreeResource();
|
||||
static AnimGraphResource sGraphGresource =
|
||||
AnimGraphResource();
|
||||
static bool sGraphLoadedThisFrame = false;
|
||||
|
||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||
|
@ -59,13 +59,13 @@ void NodeSocketEditor(Socket& socket) {
|
|||
}
|
||||
|
||||
void RemoveConnectionsForSocket(
|
||||
AnimGraphBlendTreeResource& graph_resource,
|
||||
AnimGraphResource& graph_resource,
|
||||
AnimNodeResource& node_resource,
|
||||
Socket& socket) {
|
||||
std::vector<AnimGraphConnectionResource>::iterator iter =
|
||||
graph_resource.m_connections.begin();
|
||||
std::vector<BlendTreeConnectionResource>::iterator iter =
|
||||
graph_resource.m_blend_tree_resource.m_connections.begin();
|
||||
|
||||
while (iter != graph_resource.m_connections.end()) {
|
||||
while (iter != graph_resource.m_blend_tree_resource.m_connections.end()) {
|
||||
// TODO adjust for refactor
|
||||
assert(false);
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
//
|
||||
|
||||
#include "AnimGraphNodes.h"
|
||||
#include "AnimGraphBlendTree.h"
|
||||
|
||||
#include "ozz/base/log.h"
|
||||
#include "ozz/animation/runtime/blending_job.h"
|
||||
#include "AnimGraphBlendTree.h"
|
||||
#include "ozz/animation/runtime/animation.h"
|
||||
#include "ozz/animation/runtime/blending_job.h"
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
#include "ozz/base/log.h"
|
||||
|
||||
AnimNode* AnimNodeFactory(const std::string& name) {
|
||||
AnimNode* result;
|
||||
|
@ -24,7 +24,7 @@ AnimNode* AnimNodeFactory(const std::string& name) {
|
|||
} else if (name == "BlendTree") {
|
||||
result = new AnimGraphBlendTree;
|
||||
} else if (name == "BlendTreeSockets") {
|
||||
result = new BlendTreeNode;
|
||||
result = new BlendTreeSocketNode;
|
||||
} else if (name == "MathAddNode") {
|
||||
result = new MathAddNode;
|
||||
} else if (name == "MathFloatToVec3Node") {
|
||||
|
@ -42,12 +42,39 @@ AnimNode* AnimNodeFactory(const std::string& name) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
NodeDescriptorBase* AnimNodeDescriptorFactory(
|
||||
const std::string& node_type_name,
|
||||
AnimNode* node) {
|
||||
if (node_type_name == "Blend2") {
|
||||
return CreateNodeDescriptor<Blend2Node>(node);
|
||||
} else if (node_type_name == "SpeedScale") {
|
||||
return CreateNodeDescriptor<SpeedScaleNode>(node);
|
||||
} else if (node_type_name == "AnimSampler") {
|
||||
return CreateNodeDescriptor<AnimSamplerNode>(node);
|
||||
} else if (node_type_name == "LockTranslationNode") {
|
||||
return CreateNodeDescriptor<LockTranslationNode>(node);
|
||||
} else if (node_type_name == "BlendTree") {
|
||||
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
|
||||
} else if (node_type_name == "BlendTreeSockets") {
|
||||
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
|
||||
} else if (node_type_name == "MathAddNode") {
|
||||
return CreateNodeDescriptor<MathAddNode>(node);
|
||||
} else if (node_type_name == "MathFloatToVec3Node") {
|
||||
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
|
||||
} else if (node_type_name == "ConstScalarNode") {
|
||||
return CreateNodeDescriptor<ConstScalarNode>(node);
|
||||
} else {
|
||||
std::cerr << "Invalid node type name " << node_type_name << "."
|
||||
<< std::endl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Blend2Node::Evaluate(AnimGraphContext& context) {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert (i_blend_weight != nullptr);
|
||||
assert (o_output != nullptr);
|
||||
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];
|
||||
|
@ -71,12 +98,10 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
|
|||
//
|
||||
// AnimSamplerNode
|
||||
//
|
||||
AnimSamplerNode::~AnimSamplerNode() noexcept {
|
||||
m_animation = nullptr;
|
||||
}
|
||||
AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; }
|
||||
|
||||
bool AnimSamplerNode::Init(AnimGraphContext& context) {
|
||||
assert (m_animation == nullptr);
|
||||
assert(m_animation == nullptr);
|
||||
assert(!m_filename.empty());
|
||||
|
||||
AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter;
|
||||
|
@ -103,14 +128,14 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
|
|||
context.m_animation_map[m_filename] = m_animation;
|
||||
}
|
||||
|
||||
assert (context.m_skeleton != nullptr);
|
||||
assert(context.m_skeleton != nullptr);
|
||||
m_sampling_context.Resize(context.m_skeleton->num_joints());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
|
||||
assert (o_output != nullptr);
|
||||
assert(o_output != nullptr);
|
||||
|
||||
ozz::animation::SamplingJob sampling_job;
|
||||
sampling_job.animation = m_animation;
|
||||
|
|
|
@ -7,23 +7,13 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimGraphData.h"
|
||||
#include "AnimNode.h"
|
||||
#include "SyncTrack.h"
|
||||
#include "ozz/animation/runtime/sampling_job.h"
|
||||
|
||||
struct AnimNode;
|
||||
|
||||
//
|
||||
// BlendTreeNode
|
||||
//
|
||||
struct BlendTreeNode : public AnimNode {};
|
||||
|
||||
template <>
|
||||
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(BlendTreeNode* node_) {}
|
||||
};
|
||||
|
||||
//
|
||||
// Blend2Node
|
||||
//
|
||||
|
@ -34,8 +24,9 @@ struct Blend2Node : public AnimNode {
|
|||
float* i_blend_weight = nullptr;
|
||||
bool m_sync_blend = false;
|
||||
|
||||
virtual void MarkActiveInputs() override {
|
||||
for (const auto & input : m_inputs) {
|
||||
void MarkActiveInputs(
|
||||
const std::vector<AnimGraphConnection>& input_connections) override {
|
||||
for (const auto& input : input_connections) {
|
||||
AnimNode* input_node = input.m_source_node;
|
||||
if (input_node == nullptr) {
|
||||
continue;
|
||||
|
@ -53,7 +44,7 @@ struct Blend2Node : public AnimNode {
|
|||
}
|
||||
}
|
||||
|
||||
virtual void Evaluate(AnimGraphContext& context) override;
|
||||
void Evaluate(AnimGraphContext& context) override;
|
||||
};
|
||||
|
||||
template <>
|
||||
|
@ -80,7 +71,6 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// SpeedScaleNode
|
||||
//
|
||||
|
@ -96,8 +86,8 @@ struct SpeedScaleNode : public AnimNode {
|
|||
}
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input != nullptr);
|
||||
assert (o_output != nullptr);
|
||||
assert(i_input != nullptr);
|
||||
assert(o_output != nullptr);
|
||||
|
||||
o_output->m_local_matrices = i_input->m_local_matrices;
|
||||
};
|
||||
|
@ -116,7 +106,6 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// AnimSamplerNode
|
||||
//
|
||||
|
@ -172,7 +161,6 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// ConstScalarNode
|
||||
//
|
||||
|
@ -180,9 +168,7 @@ struct ConstScalarNode : public AnimNode {
|
|||
float* o_value = nullptr;
|
||||
float value = 0.f;
|
||||
|
||||
virtual void Evaluate(AnimGraphContext& context){
|
||||
*o_value = value;
|
||||
};
|
||||
virtual void Evaluate(AnimGraphContext& context) { *o_value = value; };
|
||||
};
|
||||
|
||||
template <>
|
||||
|
@ -193,7 +179,6 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// MathAddNode
|
||||
//
|
||||
|
@ -203,8 +188,8 @@ struct MathAddNode : public AnimNode {
|
|||
float* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert(i_input0 != nullptr);
|
||||
assert(i_input1 != nullptr);
|
||||
|
||||
*o_output = *i_input0 + *i_input1;
|
||||
}
|
||||
|
@ -229,9 +214,9 @@ struct MathFloatToVec3Node : public AnimNode {
|
|||
Vec3* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert (i_input2 != nullptr);
|
||||
assert(i_input0 != nullptr);
|
||||
assert(i_input1 != nullptr);
|
||||
assert(i_input2 != nullptr);
|
||||
|
||||
o_output->v[0] = *i_input0;
|
||||
o_output->v[1] = *i_input1;
|
||||
|
@ -249,35 +234,10 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
AnimNode* AnimNodeFactory(const std::string& name);
|
||||
|
||||
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
|
||||
NodeDescriptorBase* AnimNodeDescriptorFactory(
|
||||
const std::string& node_type_name,
|
||||
AnimNode* node) {
|
||||
if (node_type_name == "Blend2") {
|
||||
return CreateNodeDescriptor<Blend2Node>(node);
|
||||
} else if (node_type_name == "SpeedScale") {
|
||||
return CreateNodeDescriptor<SpeedScaleNode>(node);
|
||||
} else if (node_type_name == "AnimSampler") {
|
||||
return CreateNodeDescriptor<AnimSamplerNode>(node);
|
||||
} else if (node_type_name == "LockTranslationNode") {
|
||||
return CreateNodeDescriptor<LockTranslationNode>(node);
|
||||
} else if (node_type_name == "BlendTree") {
|
||||
return CreateNodeDescriptor<BlendTreeNode>(node);
|
||||
} else if (node_type_name == "BlendTreeSockets") {
|
||||
return CreateNodeDescriptor<BlendTreeNode>(node);
|
||||
} else if (node_type_name == "MathAddNode") {
|
||||
return CreateNodeDescriptor<MathAddNode>(node);
|
||||
} else if (node_type_name == "MathFloatToVec3Node") {
|
||||
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
|
||||
} else if (node_type_name == "ConstScalarNode") {
|
||||
return CreateNodeDescriptor<ConstScalarNode>(node);
|
||||
} else {
|
||||
std::cerr << "Invalid node type name " << node_type_name << "."
|
||||
<< std::endl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
AnimNode* node);
|
||||
|
||||
#endif //ANIMTESTBED_ANIMGRAPHNODES_H
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
#include <fstream>
|
||||
|
||||
#include "3rdparty/json/json.hpp"
|
||||
|
||||
#include "AnimGraphBlendTree.h"
|
||||
#include "AnimGraphNodes.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// forward declarations
|
||||
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource);
|
||||
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource);
|
||||
static json sAnimGraphResourceBlendTreeToJson(
|
||||
const AnimGraphResource& anim_graph_resource);
|
||||
static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
const json& json_data,
|
||||
AnimGraphResource* result_graph_resource);
|
||||
|
||||
//
|
||||
// Socket <-> json
|
||||
|
@ -143,7 +145,6 @@ json sAnimGraphNodeToJson(
|
|||
}
|
||||
|
||||
if (node->m_node_type_name == "BlendTree") {
|
||||
|
||||
}
|
||||
|
||||
for (const auto& socket : node->m_socket_accessor->m_inputs) {
|
||||
|
@ -175,7 +176,6 @@ json sAnimGraphNodeToJson(
|
|||
AnimNodeResource* sAnimGraphNodeFromJson(
|
||||
const json& json_node,
|
||||
size_t node_index) {
|
||||
|
||||
std::string node_type = json_node["node_type"];
|
||||
|
||||
if (node_type == "BlendTree") {
|
||||
|
@ -247,20 +247,23 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson(
|
|||
return connection;
|
||||
}
|
||||
|
||||
static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_graph_resource) {
|
||||
static json sAnimGraphResourceBlendTreeToJson(
|
||||
const AnimGraphResource& anim_graph_resource) {
|
||||
json result;
|
||||
|
||||
result["name"] = anim_graph_resource.m_name;
|
||||
result["type"] = "AnimNodeResource";
|
||||
result["node_type"] = "BlendTree";
|
||||
|
||||
const BlendTreeResource& blend_tree_resource = anim_graph_resource.m_blend_tree_resource;
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
anim_graph_resource.m_blend_tree_resource;
|
||||
|
||||
for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) {
|
||||
const AnimNodeResource* node = blend_tree_resource.m_nodes[i];
|
||||
|
||||
if (node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* graph_resource = dynamic_cast<const AnimGraphResource*>(node);
|
||||
const AnimGraphResource* graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(node);
|
||||
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource);
|
||||
} else {
|
||||
result["nodes"][i] =
|
||||
|
@ -276,8 +279,7 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap
|
|||
|
||||
// Graph inputs and outputs
|
||||
{
|
||||
const AnimNodeResource* graph_output_node =
|
||||
blend_tree_resource.m_nodes[0];
|
||||
const AnimNodeResource* graph_output_node = blend_tree_resource.m_nodes[0];
|
||||
const std::vector<Socket> graph_inputs =
|
||||
graph_output_node->m_socket_accessor->m_inputs;
|
||||
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
||||
|
@ -294,8 +296,11 @@ static json sAnimGraphResourceBlendTreeToJson(const AnimGraphResource& anim_grap
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraphResource* result_graph_resource) {
|
||||
BlendTreeResource& blend_tree_resource = result_graph_resource->m_blend_tree_resource;
|
||||
static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
const json& json_data,
|
||||
AnimGraphResource* result_graph_resource) {
|
||||
BlendTreeResource& blend_tree_resource =
|
||||
result_graph_resource->m_blend_tree_resource;
|
||||
|
||||
result_graph_resource->m_graph_type_name = "BlendTree";
|
||||
result_graph_resource->m_node_type_name = "BlendTree";
|
||||
|
@ -351,14 +356,14 @@ static bool sAnimGraphResourceBlendTreeFromJson(const json& json_data, AnimGraph
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BlendTreeResource::ConnectSockets (
|
||||
bool BlendTreeResource::ConnectSockets(
|
||||
const AnimNodeResource* source_node,
|
||||
const std::string& source_socket_name,
|
||||
const AnimNodeResource* target_node,
|
||||
const std::string& target_socket_name) {
|
||||
size_t source_node_index = GetNodeIndex(source_node);
|
||||
size_t target_node_index = GetNodeIndex(target_node);
|
||||
|
||||
|
||||
if (source_node_index >= m_nodes.size()
|
||||
|| target_node_index >= m_nodes.size()) {
|
||||
std::cerr << "Cannot connect nodes: could not find nodes." << std::endl;
|
||||
|
@ -369,23 +374,29 @@ bool BlendTreeResource::ConnectSockets (
|
|||
Socket* target_socket;
|
||||
|
||||
if (target_node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* target_graph_resource = dynamic_cast<const AnimGraphResource*>(target_node);
|
||||
AnimNodeResource* graph_output_node = target_graph_resource->m_blend_tree_resource.GetGraphInputNode();
|
||||
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(target_socket_name.c_str());
|
||||
const AnimGraphResource* target_graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(target_node);
|
||||
AnimNodeResource* graph_output_node =
|
||||
target_graph_resource->m_blend_tree_resource.GetGraphInputNode();
|
||||
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(
|
||||
target_socket_name.c_str());
|
||||
} else {
|
||||
target_socket =
|
||||
target_node->m_socket_accessor->GetInputSocket(target_socket_name.c_str());
|
||||
target_socket = target_node->m_socket_accessor->GetInputSocket(
|
||||
target_socket_name.c_str());
|
||||
}
|
||||
|
||||
if (source_node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* source_graph_resource = dynamic_cast<const AnimGraphResource*>(source_node);
|
||||
AnimNodeResource* graph_output_node = source_graph_resource->m_blend_tree_resource.GetGraphOutputNode();
|
||||
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(source_socket_name.c_str());
|
||||
const AnimGraphResource* source_graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(source_node);
|
||||
AnimNodeResource* graph_output_node =
|
||||
source_graph_resource->m_blend_tree_resource.GetGraphOutputNode();
|
||||
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(
|
||||
source_socket_name.c_str());
|
||||
} else {
|
||||
source_socket =
|
||||
source_node->m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
|
||||
source_socket = source_node->m_socket_accessor->GetOutputSocket(
|
||||
source_socket_name.c_str());
|
||||
}
|
||||
|
||||
|
||||
if (source_socket == nullptr || target_socket == nullptr) {
|
||||
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
|
||||
return false;
|
||||
|
@ -443,7 +454,8 @@ bool AnimGraphResource::SaveToFile(const char* filename) const {
|
|||
return SaveStateMachineResourceToFile(filename);
|
||||
}
|
||||
|
||||
std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "." << std::endl;
|
||||
std::cerr << "Invalid AnimGraphResource type: " << m_graph_type_name << "."
|
||||
<< std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -452,8 +464,7 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
|
|||
const char* filename) const {
|
||||
json result;
|
||||
|
||||
result = sAnimGraphResourceBlendTreeToJson(
|
||||
*this);
|
||||
result = sAnimGraphResourceBlendTreeToJson(*this);
|
||||
|
||||
std::ofstream output_file;
|
||||
output_file.open(filename);
|
||||
|
@ -466,14 +477,18 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
|
|||
void AnimGraphResource::CreateBlendTreeInstance(
|
||||
AnimGraphBlendTree& result) const {
|
||||
if (m_node_type_name != "BlendTree") {
|
||||
std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
|
||||
<< m_graph_type_name << "'." << std::endl;
|
||||
std::cerr
|
||||
<< "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
|
||||
<< m_graph_type_name << "'." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
result.m_name = m_name;
|
||||
|
||||
CreateBlendTreeRuntimeNodeInstances(result);
|
||||
PrepareBlendTreeIOData(result);
|
||||
SetRuntimeNodeProperties(result);
|
||||
CreateBlendTreeConnectionInstances(result);
|
||||
|
||||
result.UpdateOrderedNodes();
|
||||
result.ResetNodeStates();
|
||||
|
@ -485,14 +500,19 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
|
|||
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
|
||||
|
||||
if (node_resource->m_node_type_name == "BlendTree") {
|
||||
AnimGraphResource* embedded_blend_tree_resource = dynamic_cast<AnimGraphResource*>(node_resource);
|
||||
AnimGraphResource* embedded_blend_tree_resource =
|
||||
dynamic_cast<AnimGraphResource*>(node_resource);
|
||||
assert(embedded_blend_tree_resource != nullptr);
|
||||
AnimGraphBlendTree* embedded_blend_tree = dynamic_cast<AnimGraphBlendTree*>(node);
|
||||
AnimGraphBlendTree* embedded_blend_tree =
|
||||
dynamic_cast<AnimGraphBlendTree*>(node);
|
||||
assert(embedded_blend_tree != nullptr);
|
||||
|
||||
embedded_blend_tree_resource->CreateBlendTreeInstance(*embedded_blend_tree);
|
||||
embedded_blend_tree_resource->m_socket_accessor->m_inputs = embedded_blend_tree->m_node_descriptor->m_outputs;
|
||||
embedded_blend_tree_resource->m_socket_accessor->m_outputs = embedded_blend_tree->m_node_descriptor->m_inputs;
|
||||
embedded_blend_tree_resource->CreateBlendTreeInstance(
|
||||
*embedded_blend_tree);
|
||||
embedded_blend_tree_resource->m_socket_accessor->m_inputs =
|
||||
embedded_blend_tree->m_node_descriptor->m_outputs;
|
||||
embedded_blend_tree_resource->m_socket_accessor->m_outputs =
|
||||
embedded_blend_tree->m_node_descriptor->m_inputs;
|
||||
}
|
||||
|
||||
node->m_name = node_resource->m_name;
|
||||
|
@ -574,16 +594,20 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||
instance.m_connection_data_storage = new char[connection_data_storage_size];
|
||||
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
||||
AnimGraphBlendTree& instance) const {
|
||||
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
||||
m_blend_tree_resource.m_nodes.size(),
|
||||
nullptr);
|
||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||
m_blend_tree_resource.m_nodes[i]->m_node_type_name,
|
||||
instance.m_nodes[i]);
|
||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||
m_blend_tree_resource.m_nodes[i]->m_node_type_name,
|
||||
instance.m_nodes[i]);
|
||||
|
||||
if (i > 1 && m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
|
||||
if (i > 1
|
||||
&& m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
|
||||
instance_node_descriptors[i]->m_inputs =
|
||||
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs;
|
||||
instance_node_descriptors[i]->m_outputs =
|
||||
|
@ -611,6 +635,72 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||
connection.target_socket_name.c_str());
|
||||
|
||||
AnimGraphConnection instance_connection;
|
||||
|
||||
if (source_node->m_node_type_name == "BlendTree") {
|
||||
// For embedded subgraphs we have to ensure two things:
|
||||
// 1. The parent graph target node must activate the source node within
|
||||
// the embedded subgraph.
|
||||
// 2. The parent graph target node must activate the AnimNode of the
|
||||
// parent graph which contains the embedded subgraph. Otherwise, the
|
||||
// embedded graph does not get evaluated.
|
||||
// For each case we have to add a connection here. First we insert the
|
||||
// default connection within the graph (i.e. for case 2). Then we alter
|
||||
// the source node such that it points to the node within the embedded
|
||||
// subgraph.
|
||||
AnimGraphConnection embedded_graph_activation_connection;
|
||||
embedded_graph_activation_connection.m_source_node = source_node;
|
||||
embedded_graph_activation_connection.m_source_socket = *source_socket;
|
||||
embedded_graph_activation_connection.m_target_node = target_node;
|
||||
embedded_graph_activation_connection.m_target_socket = *target_socket;
|
||||
instance.m_node_input_connections[connection.target_node_index].push_back(
|
||||
embedded_graph_activation_connection);
|
||||
instance.m_node_output_connections[connection.source_node_index]
|
||||
.push_back(embedded_graph_activation_connection);
|
||||
|
||||
AnimGraphResource* source_blend_tree_resource =
|
||||
dynamic_cast<AnimGraphResource*>(
|
||||
m_blend_tree_resource.m_nodes[connection.source_node_index]);
|
||||
AnimGraphBlendTree* source_blend_tree =
|
||||
dynamic_cast<AnimGraphBlendTree*>(source_node);
|
||||
|
||||
size_t source_blend_tree_output_node_index =
|
||||
source_blend_tree_resource->m_blend_tree_resource
|
||||
.GetNodeIndexForOutputSocket(connection.source_socket_name);
|
||||
source_node =
|
||||
source_blend_tree->m_nodes[source_blend_tree_output_node_index];
|
||||
instance_connection.m_crosses_hierarchy = true;
|
||||
} else if (target_node->m_node_type_name == "BlendTree") {
|
||||
// When a connection points to an embedded blend tree we have to ensure
|
||||
// that the embedded node knows about its connection partner in the parent
|
||||
// tree. This allows the embedded node to properly activate the node in
|
||||
// the parent graph.
|
||||
AnimGraphResource* target_blend_tree_resource =
|
||||
dynamic_cast<AnimGraphResource*>(
|
||||
m_blend_tree_resource.m_nodes[connection.target_node_index]);
|
||||
AnimGraphBlendTree* target_blend_tree =
|
||||
dynamic_cast<AnimGraphBlendTree*>(target_node);
|
||||
|
||||
size_t target_blend_tree_output_node_index =
|
||||
target_blend_tree_resource->m_blend_tree_resource
|
||||
.GetNodeIndexForInputSocket(connection.target_socket_name);
|
||||
target_node =
|
||||
target_blend_tree->m_nodes[target_blend_tree_output_node_index];
|
||||
|
||||
// Make sure that the embedded node input activates the parent graph node.
|
||||
std::vector<AnimGraphConnection>& embeded_target_node_inputs =
|
||||
target_blend_tree
|
||||
->m_node_input_connections[target_blend_tree_output_node_index];
|
||||
for (size_t j = 0; j < embeded_target_node_inputs.size(); j++) {
|
||||
AnimGraphConnection& embedded_target_connection =
|
||||
embeded_target_node_inputs[j];
|
||||
if (embedded_target_connection.m_source_socket.m_name
|
||||
== target_socket->m_name) {
|
||||
embedded_target_connection.m_source_node = source_node;
|
||||
embedded_target_connection.m_crosses_hierarchy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance_connection.m_source_node = source_node;
|
||||
instance_connection.m_source_socket = *source_socket;
|
||||
instance_connection.m_target_node = target_node;
|
||||
|
@ -685,8 +775,9 @@ void AnimGraphResource::SetRuntimeNodeProperties(
|
|||
for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
||||
const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i];
|
||||
|
||||
NodeDescriptorBase* node_instance_accessor =
|
||||
AnimNodeDescriptorFactory(node_resource->m_node_type_name, result.m_nodes[i]);
|
||||
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
||||
node_resource->m_node_type_name,
|
||||
result.m_nodes[i]);
|
||||
|
||||
std::vector<Socket>& resource_properties =
|
||||
node_resource->m_socket_accessor->m_properties;
|
||||
|
@ -741,7 +832,8 @@ bool AnimGraphResource::SaveStateMachineResourceToFile(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AnimGraphResource::LoadStateMachineResourceFromJson(nlohmann::json const& json_data) {
|
||||
bool AnimGraphResource::LoadStateMachineResourceFromJson(
|
||||
nlohmann::json const& json_data) {
|
||||
assert(false && "Not yet implemented");
|
||||
|
||||
return false;
|
||||
|
|
|
@ -100,6 +100,32 @@ struct BlendTreeResource {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t GetNodeIndexForOutputSocket(const std::string& socket_name) const {
|
||||
for (size_t i = 0; i < m_connections.size(); i++) {
|
||||
const BlendTreeConnectionResource& connection = m_connections[i];
|
||||
if (connection.target_node_index == 0 && connection.target_socket_name == socket_name) {
|
||||
return connection.source_node_index;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Error: could not find a node connected to output '" << socket_name << "'." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t GetNodeIndexForInputSocket(const std::string& socket_name) const {
|
||||
for (size_t i = 0; i < m_connections.size(); i++) {
|
||||
const BlendTreeConnectionResource& connection = m_connections[i];
|
||||
if (connection.source_node_index == 1 && connection.source_socket_name == socket_name) {
|
||||
return connection.target_node_index;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Error: could not find a node connected to input '" << socket_name << "'." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
struct StateMachineTransitionResources {
|
||||
|
@ -131,6 +157,7 @@ struct AnimGraphResource: AnimNodeResource {
|
|||
bool SaveBlendTreeResourceToFile(const char* filename) const;
|
||||
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
|
||||
void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const;
|
||||
void CreateBlendTreeConnectionInstances(AnimGraphBlendTree& instance) const;
|
||||
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
|
||||
|
||||
bool SaveStateMachineResourceToFile(const char* filename) const;
|
||||
|
|
|
@ -8,11 +8,11 @@ bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
|
|||
|
||||
}
|
||||
|
||||
void AnimGraphStateMachine::MarkActiveInputs() {
|
||||
void AnimGraphStateMachine::MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) {
|
||||
|
||||
}
|
||||
|
||||
void AnimGraphStateMachine::CalcSyncTrack() {
|
||||
void AnimGraphStateMachine::CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ struct AnimGraphStateMachine : public AnimNode {
|
|||
Transition* m_active_transition = nullptr;
|
||||
|
||||
bool Init(AnimGraphContext& context);
|
||||
void MarkActiveInputs() override;
|
||||
void CalcSyncTrack() override;
|
||||
void MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) override;
|
||||
void CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) override;
|
||||
void UpdateTime(float time_last, float time_now) override;
|
||||
void Evaluate(AnimGraphContext& context) override;
|
||||
};
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "SyncTrack.h"
|
||||
#include "AnimGraphData.h"
|
||||
#include "SyncTrack.h"
|
||||
|
||||
struct AnimNode;
|
||||
|
||||
|
@ -25,29 +25,35 @@ enum class AnimNodeEvalState {
|
|||
struct AnimNode {
|
||||
std::string m_name;
|
||||
std::string m_node_type_name;
|
||||
int m_tick_number = 0;
|
||||
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
|
||||
|
||||
float m_time_now = 0.f;
|
||||
float m_time_last = 0.f;
|
||||
SyncTrack m_sync_track;
|
||||
|
||||
std::vector<AnimGraphConnection> m_inputs;
|
||||
|
||||
virtual ~AnimNode() = default;
|
||||
|
||||
virtual bool Init(AnimGraphContext& context) { return true; };
|
||||
virtual bool Init(AnimGraphContext& context) {
|
||||
m_time_now = 0.f;
|
||||
m_time_last = 0.f;
|
||||
return true;
|
||||
};
|
||||
|
||||
virtual void MarkActiveInputs() {
|
||||
for (const auto & input : m_inputs) {
|
||||
virtual void MarkActiveInputs(
|
||||
const std::vector<AnimGraphConnection>& input_connections) {
|
||||
for (const auto& input : input_connections) {
|
||||
AnimNode* input_node = input.m_source_node;
|
||||
if (input_node != nullptr) {
|
||||
input_node->m_tick_number = m_tick_number;
|
||||
input_node->m_state = AnimNodeEvalState::Activated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void CalcSyncTrack() {
|
||||
for (const auto & input : m_inputs) {
|
||||
virtual void CalcSyncTrack(
|
||||
const std::vector<AnimGraphConnection>& input_connections) {
|
||||
for (const auto& input : input_connections) {
|
||||
AnimNode* input_node = input.m_source_node;
|
||||
if (input_node != nullptr
|
||||
&& input.m_source_socket.m_type == SocketType::SocketTypeAnimation
|
||||
|
|
|
@ -140,7 +140,9 @@ class EmbeddedBlendTreeGraphResource {
|
|||
BlendTreeResource* embedded_blend_tree_resource = nullptr;
|
||||
|
||||
size_t walk_node_index = -1;
|
||||
AnimNodeResource* walk_node = nullptr;
|
||||
AnimNodeResource* walk_node_resource = nullptr;
|
||||
size_t embedded_blend_tree_node_index = -1;
|
||||
size_t embedded_speed_scale_index = -1;
|
||||
|
||||
public:
|
||||
EmbeddedBlendTreeGraphResource() {
|
||||
|
@ -163,10 +165,11 @@ class EmbeddedBlendTreeGraphResource {
|
|||
// Parent AnimSampler
|
||||
parent_blend_tree_resource->m_nodes.push_back(
|
||||
AnimNodeResourceFactory("AnimSampler"));
|
||||
walk_node_index = parent_blend_tree_resource->m_nodes.size() - 1;
|
||||
|
||||
walk_node = parent_blend_tree_resource->m_nodes.back();
|
||||
walk_node->m_name = "WalkAnim";
|
||||
walk_node->m_socket_accessor->SetPropertyValue(
|
||||
walk_node_resource = parent_blend_tree_resource->m_nodes[walk_node_index];
|
||||
walk_node_resource->m_name = "WalkAnim";
|
||||
walk_node_resource->m_socket_accessor->SetPropertyValue(
|
||||
"Filename",
|
||||
std::string("media/Walking-loop.ozz"));
|
||||
|
||||
|
@ -175,7 +178,7 @@ class EmbeddedBlendTreeGraphResource {
|
|||
//
|
||||
parent_blend_tree_resource->m_nodes.push_back(
|
||||
AnimNodeResourceFactory("BlendTree"));
|
||||
size_t embedded_blend_tree_node_index =
|
||||
embedded_blend_tree_node_index =
|
||||
parent_blend_tree_resource->m_nodes.size() - 1;
|
||||
embedded_graph = dynamic_cast<AnimGraphResource*>(
|
||||
parent_blend_tree_resource->m_nodes.back());
|
||||
|
@ -201,9 +204,13 @@ class EmbeddedBlendTreeGraphResource {
|
|||
// Embedded: SpeedScale node
|
||||
embedded_blend_tree_resource->m_nodes.push_back(
|
||||
AnimNodeResourceFactory("SpeedScale"));
|
||||
embedded_speed_scale_index =
|
||||
embedded_blend_tree_resource->m_nodes.size() - 1;
|
||||
AnimNodeResource* embedded_speed_scale_resource =
|
||||
embedded_blend_tree_resource->m_nodes.back();
|
||||
embedded_speed_scale_resource->m_socket_accessor->SetInputValue("SpeedScale", 0.1f);
|
||||
embedded_blend_tree_resource->m_nodes[embedded_speed_scale_index];
|
||||
embedded_speed_scale_resource->m_socket_accessor->SetInputValue(
|
||||
"SpeedScale",
|
||||
0.1f);
|
||||
|
||||
// Embedded: setup connections
|
||||
embedded_blend_tree_resource->ConnectSockets(
|
||||
|
@ -219,7 +226,7 @@ class EmbeddedBlendTreeGraphResource {
|
|||
|
||||
// Parent: setup connections
|
||||
REQUIRE(parent_blend_tree_resource->ConnectSockets(
|
||||
walk_node,
|
||||
walk_node_resource,
|
||||
"Output",
|
||||
embedded_graph,
|
||||
"AnimInput"));
|
||||
|
@ -346,8 +353,8 @@ TEST_CASE_METHOD(
|
|||
AnimSamplerNode* anim_sampler_walk =
|
||||
dynamic_cast<AnimSamplerNode*>(anim_graph_blend_tree.m_nodes[2]);
|
||||
|
||||
BlendTreeNode* graph_output_node =
|
||||
dynamic_cast<BlendTreeNode*>(anim_graph_blend_tree.m_nodes[0]);
|
||||
BlendTreeSocketNode* graph_output_node =
|
||||
dynamic_cast<BlendTreeSocketNode*>(anim_graph_blend_tree.m_nodes[0]);
|
||||
|
||||
// check node input dependencies
|
||||
size_t anim_sampler_index =
|
||||
|
@ -937,7 +944,8 @@ TEST_CASE_METHOD(
|
|||
parent_blend_tree_resource->m_nodes.size()
|
||||
== parent_blend_tree_resource_loaded.m_nodes.size());
|
||||
for (size_t i = 0; i < parent_blend_tree_resource->m_nodes.size(); i++) {
|
||||
const AnimNodeResource* parent_node = parent_blend_tree_resource->m_nodes[i];
|
||||
const AnimNodeResource* parent_node =
|
||||
parent_blend_tree_resource->m_nodes[i];
|
||||
const AnimNodeResource* parent_node_loaded =
|
||||
parent_blend_tree_resource_loaded.m_nodes[i];
|
||||
|
||||
|
@ -949,7 +957,8 @@ TEST_CASE_METHOD(
|
|||
CHECK(
|
||||
parent_blend_tree_resource->m_connections.size()
|
||||
== parent_blend_tree_resource_loaded.m_connections.size());
|
||||
for (size_t i = 0; i < parent_blend_tree_resource->m_connections.size(); i++) {
|
||||
for (size_t i = 0; i < parent_blend_tree_resource->m_connections.size();
|
||||
i++) {
|
||||
const BlendTreeConnectionResource& parent_connection =
|
||||
parent_blend_tree_resource->m_connections[i];
|
||||
const BlendTreeConnectionResource& parent_connection_loaded =
|
||||
|
@ -1039,22 +1048,61 @@ TEST_CASE_METHOD(
|
|||
graph_context.m_skeleton = &skeleton;
|
||||
|
||||
blend_tree.Init(graph_context);
|
||||
// Marking of active inputs is not properly working as we do not properly
|
||||
// populate AnimNode::m_inputs.
|
||||
//
|
||||
// Here comes an iffy problem: How to mark nodes in a parent blend tree
|
||||
// as active?
|
||||
//
|
||||
// - Simplest would be if the AnimNodes don't distinguish between nodes within
|
||||
// the tree they are contained or in a possible parent (or nested) tree.
|
||||
// - But then how to propagate active connections across multiple layers?
|
||||
// - Therefore probably better to have a blend tree store which sockets are
|
||||
// active and use that within the graph.
|
||||
// - But then: AnimGraphConnection already stores raw AnimNode pointers of the
|
||||
// connected nodes. Still... populating them accross layers could be messy.
|
||||
blend_tree.MarkActiveInputs();
|
||||
blend_tree.UpdateTime(0.f, 0.1f);
|
||||
|
||||
const AnimSamplerNode* walk_node =
|
||||
dynamic_cast<AnimSamplerNode*>(blend_tree.m_nodes[walk_node_index]);
|
||||
const AnimGraphBlendTree* embedded_blend_tree_node =
|
||||
dynamic_cast<AnimGraphBlendTree*>(
|
||||
blend_tree.m_nodes[embedded_blend_tree_node_index]);
|
||||
const SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
||||
embedded_blend_tree_node->m_nodes[walk_node_index]);
|
||||
|
||||
blend_tree.StartUpdateTick();
|
||||
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
||||
REQUIRE(embedded_blend_tree_node->m_state == AnimNodeEvalState::Activated);
|
||||
REQUIRE(speed_scale_node->m_state == AnimNodeEvalState::Activated);
|
||||
REQUIRE(walk_node->m_state == AnimNodeEvalState::Activated);
|
||||
|
||||
float time_last = 0.f;
|
||||
float dt = 0.1f;
|
||||
blend_tree.UpdateTime(time_last, time_last + dt);
|
||||
CHECK(embedded_blend_tree_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
CHECK(speed_scale_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
CHECK(walk_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
|
||||
CHECK_THAT(
|
||||
walk_node->m_time_last,
|
||||
Catch::Matchers::WithinAbs(time_last, 0.001));
|
||||
CHECK_THAT(
|
||||
walk_node->m_time_now,
|
||||
Catch::Matchers::WithinAbs(
|
||||
time_last + dt * (*speed_scale_node->i_speed_scale),
|
||||
0.001));
|
||||
|
||||
blend_tree.Evaluate(graph_context);
|
||||
|
||||
WHEN("Updating the time a second time") {
|
||||
// Perform another update
|
||||
time_last = time_last + dt;
|
||||
dt = 0.3f;
|
||||
blend_tree.StartUpdateTick();
|
||||
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
||||
blend_tree.UpdateTime(time_last, time_last + dt);
|
||||
CHECK(embedded_blend_tree_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
CHECK(speed_scale_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
CHECK(walk_node->m_state == AnimNodeEvalState::TimeUpdated);
|
||||
|
||||
CHECK_THAT(
|
||||
walk_node->m_time_last,
|
||||
Catch::Matchers::WithinAbs(
|
||||
time_last * (*speed_scale_node->i_speed_scale),
|
||||
0.001));
|
||||
CHECK_THAT(
|
||||
walk_node->m_time_now,
|
||||
Catch::Matchers::WithinAbs(
|
||||
(time_last + dt) * (*speed_scale_node->i_speed_scale),
|
||||
0.001));
|
||||
}
|
||||
|
||||
graph_context.freeAnimations();
|
||||
}
|
Loading…
Reference in New Issue