1/**************************************************************************/
2/* polygon_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 "polygon_3d_editor_plugin.h"
32
33#include "core/input/input.h"
34#include "core/io/file_access.h"
35#include "core/math/geometry_2d.h"
36#include "core/os/keyboard.h"
37#include "editor/editor_node.h"
38#include "editor/editor_settings.h"
39#include "editor/editor_string_names.h"
40#include "editor/editor_undo_redo_manager.h"
41#include "editor/plugins/canvas_item_editor_plugin.h"
42#include "editor/plugins/node_3d_editor_plugin.h"
43#include "scene/3d/camera_3d.h"
44#include "scene/gui/separator.h"
45
46void Polygon3DEditor::_notification(int p_what) {
47 switch (p_what) {
48 case NOTIFICATION_READY: {
49 button_create->set_icon(get_editor_theme_icon(SNAME("Edit")));
50 button_edit->set_icon(get_editor_theme_icon(SNAME("MovePoint")));
51 button_edit->set_pressed(true);
52 get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed));
53
54 } break;
55
56 case NOTIFICATION_PROCESS: {
57 if (!node) {
58 return;
59 }
60
61 if (_get_depth() != prev_depth) {
62 _polygon_draw();
63 prev_depth = _get_depth();
64 }
65
66 } break;
67 }
68}
69
70void Polygon3DEditor::_node_removed(Node *p_node) {
71 if (p_node == node) {
72 node = nullptr;
73 if (imgeom->get_parent() == p_node) {
74 p_node->remove_child(imgeom);
75 }
76 hide();
77 set_process(false);
78 }
79}
80
81void Polygon3DEditor::_menu_option(int p_option) {
82 switch (p_option) {
83 case MODE_CREATE: {
84 mode = MODE_CREATE;
85 button_create->set_pressed(true);
86 button_edit->set_pressed(false);
87 } break;
88 case MODE_EDIT: {
89 mode = MODE_EDIT;
90 button_create->set_pressed(false);
91 button_edit->set_pressed(true);
92 } break;
93 }
94}
95
96void Polygon3DEditor::_wip_close() {
97 Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
98 ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
99 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
100 undo_redo->create_action(TTR("Create Polygon3D"));
101 undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));
102 undo_redo->add_do_method(obj, "set_polygon", wip);
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
114EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
115 if (!node) {
116 return EditorPlugin::AFTER_GUI_INPUT_PASS;
117 }
118
119 Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
120 Transform3D gt = node->get_global_transform();
121 Transform3D gi = gt.affine_inverse();
122 float depth = _get_depth() * 0.5;
123 Vector3 n = gt.basis.get_column(2).normalized();
124 Plane p(n, gt.origin + n * depth);
125
126 Ref<InputEventMouseButton> mb = p_event;
127
128 if (mb.is_valid()) {
129 Vector2 gpoint = mb->get_position();
130 Vector3 ray_from = p_camera->project_ray_origin(gpoint);
131 Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
132
133 Vector3 spoint;
134
135 if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
136 return EditorPlugin::AFTER_GUI_INPUT_PASS;
137 }
138
139 spoint = gi.xform(spoint);
140
141 Vector2 cpoint(spoint.x, spoint.y);
142
143 //DO NOT snap here, it's confusing in 3D for adding points.
144 //Let the snap happen when the point is being moved, instead.
145 //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
146
147 PackedVector2Array poly = _get_polygon();
148
149 //first check if a point is to be added (segment split)
150 real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
151
152 switch (mode) {
153 case MODE_CREATE: {
154 if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
155 if (!wip_active) {
156 wip.clear();
157 wip.push_back(cpoint);
158 wip_active = true;
159 edited_point_pos = cpoint;
160 snap_ignore = false;
161 _polygon_draw();
162 edited_point = 1;
163 return EditorPlugin::AFTER_GUI_INPUT_STOP;
164 } else {
165 if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
166 //wip closed
167 _wip_close();
168
169 return EditorPlugin::AFTER_GUI_INPUT_STOP;
170 } else {
171 wip.push_back(cpoint);
172 edited_point = wip.size();
173 snap_ignore = false;
174 _polygon_draw();
175 return EditorPlugin::AFTER_GUI_INPUT_STOP;
176 }
177 }
178 } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
179 _wip_close();
180 }
181
182 } break;
183
184 case MODE_EDIT: {
185 if (mb->get_button_index() == MouseButton::LEFT) {
186 if (mb->is_pressed()) {
187 if (mb->is_ctrl_pressed()) {
188 if (poly.size() < 3) {
189 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
190 undo_redo->create_action(TTR("Edit Poly"));
191 undo_redo->add_undo_method(obj, "set_polygon", poly);
192 poly.push_back(cpoint);
193 undo_redo->add_do_method(obj, "set_polygon", poly);
194 undo_redo->add_do_method(this, "_polygon_draw");
195 undo_redo->add_undo_method(this, "_polygon_draw");
196 undo_redo->commit_action();
197 return EditorPlugin::AFTER_GUI_INPUT_STOP;
198 }
199
200 //search edges
201 int closest_idx = -1;
202 Vector2 closest_pos;
203 real_t closest_dist = 1e10;
204 for (int i = 0; i < poly.size(); i++) {
205 Vector2 points[2] = {
206 p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth))),
207 p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth)))
208 };
209
210 Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points);
211 if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) {
212 continue; //not valid to reuse point
213 }
214
215 real_t d = cp.distance_to(gpoint);
216 if (d < closest_dist && d < grab_threshold) {
217 closest_dist = d;
218 closest_pos = cp;
219 closest_idx = i;
220 }
221 }
222
223 if (closest_idx >= 0) {
224 pre_move_edit = poly;
225 poly.insert(closest_idx + 1, cpoint);
226 edited_point = closest_idx + 1;
227 edited_point_pos = cpoint;
228 _set_polygon(poly);
229 _polygon_draw();
230 snap_ignore = true;
231
232 return EditorPlugin::AFTER_GUI_INPUT_STOP;
233 }
234 } else {
235 //look for points to move
236
237 int closest_idx = -1;
238 Vector2 closest_pos;
239 real_t closest_dist = 1e10;
240 for (int i = 0; i < poly.size(); i++) {
241 Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
242
243 real_t d = cp.distance_to(gpoint);
244 if (d < closest_dist && d < grab_threshold) {
245 closest_dist = d;
246 closest_pos = cp;
247 closest_idx = i;
248 }
249 }
250
251 if (closest_idx >= 0) {
252 pre_move_edit = poly;
253 edited_point = closest_idx;
254 edited_point_pos = poly[closest_idx];
255 _polygon_draw();
256 snap_ignore = false;
257 return EditorPlugin::AFTER_GUI_INPUT_STOP;
258 }
259 }
260 } else {
261 snap_ignore = false;
262
263 if (edited_point != -1) {
264 //apply
265
266 ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
267 poly.write[edited_point] = edited_point_pos;
268 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
269 undo_redo->create_action(TTR("Edit Poly"));
270 undo_redo->add_do_method(obj, "set_polygon", poly);
271 undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
272 undo_redo->add_do_method(this, "_polygon_draw");
273 undo_redo->add_undo_method(this, "_polygon_draw");
274 undo_redo->commit_action();
275
276 edited_point = -1;
277 return EditorPlugin::AFTER_GUI_INPUT_STOP;
278 }
279 }
280 }
281 if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {
282 int closest_idx = -1;
283 Vector2 closest_pos;
284 real_t closest_dist = 1e10;
285 for (int i = 0; i < poly.size(); i++) {
286 Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));
287
288 real_t d = cp.distance_to(gpoint);
289 if (d < closest_dist && d < grab_threshold) {
290 closest_dist = d;
291 closest_pos = cp;
292 closest_idx = i;
293 }
294 }
295
296 if (closest_idx >= 0) {
297 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
298 undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
299 undo_redo->add_undo_method(obj, "set_polygon", poly);
300 poly.remove_at(closest_idx);
301 undo_redo->add_do_method(obj, "set_polygon", poly);
302 undo_redo->add_do_method(this, "_polygon_draw");
303 undo_redo->add_undo_method(this, "_polygon_draw");
304 undo_redo->commit_action();
305 return EditorPlugin::AFTER_GUI_INPUT_STOP;
306 }
307 }
308
309 } break;
310 }
311 }
312
313 Ref<InputEventMouseMotion> mm = p_event;
314
315 if (mm.is_valid()) {
316 if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
317 Vector2 gpoint = mm->get_position();
318
319 Vector3 ray_from = p_camera->project_ray_origin(gpoint);
320 Vector3 ray_dir = p_camera->project_ray_normal(gpoint);
321
322 Vector3 spoint;
323
324 if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
325 return EditorPlugin::AFTER_GUI_INPUT_PASS;
326 }
327
328 spoint = gi.xform(spoint);
329
330 Vector2 cpoint(spoint.x, spoint.y);
331
332 if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
333 snap_ignore = false;
334 }
335
336 if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) {
337 cpoint = cpoint.snapped(Vector2(
338 Node3DEditor::get_singleton()->get_translate_snap(),
339 Node3DEditor::get_singleton()->get_translate_snap()));
340 }
341 edited_point_pos = cpoint;
342
343 _polygon_draw();
344 }
345 }
346
347 return EditorPlugin::AFTER_GUI_INPUT_PASS;
348}
349
350float Polygon3DEditor::_get_depth() {
351 Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
352 ERR_FAIL_NULL_V_MSG(obj, 0.0f, "Edited object is not valid.");
353
354 if (bool(obj->call("_has_editable_3d_polygon_no_depth"))) {
355 return 0.0f;
356 }
357
358 return float(obj->call("get_depth"));
359}
360
361PackedVector2Array Polygon3DEditor::_get_polygon() {
362 Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
363 ERR_FAIL_NULL_V_MSG(obj, PackedVector2Array(), "Edited object is not valid.");
364 return PackedVector2Array(obj->call("get_polygon"));
365}
366
367void Polygon3DEditor::_set_polygon(PackedVector2Array p_poly) {
368 Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
369 ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");
370 obj->call("set_polygon", p_poly);
371}
372
373void Polygon3DEditor::_polygon_draw() {
374 if (!node) {
375 return;
376 }
377
378 PackedVector2Array poly;
379
380 if (wip_active) {
381 poly = wip;
382 } else {
383 poly = _get_polygon();
384 }
385
386 float depth = _get_depth() * 0.5;
387
388 m->clear_surfaces();
389 imesh->clear_surfaces();
390 imgeom->set_material_override(line_material);
391 imesh->surface_begin(Mesh::PRIMITIVE_LINES);
392
393 Rect2 rect;
394
395 for (int i = 0; i < poly.size(); i++) {
396 Vector2 p, p2;
397 p = i == edited_point ? edited_point_pos : poly[i];
398 if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {
399 p2 = edited_point_pos;
400 } else {
401 p2 = poly[(i + 1) % poly.size()];
402 }
403
404 if (i == 0) {
405 rect.position = p;
406 } else {
407 rect.expand_to(p);
408 }
409
410 Vector3 point = Vector3(p.x, p.y, depth);
411 Vector3 next_point = Vector3(p2.x, p2.y, depth);
412
413 imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
414 imesh->surface_add_vertex(point);
415 imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));
416 imesh->surface_add_vertex(next_point);
417
418 //Color col=Color(1,0.3,0.1,0.8);
419 //vpc->draw_line(point,next_point,col,2);
420 //vpc->draw_texture(handle,point-handle->get_size()*0.5);
421 }
422
423 rect = rect.grow(1);
424
425 AABB r;
426 r.position.x = rect.position.x;
427 r.position.y = rect.position.y;
428 r.position.z = depth;
429 r.size.x = rect.size.x;
430 r.size.y = rect.size.y;
431 r.size.z = 0;
432
433 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
434 imesh->surface_add_vertex(r.position);
435 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
436 imesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));
437 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
438 imesh->surface_add_vertex(r.position);
439 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
440 imesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));
441
442 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
443 imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
444 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
445 imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));
446 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
447 imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));
448 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
449 imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));
450
451 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
452 imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
453 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
454 imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));
455 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
456 imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));
457 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
458 imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));
459
460 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
461 imesh->surface_add_vertex(r.position + r.size);
462 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
463 imesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));
464 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
465 imesh->surface_add_vertex(r.position + r.size);
466 imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));
467 imesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));
468
469 imesh->surface_end();
470
471 if (poly.size() == 0) {
472 return;
473 }
474
475 Array a;
476 a.resize(Mesh::ARRAY_MAX);
477 Vector<Vector3> va;
478 {
479 va.resize(poly.size());
480 Vector3 *w = va.ptrw();
481 for (int i = 0; i < poly.size(); i++) {
482 Vector2 p, p2;
483 p = i == edited_point ? edited_point_pos : poly[i];
484
485 Vector3 point = Vector3(p.x, p.y, depth);
486 w[i] = point;
487 }
488 }
489 a[Mesh::ARRAY_VERTEX] = va;
490 m->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);
491 m->surface_set_material(0, handle_material);
492}
493
494void Polygon3DEditor::edit(Node *p_node) {
495 if (p_node) {
496 node = Object::cast_to<Node3D>(p_node);
497 node_resource = node->call("_get_editable_3d_polygon_resource");
498
499 if (node_resource.is_valid()) {
500 node_resource->connect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
501 }
502 //Enable the pencil tool if the polygon is empty
503 if (_get_polygon().is_empty()) {
504 _menu_option(MODE_CREATE);
505 }
506 wip.clear();
507 wip_active = false;
508 edited_point = -1;
509 if (imgeom->get_parent()) {
510 imgeom->reparent(p_node, false);
511 } else {
512 p_node->add_child(imgeom);
513 }
514 _polygon_draw();
515 set_process(true);
516 prev_depth = -1;
517
518 } else {
519 node = nullptr;
520 if (node_resource.is_valid()) {
521 node_resource->disconnect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));
522 }
523 node_resource.unref();
524
525 if (imgeom->get_parent()) {
526 imgeom->get_parent()->remove_child(imgeom);
527 }
528
529 set_process(false);
530 }
531}
532
533void Polygon3DEditor::_bind_methods() {
534 ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
535}
536
537Polygon3DEditor::Polygon3DEditor() {
538 node = nullptr;
539
540 button_create = memnew(Button);
541 button_create->set_flat(true);
542 add_child(button_create);
543 button_create->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_CREATE));
544 button_create->set_toggle_mode(true);
545
546 button_edit = memnew(Button);
547 button_edit->set_flat(true);
548 add_child(button_edit);
549 button_edit->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_EDIT));
550 button_edit->set_toggle_mode(true);
551
552 mode = MODE_EDIT;
553 wip_active = false;
554 imgeom = memnew(MeshInstance3D);
555 imesh.instantiate();
556 imgeom->set_mesh(imesh);
557 imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
558
559 line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
560 line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
561 line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
562 line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
563 line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
564 line_material->set_albedo(Color(1, 1, 1));
565
566 handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
567 handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
568 handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
569 handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
570 handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
571 handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
572 Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
573 handle_material->set_point_size(handle->get_width());
574 handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);
575
576 pointsm = memnew(MeshInstance3D);
577 imgeom->add_child(pointsm);
578 m.instantiate();
579 pointsm->set_mesh(m);
580 pointsm->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
581
582 snap_ignore = false;
583}
584
585Polygon3DEditor::~Polygon3DEditor() {
586 memdelete(imgeom);
587}
588
589void Polygon3DEditorPlugin::edit(Object *p_object) {
590 polygon_editor->edit(Object::cast_to<Node>(p_object));
591}
592
593bool Polygon3DEditorPlugin::handles(Object *p_object) const {
594 return Object::cast_to<Node3D>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));
595}
596
597void Polygon3DEditorPlugin::make_visible(bool p_visible) {
598 if (p_visible) {
599 polygon_editor->show();
600 } else {
601 polygon_editor->hide();
602 polygon_editor->edit(nullptr);
603 }
604}
605
606Polygon3DEditorPlugin::Polygon3DEditorPlugin() {
607 polygon_editor = memnew(Polygon3DEditor);
608 Node3DEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);
609
610 polygon_editor->hide();
611}
612
613Polygon3DEditorPlugin::~Polygon3DEditorPlugin() {
614}
615