1 /*************************************************************************/
2 /* path_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 "path_editor_plugin.h"
32
33 #include "core/os/keyboard.h"
34 #include "scene/resources/curve.h"
35 #include "spatial_editor_plugin.h"
36
get_handle_name(int p_idx) const37 String PathSpatialGizmo::get_handle_name(int p_idx) const {
38
39 Ref<Curve3D> c = path->get_curve();
40 if (c.is_null())
41 return "";
42
43 if (p_idx < c->get_point_count()) {
44
45 return TTR("Curve Point #") + itos(p_idx);
46 }
47
48 p_idx = p_idx - c->get_point_count() + 1;
49
50 int idx = p_idx / 2;
51 int t = p_idx % 2;
52 String n = TTR("Curve Point #") + itos(idx);
53 if (t == 0)
54 n += " In";
55 else
56 n += " Out";
57
58 return n;
59 }
get_handle_value(int p_idx)60 Variant PathSpatialGizmo::get_handle_value(int p_idx) {
61
62 Ref<Curve3D> c = path->get_curve();
63 if (c.is_null())
64 return Variant();
65
66 if (p_idx < c->get_point_count()) {
67
68 original = c->get_point_position(p_idx);
69 return original;
70 }
71
72 p_idx = p_idx - c->get_point_count() + 1;
73
74 int idx = p_idx / 2;
75 int t = p_idx % 2;
76
77 Vector3 ofs;
78 if (t == 0)
79 ofs = c->get_point_in(idx);
80 else
81 ofs = c->get_point_out(idx);
82
83 original = ofs + c->get_point_position(idx);
84
85 return ofs;
86 }
set_handle(int p_idx,Camera * p_camera,const Point2 & p_point)87 void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
88
89 Ref<Curve3D> c = path->get_curve();
90 if (c.is_null())
91 return;
92
93 Transform gt = path->get_global_transform();
94 Transform gi = gt.affine_inverse();
95 Vector3 ray_from = p_camera->project_ray_origin(p_point);
96 Vector3 ray_dir = p_camera->project_ray_normal(p_point);
97
98 // Setting curve point positions
99 if (p_idx < c->get_point_count()) {
100
101 Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
102
103 Vector3 inters;
104
105 if (p.intersects_ray(ray_from, ray_dir, &inters)) {
106
107 if (SpatialEditor::get_singleton()->is_snap_enabled()) {
108 float snap = SpatialEditor::get_singleton()->get_translate_snap();
109 inters.snap(Vector3(snap, snap, snap));
110 }
111
112 Vector3 local = gi.xform(inters);
113 c->set_point_position(p_idx, local);
114 }
115
116 return;
117 }
118
119 p_idx = p_idx - c->get_point_count() + 1;
120
121 int idx = p_idx / 2;
122 int t = p_idx % 2;
123
124 Vector3 base = c->get_point_position(idx);
125
126 Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
127
128 Vector3 inters;
129
130 // Setting curve in/out positions
131 if (p.intersects_ray(ray_from, ray_dir, &inters)) {
132
133 if (!PathEditorPlugin::singleton->is_handle_clicked()) {
134 orig_in_length = c->get_point_in(idx).length();
135 orig_out_length = c->get_point_out(idx).length();
136 PathEditorPlugin::singleton->set_handle_clicked(true);
137 }
138
139 Vector3 local = gi.xform(inters) - base;
140 if (SpatialEditor::get_singleton()->is_snap_enabled()) {
141 float snap = SpatialEditor::get_singleton()->get_translate_snap();
142 local.snap(Vector3(snap, snap, snap));
143 }
144
145 if (t == 0) {
146 c->set_point_in(idx, local);
147 if (PathEditorPlugin::singleton->mirror_angle_enabled())
148 c->set_point_out(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length));
149 } else {
150 c->set_point_out(idx, local);
151 if (PathEditorPlugin::singleton->mirror_angle_enabled())
152 c->set_point_in(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_in_length));
153 }
154 }
155 }
156
commit_handle(int p_idx,const Variant & p_restore,bool p_cancel)157 void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
158
159 Ref<Curve3D> c = path->get_curve();
160 if (c.is_null())
161 return;
162
163 UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
164
165 if (p_idx < c->get_point_count()) {
166
167 if (p_cancel) {
168
169 c->set_point_position(p_idx, p_restore);
170 return;
171 }
172 ur->create_action(TTR("Set Curve Point Position"));
173 ur->add_do_method(c.ptr(), "set_point_position", p_idx, c->get_point_position(p_idx));
174 ur->add_undo_method(c.ptr(), "set_point_position", p_idx, p_restore);
175 ur->commit_action();
176
177 return;
178 }
179
180 p_idx = p_idx - c->get_point_count() + 1;
181
182 int idx = p_idx / 2;
183 int t = p_idx % 2;
184
185 if (t == 0) {
186 if (p_cancel) {
187 c->set_point_in(p_idx, p_restore);
188 return;
189 }
190
191 ur->create_action(TTR("Set Curve In Position"));
192 ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
193 ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);
194
195 if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
196 ur->add_do_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_in(idx) : (-c->get_point_in(idx).normalized() * orig_out_length));
197 ur->add_undo_method(c.ptr(), "set_point_out", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_out_length));
198 }
199 ur->commit_action();
200
201 } else {
202 if (p_cancel) {
203 c->set_point_out(idx, p_restore);
204
205 return;
206 }
207
208 ur->create_action(TTR("Set Curve Out Position"));
209 ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
210 ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);
211
212 if (PathEditorPlugin::singleton->mirror_angle_enabled()) {
213 ur->add_do_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -c->get_point_out(idx) : (-c->get_point_out(idx).normalized() * orig_in_length));
214 ur->add_undo_method(c.ptr(), "set_point_in", idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -static_cast<Vector3>(p_restore) : (-static_cast<Vector3>(p_restore).normalized() * orig_in_length));
215 }
216 ur->commit_action();
217 }
218 }
219
redraw()220 void PathSpatialGizmo::redraw() {
221
222 clear();
223
224 Ref<SpatialMaterial> path_material = gizmo_plugin->get_material("path_material", this);
225 Ref<SpatialMaterial> path_thin_material = gizmo_plugin->get_material("path_thin_material", this);
226 Ref<SpatialMaterial> handles_material = gizmo_plugin->get_material("handles");
227
228 Ref<Curve3D> c = path->get_curve();
229 if (c.is_null())
230 return;
231
232 PoolVector<Vector3> v3a = c->tessellate();
233 //PoolVector<Vector3> v3a=c->get_baked_points();
234
235 int v3s = v3a.size();
236 if (v3s == 0)
237 return;
238 Vector<Vector3> v3p;
239 PoolVector<Vector3>::Read r = v3a.read();
240
241 // BUG: the following won't work when v3s, avoid drawing as a temporary workaround.
242 for (int i = 0; i < v3s - 1; i++) {
243
244 v3p.push_back(r[i]);
245 v3p.push_back(r[i + 1]);
246 //v3p.push_back(r[i]);
247 //v3p.push_back(r[i]+Vector3(0,0.2,0));
248 }
249
250 if (v3p.size() > 1) {
251 add_lines(v3p, path_material);
252 add_collision_segments(v3p);
253 }
254
255 if (PathEditorPlugin::singleton->get_edited_path() == path) {
256 v3p.clear();
257 Vector<Vector3> handles;
258 Vector<Vector3> sec_handles;
259
260 for (int i = 0; i < c->get_point_count(); i++) {
261
262 Vector3 p = c->get_point_position(i);
263 handles.push_back(p);
264 if (i > 0) {
265 v3p.push_back(p);
266 v3p.push_back(p + c->get_point_in(i));
267 sec_handles.push_back(p + c->get_point_in(i));
268 }
269
270 if (i < c->get_point_count() - 1) {
271 v3p.push_back(p);
272 v3p.push_back(p + c->get_point_out(i));
273 sec_handles.push_back(p + c->get_point_out(i));
274 }
275 }
276
277 if (v3p.size() > 1) {
278 add_lines(v3p, path_thin_material);
279 }
280 if (handles.size()) {
281 add_handles(handles, handles_material);
282 }
283 if (sec_handles.size()) {
284 add_handles(sec_handles, handles_material, false, true);
285 }
286 }
287 }
288
PathSpatialGizmo(Path * p_path)289 PathSpatialGizmo::PathSpatialGizmo(Path *p_path) {
290
291 path = p_path;
292 set_spatial_node(p_path);
293 }
294
forward_spatial_gui_input(Camera * p_camera,const Ref<InputEvent> & p_event)295 bool PathEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
296
297 if (!path)
298 return false;
299 Ref<Curve3D> c = path->get_curve();
300 if (c.is_null())
301 return false;
302 Transform gt = path->get_global_transform();
303 Transform it = gt.affine_inverse();
304
305 static const int click_dist = 10; //should make global
306
307 Ref<InputEventMouseButton> mb = p_event;
308
309 if (mb.is_valid()) {
310
311 Point2 mbpos(mb->get_position().x, mb->get_position().y);
312
313 if (!mb->is_pressed())
314 set_handle_clicked(false);
315
316 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->get_control()))) {
317 //click into curve, break it down
318 PoolVector<Vector3> v3a = c->tessellate();
319 int idx = 0;
320 int rc = v3a.size();
321 int closest_seg = -1;
322 Vector3 closest_seg_point;
323 float closest_d = 1e20;
324
325 if (rc >= 2) {
326 PoolVector<Vector3>::Read r = v3a.read();
327
328 if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist)
329 return false; //nope, existing
330
331 for (int i = 0; i < c->get_point_count() - 1; i++) {
332 //find the offset and point index of the place to break up
333 int j = idx;
334 if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist)
335 return false; //nope, existing
336
337 while (j < rc && c->get_point_position(i + 1) != r[j]) {
338
339 Vector3 from = r[j];
340 Vector3 to = r[j + 1];
341 real_t cdist = from.distance_to(to);
342 from = gt.xform(from);
343 to = gt.xform(to);
344 if (cdist > 0) {
345 Vector2 s[2];
346 s[0] = p_camera->unproject_position(from);
347 s[1] = p_camera->unproject_position(to);
348 Vector2 inters = Geometry::get_closest_point_to_segment_2d(mbpos, s);
349 float d = inters.distance_to(mbpos);
350
351 if (d < 10 && d < closest_d) {
352
353 closest_d = d;
354 closest_seg = i;
355 Vector3 ray_from = p_camera->project_ray_origin(mbpos);
356 Vector3 ray_dir = p_camera->project_ray_normal(mbpos);
357
358 Vector3 ra, rb;
359 Geometry::get_closest_points_between_segments(ray_from, ray_from + ray_dir * 4096, from, to, ra, rb);
360
361 closest_seg_point = it.xform(rb);
362 }
363 }
364 j++;
365 }
366 if (idx == j)
367 idx++; //force next
368 else
369 idx = j; //swap
370
371 if (j == rc)
372 break;
373 }
374 }
375
376 UndoRedo *ur = editor->get_undo_redo();
377 if (closest_seg != -1) {
378 //subdivide
379
380 ur->create_action(TTR("Split Path"));
381 ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1);
382 ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1);
383 ur->commit_action();
384 return true;
385
386 } else {
387
388 Vector3 org;
389 if (c->get_point_count() == 0)
390 org = path->get_transform().get_origin();
391 else
392 org = gt.xform(c->get_point_position(c->get_point_count() - 1));
393 Plane p(org, p_camera->get_transform().basis.get_axis(2));
394 Vector3 ray_from = p_camera->project_ray_origin(mbpos);
395 Vector3 ray_dir = p_camera->project_ray_normal(mbpos);
396
397 Vector3 inters;
398 if (p.intersects_ray(ray_from, ray_dir, &inters)) {
399
400 ur->create_action(TTR("Add Point to Curve"));
401 ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1);
402 ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
403 ur->commit_action();
404 return true;
405 }
406
407 //add new at pos
408 }
409
410 } else if (mb->is_pressed() && ((mb->get_button_index() == BUTTON_LEFT && curve_del->is_pressed()) || (mb->get_button_index() == BUTTON_RIGHT && curve_edit->is_pressed()))) {
411
412 for (int i = 0; i < c->get_point_count(); i++) {
413 real_t dist_to_p = p_camera->unproject_position(gt.xform(c->get_point_position(i))).distance_to(mbpos);
414 real_t dist_to_p_out = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_out(i))).distance_to(mbpos);
415 real_t dist_to_p_in = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_in(i))).distance_to(mbpos);
416
417 // Find the offset and point index of the place to break up.
418 // Also check for the control points.
419 if (dist_to_p < click_dist) {
420
421 UndoRedo *ur = editor->get_undo_redo();
422 ur->create_action(TTR("Remove Path Point"));
423 ur->add_do_method(c.ptr(), "remove_point", i);
424 ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
425 ur->commit_action();
426 return true;
427 } else if (dist_to_p_out < click_dist) {
428
429 UndoRedo *ur = editor->get_undo_redo();
430 ur->create_action(TTR("Remove Out-Control Point"));
431 ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
432 ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
433 ur->commit_action();
434 return true;
435 } else if (dist_to_p_in < click_dist) {
436
437 UndoRedo *ur = editor->get_undo_redo();
438 ur->create_action(TTR("Remove In-Control Point"));
439 ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
440 ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
441 ur->commit_action();
442 return true;
443 }
444 }
445 }
446 }
447
448 return false;
449 }
450
edit(Object * p_object)451 void PathEditorPlugin::edit(Object *p_object) {
452
453 if (p_object) {
454 path = Object::cast_to<Path>(p_object);
455 if (path) {
456
457 if (path->get_curve().is_valid()) {
458 path->get_curve()->emit_signal("changed");
459 }
460 }
461 } else {
462 Path *pre = path;
463 path = NULL;
464 if (pre) {
465 pre->get_curve()->emit_signal("changed");
466 }
467 }
468 //collision_polygon_editor->edit(Object::cast_to<Node>(p_object));
469 }
470
handles(Object * p_object) const471 bool PathEditorPlugin::handles(Object *p_object) const {
472
473 return p_object->is_class("Path");
474 }
475
make_visible(bool p_visible)476 void PathEditorPlugin::make_visible(bool p_visible) {
477
478 if (p_visible) {
479
480 curve_create->show();
481 curve_edit->show();
482 curve_del->show();
483 curve_close->show();
484 handle_menu->show();
485 sep->show();
486 } else {
487
488 curve_create->hide();
489 curve_edit->hide();
490 curve_del->hide();
491 curve_close->hide();
492 handle_menu->hide();
493 sep->hide();
494
495 {
496 Path *pre = path;
497 path = NULL;
498 if (pre && pre->get_curve().is_valid()) {
499 pre->get_curve()->emit_signal("changed");
500 }
501 }
502 }
503 }
504
_mode_changed(int p_idx)505 void PathEditorPlugin::_mode_changed(int p_idx) {
506
507 curve_create->set_pressed(p_idx == 0);
508 curve_edit->set_pressed(p_idx == 1);
509 curve_del->set_pressed(p_idx == 2);
510 }
511
_close_curve()512 void PathEditorPlugin::_close_curve() {
513
514 Ref<Curve3D> c = path->get_curve();
515 if (c.is_null())
516 return;
517 if (c->get_point_count() < 2)
518 return;
519 c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0));
520 }
521
_handle_option_pressed(int p_option)522 void PathEditorPlugin::_handle_option_pressed(int p_option) {
523
524 PopupMenu *pm;
525 pm = handle_menu->get_popup();
526
527 switch (p_option) {
528 case HANDLE_OPTION_ANGLE: {
529 bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
530 mirror_handle_angle = !is_checked;
531 pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
532 pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
533 } break;
534 case HANDLE_OPTION_LENGTH: {
535 bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
536 mirror_handle_length = !is_checked;
537 pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
538 } break;
539 }
540 }
541
_notification(int p_what)542 void PathEditorPlugin::_notification(int p_what) {
543
544 if (p_what == NOTIFICATION_ENTER_TREE) {
545
546 curve_create->connect("pressed", this, "_mode_changed", make_binds(0));
547 curve_edit->connect("pressed", this, "_mode_changed", make_binds(1));
548 curve_del->connect("pressed", this, "_mode_changed", make_binds(2));
549 curve_close->connect("pressed", this, "_close_curve");
550 }
551 }
552
_bind_methods()553 void PathEditorPlugin::_bind_methods() {
554
555 ClassDB::bind_method(D_METHOD("_mode_changed"), &PathEditorPlugin::_mode_changed);
556 ClassDB::bind_method(D_METHOD("_close_curve"), &PathEditorPlugin::_close_curve);
557 ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &PathEditorPlugin::_handle_option_pressed);
558 }
559
560 PathEditorPlugin *PathEditorPlugin::singleton = NULL;
561
PathEditorPlugin(EditorNode * p_node)562 PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) {
563
564 path = NULL;
565 editor = p_node;
566 singleton = this;
567 mirror_handle_angle = true;
568 mirror_handle_length = true;
569
570 Ref<PathSpatialGizmoPlugin> gizmo_plugin;
571 gizmo_plugin.instance();
572 SpatialEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
573
574 sep = memnew(VSeparator);
575 sep->hide();
576 SpatialEditor::get_singleton()->add_control_to_menu_panel(sep);
577 curve_edit = memnew(ToolButton);
578 curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons"));
579 curve_edit->set_toggle_mode(true);
580 curve_edit->hide();
581 curve_edit->set_focus_mode(Control::FOCUS_NONE);
582 curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
583 SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
584 curve_create = memnew(ToolButton);
585 curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons"));
586 curve_create->set_toggle_mode(true);
587 curve_create->hide();
588 curve_create->set_focus_mode(Control::FOCUS_NONE);
589 curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)"));
590 SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_create);
591 curve_del = memnew(ToolButton);
592 curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete", "EditorIcons"));
593 curve_del->set_toggle_mode(true);
594 curve_del->hide();
595 curve_del->set_focus_mode(Control::FOCUS_NONE);
596 curve_del->set_tooltip(TTR("Delete Point"));
597 SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_del);
598 curve_close = memnew(ToolButton);
599 curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveClose", "EditorIcons"));
600 curve_close->hide();
601 curve_close->set_focus_mode(Control::FOCUS_NONE);
602 curve_close->set_tooltip(TTR("Close Curve"));
603 SpatialEditor::get_singleton()->add_control_to_menu_panel(curve_close);
604
605 PopupMenu *menu;
606
607 handle_menu = memnew(MenuButton);
608 handle_menu->set_text(TTR("Options"));
609 handle_menu->hide();
610 SpatialEditor::get_singleton()->add_control_to_menu_panel(handle_menu);
611
612 menu = handle_menu->get_popup();
613 menu->add_check_item(TTR("Mirror Handle Angles"));
614 menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
615 menu->add_check_item(TTR("Mirror Handle Lengths"));
616 menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
617 menu->connect("id_pressed", this, "_handle_option_pressed");
618
619 curve_edit->set_pressed(true);
620 /*
621 collision_polygon_editor = memnew( PathEditor(p_node) );
622 editor->get_viewport()->add_child(collision_polygon_editor);
623 collision_polygon_editor->set_margin(MARGIN_LEFT,200);
624 collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
625 collision_polygon_editor->set_margin(MARGIN_TOP,0);
626 collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
627 collision_polygon_editor->hide();
628 */
629 }
630
~PathEditorPlugin()631 PathEditorPlugin::~PathEditorPlugin() {
632 }
633
create_gizmo(Spatial * p_spatial)634 Ref<EditorSpatialGizmo> PathSpatialGizmoPlugin::create_gizmo(Spatial *p_spatial) {
635 Ref<PathSpatialGizmo> ref;
636
637 Path *path = Object::cast_to<Path>(p_spatial);
638 if (path) ref = Ref<PathSpatialGizmo>(memnew(PathSpatialGizmo(path)));
639
640 return ref;
641 }
642
get_name() const643 String PathSpatialGizmoPlugin::get_name() const {
644 return "Path";
645 }
646
get_priority() const647 int PathSpatialGizmoPlugin::get_priority() const {
648 return -1;
649 }
650
PathSpatialGizmoPlugin()651 PathSpatialGizmoPlugin::PathSpatialGizmoPlugin() {
652
653 Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8));
654 create_material("path_material", path_color);
655 create_material("path_thin_material", Color(0.5, 0.5, 0.5));
656 create_handle_material("handles");
657 }
658