Simple time propagation works.

AnimGraphEditor
Martin Felis 2022-02-22 22:03:26 +01:00
parent 87eb2ab7df
commit af5d27db85
3 changed files with 158 additions and 37 deletions

View File

@ -355,10 +355,11 @@ void AnimGraph::UpdateOrderedNodes() {
std::vector<int> node_index_stack; std::vector<int> node_index_stack;
node_index_stack.push_back(0); node_index_stack.push_back(0);
m_ordered_nodes.clear(); m_eval_ordered_nodes.clear();
while (node_index_stack.size() > 0) { while (node_index_stack.size() > 0) {
std::vector<AnimNodeInput>& node_inputs = m_node_inputs[node_index_stack.back()]; std::vector<AnimNodeInput>& node_inputs =
m_node_inputs[node_index_stack.back()];
node_index_stack.pop_back(); node_index_stack.pop_back();
for (size_t i = 0, n = node_inputs.size(); i < n; i++) { for (size_t i = 0, n = node_inputs.size(); i < n; i++) {
@ -369,8 +370,8 @@ void AnimGraph::UpdateOrderedNodes() {
int input_node_index = getAnimNodeIndex(input_node); int input_node_index = getAnimNodeIndex(input_node);
bool is_node_processed = false; bool is_node_processed = false;
for (size_t j = 0, m = m_ordered_nodes.size(); j < m; j++) { for (size_t j = 0, m = m_eval_ordered_nodes.size(); j < m; j++) {
if (m_ordered_nodes[j] == input_node) { if (m_eval_ordered_nodes[j] == input_node) {
is_node_processed = true; is_node_processed = true;
break; break;
} }
@ -380,31 +381,61 @@ void AnimGraph::UpdateOrderedNodes() {
continue; continue;
} }
m_ordered_nodes.push_back(input_node); m_eval_ordered_nodes.push_back(input_node);
node_index_stack.push_back(input_node_index); node_index_stack.push_back(input_node_index);
} }
} }
} }
void AnimGraph::MarkActiveNodes() { void AnimGraph::MarkActiveNodes() {
m_frame_counter++; for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
}
const std::vector<AnimNodeInput> graph_output_inputs = m_node_inputs[0]; const std::vector<AnimNodeInput> graph_output_inputs = m_node_inputs[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 = m_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (node != nullptr) { if (node != nullptr) {
node->m_frame_counter = m_frame_counter; node->m_state = AnimNodeEvalState::Activated;
} }
} }
for (size_t i = 0, n = m_ordered_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_ordered_nodes[i]; AnimNode* node = m_eval_ordered_nodes[i];
if (CheckIsNodeActive(node)) { if (CheckIsNodeActive(node)) {
int node_index = getAnimNodeIndex(node); int node_index = getAnimNodeIndex(node);
node->UpdateActiveInputFrameCounters(m_node_inputs[node_index]); node->MarkActiveInputs(m_node_inputs[node_index]);
} else { }
// Avoid active frame counter overrun. }
node->m_frame_counter = m_frame_counter - 1; }
void AnimGraph::UpdateTime(float dt) {
const std::vector<AnimNodeInput> graph_output_inputs = m_node_inputs[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node != nullptr) {
node->UpdateTime(node->m_time_now, node->m_time_now + dt);
}
}
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue;
}
int node_index = getAnimNodeIndex(node);
const std::vector<AnimNodeInput> node_inputs = m_node_inputs[node_index];
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
for (size_t i = 0, n = node_inputs.size(); i < n; i++) {
AnimNode* input_node = node_inputs[i].m_node;
if (input_node != nullptr
&& input_node->m_state == AnimNodeEvalState::Activated
&& node_inputs[i].m_type == SocketType::SocketTypeAnimation) {
input_node->UpdateTime(node_time_last, node_time_now);
}
} }
} }
} }
@ -427,4 +458,4 @@ void* AnimGraph::GetInput(const std::string& name) const {
return *(socket->m_value.ptr_ptr); return *(socket->m_value.ptr_ptr);
} }
} } // namespace AnimGraphCode

View File

@ -84,26 +84,42 @@ struct AnimNodeResource {
struct AnimNodeInput { struct AnimNodeInput {
AnimNode* m_node; AnimNode* m_node;
SocketType m_type = SocketType::SocketTypeUndefined;
std::string m_input_name; std::string m_input_name;
}; };
enum class AnimNodeEvalState {
Undefined,
Deactivated,
Activated,
SyncTrackUpdated,
TimeUpdated,
Evaluated
};
struct AnimNode { struct AnimNode {
virtual ~AnimNode(){}; virtual ~AnimNode(){};
virtual void UpdateActiveInputFrameCounters (const std::vector<AnimNodeInput>& inputs) { virtual void MarkActiveInputs(const std::vector<AnimNodeInput>& inputs) {
for (size_t i = 0, n = inputs.size(); i < n; i++) { for (size_t i = 0, n = inputs.size(); i < n; i++) {
if (inputs[i].m_node != nullptr) { AnimNode* input_node = inputs[i].m_node;
inputs[i].m_node->m_frame_counter = m_frame_counter; if (input_node != nullptr) {
input_node->m_state = AnimNodeEvalState::Activated;
} }
} }
} }
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
std::string m_name; std::string m_name;
std::string m_node_type_name; std::string m_node_type_name;
bool m_is_time_synced; float m_time_now = 0.f;
float m_time_now; float m_time_last = 0.f;
float m_time_last; AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
int m_frame_counter = 0;
SyncTrack m_sync_track; SyncTrack m_sync_track;
}; };
@ -321,7 +337,7 @@ struct Blend2Node : public AnimNode {
bool m_sync_blend = false; bool m_sync_blend = false;
virtual void UpdateActiveInputFrameCounters (const std::vector<AnimNodeInput>& inputs) override { virtual void MarkActiveInputs(const std::vector<AnimNodeInput>& inputs) override {
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_node; AnimNode* input_node = inputs[i].m_node;
if (input_node == nullptr) { if (input_node == nullptr) {
@ -329,12 +345,33 @@ struct Blend2Node : public AnimNode {
} }
if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) { if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) {
input_node->m_frame_counter = m_frame_counter; input_node->m_state = AnimNodeEvalState::Activated;
continue; continue;
} }
if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) { if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) {
input_node->m_frame_counter = m_frame_counter; input_node->m_state = AnimNodeEvalState::Activated;
continue;
}
}
}
virtual void UpdateTime(float dt, std::vector<AnimNodeInput>& 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; continue;
} }
} }
@ -373,6 +410,12 @@ struct SpeedScaleNode : public AnimNode {
AnimData m_input; AnimData m_input;
AnimData* m_output = nullptr; AnimData* m_output = nullptr;
float m_speed_scale = 0.f; float m_speed_scale = 0.f;
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_last + (time_now - time_last) * m_speed_scale;
m_state = AnimNodeEvalState::TimeUpdated;
}
}; };
template <> template <>
@ -553,16 +596,22 @@ struct AnimGraph {
void UpdateOrderedNodes(); void UpdateOrderedNodes();
void MarkActiveNodes(); void MarkActiveNodes();
bool CheckIsNodeActive(AnimNode* node) { bool CheckIsNodeActive(AnimNode* node) {
return node->m_frame_counter == m_frame_counter; return node->m_state != AnimNodeEvalState::Deactivated;
} }
void UpdateTime(float dt); void UpdateTime(float dt);
void Evaluate(); void Evaluate();
void Reset() {
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_last = 0.f;
m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
}
}
int m_frame_counter = 0;
AnimData m_local_transforms; AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes; std::vector<AnimNode*> m_nodes;
std::vector<AnimNode*> m_ordered_nodes; std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimNodeInput> > m_node_inputs; std::vector<std::vector<AnimNodeInput> > m_node_inputs;
NodeSocketAccessorBase* m_socket_accessor; NodeSocketAccessorBase* m_socket_accessor;
char* m_input_buffer = nullptr; char* m_input_buffer = nullptr;
@ -574,9 +623,9 @@ struct AnimGraph {
void* GetOutput(const std::string& name) const; void* GetOutput(const std::string& name) const;
void* GetInput(const std::string& name) const; void* GetInput(const std::string& name) const;
int getAnimNodeOrderIndex(const AnimNode* node) { int getNodeEvalOrderIndex(const AnimNode* node) {
for (size_t i = 0, n = m_ordered_nodes.size(); i < n; i++) { for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
if (m_ordered_nodes[i] == node) { if (m_eval_ordered_nodes[i] == node) {
return i; return i;
} }
} }
@ -641,6 +690,7 @@ struct AnimGraph {
node_resource.m_socket_accessor->m_inputs[j]; node_resource.m_socket_accessor->m_inputs[j];
AnimNodeInput input; AnimNodeInput input;
input.m_node = nullptr; input.m_node = nullptr;
input.m_type = input_socket.m_type;
input.m_input_name = input_socket.m_name; input.m_input_name = input_socket.m_name;
node_inputs.push_back(input); node_inputs.push_back(input);
@ -789,6 +839,7 @@ struct AnimGraph {
} }
result.UpdateOrderedNodes(); result.UpdateOrderedNodes();
result.Reset();
return result; return result;
} }

View File

@ -247,6 +247,9 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
graph_input_node.m_socket_accessor->RegisterOutput<float>( graph_input_node.m_socket_accessor->RegisterOutput<float>(
"GraphFloatInput", "GraphFloatInput",
nullptr); nullptr);
graph_input_node.m_socket_accessor->RegisterOutput<float>(
"SpeedScaleInput",
nullptr);
graph_input_node.m_socket_accessor->RegisterOutput<AnimData>( graph_input_node.m_socket_accessor->RegisterOutput<AnimData>(
"GraphAnimInput0", "GraphAnimInput0",
nullptr); nullptr);
@ -372,6 +375,12 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
blend2_node_resource, blend2_node_resource,
"Weight")); "Weight"));
REQUIRE(graph_resource.connectSockets(
graph_resource.getGraphInputNode(),
"SpeedScaleInput",
speed_scale_node_resource,
"SpeedScale"));
REQUIRE(graph_resource.connectSockets( REQUIRE(graph_resource.connectSockets(
graph_resource.getGraphInputNode(), graph_resource.getGraphInputNode(),
"GraphAnimInput0", "GraphAnimInput0",
@ -440,14 +449,14 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
// check ordering // check ordering
// //
REQUIRE( REQUIRE(
anim_graph.getAnimNodeOrderIndex(blend2_node) anim_graph.getNodeEvalOrderIndex(blend2_node)
< anim_graph.getAnimNodeOrderIndex(sampler_node)); < anim_graph.getNodeEvalOrderIndex(sampler_node));
REQUIRE( REQUIRE(
anim_graph.getAnimNodeOrderIndex(blend2_node) anim_graph.getNodeEvalOrderIndex(blend2_node)
< anim_graph.getAnimNodeOrderIndex(speed_scale_node)); < anim_graph.getNodeEvalOrderIndex(speed_scale_node));
REQUIRE( REQUIRE(
anim_graph.getAnimNodeOrderIndex(speed_scale_node) anim_graph.getNodeEvalOrderIndex(speed_scale_node)
< anim_graph.getAnimNodeOrderIndex(sampler_node)); < anim_graph.getNodeEvalOrderIndex(sampler_node));
} }
WHEN("Instantiating graph") { WHEN("Instantiating graph") {
@ -491,6 +500,36 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
REQUIRE(anim_graph.CheckIsNodeActive(sampler_node) == true); REQUIRE(anim_graph.CheckIsNodeActive(sampler_node) == true);
} }
} }
WHEN("Updating time with dt = 0.3f and speed scale = 1.0f") {
float* speed_scale_input =
reinterpret_cast<float*>(anim_graph.GetInput("SpeedScaleInput"));
*blend_weight_input = 0.1;
*speed_scale_input = 1.0f;
anim_graph.MarkActiveNodes();
anim_graph.UpdateTime(0.3f);
THEN ("Anim sampler node time now must be 0.3f") {
REQUIRE(sampler_node->m_time_now == Approx(0.3f));
}
}
WHEN("Updating time with dt = 0.3f and speed scale = 1.3f") {
float* speed_scale_input =
reinterpret_cast<float*>(anim_graph.GetInput("SpeedScaleInput"));
*blend_weight_input = 0.1;
*speed_scale_input = 1.3f;
anim_graph.MarkActiveNodes();
anim_graph.UpdateTime(0.3f);
THEN ("Anim sampler node time now must be 0.39f") {
REQUIRE(sampler_node->m_time_now == Approx(0.39f));
}
}
} }
} }
} }