diff --git a/src/AnimGraphEditor.cc b/src/AnimGraphEditor.cc index 1ccd244..e236b08 100644 --- a/src/AnimGraphEditor.cc +++ b/src/AnimGraphEditor.cc @@ -43,7 +43,7 @@ void AnimGraphEditorUpdate() { if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImNodes::IsEditorHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) { - AnimNodeResource node_resource = AnimNodeResourceFactory("Blend2"); + AnimNodeResource node_resource = AnimNodeResourceFactory("SpeedScale"); size_t node_id = gGraphResource.m_nodes.size(); ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos()); gGraphResource.m_nodes.push_back(node_resource); diff --git a/src/AnimGraphResource.cc b/src/AnimGraphResource.cc index c1f2c5b..daaf88c 100644 --- a/src/AnimGraphResource.cc +++ b/src/AnimGraphResource.cc @@ -88,6 +88,9 @@ AnimGraphConnection sAnimGraphConnectionFromJson(const json& json_node) { void AnimGraphResource::clear() { m_name = ""; m_nodes.clear(); + + m_nodes.push_back(AnimNodeResourceFactory("BlendTree")); + m_connections.clear(); } diff --git a/src/AnimGraphResource.h b/src/AnimGraphResource.h index 285ce8c..1a9b914 100644 --- a/src/AnimGraphResource.h +++ b/src/AnimGraphResource.h @@ -62,12 +62,14 @@ struct Socket { Vec3* vec3; Quat* quat; std::string* str; + void* ptr; bool** flag_ptr; AnimData** anim_data_ptr; float** real_ptr; float** vec3_ptr; float** quat_ptr; std::string** str_ptr; + void** ptr_ptr; }; Value m_value; }; @@ -111,16 +113,43 @@ struct NodeSocketAccessorBase { return result; } + const Socket* FindSocket( + const std::vector& sockets, + const std::string& name) const { + const Socket* result = nullptr; + for (size_t i = 0, n = sockets.size(); i < n; i++) { + if (sockets[i].m_name == name) { + result = &sockets[i]; + break; + } + } + + return result; + } + SocketType GetSocketType( - const std::vector sockets, + const std::vector& sockets, const std::string& name) { - Socket* socket = FindSocket(m_properties, name); + const Socket* socket = FindSocket(sockets, name); if (socket == nullptr) { return SocketType::SocketTypeUndefined; } return socket->m_type; } + size_t GetSocketIndex( + const std::vector& sockets, + const std::string& name) const { + for (size_t i = 0, n = sockets.size(); i < n; i++) { + std::cout << i << ", " << sockets[i].m_name << std::endl; + if (sockets[i].m_name == name) { + return i; + } + } + + return -1; + } + template bool RegisterSocket( std::vector& sockets, @@ -294,9 +323,15 @@ struct NodeSocketAccessorBase { T* GetInput(const std::string& name, T* value) { return GetSocketValue(m_inputs, name, value); } + Socket* FindInputSocket(const std::string& name) { + return FindSocket(m_inputs, name); + } SocketType GetInputType(const std::string& name) { return GetSocketType(m_inputs, name); } + size_t GetInputIndex(const std::string& name) { + return GetSocketIndex(m_inputs, name); + } template bool RegisterOutput(const std::string& name, T** value) { @@ -309,6 +344,12 @@ struct NodeSocketAccessorBase { SocketType GetOutputType(const std::string& name) { return GetSocketType(m_outputs, name); } + Socket* FindOutputSocket(const std::string& name) { + return FindSocket(m_outputs, name); + } + size_t GetOutputIndex(const std::string& name) { + return GetSocketIndex(m_outputs, name); + } std::vector m_properties; std::vector m_inputs; @@ -327,6 +368,13 @@ struct NodeRegistry { AnimNode* node); }; +struct BlendTreeNode : public AnimNode {}; + +template <> +struct NodeSocketAccessor : public NodeSocketAccessorBase { + NodeSocketAccessor(AnimNode* node_) : NodeSocketAccessorBase(node_) {} +}; + struct Blend2Node : public AnimNode { AnimData m_input0; AnimData m_input1; @@ -392,21 +440,41 @@ struct AnimGraphResource { std::string m_name; std::vector m_nodes; std::vector m_connections; - std::vector m_inputs; - std::vector m_outputs; + + ~AnimGraphResource() { + for (size_t i = 0, n = m_nodes.size(); i < n; i++) { + delete m_nodes[i].m_anim_node; + delete m_nodes[i].m_socket_accessor; + } + } + + AnimGraphResource() { clear(); } void clear(); bool saveToFile(const char* filename) const; bool loadFromFile(const char* filename); + + size_t addNode(AnimNodeResource node_resource) { + m_nodes.push_back(node_resource); + return m_nodes.size() - 1; + } }; static inline AnimNode* AnimNodeFactory(const std::string& name) { + AnimNode* result; if (name == "Blend2") { - return new Blend2Node; + result = new Blend2Node; } else if (name == "SpeedScale") { - return new SpeedScaleNode; + result = new SpeedScaleNode; } else if (name == "AnimSampler") { - return new AnimSamplerNode; + result = new AnimSamplerNode; + } else if (name == "BlendTree") { + result = new BlendTreeNode; + } + + if (result != nullptr) { + result->m_node_type_name = name; + return result; } std::cerr << "Invalid node type: " << name << std::endl; @@ -422,6 +490,8 @@ static inline NodeSocketAccessorBase* AnimNodeAccessorFactory( return new NodeSocketAccessor(node); } else if (node_type_name == "AnimSampler") { return new NodeSocketAccessor(node); + } else if (node_type_name == "BlendTree") { + return new NodeSocketAccessor(node); } else { std::cerr << "Invalid node type name " << node_type_name << "." << std::endl; @@ -529,23 +599,26 @@ struct AnimGraph { continue; } - if (source_node_accessor->m_outputs[connection.m_source_socket_index].m_type - != target_node_accessor->m_inputs[connection.m_target_socket_index].m_type) { + if (source_node_accessor->m_outputs[connection.m_source_socket_index] + .m_type + != target_node_accessor->m_inputs[connection.m_target_socket_index] + .m_type) { std::cerr << "Invalid connection connecting nodes '" << source_node_name << "' and '" << target_node_name << "'." << std::endl; return result; } - // TODO! How to wire up the different types? -// std::string socket_name = connection.m_target_socket_name; -// AnimData* input_ref = -// target_node_accessor->GetInput(socket_name, nullptr); -// source_node_accessor->SetOutputRef( -// connection.m_source_socket_name, -// input_ref); + Socket* source_socket = + &source_node_accessor->m_outputs[connection.m_source_socket_index]; + Socket* target_socket = + &target_node_accessor->m_inputs[connection.m_target_socket_index]; + if (source_socket->m_type != target_socket->m_type) { + std::cerr << "Cannot connect sockets: invalid types!" << std::endl; + } + + (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr; size_t target_node_index = result.getAnimNodeIndex(target_node); - result.m_node_inputs[target_node_index].push_back(source_node); } diff --git a/tests/AnimGraphResourceTests.cc b/tests/AnimGraphResourceTests.cc index 1c55a73..adc3713 100644 --- a/tests/AnimGraphResourceTests.cc +++ b/tests/AnimGraphResourceTests.cc @@ -8,41 +8,56 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") { AnimGraphResource graph_resource; + graph_resource.clear(); graph_resource.m_name = "WalkRunBlendGraph"; - AnimNodeResource walk_node; + // Prepare graph inputs and outputs + size_t walk_node_index = + graph_resource.addNode(AnimNodeResourceFactory("AnimSampler")); + size_t run_node_index = + graph_resource.addNode(AnimNodeResourceFactory("AnimSampler")); + size_t blend_node_index = + graph_resource.addNode(AnimNodeResourceFactory("Blend2")); + + AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index]; walk_node.m_name = "WalkAnim"; - walk_node.m_type_name = "AnimSampler"; - graph_resource.m_nodes.push_back(walk_node); + AnimNodeResource& run_node = graph_resource.m_nodes[run_node_index]; + walk_node.m_name = "RunAnim"; + AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index]; + walk_node.m_name = "BlendWalkRun"; - AnimNodeResource run_node; - run_node.m_name = "RunAnim"; - run_node.m_type_name = "AnimSampler"; - graph_resource.m_nodes.push_back(run_node); + AnimNodeResource& graph_node = graph_resource.m_nodes[0]; + graph_node.m_socket_accessor->RegisterInput("Output", nullptr); - AnimNodeResource blend_node; - blend_node.m_name = "BlendWalkRun"; - blend_node.m_type_name = "Blend2"; - graph_resource.m_nodes.push_back(blend_node); + REQUIRE(graph_node.m_socket_accessor->m_inputs.size() == 1); + REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input0") == 0); + REQUIRE(blend_node.m_socket_accessor->GetInputIndex("Input1") == 1); AnimGraphConnection walk_to_blend; - walk_to_blend.m_source_node = &walk_node; - walk_to_blend.m_source_socket_name = "Output"; - walk_to_blend.m_target_node = &blend_node; - walk_to_blend.m_target_socket_name = "Input0"; + walk_to_blend.m_source_node_index = walk_node_index; + walk_to_blend.m_source_socket_index = + walk_node.m_socket_accessor->GetOutputIndex("Output"); + walk_to_blend.m_target_node_index = blend_node_index; + walk_to_blend.m_target_socket_index = + blend_node.m_socket_accessor->GetInputIndex("Input0"); graph_resource.m_connections.push_back(walk_to_blend); AnimGraphConnection run_to_blend; - run_to_blend.m_source_node = &run_node; - run_to_blend.m_source_socket_name = "Output"; - run_to_blend.m_target_node = &blend_node; - run_to_blend.m_target_socket_name = "Input1"; + run_to_blend.m_source_node_index = run_node_index; + run_to_blend.m_source_socket_index = + run_node.m_socket_accessor->GetOutputIndex("Output"); + run_to_blend.m_target_node_index = blend_node_index; + run_to_blend.m_target_socket_index = + blend_node.m_socket_accessor->GetInputIndex("Input1"); graph_resource.m_connections.push_back(run_to_blend); AnimGraphConnection blend_to_output; - blend_to_output.m_source_node = &blend_node; - blend_to_output.m_source_socket_name = "Output"; - blend_to_output.m_target_socket_name = "GraphOutput"; + blend_to_output.m_source_node_index = blend_node_index; + blend_to_output.m_source_socket_index = + blend_node.m_socket_accessor->GetOutputIndex("Output"); + blend_to_output.m_target_node_index = 0; + blend_to_output.m_target_socket_index = + graph_node.m_socket_accessor->GetInputIndex("Output"); graph_resource.m_connections.push_back(blend_to_output); graph_resource.saveToFile("WalkGraph.animgraph.json"); @@ -55,16 +70,17 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") { << std::endl; } - CHECK(graph.m_nodes.size() == 3); - CHECK(graph.m_nodes[0]->m_node_type_name == "AnimSampler"); + CHECK(graph.m_nodes.size() == 4); + CHECK(graph.m_nodes[0]->m_node_type_name == "Graph"); CHECK(graph.m_nodes[1]->m_node_type_name == "AnimSampler"); - CHECK(graph.m_nodes[2]->m_node_type_name == "Blend2"); + CHECK(graph.m_nodes[2]->m_node_type_name == "AnimSampler"); + CHECK(graph.m_nodes[3]->m_node_type_name == "Blend2"); AnimSamplerNode* anim_sampler_instance0 = - dynamic_cast(graph.m_nodes[0]); - AnimSamplerNode* anim_sampler_instance1 = dynamic_cast(graph.m_nodes[1]); - Blend2Node* blend2_instance = dynamic_cast(graph.m_nodes[2]); + AnimSamplerNode* anim_sampler_instance1 = + dynamic_cast(graph.m_nodes[2]); + Blend2Node* blend2_instance = dynamic_cast(graph.m_nodes[3]); CHECK(anim_sampler_instance0->m_output == &blend2_instance->m_input0); CHECK(anim_sampler_instance1->m_output == &blend2_instance->m_input1);