1 | /**************************************************************************/ |
2 | /* navigation_obstacle_3d_editor_plugin.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "navigation_obstacle_3d_editor_plugin.h" |
32 | |
33 | #include "canvas_item_editor_plugin.h" |
34 | #include "core/core_string_names.h" |
35 | #include "core/input/input.h" |
36 | #include "core/io/file_access.h" |
37 | #include "core/math/geometry_2d.h" |
38 | #include "core/os/keyboard.h" |
39 | #include "editor/editor_node.h" |
40 | #include "editor/editor_settings.h" |
41 | #include "editor/editor_string_names.h" |
42 | #include "editor/editor_undo_redo_manager.h" |
43 | #include "node_3d_editor_plugin.h" |
44 | #include "scene/3d/camera_3d.h" |
45 | #include "scene/gui/separator.h" |
46 | |
47 | void NavigationObstacle3DEditor::_notification(int p_what) { |
48 | switch (p_what) { |
49 | case NOTIFICATION_READY: { |
50 | button_create->set_icon(get_editor_theme_icon(SNAME("Edit" ))); |
51 | button_edit->set_icon(get_editor_theme_icon(SNAME("MovePoint" ))); |
52 | button_edit->set_pressed(true); |
53 | get_tree()->connect("node_removed" , callable_mp(this, &NavigationObstacle3DEditor::_node_removed)); |
54 | |
55 | } break; |
56 | } |
57 | } |
58 | |
59 | void NavigationObstacle3DEditor::_node_removed(Node *p_node) { |
60 | if (p_node == obstacle_node) { |
61 | obstacle_node = nullptr; |
62 | if (point_lines_meshinstance->get_parent() == p_node) { |
63 | p_node->remove_child(point_lines_meshinstance); |
64 | } |
65 | hide(); |
66 | } |
67 | } |
68 | |
69 | void NavigationObstacle3DEditor::(int p_option) { |
70 | switch (p_option) { |
71 | case MODE_CREATE: { |
72 | mode = MODE_CREATE; |
73 | button_create->set_pressed(true); |
74 | button_edit->set_pressed(false); |
75 | } break; |
76 | case MODE_EDIT: { |
77 | mode = MODE_EDIT; |
78 | button_create->set_pressed(false); |
79 | button_edit->set_pressed(true); |
80 | } break; |
81 | } |
82 | } |
83 | |
84 | void NavigationObstacle3DEditor::_wip_close() { |
85 | ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid." ); |
86 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
87 | undo_redo->create_action(TTR("Set NavigationObstacle3D Vertices" )); |
88 | undo_redo->add_undo_method(obstacle_node, "set_vertices" , obstacle_node->get_vertices()); |
89 | |
90 | PackedVector3Array polygon_3d_vertices; |
91 | Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip); |
92 | |
93 | if (!triangulated_polygon_2d_indices.is_empty()) { |
94 | polygon_3d_vertices.resize(wip.size()); |
95 | Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw(); |
96 | for (int i = 0; i < wip.size(); i++) { |
97 | const Vector2 &vert = wip[i]; |
98 | polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y); |
99 | } |
100 | } |
101 | |
102 | undo_redo->add_do_method(obstacle_node, "set_vertices" , polygon_3d_vertices); |
103 | undo_redo->add_do_method(this, "_polygon_draw" ); |
104 | undo_redo->add_undo_method(this, "_polygon_draw" ); |
105 | wip.clear(); |
106 | wip_active = false; |
107 | mode = MODE_EDIT; |
108 | button_edit->set_pressed(true); |
109 | button_create->set_pressed(false); |
110 | edited_point = -1; |
111 | undo_redo->commit_action(); |
112 | } |
113 | |
114 | EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { |
115 | if (!obstacle_node) { |
116 | return EditorPlugin::AFTER_GUI_INPUT_PASS; |
117 | } |
118 | |
119 | Transform3D gt = obstacle_node->get_global_transform(); |
120 | Transform3D gi = gt.affine_inverse(); |
121 | Plane p(Vector3(0.0, 1.0, 0.0), gt.origin); |
122 | |
123 | Ref<InputEventMouseButton> mb = p_event; |
124 | |
125 | if (mb.is_valid()) { |
126 | Vector2 gpoint = mb->get_position(); |
127 | Vector3 ray_from = p_camera->project_ray_origin(gpoint); |
128 | Vector3 ray_dir = p_camera->project_ray_normal(gpoint); |
129 | |
130 | Vector3 spoint; |
131 | |
132 | if (!p.intersects_ray(ray_from, ray_dir, &spoint)) { |
133 | return EditorPlugin::AFTER_GUI_INPUT_PASS; |
134 | } |
135 | |
136 | spoint = gi.xform(spoint); |
137 | |
138 | Vector2 cpoint(spoint.x, spoint.z); |
139 | |
140 | //DO NOT snap here, it's confusing in 3D for adding points. |
141 | //Let the snap happen when the point is being moved, instead. |
142 | //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); |
143 | |
144 | PackedVector2Array poly = _get_polygon(); |
145 | |
146 | //first check if a point is to be added (segment split) |
147 | real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius" ); |
148 | |
149 | switch (mode) { |
150 | case MODE_CREATE: { |
151 | if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { |
152 | if (!wip_active) { |
153 | wip.clear(); |
154 | wip.push_back(cpoint); |
155 | wip_active = true; |
156 | edited_point_pos = cpoint; |
157 | snap_ignore = false; |
158 | _polygon_draw(); |
159 | edited_point = 1; |
160 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
161 | } else { |
162 | if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) { |
163 | //wip closed |
164 | _wip_close(); |
165 | |
166 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
167 | } else { |
168 | wip.push_back(cpoint); |
169 | edited_point = wip.size(); |
170 | snap_ignore = false; |
171 | _polygon_draw(); |
172 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
173 | } |
174 | } |
175 | } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) { |
176 | _wip_close(); |
177 | } |
178 | |
179 | } break; |
180 | |
181 | case MODE_EDIT: { |
182 | if (mb->get_button_index() == MouseButton::LEFT) { |
183 | if (mb->is_pressed()) { |
184 | if (mb->is_ctrl_pressed()) { |
185 | if (poly.size() < 3) { |
186 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
187 | undo_redo->create_action(TTR("Edit Vertices" )); |
188 | undo_redo->add_undo_method(obstacle_node, "set_vertices" , obstacle_node->get_vertices()); |
189 | poly.push_back(cpoint); |
190 | undo_redo->add_do_method(this, "_polygon_draw" ); |
191 | undo_redo->add_undo_method(this, "_polygon_draw" ); |
192 | undo_redo->commit_action(); |
193 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
194 | } |
195 | |
196 | //search edges |
197 | int closest_idx = -1; |
198 | Vector2 closest_pos; |
199 | real_t closest_dist = 1e10; |
200 | for (int i = 0; i < poly.size(); i++) { |
201 | Vector2 points[2] = { |
202 | p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))), |
203 | p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y))) |
204 | }; |
205 | |
206 | Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points); |
207 | if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) { |
208 | continue; //not valid to reuse point |
209 | } |
210 | |
211 | real_t d = cp.distance_to(gpoint); |
212 | if (d < closest_dist && d < grab_threshold) { |
213 | closest_dist = d; |
214 | closest_pos = cp; |
215 | closest_idx = i; |
216 | } |
217 | } |
218 | |
219 | if (closest_idx >= 0) { |
220 | pre_move_edit = poly; |
221 | poly.insert(closest_idx + 1, cpoint); |
222 | edited_point = closest_idx + 1; |
223 | edited_point_pos = cpoint; |
224 | _set_polygon(poly); |
225 | _polygon_draw(); |
226 | snap_ignore = true; |
227 | |
228 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
229 | } |
230 | } else { |
231 | //look for points to move |
232 | |
233 | int closest_idx = -1; |
234 | Vector2 closest_pos; |
235 | real_t closest_dist = 1e10; |
236 | for (int i = 0; i < poly.size(); i++) { |
237 | Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))); |
238 | |
239 | real_t d = cp.distance_to(gpoint); |
240 | if (d < closest_dist && d < grab_threshold) { |
241 | closest_dist = d; |
242 | closest_pos = cp; |
243 | closest_idx = i; |
244 | } |
245 | } |
246 | |
247 | if (closest_idx >= 0) { |
248 | pre_move_edit = poly; |
249 | edited_point = closest_idx; |
250 | edited_point_pos = poly[closest_idx]; |
251 | _polygon_draw(); |
252 | snap_ignore = false; |
253 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
254 | } |
255 | } |
256 | } else { |
257 | snap_ignore = false; |
258 | |
259 | if (edited_point != -1) { |
260 | //apply |
261 | |
262 | ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS); |
263 | poly.write[edited_point] = edited_point_pos; |
264 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
265 | undo_redo->create_action(TTR("Edit Poly" )); |
266 | //undo_redo->add_do_method(obj, "set_polygon", poly); |
267 | //undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit); |
268 | undo_redo->add_do_method(this, "_polygon_draw" ); |
269 | undo_redo->add_undo_method(this, "_polygon_draw" ); |
270 | undo_redo->commit_action(); |
271 | |
272 | edited_point = -1; |
273 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
274 | } |
275 | } |
276 | } |
277 | if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) { |
278 | int closest_idx = -1; |
279 | Vector2 closest_pos; |
280 | real_t closest_dist = 1e10; |
281 | for (int i = 0; i < poly.size(); i++) { |
282 | Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))); |
283 | |
284 | real_t d = cp.distance_to(gpoint); |
285 | if (d < closest_dist && d < grab_threshold) { |
286 | closest_dist = d; |
287 | closest_pos = cp; |
288 | closest_idx = i; |
289 | } |
290 | } |
291 | |
292 | if (closest_idx >= 0) { |
293 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
294 | undo_redo->create_action(TTR("Edit Poly (Remove Point)" )); |
295 | //undo_redo->add_undo_method(obj, "set_polygon", poly); |
296 | poly.remove_at(closest_idx); |
297 | //undo_redo->add_do_method(obj, "set_polygon", poly); |
298 | undo_redo->add_do_method(this, "_polygon_draw" ); |
299 | undo_redo->add_undo_method(this, "_polygon_draw" ); |
300 | undo_redo->commit_action(); |
301 | return EditorPlugin::AFTER_GUI_INPUT_STOP; |
302 | } |
303 | } |
304 | |
305 | } break; |
306 | } |
307 | } |
308 | |
309 | Ref<InputEventMouseMotion> mm = p_event; |
310 | |
311 | if (mm.is_valid()) { |
312 | if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { |
313 | Vector2 gpoint = mm->get_position(); |
314 | |
315 | Vector3 ray_from = p_camera->project_ray_origin(gpoint); |
316 | Vector3 ray_dir = p_camera->project_ray_normal(gpoint); |
317 | |
318 | Vector3 spoint; |
319 | |
320 | if (!p.intersects_ray(ray_from, ray_dir, &spoint)) { |
321 | return EditorPlugin::AFTER_GUI_INPUT_PASS; |
322 | } |
323 | |
324 | spoint = gi.xform(spoint); |
325 | |
326 | Vector2 cpoint(spoint.x, spoint.z); |
327 | |
328 | if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) { |
329 | snap_ignore = false; |
330 | } |
331 | |
332 | if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) { |
333 | cpoint = cpoint.snapped(Vector2( |
334 | Node3DEditor::get_singleton()->get_translate_snap(), |
335 | Node3DEditor::get_singleton()->get_translate_snap())); |
336 | } |
337 | edited_point_pos = cpoint; |
338 | |
339 | _polygon_draw(); |
340 | } |
341 | } |
342 | |
343 | return EditorPlugin::AFTER_GUI_INPUT_PASS; |
344 | } |
345 | |
346 | PackedVector2Array NavigationObstacle3DEditor::_get_polygon() { |
347 | ERR_FAIL_NULL_V_MSG(obstacle_node, PackedVector2Array(), "Edited object is not valid." ); |
348 | return PackedVector2Array(obstacle_node->call("get_polygon" )); |
349 | } |
350 | |
351 | void NavigationObstacle3DEditor::_set_polygon(PackedVector2Array p_poly) { |
352 | ERR_FAIL_NULL_MSG(obstacle_node, "Edited object is not valid." ); |
353 | obstacle_node->call("set_polygon" , p_poly); |
354 | } |
355 | |
356 | void NavigationObstacle3DEditor::_polygon_draw() { |
357 | if (!obstacle_node) { |
358 | return; |
359 | } |
360 | |
361 | PackedVector2Array poly; |
362 | PackedVector3Array polygon_3d_vertices; |
363 | |
364 | if (wip_active) { |
365 | poly = wip; |
366 | } else { |
367 | poly = _get_polygon(); |
368 | } |
369 | polygon_3d_vertices.resize(poly.size()); |
370 | Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw(); |
371 | |
372 | for (int i = 0; i < poly.size(); i++) { |
373 | const Vector2 &vert = poly[i]; |
374 | polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y); |
375 | } |
376 | |
377 | point_handle_mesh->clear_surfaces(); |
378 | point_lines_mesh->clear_surfaces(); |
379 | point_lines_meshinstance->set_material_override(line_material); |
380 | point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES); |
381 | |
382 | Rect2 rect; |
383 | |
384 | for (int i = 0; i < poly.size(); i++) { |
385 | Vector2 p, p2; |
386 | if (i == edited_point) { |
387 | p = edited_point_pos; |
388 | } else { |
389 | p = poly[i]; |
390 | } |
391 | |
392 | if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) { |
393 | p2 = edited_point_pos; |
394 | } else { |
395 | p2 = poly[(i + 1) % poly.size()]; |
396 | } |
397 | |
398 | if (i == 0) { |
399 | rect.position = p; |
400 | } else { |
401 | rect.expand_to(p); |
402 | } |
403 | |
404 | Vector3 point = Vector3(p.x, 0.0, p.y); |
405 | Vector3 next_point = Vector3(p2.x, 0.0, p2.y); |
406 | |
407 | point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8)); |
408 | point_lines_mesh->surface_add_vertex(point); |
409 | point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8)); |
410 | point_lines_mesh->surface_add_vertex(next_point); |
411 | |
412 | //Color col=Color(1,0.3,0.1,0.8); |
413 | //vpc->draw_line(point,next_point,col,2); |
414 | //vpc->draw_texture(handle,point-handle->get_size()*0.5); |
415 | } |
416 | |
417 | rect = rect.grow(1); |
418 | |
419 | AABB r; |
420 | r.position.x = rect.position.x; |
421 | r.position.y = 0.0; |
422 | r.position.z = rect.position.y; |
423 | r.size.x = rect.size.x; |
424 | r.size.y = 0; |
425 | r.size.z = rect.size.y; |
426 | |
427 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
428 | point_lines_mesh->surface_add_vertex(r.position); |
429 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
430 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0)); |
431 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
432 | point_lines_mesh->surface_add_vertex(r.position); |
433 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
434 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0)); |
435 | |
436 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
437 | point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); |
438 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
439 | point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0)); |
440 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
441 | point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); |
442 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
443 | point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0)); |
444 | |
445 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
446 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); |
447 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
448 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0)); |
449 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
450 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); |
451 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
452 | point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0)); |
453 | |
454 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
455 | point_lines_mesh->surface_add_vertex(r.position + r.size); |
456 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
457 | point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0)); |
458 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
459 | point_lines_mesh->surface_add_vertex(r.position + r.size); |
460 | point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); |
461 | point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0)); |
462 | |
463 | point_lines_mesh->surface_end(); |
464 | |
465 | if (poly.size() == 0) { |
466 | return; |
467 | } |
468 | |
469 | Array point_handle_mesh_array; |
470 | point_handle_mesh_array.resize(Mesh::ARRAY_MAX); |
471 | Vector<Vector3> point_handle_mesh_vertices; |
472 | |
473 | point_handle_mesh_vertices.resize(poly.size()); |
474 | Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw(); |
475 | |
476 | for (int i = 0; i < poly.size(); i++) { |
477 | Vector2 point_2d; |
478 | Vector2 p2; |
479 | |
480 | if (i == edited_point) { |
481 | point_2d = edited_point_pos; |
482 | } else { |
483 | point_2d = poly[i]; |
484 | } |
485 | |
486 | Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y); |
487 | point_handle_mesh_vertices_ptr[i] = point_handle_3d; |
488 | } |
489 | |
490 | point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices; |
491 | point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array); |
492 | point_handle_mesh->surface_set_material(0, handle_material); |
493 | } |
494 | |
495 | void NavigationObstacle3DEditor::edit(Node *p_node) { |
496 | obstacle_node = Object::cast_to<NavigationObstacle3D>(p_node); |
497 | |
498 | if (obstacle_node) { |
499 | //Enable the pencil tool if the polygon is empty |
500 | if (_get_polygon().is_empty()) { |
501 | _menu_option(MODE_CREATE); |
502 | } |
503 | wip.clear(); |
504 | wip_active = false; |
505 | edited_point = -1; |
506 | p_node->add_child(point_lines_meshinstance); |
507 | _polygon_draw(); |
508 | |
509 | } else { |
510 | obstacle_node = nullptr; |
511 | |
512 | if (point_lines_meshinstance->get_parent()) { |
513 | point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance); |
514 | } |
515 | } |
516 | } |
517 | |
518 | void NavigationObstacle3DEditor::_bind_methods() { |
519 | ClassDB::bind_method(D_METHOD("_polygon_draw" ), &NavigationObstacle3DEditor::_polygon_draw); |
520 | } |
521 | |
522 | NavigationObstacle3DEditor::NavigationObstacle3DEditor() { |
523 | obstacle_node = nullptr; |
524 | |
525 | button_create = memnew(Button); |
526 | button_create->set_flat(true); |
527 | add_child(button_create); |
528 | button_create->connect("pressed" , callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_CREATE)); |
529 | button_create->set_toggle_mode(true); |
530 | |
531 | button_edit = memnew(Button); |
532 | button_edit->set_flat(true); |
533 | add_child(button_edit); |
534 | button_edit->connect("pressed" , callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_EDIT)); |
535 | button_edit->set_toggle_mode(true); |
536 | |
537 | mode = MODE_EDIT; |
538 | wip_active = false; |
539 | point_lines_meshinstance = memnew(MeshInstance3D); |
540 | point_lines_mesh.instantiate(); |
541 | point_lines_meshinstance->set_mesh(point_lines_mesh); |
542 | point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); |
543 | |
544 | line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); |
545 | line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); |
546 | line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); |
547 | line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); |
548 | line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); |
549 | line_material->set_albedo(Color(1, 1, 1)); |
550 | |
551 | handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); |
552 | handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); |
553 | handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); |
554 | handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); |
555 | handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); |
556 | handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); |
557 | Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle" ), EditorStringName(EditorIcons)); |
558 | handle_material->set_point_size(handle->get_width()); |
559 | handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle); |
560 | |
561 | point_handles_meshinstance = memnew(MeshInstance3D); |
562 | point_lines_meshinstance->add_child(point_handles_meshinstance); |
563 | point_handle_mesh.instantiate(); |
564 | point_handles_meshinstance->set_mesh(point_handle_mesh); |
565 | point_handles_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); |
566 | |
567 | snap_ignore = false; |
568 | } |
569 | |
570 | NavigationObstacle3DEditor::~NavigationObstacle3DEditor() { |
571 | memdelete(point_lines_meshinstance); |
572 | } |
573 | |
574 | void NavigationObstacle3DEditorPlugin::edit(Object *p_object) { |
575 | obstacle_editor->edit(Object::cast_to<Node>(p_object)); |
576 | } |
577 | |
578 | bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const { |
579 | return Object::cast_to<NavigationObstacle3D>(p_object); |
580 | } |
581 | |
582 | void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) { |
583 | if (p_visible) { |
584 | obstacle_editor->show(); |
585 | } else { |
586 | obstacle_editor->hide(); |
587 | obstacle_editor->edit(nullptr); |
588 | } |
589 | } |
590 | |
591 | NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() { |
592 | obstacle_editor = memnew(NavigationObstacle3DEditor); |
593 | Node3DEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor); |
594 | |
595 | obstacle_editor->hide(); |
596 | } |
597 | |
598 | NavigationObstacle3DEditorPlugin::~NavigationObstacle3DEditorPlugin() { |
599 | } |
600 | |