From a1c4630ee7c1d3c175c8875c40cce5fbb2a1e674 Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Sun, 2 Mar 2025 16:14:07 +0100 Subject: [PATCH] Nodes can now be deleted in the blend tree editor. --- .clang-format | 3 +- src/AnimGraph/AnimGraphResource.cc | 104 ++++++++++++--- src/AnimGraph/AnimGraphResource.h | 48 ++++--- src/AnimGraphEditor/AnimGraphEditor.cc | 39 +++--- tests/AnimGraphResourceTests.cc | 178 ++++++++++++------------- 5 files changed, 221 insertions(+), 151 deletions(-) diff --git a/.clang-format b/.clang-format index 1d44113..b2cc174 100644 --- a/.clang-format +++ b/.clang-format @@ -9,5 +9,4 @@ BinPackParameters: 'false' BreakBeforeBinaryOperators: NonAssignment ExperimentalAutoDetectBinPacking: 'false' ReflowComments: 'false' - -... +DerivePointerAlignment: false diff --git a/src/AnimGraph/AnimGraphResource.cc b/src/AnimGraph/AnimGraphResource.cc index 9b0faed..0df9a5a 100644 --- a/src/AnimGraph/AnimGraphResource.cc +++ b/src/AnimGraph/AnimGraphResource.cc @@ -192,9 +192,6 @@ AnimNodeResource* sAnimGraphNodeFromJson( result->m_position[0] = json_node["position"][0]; result->m_position[1] = json_node["position"][1]; - result->m_virtual_socket_accessor = - VirtualAnimNodeDescriptorFactory(result->m_node_type_name); - for (auto& property : result->m_virtual_socket_accessor->m_properties) { property = sJsonToSocket(json_node["properties"][property.m_name]); } @@ -310,6 +307,9 @@ static bool sAnimGraphResourceBlendTreeFromJson( result_graph_resource->m_position[0] = json_data["position"][0]; result_graph_resource->m_position[1] = json_data["position"][1]; + // Clear all nodes as we overwrite them here anyway. + blend_tree_resource.ClearAllNodes(); + // Load nodes for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) { const json& json_node = json_data["nodes"][i]; @@ -367,6 +367,76 @@ static bool sAnimGraphResourceBlendTreeFromJson( return true; } +static bool sAnimGraphResourceStateMachineFromJson( + const json& json_data, + AnimGraphResource* result_graph_resource) { + assert(false && !"Not yet implemented!"); + + return false; +} + +void BlendTreeResource::RemoveConnectionsForSocket( + const AnimNodeResource* node_resource, + const Socket& socket) { + const BlendTreeConnectionResource* connection = + FindConnectionForSocket(node_resource, socket.m_name); + while (connection != nullptr) { + DisconnectSockets( + GetNode(connection->source_node_index), + connection->source_socket_name, + GetNode(connection->target_node_index), + connection->target_socket_name); + + connection = FindConnectionForSocket(node_resource, socket.m_name); + } +} + +void BlendTreeResource::RemoveNodeConnections(AnimNodeResource* node_resource) { + for (const Socket& socket : + node_resource->m_virtual_socket_accessor->m_inputs) { + RemoveConnectionsForSocket(node_resource, socket); + } + + for (const Socket& socket : + node_resource->m_virtual_socket_accessor->m_outputs) { + RemoveConnectionsForSocket(node_resource, socket); + } +} + +bool BlendTreeResource::RemoveNode(AnimNodeResource* node_resource) { + std::vector::iterator node_iterator = + std::find(m_nodes.begin(), m_nodes.end(), node_resource); + + if (node_iterator == m_nodes.end()) { + return true; + } + + const size_t node_index = node_iterator - m_nodes.begin(); + + if (m_node_input_connection_indices[node_index].size() > 0) { + std::cerr << "Cannot remove node, node still has input connections!" + << std::endl; + return false; + } + m_node_input_connection_indices.erase( + m_node_input_connection_indices.begin() + node_index); + m_nodes.erase(node_iterator); + + for (BlendTreeConnectionResource& connection : m_connections) { + if (connection.source_node_index > node_index) { + connection.source_node_index--; + } + if (connection.target_node_index > node_index) { + connection.target_node_index--; + } + } + + UpdateNodeEvalOrder(); + UpdateTreeTopologyInfo(); + + return true; +} + bool BlendTreeResource::ConnectSockets( const AnimNodeResource* source_node, const std::string& source_socket_name, @@ -760,7 +830,8 @@ AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) { m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree"); m_blend_tree_resource.InitGraphConnectors(); - RegisterBlendTreeOutputSocket("Output"); + RegisterBlendTreeOutputSocket( + AnimGraphResource::DefaultAnimOutput); } else { std::cerr << "Warning: construction of state machine graphs not yet implemented!" @@ -768,9 +839,7 @@ AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) { } } -bool AnimGraphResource::LoadFromFile(const char* filename) { - Clear(); - +AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) { std::ifstream input_file; input_file.open(filename); std::stringstream buffer; @@ -781,7 +850,7 @@ bool AnimGraphResource::LoadFromFile(const char* filename) { std::cerr << "Error parsing json of file '" << filename << "'." << std::endl; - return false; + return nullptr; } if (json_data["type"] != "AnimNodeResource") { @@ -789,20 +858,23 @@ bool AnimGraphResource::LoadFromFile(const char* filename) { << "Invalid json object. Expected type 'AnimNodeResource' but got '" << json_data["type"] << "'." << std::endl; - return false; + return nullptr; } + AnimGraphResource* result = nullptr; if (json_data["node_type"] == "BlendTree") { - return sAnimGraphResourceBlendTreeFromJson(json_data, this); + result = + dynamic_cast(AnimNodeResourceFactory("BlendTree")); + sAnimGraphResourceBlendTreeFromJson(json_data, result); } else if (json_data["node_type"] == "StateMachine") { - return LoadStateMachineResourceFromJson(json_data); + sAnimGraphResourceStateMachineFromJson(json_data, result); + } else { + std::cerr << "Invalid node_type. Expected type 'BlendTree' or " + "'StateMachine' but got '" + << json_data["node_type"] << "'." << std::endl; } - std::cerr << "Invalid node_type. Expected type 'BlendTree' or " - "'StateMachine' but got '" - << json_data["node_type"] << "'." << std::endl; - - return false; + return result; } bool AnimGraphResource::SaveToFile(const char* filename) const { diff --git a/src/AnimGraph/AnimGraphResource.h b/src/AnimGraph/AnimGraphResource.h index fb19cc3..be02716 100644 --- a/src/AnimGraph/AnimGraphResource.h +++ b/src/AnimGraph/AnimGraphResource.h @@ -43,10 +43,10 @@ struct BlendTreeResource { std::vector > m_node_input_connection_indices; std::vector > m_node_inputs_subtree; - ~BlendTreeResource() { CleanupNodes(); } + ~BlendTreeResource() { ClearAllNodes(); } void Reset() { - CleanupNodes(); + ClearAllNodes(); m_connections.clear(); @@ -54,7 +54,7 @@ struct BlendTreeResource { m_node_inputs_subtree.clear(); } - void CleanupNodes() { + void ClearAllNodes() { for (AnimNodeResource* node_resource : m_nodes) { delete node_resource; } @@ -62,18 +62,6 @@ struct BlendTreeResource { m_nodes.clear(); } - void InitGraphConnectors() { - AddNode(AnimNodeResourceFactory("BlendTreeSockets")); - AnimNodeResource* output_node = GetGraphOutputNode(); - output_node->m_name = "Outputs"; - output_node->m_position[0] = 200; - - AddNode(AnimNodeResourceFactory("BlendTreeSockets")); - AnimNodeResource* input_node = GetGraphInputNode(); - input_node->m_name = "Inputs"; - input_node->m_position[0] = -200; - } - [[nodiscard]] AnimNodeResource* GetGraphOutputNode() const { return m_nodes[0]; } @@ -134,6 +122,12 @@ struct BlendTreeResource { return m_nodes.size() - 1; } + void RemoveConnectionsForSocket( + const AnimNodeResource* node_resource, + const Socket& socket); + void RemoveNodeConnections(AnimNodeResource* node_resource); + [[maybe_unused]] bool RemoveNode(AnimNodeResource* node_resource); + [[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); } [[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; } [[nodiscard]] const AnimNodeResource* GetNode(size_t i) const { @@ -271,6 +265,18 @@ struct BlendTreeResource { } private: + void InitGraphConnectors() { + AddNode(AnimNodeResourceFactory("BlendTreeSockets")); + AnimNodeResource* output_node = GetGraphOutputNode(); + output_node->m_name = "Outputs"; + output_node->m_position[0] = 200; + + AddNode(AnimNodeResourceFactory("BlendTreeSockets")); + AnimNodeResource* input_node = GetGraphInputNode(); + input_node->m_name = "Inputs"; + input_node->m_position[0] = -200; + } + void UpdateNodeEvalOrder() { m_node_eval_order.clear(); UpdateNodeEvalOrderRecursive(0); @@ -281,6 +287,8 @@ struct BlendTreeResource { std::vector m_nodes; std::vector m_connections; std::vector m_node_eval_order; + + friend class AnimGraphResource; }; struct StateMachineTransitionResources { @@ -299,6 +307,8 @@ struct AnimGraphResource : AnimNodeResource { explicit AnimGraphResource(AnimGraphType graph_type); virtual ~AnimGraphResource() { Clear(); }; + static constexpr char DefaultAnimOutput[] = "Output"; + std::string m_graph_type_name; BlendTreeResource m_blend_tree_resource; @@ -308,8 +318,8 @@ struct AnimGraphResource : AnimNodeResource { StateMachineResource m_state_machine_resource; void Clear() { m_blend_tree_resource.Reset(); } - bool SaveToFile(const char* filename) const; - bool LoadFromFile(const char* filename); + [[maybe_unused]] bool SaveToFile(const char* filename) const; + static AnimGraphResource* CreateFromFile(const char* filename); void CreateBlendTreeInstance(AnimGraphBlendTree& result) const; @@ -389,7 +399,9 @@ struct AnimGraphResource : AnimNodeResource { bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data); }; -static inline AnimNodeResource* AnimNodeResourceFactory( +typedef std::unique_ptr AnimGraphResourcePtr; + +inline AnimNodeResource* AnimNodeResourceFactory( const std::string& node_type_name) { AnimNodeResource* result; diff --git a/src/AnimGraphEditor/AnimGraphEditor.cc b/src/AnimGraphEditor/AnimGraphEditor.cc index 4b21296..cbecc33 100644 --- a/src/AnimGraphEditor/AnimGraphEditor.cc +++ b/src/AnimGraphEditor/AnimGraphEditor.cc @@ -132,25 +132,6 @@ bool NodeSocketEditor(Socket& socket) { return modified; } -void RemoveBlendTreeConnectionsForSocket( - BlendTreeResource& blend_tree_resource, - AnimNodeResource* node_resource, - Socket& socket) { - const BlendTreeConnectionResource* connection = - blend_tree_resource.FindConnectionForSocket(node_resource, socket.m_name); - while (connection != nullptr) { - blend_tree_resource.DisconnectSockets( - blend_tree_resource.GetNode(connection->source_node_index), - connection->source_socket_name, - blend_tree_resource.GetNode(connection->target_node_index), - connection->target_socket_name); - - connection = blend_tree_resource.FindConnectionForSocket( - node_resource, - socket.m_name); - } -} - void SyncTrackEditor(SyncTrack* sync_track) { ImGui::SliderFloat("duration", &sync_track->m_duration, 0.001f, 10.f); @@ -363,10 +344,7 @@ void AnimGraphEditorRenderSidebar( current_graph_resource->m_virtual_socket_accessor->m_inputs = inputs; } if (ImGui::Button("X")) { - RemoveBlendTreeConnectionsForSocket( - blend_tree_resource, - node_resource, - input); + blend_tree_resource.RemoveConnectionsForSocket(node_resource, input); iter = inputs.erase(iter); } else { iter++; @@ -947,6 +925,21 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { } } + ax::NodeEditor::NodeId hovered_node = ax::NodeEditor::GetHoveredNode(); + if (!hovered_node.Invalid) { + AnimNodeResource* node_resource = + hovered_node.AsPointer(); + + if (node_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) { + AnimGraphResource* current_graph_resource = + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]; + + current_graph_resource->m_blend_tree_resource.RemoveNodeConnections( + node_resource); + current_graph_resource->m_blend_tree_resource.RemoveNode(node_resource); + } + } + ax::NodeEditor::SetCurrentEditor(nullptr); } diff --git a/tests/AnimGraphResourceTests.cc b/tests/AnimGraphResourceTests.cc index cb60f0f..524de65 100644 --- a/tests/AnimGraphResourceTests.cc +++ b/tests/AnimGraphResourceTests.cc @@ -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( + 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( - "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("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("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("AnimOutput"); - // Embedded: inputs embedded_graph->RegisterBlendTreeInputSocket("AnimInput"); embedded_graph->RegisterBlendTreeInputSocket("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 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(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( - "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(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& 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& 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(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( - blend_tree_graph.GetOutputPtr("GraphOutput")); + AnimData* graph_output = + static_cast(blend_tree_graph.GetOutputPtr( + 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; -} \ No newline at end of file +}