collision_polygon_editor_plugin.cpp 18 KB

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