Nodes can now be deleted in the blend tree editor.

This commit is contained in:
Martin Felis
2025-03-02 19:30:26 +01:00
parent acbe3a4ed5
commit a1c4630ee7
5 changed files with 221 additions and 151 deletions
+86 -92
View File
@@ -19,13 +19,13 @@ class SimpleAnimSamplerGraphResource {
AnimNodeResource* walk_node = nullptr;
public:
SimpleAnimSamplerGraphResource() {
SimpleAnimSamplerGraphResource()
: graph_resource(AnimGraphType::GraphTypeBlendTree) {
graph_resource.m_name = "AnimSamplerBlendTree";
graph_resource.m_node_type_name = "BlendTree";
graph_resource.m_graph_type_name = "BlendTree";
blend_tree_resource = &graph_resource.m_blend_tree_resource;
blend_tree_resource->InitGraphConnectors();
// Prepare graph inputs and outputs
walk_node_index =
@@ -45,13 +45,13 @@ class SimpleAnimSamplerGraphResource {
walk_node,
"Output",
blend_tree_resource->GetGraphOutputNode(),
"GraphOutput");
AnimGraphResource::DefaultAnimOutput);
}
};
class Blend2GraphResource {
protected:
AnimGraphResource graph_resource;
AnimGraphResourcePtr graph_resource;
BlendTreeResource* blend_tree_resource = nullptr;
size_t walk_node_index = -1;
size_t run_node_index = -1;
@@ -61,13 +61,13 @@ class Blend2GraphResource {
AnimNodeResource* blend_node = nullptr;
public:
Blend2GraphResource() {
graph_resource.m_name = "WalkRunBlendGraph";
graph_resource.m_node_type_name = "BlendTree";
graph_resource.m_graph_type_name = "BlendTree";
Blend2GraphResource()
: graph_resource(
dynamic_cast<AnimGraphResource*>(
AnimNodeResourceFactory("BlendTree"))) {
graph_resource->m_name = "WalkRunBlendGraph";
blend_tree_resource = &graph_resource.m_blend_tree_resource;
blend_tree_resource->InitGraphConnectors();
blend_tree_resource = &graph_resource->m_blend_tree_resource;
// Prepare graph inputs and outputs
walk_node_index =
@@ -93,11 +93,12 @@ class Blend2GraphResource {
blend_node->m_name = "BlendWalkRun";
AnimNodeResource* graph_node = blend_tree_resource->GetGraphOutputNode();
graph_node->m_virtual_socket_accessor->RegisterInput<AnimData>(
"GraphOutput",
nullptr);
REQUIRE(graph_node->m_virtual_socket_accessor->m_inputs.size() == 1);
REQUIRE(
graph_node->m_virtual_socket_accessor->m_inputs[0].m_name
== AnimGraphResource::DefaultAnimOutput);
REQUIRE(
blend_node->m_virtual_socket_accessor->GetInputIndex("Input0") == 0);
REQUIRE(
@@ -112,7 +113,7 @@ class Blend2GraphResource {
blend_node,
"Output",
blend_tree_resource->GetGraphOutputNode(),
"GraphOutput");
AnimGraphResource::DefaultAnimOutput);
}
};
@@ -142,20 +143,13 @@ class EmbeddedBlendTreeGraphResource {
size_t embedded_speed_scale_index = -1;
public:
EmbeddedBlendTreeGraphResource() {
EmbeddedBlendTreeGraphResource()
: parent_graph_resource(AnimGraphType::GraphTypeBlendTree) {
parent_graph_resource.m_name = "ParentBlendTree";
parent_graph_resource.m_graph_type_name = "BlendTree";
parent_graph_resource.m_node_type_name = "BlendTree";
parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource;
parent_blend_tree_resource->Reset();
parent_blend_tree_resource->InitGraphConnectors();
// Setup parent outputs
AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode();
parent_blend_tree_outputs->m_virtual_socket_accessor
->RegisterInput<AnimData>("Output", nullptr);
// Parent AnimSampler
walk_node_index = parent_blend_tree_resource->AddNode(
@@ -215,6 +209,9 @@ class EmbeddedBlendTreeGraphResource {
"AnimOutput");
// Parent: setup connections
const AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode();
REQUIRE(parent_blend_tree_resource->ConnectSockets(
walk_node_resource,
"Output",
@@ -259,20 +256,13 @@ class EmbeddedTreeBlend2GraphResource {
AnimNodeResource* embedded_run_node_resource = nullptr;
public:
EmbeddedTreeBlend2GraphResource() {
EmbeddedTreeBlend2GraphResource()
: parent_graph_resource(AnimGraphType::GraphTypeBlendTree) {
parent_graph_resource.m_name = "ParentBlendTree";
parent_graph_resource.m_graph_type_name = "BlendTree";
parent_graph_resource.m_node_type_name = "BlendTree";
parent_blend_tree_resource = &parent_graph_resource.m_blend_tree_resource;
parent_blend_tree_resource->Reset();
parent_blend_tree_resource->InitGraphConnectors();
// Setup parent outputs
AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode();
parent_blend_tree_outputs->m_virtual_socket_accessor
->RegisterInput<AnimData>("Output", nullptr);
// Setup parent inputs
AnimNodeResource* parent_blend_tree_inputs =
@@ -303,9 +293,6 @@ class EmbeddedTreeBlend2GraphResource {
embedded_graph->m_graph_type_name = "BlendTree";
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
// Embedded: outputs
embedded_graph->RegisterBlendTreeOutputSocket<AnimData>("AnimOutput");
// Embedded: inputs
embedded_graph->RegisterBlendTreeInputSocket<AnimData>("AnimInput");
embedded_graph->RegisterBlendTreeInputSocket<float>("BlendWeight");
@@ -346,7 +333,7 @@ class EmbeddedTreeBlend2GraphResource {
embedded_blend2_node_resource,
"Output",
embedded_blend_tree_resource->GetGraphOutputNode(),
"AnimOutput"));
AnimGraphResource::DefaultAnimOutput));
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
embedded_blend_tree_resource->GetGraphInputNode(),
"BlendWeight",
@@ -354,6 +341,9 @@ class EmbeddedTreeBlend2GraphResource {
"Weight"));
// Parent: setup connections
AnimNodeResource* parent_blend_tree_outputs =
parent_blend_tree_resource->GetGraphOutputNode();
REQUIRE(parent_blend_tree_resource->ConnectSockets(
walk_node_resource,
"Output",
@@ -361,9 +351,9 @@ class EmbeddedTreeBlend2GraphResource {
"AnimInput"));
REQUIRE(parent_blend_tree_resource->ConnectSockets(
embedded_graph,
"AnimOutput",
AnimGraphResource::DefaultAnimOutput,
parent_blend_tree_outputs,
"Output"));
AnimGraphResource::DefaultAnimOutput));
REQUIRE(parent_blend_tree_resource->ConnectSockets(
parent_blend_tree_inputs,
"EmbeddedBlend2Weight",
@@ -477,10 +467,10 @@ TEST_CASE_METHOD(
"[SimpleAnimSamplerGraphResource]") {
graph_resource.SaveToFile("TestGraphAnimSamplerBlendTree.json");
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.LoadFromFile("TestGraphAnimSamplerBlendTree.json");
std::unique_ptr<AnimGraphResource> graph_resource_loaded(
AnimGraphResource::CreateFromFile("TestGraphAnimSamplerBlendTree.json"));
CheckAnimGraphResourceEqual(graph_resource, graph_resource_loaded);
CheckAnimGraphResourceEqual(graph_resource, *graph_resource_loaded);
}
TEST_CASE_METHOD(
@@ -530,7 +520,9 @@ TEST_CASE_METHOD(
// Ensure that outputs are properly propagated.
AnimData output;
output.m_local_matrices.resize(skeleton.num_soa_joints());
anim_graph_blend_tree.SetOutput("GraphOutput", &output);
anim_graph_blend_tree.SetOutput(
AnimGraphResource::DefaultAnimOutput,
&output);
REQUIRE(anim_sampler_walk->o_output == &output);
WHEN("Emulating Graph Evaluation") {
@@ -545,13 +537,13 @@ TEST_CASE_METHOD(
// Checks that node const inputs are properly set.
//
TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
AnimGraphResource graph_resource;
graph_resource.m_name = "AnimSamplerSpeedScaleGraph";
graph_resource.m_graph_type_name = "BlendTree";
AnimGraphResourcePtr graph_resource(
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree")));
graph_resource->m_name = "AnimSamplerSpeedScaleGraph";
graph_resource->m_graph_type_name = "BlendTree";
BlendTreeResource& blend_tree_resource = graph_resource.m_blend_tree_resource;
blend_tree_resource.Reset();
blend_tree_resource.InitGraphConnectors();
BlendTreeResource& blend_tree_resource =
graph_resource->m_blend_tree_resource;
// Prepare graph inputs and outputs
size_t walk_node_index =
@@ -574,11 +566,6 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
"SpeedScale",
speed_scale_value);
AnimNodeResource* graph_node = blend_tree_resource.GetGraphOutputNode();
graph_node->m_virtual_socket_accessor->RegisterInput<AnimData>(
"GraphOutput",
nullptr);
blend_tree_resource
.ConnectSockets(walk_node, "Output", speed_scale_node, "Input");
@@ -586,16 +573,16 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
speed_scale_node,
"Output",
blend_tree_resource.GetGraphOutputNode(),
"GraphOutput");
AnimGraphResource::DefaultAnimOutput);
graph_resource.SaveToFile(
"TestGraphAnimSamplerSpeedScaleGraph.animgraph.json");
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.LoadFromFile(
graph_resource->SaveToFile(
"TestGraphAnimSamplerSpeedScaleGraph.animgraph.json");
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile(
"TestGraphAnimSamplerSpeedScaleGraph.animgraph.json"));
BlendTreeResource& blend_tree_resource_loaded =
graph_resource_loaded.m_blend_tree_resource;
graph_resource_loaded->m_blend_tree_resource;
Socket* speed_scale_resource_loaded_input =
blend_tree_resource_loaded.GetNode(speed_scale_node_index)
@@ -607,7 +594,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
AnimGraphBlendTree blend_tree;
graph_resource_loaded.CreateBlendTreeInstance(blend_tree);
graph_resource_loaded->CreateBlendTreeInstance(blend_tree);
REQUIRE_THAT(
*dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index])
@@ -616,7 +603,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
WHEN("Checking node eval order and node subtrees") {
const std::vector<size_t>& eval_order =
graph_resource_loaded.m_blend_tree_resource.GetNodeEvalOrder();
graph_resource_loaded->m_blend_tree_resource.GetNodeEvalOrder();
THEN("Walk node gets evaluated before speed scale node") {
CHECK(eval_order.size() == 2);
@@ -626,12 +613,12 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
THEN("Subtree of the speed scale node contains only the walk node") {
CHECK(
graph_resource_loaded.m_blend_tree_resource
graph_resource_loaded->m_blend_tree_resource
.m_node_inputs_subtree[speed_scale_node_index]
.size()
== 1);
CHECK(
graph_resource_loaded.m_blend_tree_resource
graph_resource_loaded->m_blend_tree_resource
.m_node_inputs_subtree[speed_scale_node_index][0]
== walk_node_index);
}
@@ -651,7 +638,7 @@ TEST_CASE_METHOD(
blend_node,
"Output",
blend_tree_resource->GetGraphOutputNode(),
"GraphOutput")
AnimGraphResource::DefaultAnimOutput)
== true);
CHECK(blend_tree_resource->GetNodeEvalOrder().empty());
@@ -667,7 +654,7 @@ TEST_CASE_METHOD(
speed_scale_node_resource,
"Output",
blend_tree_resource->GetGraphOutputNode(),
"GraphOutput")
AnimGraphResource::DefaultAnimOutput)
== true);
const std::vector<size_t>& tree_eval_order =
@@ -694,7 +681,7 @@ TEST_CASE_METHOD(
speed_scale_node_resource,
"Output",
blend_tree_resource->GetGraphOutputNode(),
"GraphOutput"));
AnimGraphResource::DefaultAnimOutput));
CHECK(blend_tree_resource
->DisconnectSockets(walk_node, "Output", blend_node, "Input0"));
CHECK(
@@ -706,19 +693,29 @@ TEST_CASE_METHOD(
== false);
}
TEST_CASE("FreeAnimGraphResource", "[Test]") {
AnimGraphResourcePtr graph_resource(
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree")));
graph_resource->SaveToFile("UniqueSaveToFile.json");
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile("UniqueSaveToFile.json"));
}
TEST_CASE_METHOD(
Blend2GraphResource,
"Blend2GraphResource saving and loading results in same resource",
"[Blend2GraphResource]") {
graph_resource.SaveToFile("TestGraphBlend2Graph.animgraph.json");
graph_resource->SaveToFile("TestGraphBlend2Graph.animgraph.json");
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.LoadFromFile("TestGraphBlend2Graph.animgraph.json");
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile("TestGraphBlend2Graph.animgraph.json"));
CheckAnimGraphResourceEqual(graph_resource, graph_resource_loaded);
CheckAnimGraphResourceEqual(*graph_resource, *graph_resource_loaded);
BlendTreeResource* blend_tree_resource_loaded =
&graph_resource_loaded.m_blend_tree_resource;
&graph_resource_loaded->m_blend_tree_resource;
// Check that the constant weight of the Blend2 node was properly applied when
// loading the resource.
@@ -739,7 +736,7 @@ TEST_CASE_METHOD(
"Blend2GraphResource graph unsynced evaluation",
"[Blend2GraphResource]") {
AnimGraphBlendTree blend_tree_graph;
graph_resource.CreateBlendTreeInstance(blend_tree_graph);
graph_resource->CreateBlendTreeInstance(blend_tree_graph);
AnimGraphContext graph_context;
ozz::animation::Skeleton skeleton;
@@ -806,8 +803,9 @@ TEST_CASE_METHOD(
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
AnimData* graph_output = static_cast<AnimData*>(
blend_tree_graph.GetOutputPtr<AnimData>("GraphOutput"));
AnimData* graph_output =
static_cast<AnimData*>(blend_tree_graph.GetOutputPtr<AnimData>(
AnimGraphResource::DefaultAnimOutput));
CHECK(
graph_output->m_local_matrices.size()
@@ -837,14 +835,12 @@ TEST_CASE_METHOD(
//
//
TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
AnimGraphResource graph_resource_origin;
AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree);
graph_resource_origin.m_name = "TestInputOutputGraph";
graph_resource_origin.m_graph_type_name = "BlendTree";
BlendTreeResource& blend_tree_resource =
graph_resource_origin.m_blend_tree_resource;
blend_tree_resource.Reset();
blend_tree_resource.InitGraphConnectors();
// Prepare graph inputs and outputs
size_t float_to_vec3_node_index = blend_tree_resource.AddNode(
@@ -901,11 +897,11 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
const char* filename = "TestGraphResourceSaveLoadGraphInputs.json";
graph_resource_origin.SaveToFile(filename);
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.LoadFromFile(filename);
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile(filename));
BlendTreeResource& graph_blend_tree_loaded =
graph_resource_loaded.m_blend_tree_resource;
graph_resource_loaded->m_blend_tree_resource;
const AnimNodeResource* graph_loaded_output_node =
graph_blend_tree_loaded.GetGraphOutputNode();
@@ -939,7 +935,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
WHEN("Instantiating an AnimGraph") {
AnimGraphBlendTree blend_tree_node;
graph_resource_loaded.CreateBlendTreeInstance(blend_tree_node);
graph_resource_loaded->CreateBlendTreeInstance(blend_tree_node);
float graph_float_input = 123.456f;
blend_tree_node.SetInput("GraphFloatInput", &graph_float_input);
@@ -996,14 +992,12 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
// GraphFloat1Output -> GraphFLoatInputSingle * 3
//
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
AnimGraphResource graph_resource_origin;
AnimGraphResource graph_resource_origin(AnimGraphType::GraphTypeBlendTree);
graph_resource_origin.m_name = "TestSimpleMathGraph";
graph_resource_origin.m_graph_type_name = "BlendTree";
BlendTreeResource& blend_tree_resource =
graph_resource_origin.m_blend_tree_resource;
blend_tree_resource.Reset();
blend_tree_resource.InitGraphConnectors();
// Prepare graph inputs and outputs
size_t math_add0_node_index =
@@ -1084,12 +1078,12 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
const char* filename = "TestGraphResourceSaveLoadGraphInputs.json";
graph_resource_origin.SaveToFile(filename);
AnimGraphResource graph_resource_loaded;
graph_resource_loaded.LoadFromFile(filename);
AnimGraphResourcePtr graph_resource_loaded(
AnimGraphResource::CreateFromFile(filename));
WHEN("Instantiating an AnimGraph") {
AnimGraphBlendTree blend_tree;
graph_resource_loaded.CreateBlendTreeInstance(blend_tree);
graph_resource_loaded->CreateBlendTreeInstance(blend_tree);
float graph_float_input = 123.456f;
blend_tree.SetInput("GraphFloatInput", &graph_float_input);
@@ -1145,16 +1139,16 @@ TEST_CASE_METHOD(
"[EmbeddedBlendTreeGraphResource]") {
parent_graph_resource.SaveToFile("TestGraphEmbeddedBlendTree.json");
AnimGraphResource parent_graph_resource_loaded;
parent_graph_resource_loaded.LoadFromFile("TestGraphEmbeddedBlendTree.json");
AnimGraphResourcePtr parent_graph_resource_loaded(
AnimGraphResource::CreateFromFile("TestGraphEmbeddedBlendTree.json"));
// Check the loaded parent graph
CheckAnimGraphResourceEqual(
parent_graph_resource,
parent_graph_resource_loaded);
*parent_graph_resource_loaded);
const BlendTreeResource& parent_blend_tree_resource_loaded =
parent_graph_resource_loaded.m_blend_tree_resource;
parent_graph_resource_loaded->m_blend_tree_resource;
// Check the loaded embedded graph
REQUIRE(
@@ -1355,4 +1349,4 @@ TEST_CASE(
CHECK(!blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket));
delete blend_tree_anim_node_resource;
}
}