diff --git a/demo/addons/blendalot/blendalot_main_panel.gd b/demo/addons/blendalot/blendalot_main_panel.gd index 46e7bdf..929733d 100644 --- a/demo/addons/blendalot/blendalot_main_panel.gd +++ b/demo/addons/blendalot/blendalot_main_panel.gd @@ -1,16 +1,222 @@ @tool extends Control +@onready var blend_tree_graph_edit: GraphEdit = %BlendTreeGraphEdit +@onready var file_name_line_edit: LineEdit = %FileNameLineEdit +@onready var tree_option_button: OptionButton = %TreeOptionButton +@onready var add_node_popup_menu: PopupMenu = %AddNodePopupMenu + +var blend_tree:BLTAnimationNodeBlendTree = BLTAnimationNodeBlendTree.new() +var blend_tree_node_to_graph_node = {} +var graph_node_to_blend_tree_node = {} +var selected_nodes = {} +var new_node_position = Vector2.INF + +var registered_nodes = [ + "BLTAnimationNodeSampler", + "BLTAnimationNodeBlend2", + "BLTAnimationNodeBlendTree", + ] # Called when the node enters the scene tree for the first time. func _ready() -> void: - pass # Replace with function body. + for node_name in registered_nodes: + add_node_popup_menu.add_item(node_name) # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: pass +func create_node_for_blt_node(blt_node: BLTAnimationNode) -> GraphNode: + var result_graph_node:GraphNode = GraphNode.new() + result_graph_node.name = blt_node.resource_name + result_graph_node.title = blt_node.resource_name + result_graph_node.position_offset = blt_node.position + + var result_slot_offset = 0 + + if (blt_node.get_class() != "BLTAnimationNodeOutput"): + result_slot_offset = 1 + var output_slot_label:Label = Label.new() + output_slot_label.text = "Result" + result_graph_node.add_child(output_slot_label) + result_graph_node.set_slot(0, false, 1, Color.WHITE, true, 1, Color.WHITE) + + var inputs = blt_node.get_input_names() + for i in range(len(inputs)): + var slot_label:Label = Label.new() + slot_label.text = inputs[i] + result_graph_node.add_child(slot_label) + result_graph_node.set_slot(i + result_slot_offset, true, 1, Color.WHITE, false, 1, Color.BLACK) + + return result_graph_node -func _on_hit_me_button_pressed() -> void: - print("Hello from the main screen plugin!") + +func _on_add_node_button_pressed() -> void: + blend_tree_graph_edit.add_child(create_node_for_blt_node(BLTAnimationNodeOutput.new())) + blend_tree_graph_edit.add_child(create_node_for_blt_node(BLTAnimationNodeBlend2.new())) + + +func _on_reset_graph_button_pressed() -> void: + _reset_editor() + _update_editor_from_blend_tree() + + +func _reset_editor(): + for child in blend_tree_graph_edit.get_children(): + if child.name == "_connection_layer": + continue + + child.get_parent().remove_child(child) + child.queue_free() + + blend_tree_graph_edit.clear_connections() + + blend_tree = BLTAnimationNodeBlendTree.new() + blend_tree_node_to_graph_node = {} + graph_node_to_blend_tree_node = {} + selected_nodes = {} + + +func _update_editor_nodes_from_blend_tree(): + for node_name in blend_tree.get_node_names(): + var blend_tree_node:BLTAnimationNode = blend_tree.get_node(node_name) + var graph_node:GraphNode = create_node_for_blt_node(blend_tree_node) + blend_tree_graph_edit.add_child(graph_node) + + blend_tree_node_to_graph_node[blend_tree_node] = graph_node + graph_node_to_blend_tree_node[graph_node] = blend_tree_node + + +func _update_editor_connections_from_blend_tree(): + var connection_array = blend_tree.get_connections() + + for i in range(len(connection_array) / 3): + var source_node:BLTAnimationNode = connection_array[i * 3] + var target_node:BLTAnimationNode = connection_array[i * 3 + 1] + var target_port = connection_array[i * 3 + 2] + + var source_graph_node = blend_tree_node_to_graph_node[source_node] + + var connect_result = blend_tree_graph_edit.connect_node(source_node.resource_name, 0, target_node.resource_name, target_node.get_input_index(target_port), true) + + +func _update_editor_from_blend_tree(): + _update_editor_nodes_from_blend_tree() + _update_editor_connections_from_blend_tree() + + +func _on_save_button_pressed() -> void: + ResourceSaver.save(blend_tree, "res://" + file_name_line_edit.text) + + +func _on_load_button_pressed() -> void: + var loaded_blend_tree:BLTAnimationNodeBlendTree = ResourceLoader.load("res://" + file_name_line_edit.text, "BLTAnimationNodeBlendTree", 0) + + if loaded_blend_tree: + _reset_editor() + blend_tree = loaded_blend_tree + _update_editor_from_blend_tree() + + +func _on_instantiate_tree_button_pressed() -> void: + var graph_name = tree_option_button.get_item_text(tree_option_button.selected) + + if graph_name == "AnimationSampler": + _reset_editor() + + var sampler_node:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new() + sampler_node.animation = "SampleLibrary/Idle" + + blend_tree.add_node(sampler_node) + blend_tree.add_connection(sampler_node, blend_tree.get_output_node(), "Output") + + _update_editor_from_blend_tree() + elif graph_name == "Blend2": + _reset_editor() + + var sampler_node_walk:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new() + sampler_node_walk.animation = "SampleLibrary/Walk" + + var sampler_node_run:BLTAnimationNodeSampler = BLTAnimationNodeSampler.new() + sampler_node_run.animation = "SampleLibrary/Run" + + var blend2_node:BLTAnimationNodeBlend2 = BLTAnimationNodeBlend2.new() + + blend_tree.add_node(sampler_node_walk) + blend_tree.add_node(sampler_node_run) + blend_tree.add_node(blend2_node) + + blend_tree.add_connection(blend2_node, blend_tree.get_output_node(), "Output") + + blend_tree.add_connection(sampler_node_walk, blend2_node, "Input0") + blend_tree.add_connection(sampler_node_run, blend2_node, "Input1") + + _update_editor_from_blend_tree() + + +func _on_blend_tree_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void: + print("Trying to connect '%s' port %d to node '%s' port %d" % [from_node, from_port, to_node, to_port]) + + var source_node:BLTAnimationNode = blend_tree.get_node(from_node) + var target_node:BLTAnimationNode = blend_tree.get_node(to_node) + + if target_node == null: + push_error("Invalid connection, target node %s not found." % to_node) + return + + var target_node_port_name = target_node.get_input_names()[to_port] + + var connection_result = blend_tree.is_connection_valid(source_node, target_node, target_node_port_name) + if connection_result != blend_tree.CONNECTION_OK: + push_error("Could not add connection (error %d)" % connection_result) + return + + blend_tree.add_connection(source_node, target_node, target_node_port_name) + + var connect_result = blend_tree_graph_edit.connect_node(from_node, from_port, to_node, to_port, true) + print("graph connect result: " + str(connect_result)) + + print("Success!") + + +func _on_blend_tree_graph_edit_end_node_move() -> void: + for graph_node:GraphNode in selected_nodes.keys(): + graph_node_to_blend_tree_node[graph_node].position = graph_node.position + + +func _on_blend_tree_graph_edit_begin_node_move() -> void: + pass # Replace with function body. + + +func _on_blend_tree_graph_edit_node_selected(node: Node) -> void: + selected_nodes[node] = node + + +func _on_blend_tree_graph_edit_node_deselected(node: Node) -> void: + if selected_nodes.has(node): + selected_nodes.erase(node) + + +func _on_blend_tree_graph_edit_popup_request(at_position: Vector2) -> void: + add_node_popup_menu.position = get_screen_position() + get_local_mouse_position() + add_node_popup_menu.reset_size() + add_node_popup_menu.popup() + new_node_position = at_position + + +func _on_add_node_popup_menu_index_pressed(index: int) -> void: + var new_blend_tree_node: BLTAnimationNode = ClassDB.instantiate(registered_nodes[index]) + blend_tree.add_node(new_blend_tree_node) + var graph_node:GraphNode = create_node_for_blt_node(new_blend_tree_node) + + graph_node_to_blend_tree_node[graph_node] = new_blend_tree_node + blend_tree_node_to_graph_node[new_blend_tree_node] = graph_node + + blend_tree_graph_edit.add_child(graph_node) + + if new_node_position != Vector2.INF: + graph_node.position_offset = new_node_position + + new_node_position = Vector2.INF diff --git a/demo/addons/blendalot/blendalot_main_panel.tscn b/demo/addons/blendalot/blendalot_main_panel.tscn index 796e64c..9ed5e99 100644 --- a/demo/addons/blendalot/blendalot_main_panel.tscn +++ b/demo/addons/blendalot/blendalot_main_panel.tscn @@ -13,7 +13,7 @@ size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1_427jg") -[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=2044593527] +[node name="BlendTreeEditorContainer" type="VBoxContainer" parent="." unique_id=2044593527] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -21,13 +21,78 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="HitMeButton" type="Button" parent="VBoxContainer" unique_id=1060776498] +[node name="HBoxContainer" type="HBoxContainer" parent="BlendTreeEditorContainer" unique_id=1742071203] +layout_mode = 2 + +[node name="ResetGraphButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=1060776498] layout_mode = 2 size_flags_horizontal = 0 -text = "Hit me!" +text = "Reset" -[node name="GraphFrame" type="GraphFrame" parent="VBoxContainer" unique_id=184673233] +[node name="FileNameLineEdit" type="LineEdit" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=2033619565] +unique_name_in_owner = true +custom_minimum_size = Vector2(250, 0) +layout_mode = 2 +text = "editor_test_tree.tres" + +[node name="SaveButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=597228263] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Save +" + +[node name="LoadButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=559146765] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Load +" + +[node name="AddNodeButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=985452697] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Add Node" + +[node name="TreeOptionButton" type="OptionButton" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=760974122] +unique_name_in_owner = true +layout_mode = 2 +selected = 0 +item_count = 2 +popup/item_0/text = "AnimationSampler" +popup/item_0/id = 1 +popup/item_1/text = "Blend2" +popup/item_1/id = 2 + +[node name="InstantiateTreeButton" type="Button" parent="BlendTreeEditorContainer/HBoxContainer" unique_id=127759440] +layout_mode = 2 +size_flags_horizontal = 0 +text = "Instantiate" + +[node name="Panel" type="Panel" parent="BlendTreeEditorContainer" unique_id=424652158] layout_mode = 2 size_flags_vertical = 3 -[connection signal="pressed" from="VBoxContainer/HitMeButton" to="." method="_on_hit_me_button_pressed"] +[node name="AddNodePopupMenu" type="PopupMenu" parent="BlendTreeEditorContainer/Panel" unique_id=2020489213] +unique_name_in_owner = true +oversampling_override = 1.0 + +[node name="BlendTreeGraphEdit" type="GraphEdit" parent="BlendTreeEditorContainer/Panel" unique_id=387715755] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/ResetGraphButton" to="." method="_on_reset_graph_button_pressed"] +[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/SaveButton" to="." method="_on_save_button_pressed"] +[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/LoadButton" to="." method="_on_load_button_pressed"] +[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/AddNodeButton" to="." method="_on_add_node_button_pressed"] +[connection signal="pressed" from="BlendTreeEditorContainer/HBoxContainer/InstantiateTreeButton" to="." method="_on_instantiate_tree_button_pressed"] +[connection signal="index_pressed" from="BlendTreeEditorContainer/Panel/AddNodePopupMenu" to="." method="_on_add_node_popup_menu_index_pressed"] +[connection signal="begin_node_move" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_begin_node_move"] +[connection signal="connection_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_connection_request"] +[connection signal="end_node_move" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_end_node_move"] +[connection signal="node_deselected" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_deselected"] +[connection signal="node_selected" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_node_selected"] +[connection signal="popup_request" from="BlendTreeEditorContainer/Panel/BlendTreeGraphEdit" to="." method="_on_blend_tree_graph_edit_popup_request"] diff --git a/demo/addons/blendalot/blendalot_plugin.gd b/demo/addons/blendalot/blendalot_plugin.gd index 47c4336..f794e23 100644 --- a/demo/addons/blendalot/blendalot_plugin.gd +++ b/demo/addons/blendalot/blendalot_plugin.gd @@ -43,3 +43,7 @@ func _get_plugin_name(): func _get_plugin_icon(): return EditorInterface.get_editor_theme().get_icon("Node", "EditorIcons") + + +func _handles(obj: Object) -> bool: + return obj is BLTAnimationNodeBlendTree diff --git a/demo/project.godot b/demo/project.godot index 69fbb8d..a54ee6f 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -23,3 +23,7 @@ window/size/viewport_height=1024 [dotnet] project/assembly_name="Synced Blend Tree Test" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/blendalot/plugin.cfg")