Added node ordering code and tests.
parent
bd304bde4e
commit
da916a7346
|
@ -185,7 +185,7 @@ void AnimGraphEditorUpdate() {
|
||||||
GenerateInputAttributeId(i, j),
|
GenerateInputAttributeId(i, j),
|
||||||
sGetSocketShapeFromSocketType(socket.m_type),
|
sGetSocketShapeFromSocketType(socket.m_type),
|
||||||
socket_color);
|
socket_color);
|
||||||
ImGui::Text(socket.m_name.c_str());
|
ImGui::TextUnformatted(socket.m_name.c_str());
|
||||||
|
|
||||||
ImNodes::PushAttributeFlag(
|
ImNodes::PushAttributeFlag(
|
||||||
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
||||||
|
@ -201,7 +201,7 @@ void AnimGraphEditorUpdate() {
|
||||||
GenerateOutputAttributeId(i, j),
|
GenerateOutputAttributeId(i, j),
|
||||||
sGetSocketShapeFromSocketType(socket.m_type),
|
sGetSocketShapeFromSocketType(socket.m_type),
|
||||||
ImColor(255, 255, 255, 255));
|
ImColor(255, 255, 255, 255));
|
||||||
ImGui::Text(socket.m_name.c_str());
|
ImGui::TextUnformatted(socket.m_name.c_str());
|
||||||
ImNodes::PushAttributeFlag(
|
ImNodes::PushAttributeFlag(
|
||||||
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
||||||
ImNodes::EndInputAttribute();
|
ImNodes::EndInputAttribute();
|
||||||
|
|
|
@ -351,6 +351,41 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimGraph::UpdateOrderedNodes() {
|
||||||
|
std::vector<int> node_index_stack;
|
||||||
|
node_index_stack.push_back(0);
|
||||||
|
|
||||||
|
m_ordered_nodes.clear();
|
||||||
|
|
||||||
|
while (node_index_stack.size() > 0) {
|
||||||
|
std::vector<AnimNodeInput>& node_inputs = m_node_inputs[node_index_stack.back()];
|
||||||
|
node_index_stack.pop_back();
|
||||||
|
|
||||||
|
for (size_t i = 0, n = node_inputs.size(); i < n; i++) {
|
||||||
|
AnimNode* input_node = node_inputs[i].m_node;
|
||||||
|
if (input_node == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_node_index = getAnimNodeIndex(input_node);
|
||||||
|
bool is_node_processed = false;
|
||||||
|
for (size_t j = 0, m = m_ordered_nodes.size(); j < m; j++) {
|
||||||
|
if (m_ordered_nodes[j] == input_node) {
|
||||||
|
is_node_processed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_node_processed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ordered_nodes.push_back(input_node);
|
||||||
|
node_index_stack.push_back(input_node_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AnimGraph::MarkActiveNodes() {
|
void AnimGraph::MarkActiveNodes() {
|
||||||
m_frame_counter++;
|
m_frame_counter++;
|
||||||
|
|
||||||
|
|
|
@ -81,9 +81,23 @@ struct AnimNodeResource {
|
||||||
float m_position[2] = {0.f, 0.f};
|
float m_position[2] = {0.f, 0.f};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct AnimNodeInput {
|
||||||
|
AnimNode* m_node;
|
||||||
|
std::string m_input_name;
|
||||||
|
};
|
||||||
|
|
||||||
struct AnimNode {
|
struct AnimNode {
|
||||||
virtual ~AnimNode(){};
|
virtual ~AnimNode(){};
|
||||||
|
|
||||||
|
virtual void UpdateActiveInputFrameCounters (const std::vector<AnimNodeInput>& inputs) {
|
||||||
|
for (size_t i = 0, n = inputs.size(); i < n; i++) {
|
||||||
|
if (inputs[i].m_node != nullptr) {
|
||||||
|
inputs[i].m_node->m_frame_counter = m_frame_counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
bool m_is_time_synced;
|
||||||
|
@ -93,10 +107,6 @@ struct AnimNode {
|
||||||
SyncTrack m_sync_track;
|
SyncTrack m_sync_track;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimNodeInput {
|
|
||||||
AnimNode* m_node;
|
|
||||||
std::string m_input_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NodeSocketAccessorBase {
|
struct NodeSocketAccessorBase {
|
||||||
NodeSocketAccessorBase() {}
|
NodeSocketAccessorBase() {}
|
||||||
|
@ -521,6 +531,7 @@ struct AnimGraph {
|
||||||
delete m_socket_accessor;
|
delete m_socket_accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateOrderedNodes();
|
||||||
void MarkActiveNodes();
|
void MarkActiveNodes();
|
||||||
bool CheckNodeActive(int node_index) {
|
bool CheckNodeActive(int node_index) {
|
||||||
assert(node_index < m_nodes.size());
|
assert(node_index < m_nodes.size());
|
||||||
|
@ -533,6 +544,7 @@ struct AnimGraph {
|
||||||
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<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;
|
||||||
|
@ -544,6 +556,15 @@ 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) {
|
||||||
|
for (size_t i = 0, n = m_ordered_nodes.size(); i < n; i++) {
|
||||||
|
if (m_ordered_nodes[i] == node) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
AnimNode* getAnimNodeForInput(
|
AnimNode* getAnimNodeForInput(
|
||||||
size_t node_index,
|
size_t node_index,
|
||||||
const std::string& input_name) {
|
const std::string& input_name) {
|
||||||
|
@ -749,6 +770,8 @@ struct AnimGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.UpdateOrderedNodes();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -269,7 +269,7 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
||||||
|
|
||||||
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
||||||
|
|
||||||
THEN ("Writing to the input pointer changes the value of the output.") {
|
THEN("Writing to the input pointer changes the value of the output.") {
|
||||||
float* float_input_ptr = (float*)anim_graph.GetInput("GraphFloatInput");
|
float* float_input_ptr = (float*)anim_graph.GetInput("GraphFloatInput");
|
||||||
REQUIRE(float_input_ptr != nullptr);
|
REQUIRE(float_input_ptr != nullptr);
|
||||||
*float_input_ptr = 23.123f;
|
*float_input_ptr = 23.123f;
|
||||||
|
@ -282,8 +282,10 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Connecting adding a Blend2 node") {
|
WHEN("Connecting adding a Blend2 node") {
|
||||||
size_t blend2_node_index = graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
size_t blend2_node_index =
|
||||||
AnimNodeResource& blend2_node_resource = graph_resource.m_nodes[blend2_node_index];
|
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
||||||
|
AnimNodeResource& blend2_node_resource =
|
||||||
|
graph_resource.m_nodes[blend2_node_index];
|
||||||
|
|
||||||
REQUIRE(graph_resource.connectSockets(
|
REQUIRE(graph_resource.connectSockets(
|
||||||
graph_resource.getGraphInputNode(),
|
graph_resource.getGraphInputNode(),
|
||||||
|
@ -291,17 +293,21 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
||||||
blend2_node_resource,
|
blend2_node_resource,
|
||||||
"Weight"));
|
"Weight"));
|
||||||
|
|
||||||
THEN ("Connected float input points to the blend weight.") {
|
THEN("Connected float input points to the blend weight.") {
|
||||||
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
||||||
Blend2Node* blend2_node = dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
Blend2Node* blend2_node =
|
||||||
|
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||||
|
|
||||||
REQUIRE (*anim_graph.m_socket_accessor->m_outputs[0].m_value.ptr_ptr == &blend2_node->m_blend_weight);
|
REQUIRE(
|
||||||
|
*anim_graph.m_socket_accessor->m_outputs[0].m_value.ptr_ptr
|
||||||
|
== &blend2_node->m_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->m_blend_weight);
|
REQUIRE(float_input_ptr == &blend2_node->m_blend_weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WHEN(
|
||||||
WHEN ("Connecting AnimData inputs to blend2 node and blend2 output to graph output.") {
|
"Connecting AnimData inputs to blend2 node and blend2 output to graph "
|
||||||
|
"output.") {
|
||||||
REQUIRE(graph_resource.connectSockets(
|
REQUIRE(graph_resource.connectSockets(
|
||||||
graph_resource.getGraphInputNode(),
|
graph_resource.getGraphInputNode(),
|
||||||
"GraphAnimInput0",
|
"GraphAnimInput0",
|
||||||
|
@ -318,25 +324,125 @@ TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
||||||
blend2_node_resource,
|
blend2_node_resource,
|
||||||
"Output",
|
"Output",
|
||||||
graph_resource.getGraphOutputNode(),
|
graph_resource.getGraphOutputNode(),
|
||||||
"GraphAnimOutput"
|
"GraphAnimOutput"));
|
||||||
));
|
|
||||||
|
|
||||||
THEN("AnimData from output gets blended and result is written to Output.") {
|
THEN(
|
||||||
|
"AnimData from output gets blended and result is written to "
|
||||||
|
"Output.") {
|
||||||
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
||||||
Blend2Node* blend2_node = dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
Blend2Node* blend2_node =
|
||||||
|
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||||
|
|
||||||
AnimData* graph_input0 = (AnimData*) anim_graph.GetInput("GraphAnimInput0");
|
AnimData* graph_input0 =
|
||||||
|
(AnimData*)anim_graph.GetInput("GraphAnimInput0");
|
||||||
REQUIRE(graph_input0 == &blend2_node->m_input0);
|
REQUIRE(graph_input0 == &blend2_node->m_input0);
|
||||||
REQUIRE(anim_graph.m_nodes[1] == anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
REQUIRE(
|
||||||
|
anim_graph.m_nodes[1]
|
||||||
|
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
||||||
|
|
||||||
AnimData* graph_input1 = (AnimData*) anim_graph.GetInput("GraphAnimInput1");
|
AnimData* graph_input1 =
|
||||||
|
(AnimData*)anim_graph.GetInput("GraphAnimInput1");
|
||||||
REQUIRE(graph_input1 == &blend2_node->m_input1);
|
REQUIRE(graph_input1 == &blend2_node->m_input1);
|
||||||
REQUIRE(anim_graph.m_nodes[1] == anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
REQUIRE(
|
||||||
|
anim_graph.m_nodes[1]
|
||||||
|
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
||||||
|
|
||||||
AnimData* graph_output = (AnimData*) anim_graph.GetOutput("GraphAnimOutput");
|
AnimData* graph_output =
|
||||||
|
(AnimData*)anim_graph.GetOutput("GraphAnimOutput");
|
||||||
REQUIRE(graph_output == blend2_node->m_output);
|
REQUIRE(graph_output == blend2_node->m_output);
|
||||||
REQUIRE(anim_graph.m_nodes[blend2_node_index] == anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
REQUIRE(
|
||||||
|
anim_graph.m_nodes[blend2_node_index]
|
||||||
|
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WHEN("Adding AnimSampler Nodes") {
|
||||||
|
size_t blend2_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("Blend2"));
|
||||||
|
size_t sampler_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
|
size_t speed_scale_node_index =
|
||||||
|
graph_resource.addNode(AnimNodeResourceFactory("SpeedScale"));
|
||||||
|
|
||||||
|
AnimNodeResource& blend2_node_resource =
|
||||||
|
graph_resource.m_nodes[blend2_node_index];
|
||||||
|
AnimNodeResource& sampler_node_resource =
|
||||||
|
graph_resource.m_nodes[sampler_node_index];
|
||||||
|
AnimNodeResource& speed_scale_node_resource =
|
||||||
|
graph_resource.m_nodes[speed_scale_node_index];
|
||||||
|
|
||||||
|
REQUIRE(graph_resource.connectSockets(
|
||||||
|
graph_resource.getGraphInputNode(),
|
||||||
|
"GraphFloatInput",
|
||||||
|
blend2_node_resource,
|
||||||
|
"Weight"));
|
||||||
|
|
||||||
|
REQUIRE(graph_resource.connectSockets(
|
||||||
|
graph_resource.getGraphInputNode(),
|
||||||
|
"GraphAnimInput0",
|
||||||
|
blend2_node_resource,
|
||||||
|
"Input0"));
|
||||||
|
|
||||||
|
REQUIRE(graph_resource.connectSockets(
|
||||||
|
sampler_node_resource,
|
||||||
|
"Output",
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Input"));
|
||||||
|
|
||||||
|
REQUIRE(graph_resource.connectSockets(
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Output",
|
||||||
|
blend2_node_resource,
|
||||||
|
"Input1"));
|
||||||
|
|
||||||
|
REQUIRE(graph_resource.connectSockets(
|
||||||
|
blend2_node_resource,
|
||||||
|
"Output",
|
||||||
|
graph_resource.getGraphOutputNode(),
|
||||||
|
"GraphAnimOutput"));
|
||||||
|
|
||||||
|
THEN("Data flow and node ordering must be correct.") {
|
||||||
|
AnimGraph anim_graph = AnimGraph::createFromResource(graph_resource);
|
||||||
|
Blend2Node* blend2_node =
|
||||||
|
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||||
|
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
||||||
|
anim_graph.m_nodes[speed_scale_node_index]);
|
||||||
|
AnimSamplerNode* sampler_node = dynamic_cast<AnimSamplerNode*>(
|
||||||
|
anim_graph.m_nodes[sampler_node_index]);
|
||||||
|
|
||||||
|
AnimData* graph_input0 =
|
||||||
|
(AnimData*)anim_graph.GetInput("GraphAnimInput0");
|
||||||
|
REQUIRE(graph_input0 == &blend2_node->m_input0);
|
||||||
|
REQUIRE(
|
||||||
|
anim_graph.m_nodes[1]
|
||||||
|
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
||||||
|
|
||||||
|
AnimData* graph_input1 =
|
||||||
|
(AnimData*)anim_graph.GetInput("GraphAnimInput1");
|
||||||
|
REQUIRE(graph_input1 == nullptr);
|
||||||
|
|
||||||
|
REQUIRE(sampler_node->m_output == &speed_scale_node->m_input);
|
||||||
|
REQUIRE(
|
||||||
|
sampler_node
|
||||||
|
== anim_graph.getAnimNodeForInput(speed_scale_node_index, "Input"));
|
||||||
|
|
||||||
|
REQUIRE(speed_scale_node->m_output == &blend2_node->m_input1);
|
||||||
|
REQUIRE(
|
||||||
|
speed_scale_node
|
||||||
|
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
||||||
|
|
||||||
|
AnimData* graph_output =
|
||||||
|
(AnimData*)anim_graph.GetOutput("GraphAnimOutput");
|
||||||
|
REQUIRE(graph_output == blend2_node->m_output);
|
||||||
|
REQUIRE(
|
||||||
|
anim_graph.m_nodes[blend2_node_index]
|
||||||
|
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
||||||
|
|
||||||
|
// Check ordering
|
||||||
|
REQUIRE (anim_graph.getAnimNodeOrderIndex(blend2_node) < anim_graph.getAnimNodeOrderIndex(sampler_node));
|
||||||
|
REQUIRE (anim_graph.getAnimNodeOrderIndex(blend2_node) < anim_graph.getAnimNodeOrderIndex(speed_scale_node));
|
||||||
|
REQUIRE (anim_graph.getAnimNodeOrderIndex(speed_scale_node) < anim_graph.getAnimNodeOrderIndex(sampler_node));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue