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