collision_polygon_editor_plugin.cpp 17 KB

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