From 9fba852a5f204f7a1dac5685f84e5a3e521870ef Mon Sep 17 00:00:00 2001 From: LMW Date: Wed, 31 Dec 2025 18:42:39 +0800 Subject: [PATCH] Animated highlighted connections --- material_maker/nodes/base.gd | 3 + .../panels/graph_edit/graph_edit.gd | 14 --- .../panels/graph_edit/graph_edit.tscn | 18 ++- .../panels/graph_edit/highlights_overlay.gd | 109 ++++++++++++++++++ .../graph_edit/highlights_overlay.gd.uid | 1 + 5 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 material_maker/panels/graph_edit/highlights_overlay.gd create mode 100644 material_maker/panels/graph_edit/highlights_overlay.gd.uid diff --git a/material_maker/nodes/base.gd b/material_maker/nodes/base.gd index fb070f3eb..4436b9d3b 100644 --- a/material_maker/nodes/base.gd +++ b/material_maker/nodes/base.gd @@ -252,6 +252,9 @@ func _draw() -> void: if control.get("modulate"): control.modulate.a = opacity + get_parent().get_node("HighlightsOverlay").update_connections() + + func draw_portgroup_stylebox(first_port : Vector2, last_port : Vector2) -> void: var stylebox_position: Vector2 = first_port + Vector2(-0.5,-0.5) * portgroup_width var stylebox_size: Vector2 = Vector2(portgroup_width, last_port.y - first_port.y + portgroup_width) diff --git a/material_maker/panels/graph_edit/graph_edit.gd b/material_maker/panels/graph_edit/graph_edit.gd index d9bb53344..d28d5337b 100644 --- a/material_maker/panels/graph_edit/graph_edit.gd +++ b/material_maker/panels/graph_edit/graph_edit.gd @@ -1163,18 +1163,6 @@ func _on_ButtonTransmitsSeed_toggled(button_pressed) -> void: # Node selection -var highlighting_connections : bool = false - -func highlight_connections() -> void: - if highlighting_connections: - return - highlighting_connections = true - while Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): - await get_tree().process_frame - for c in get_connection_list(): - set_connection_activity(c.from_node, c.from_port, c.to_node, c.to_port, 1.0 if get_node(NodePath(c.from_node)).selected or get_node(NodePath(c.to_node)).selected else 0.0) - highlighting_connections = false - func _on_GraphEdit_node_selected(node : GraphElement) -> void: if node is MMGraphComment: #print("Selecting enclosed nodes...") @@ -1185,7 +1173,6 @@ func _on_GraphEdit_node_selected(node : GraphElement) -> void: elif node is MMGraphCommentLine: pass else: - highlight_connections() await get_tree().process_frame if current_preview[0] != null: for n in get_selected_nodes(): @@ -1204,7 +1191,6 @@ func _on_GraphEdit_node_selected(node : GraphElement) -> void: mm_globals.main_window.update_menus() func _on_GraphEdit_node_unselected(_node): - highlight_connections() undoredo_move_node_selection_changed = true mm_globals.main_window.update_menus() diff --git a/material_maker/panels/graph_edit/graph_edit.tscn b/material_maker/panels/graph_edit/graph_edit.tscn index 2b27c379d..0966eeadb 100644 --- a/material_maker/panels/graph_edit/graph_edit.tscn +++ b/material_maker/panels/graph_edit/graph_edit.tscn @@ -5,6 +5,7 @@ [ext_resource type="Script" uid="uid://bne3k0g56crmy" path="res://material_maker/tools/undo_redo/undo_redo.gd" id="3"] [ext_resource type="PackedScene" uid="uid://buj231c2gxm4o" path="res://material_maker/widgets/desc_button/desc_button.tscn" id="4"] [ext_resource type="Texture2D" uid="uid://1s0c37uoj4rf" path="res://material_maker/theme/default_theme_icons.svg" id="4_u5byk"] +[ext_resource type="Script" uid="uid://dlah77kjy12l6" path="res://material_maker/panels/graph_edit/highlights_overlay.gd" id="5_u5byk"] [sub_resource type="AtlasTexture" id="3"] atlas = ExtResource("4_u5byk") @@ -22,7 +23,17 @@ region = Rect2(96, 64, 16, 16) atlas = ExtResource("2") region = Rect2(0, 48, 16, 16) -[node name="GraphEdit" type="GraphEdit" unique_id=909518343 groups=["mm_graphedit"]] +[sub_resource type="Shader" id="Shader_u5byk"] +code = "shader_type canvas_item; + +void fragment() { + COLOR = vec4(vec3(0.0), 0.25 * sin(UV.x - TIME * TAU) + 0.25); +}" + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_nlxn2"] +shader = SubResource("Shader_u5byk") + +[node name="GraphEdit" type="GraphEdit" unique_id=175822157 groups=["mm_graphedit"]] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -102,6 +113,11 @@ layout_mode = 2 [node name="UndoRedo" type="Node" parent="." unique_id=118784873] script = ExtResource("3") +[node name="HighlightsOverlay" type="Control" parent="." unique_id=2128156926] +anchors_preset = 0 +script = ExtResource("5_u5byk") +line_material = SubResource("ShaderMaterial_nlxn2") + [connection signal="connection_drag_ended" from="." to="." method="_on_connection_drag_ended"] [connection signal="connection_drag_started" from="." to="." method="_on_connection_drag_started"] [connection signal="connection_from_empty" from="." to="." method="request_popup" binds= [true]] diff --git a/material_maker/panels/graph_edit/highlights_overlay.gd b/material_maker/panels/graph_edit/highlights_overlay.gd new file mode 100644 index 000000000..35d3eaf24 --- /dev/null +++ b/material_maker/panels/graph_edit/highlights_overlay.gd @@ -0,0 +1,109 @@ +class_name HighlightsOverlay +extends Control + + +class AnimatedConnection extends Line2D: + var connection : Dictionary + +@onready var graph : MMGraphEdit = owner +@export var line_material : ShaderMaterial + +var active_connections : Array[Dictionary] +var is_updating_connections : bool = false + +func _ready() -> void: + graph.child_order_changed.connect(move_above_connections.call_deferred) + graph.get_node("_connection_layer").draw.connect(queue_redraw) + graph.get_node("_connection_layer").child_order_changed.connect(update_connections) + graph.connection_drag_started.connect(update_connections.bind(1).unbind(3)) + graph.node_selected.connect(update_connections.unbind(1)) + graph.node_deselected.connect(update_connections.unbind(1)) + graph.gui_input.connect(graph_gui_input) + +func _draw() -> void: + if get_children().is_empty(): + return + + for line : AnimatedConnection in get_children(): + var conn : Dictionary = line.connection + var positions : Dictionary[String, Vector2] = get_connection_positions(conn) + if positions.is_empty(): + continue + + line.visible = get_viewport_rect().intersects( + Rect2(positions.from, positions.to - positions.from).abs()) + + line.points = graph._get_connection_line(positions.from, positions.to) + line.width = graph.connection_lines_thickness + +func move_above_connections() -> void: + graph.move_child(self, graph.get_node("_connection_layer").get_index() + 1) + +func graph_gui_input(event : InputEvent) -> void: + if event is InputEventKey: + update_connections(true) + +func update_connections(should_hide_when_updating : bool = false) -> void: + if is_updating_connections: + return + is_updating_connections = true + + if should_hide_when_updating: + hide() + + while Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT): + await get_tree().process_frame + + active_connections.clear() + for c in graph.get_connection_list(): + if (graph.get_node(NodePath(c.from_node)).selected + or graph.get_node(NodePath(c.to_node)).selected): + active_connections.append(c) + await get_tree().process_frame + create_animated_connections() + + # Remove inactive/invalid animated conections + if get_children().size(): + for anim_line : AnimatedConnection in get_children(): + if (anim_line.connection not in active_connections + or get_connection_positions(anim_line.connection).is_empty()): + remove_child(anim_line) + anim_line.free() + + queue_redraw() + is_updating_connections = false + show.call_deferred() + +func create_animated_connections() -> void: + for active_connection in active_connections: + var positions : Dictionary[String, Vector2] = get_connection_positions(active_connection) + if positions.is_empty(): + continue + + var has_existing_connection : bool = false + for c in get_children(): + if c.connection == active_connection: + has_existing_connection = true + break + + if not has_existing_connection: + var ac : AnimatedConnection = AnimatedConnection.new() + ac.connection = active_connection + ac.texture_mode = Line2D.LINE_TEXTURE_TILE + ac.material = line_material + ac.points = graph._get_connection_line(positions.from, positions.to) + ac.width = graph.connection_lines_thickness + add_child(ac) + +func get_connection_positions(from_connection : Dictionary) -> Dictionary[String, Vector2]: + var c : Dictionary = from_connection + var positions : Dictionary[String, Vector2] + if graph.has_node(NodePath(c.from_node)) and graph.has_node(NodePath(c.to_node)): + var from_node : MMGraphNodeMinimal = graph.get_node(NodePath(c.from_node)) + var to_node : MMGraphNodeMinimal = graph.get_node(NodePath(c.to_node)) + if from_node and to_node: + var from_pos : Vector2 = from_node.get_output_port_position(c.from_port) + var to_pos : Vector2 = to_node.get_input_port_position(c.to_port) + positions.from = from_pos * graph.zoom + from_node.position + positions.to = to_pos * graph.zoom + to_node.position + return positions diff --git a/material_maker/panels/graph_edit/highlights_overlay.gd.uid b/material_maker/panels/graph_edit/highlights_overlay.gd.uid new file mode 100644 index 000000000..b8762680f --- /dev/null +++ b/material_maker/panels/graph_edit/highlights_overlay.gd.uid @@ -0,0 +1 @@ +uid://dlah77kjy12l6