-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetwork_utility.gd
More file actions
220 lines (174 loc) · 6.82 KB
/
network_utility.gd
File metadata and controls
220 lines (174 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
@tool
extends EditorPlugin
## Container we add the toolbar to
const container = CONTAINER_SPATIAL_EDITOR_MENU
const RAY_LENGTH:float = 500
const SNAP_DISTANCE:float = 0.1
## The active [NetworkEditorUtility].
var utility:NetworkEditorUtility
## Gizmo instance for a [NetworkInstance].
var network_gizmo:NetworkGizmo = NetworkGizmo.new()
## Currently editing network.
var target:Network:
get:
return target
set(val):
if target == val: # Prevent reinitializing a whole lot. may be redundant.
return
if target and target.redraw.is_connected(_redraw_gizmo.bind()):
# unsubscribe from redraw if we are changing selection
target.redraw.disconnect(_redraw_gizmo.bind())
target = val
if target:
target.redraw.connect(_redraw_gizmo.bind()) # subscribe to redraw
_set_toolbar_visibility(not val == null) # set toolbar visibility to true if it isnt null
var _target_node:NetworkInstance
func _enter_tree() -> void:
# Initialize utility
utility = load("addons/network_utility/Editor/editor_toolbar.tscn").instantiate()
add_control_to_container(container, utility) # add to spatial toolbar
_set_toolbar_visibility(false) # hide by default
# Connect the buttons
utility.link.connect(_on_link.bind())
utility.merge.connect(_on_merge.bind())
utility.remove.connect(_on_remove.bind())
utility.dissolve.connect(_on_dissolve.bind())
utility.subdivide.connect(_on_subdivide.bind())
utility.unlink.connect(_on_unlink.bind())
utility.change_cost_accepted.connect(_change_cost.bind())
# Selection
get_editor_interface()\
.get_selection()\
.selection_changed\
.connect(_selection_changed.bind())
# Gizmo
network_gizmo._plugin = self
add_node_3d_gizmo_plugin(network_gizmo)
func _exit_tree() -> void:
remove_control_from_container(container, utility)
remove_node_3d_gizmo_plugin(network_gizmo)
func _handles(object: Object) -> bool:
return object is NetworkInstance
func _selection_changed() -> void:
# Get selected nodes
var selections = get_editor_interface()\
.get_selection()\
.get_selected_nodes()
# Skip if no selections
if selections.is_empty():
target = null
return
# Set target if network instance
if selections[0] is NetworkInstance:
_target_node = selections[0]
target = selections[0].network
return
target = null
func _forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> int:
# if no utility, pass
if not utility:
return AFTER_GUI_INPUT_PASS
# if no target, pass
if target == null:
return AFTER_GUI_INPUT_PASS
# if not in add mode, pass
if not utility.add_mode:
return AFTER_GUI_INPUT_PASS
# pass if event isn't a mouse button
if not event is InputEventMouseButton:
return AFTER_GUI_INPUT_PASS
# pass if not mouse right click down
if not (event as InputEventMouseButton).button_index == MOUSE_BUTTON_LEFT or \
not (event as InputEventMouseButton).pressed:
return AFTER_GUI_INPUT_PASS
# If we passed all failure conditions, we block and add point
_on_add_point(viewport_camera, event)
return AFTER_GUI_INPUT_STOP
func _set_toolbar_visibility(state:bool) -> void:
if utility:
if state:
utility.show()
else:
utility.hide()
func _on_dissolve() -> void:
if target and network_gizmo.last_modified:
# dissolve selected point
target.dissolve_point(network_gizmo.last_modified)
network_gizmo.last_modified = null
network_gizmo.second_last_modified = null
func _on_remove() -> void:
if target and network_gizmo.last_modified:
# remove selected point
target.remove_point(network_gizmo.last_modified)
network_gizmo.last_modified = null
network_gizmo.second_last_modified = null
func _on_merge() -> void:
if target and network_gizmo.last_modified and network_gizmo.second_last_modified:
# Merge the last two selected points
target.merge_points(network_gizmo.last_modified, network_gizmo.second_last_modified)
network_gizmo.last_modified = null
network_gizmo.second_last_modified = null
func _on_link() -> void:
if target and network_gizmo.last_modified and network_gizmo.second_last_modified:
# Add an edge between the two last selected points
target.add_edge(network_gizmo.last_modified, network_gizmo.second_last_modified)
func _on_subdivide() -> void:
if target and network_gizmo.last_modified and network_gizmo.second_last_modified:
var edge = target.find_edge(network_gizmo.last_modified, network_gizmo.second_last_modified)
if edge:
var middle_node = target.subdivide_edge(edge)
network_gizmo.last_modified = middle_node
func _on_unlink() -> void:
if target and network_gizmo.last_modified and network_gizmo.second_last_modified:
var edge = target.find_edge(network_gizmo.last_modified, network_gizmo.second_last_modified)
if edge:
target.remove_edge(edge)
func _on_add_point(camera: Camera3D, event: InputEventMouseButton) -> void:
_add_point(camera, event.position, utility.portal_mode)
## Add or link nodes.
func _add_point(camera: Camera3D, position:Vector2, portal:bool = false) -> void:
# return if no target
if not _target_node:
return
var hit_pos:Vector3
# Step 1: find hit point
var from = camera.project_ray_origin(position)
var to = from + (camera.project_ray_normal(position) * RAY_LENGTH)
var ray = PhysicsRayQueryParameters3D.create(from, to)
# wait for physics
await _target_node.get_tree().physics_frame
var hits = _target_node.get_world_3d().direct_space_state.intersect_ray(ray)
if hits:
hit_pos = hits["position"]
else:
return
# Step 2: determine if linking to anything else
var linking:bool = false
var link_target:NetworkPoint
var candidates = target.points.filter(func(x:NetworkPoint): return hit_pos.distance_to(x.position) <= SNAP_DISTANCE) # Get all points within distance
candidates.sort_custom(func(a:NetworkPoint, b:NetworkPoint): return hit_pos.distance_to(a.position) < hit_pos.distance_to(b.position)) # sort by distance
# if we have candidates, grab closest one
if not candidates.is_empty():
linking = true
link_target = candidates[0]
# Step 3: perform link or add
if linking: # we are linking
target.add_edge(link_target, network_gizmo.last_modified) # add between last selected and this one
network_gizmo.last_modified = link_target # set last modified to link target for easier chaining
else: # add new node
var new_pt = target.add_point(hit_pos, portal)
# if there is something to link, try linking
if network_gizmo.last_modified:
target.add_edge(new_pt, network_gizmo.last_modified)
network_gizmo.last_modified = new_pt # set last modified so we can chain
utility.reset_portal_mode()
func _change_cost(text:String) -> void:
if target and network_gizmo.last_modified and network_gizmo.second_last_modified:
var edge = target.find_edge(network_gizmo.last_modified, network_gizmo.second_last_modified)
if edge:
edge.cost = text.to_float()
return
push_warning("must select two connected nodes")
func _redraw_gizmo():
if _target_node:
_target_node.update_gizmos()