mesh_instance_3d_editor_plugin.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. /**************************************************************************/
  2. /* mesh_instance_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. #include "mesh_instance_3d_editor_plugin.h"
  31. #include "editor/editor_node.h"
  32. #include "editor/editor_string_names.h"
  33. #include "editor/editor_undo_redo_manager.h"
  34. #include "editor/multi_node_edit.h"
  35. #include "editor/plugins/node_3d_editor_plugin.h"
  36. #include "editor/themes/editor_scale.h"
  37. #include "scene/3d/navigation_region_3d.h"
  38. #include "scene/3d/physics/collision_shape_3d.h"
  39. #include "scene/3d/physics/static_body_3d.h"
  40. #include "scene/gui/aspect_ratio_container.h"
  41. #include "scene/gui/box_container.h"
  42. #include "scene/gui/dialogs.h"
  43. #include "scene/gui/menu_button.h"
  44. #include "scene/gui/spin_box.h"
  45. #include "scene/resources/3d/concave_polygon_shape_3d.h"
  46. #include "scene/resources/3d/convex_polygon_shape_3d.h"
  47. #include "scene/resources/3d/primitive_meshes.h"
  48. void MeshInstance3DEditor::_node_removed(Node *p_node) {
  49. if (p_node == node) {
  50. node = nullptr;
  51. options->hide();
  52. }
  53. }
  54. void MeshInstance3DEditor::edit(MeshInstance3D *p_mesh) {
  55. node = p_mesh;
  56. }
  57. Vector<Ref<Shape3D>> MeshInstance3DEditor::create_shape_from_mesh(Ref<Mesh> p_mesh, int p_option, bool p_verbose) {
  58. Vector<Ref<Shape3D>> shapes;
  59. switch (p_option) {
  60. case SHAPE_TYPE_TRIMESH: {
  61. shapes.push_back(p_mesh->create_trimesh_shape());
  62. if (p_verbose && shapes.is_empty()) {
  63. err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
  64. err_dialog->popup_centered();
  65. }
  66. } break;
  67. case SHAPE_TYPE_SINGLE_CONVEX: {
  68. shapes.push_back(p_mesh->create_convex_shape(true, false));
  69. if (p_verbose && shapes.is_empty()) {
  70. err_dialog->set_text(TTR("Couldn't create a single collision shape."));
  71. err_dialog->popup_centered();
  72. }
  73. } break;
  74. case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
  75. shapes.push_back(p_mesh->create_convex_shape(true, true));
  76. if (p_verbose && shapes.is_empty()) {
  77. err_dialog->set_text(TTR("Couldn't create a simplified collision shape."));
  78. err_dialog->popup_centered();
  79. }
  80. } break;
  81. case SHAPE_TYPE_MULTIPLE_CONVEX: {
  82. Ref<MeshConvexDecompositionSettings> settings;
  83. settings.instantiate();
  84. settings->set_max_convex_hulls(32);
  85. settings->set_max_concavity(0.001);
  86. shapes = p_mesh->convex_decompose(settings);
  87. if (p_verbose && shapes.is_empty()) {
  88. err_dialog->set_text(TTR("Couldn't create any collision shapes."));
  89. err_dialog->popup_centered();
  90. }
  91. } break;
  92. default:
  93. break;
  94. }
  95. return shapes;
  96. }
  97. void MeshInstance3DEditor::_create_collision_shape() {
  98. int placement_option = shape_placement->get_selected();
  99. int shape_type_option = shape_type->get_selected();
  100. EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
  101. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  102. switch (shape_type_option) {
  103. case SHAPE_TYPE_TRIMESH: {
  104. ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Trimesh Collision Shape Sibling" : "Create Trimesh Static Body"));
  105. } break;
  106. case SHAPE_TYPE_SINGLE_CONVEX: {
  107. ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Single Convex Collision Shape Sibling" : "Create Single Convex Static Body"));
  108. } break;
  109. case SHAPE_TYPE_SIMPLIFIED_CONVEX: {
  110. ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Simplified Convex Collision Shape Sibling" : "Create Simplified Convex Static Body"));
  111. } break;
  112. case SHAPE_TYPE_MULTIPLE_CONVEX: {
  113. ur->create_action(TTR(placement_option == SHAPE_PLACEMENT_SIBLING ? "Create Multiple Convex Collision Shape Siblings" : "Create Multiple Convex Static Body"));
  114. } break;
  115. default:
  116. break;
  117. }
  118. List<Node *> selection = editor_selection->get_selected_node_list();
  119. bool verbose = false;
  120. if (selection.is_empty()) {
  121. selection.push_back(node);
  122. verbose = true;
  123. }
  124. for (Node *E : selection) {
  125. if (placement_option == SHAPE_PLACEMENT_SIBLING && E == get_tree()->get_edited_scene_root()) {
  126. if (verbose) {
  127. err_dialog->set_text(TTR("Can't create a collision shape as sibling for the scene root."));
  128. err_dialog->popup_centered();
  129. }
  130. continue;
  131. }
  132. MeshInstance3D *instance = Object::cast_to<MeshInstance3D>(E);
  133. if (!instance) {
  134. continue;
  135. }
  136. Ref<Mesh> m = instance->get_mesh();
  137. if (m.is_null()) {
  138. continue;
  139. }
  140. Vector<Ref<Shape3D>> shapes = create_shape_from_mesh(m, shape_type_option, verbose);
  141. if (shapes.is_empty()) {
  142. return;
  143. }
  144. Node *owner = get_tree()->get_edited_scene_root();
  145. if (placement_option == SHAPE_PLACEMENT_STATIC_BODY_CHILD) {
  146. StaticBody3D *body = memnew(StaticBody3D);
  147. ur->add_do_method(instance, "add_child", body, true);
  148. ur->add_do_method(body, "set_owner", owner);
  149. ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), body);
  150. for (Ref<Shape3D> shape : shapes) {
  151. CollisionShape3D *cshape = memnew(CollisionShape3D);
  152. cshape->set_shape(shape);
  153. body->add_child(cshape, true);
  154. ur->add_do_method(cshape, "set_owner", owner);
  155. ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
  156. }
  157. ur->add_do_reference(body);
  158. ur->add_undo_method(instance, "remove_child", body);
  159. } else {
  160. for (Ref<Shape3D> shape : shapes) {
  161. CollisionShape3D *cshape = memnew(CollisionShape3D);
  162. cshape->set_shape(shape);
  163. cshape->set_name("CollisionShape3D");
  164. cshape->set_transform(instance->get_transform());
  165. ur->add_do_method(E, "add_sibling", cshape, true);
  166. ur->add_do_method(cshape, "set_owner", owner);
  167. ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
  168. ur->add_do_reference(cshape);
  169. ur->add_undo_method(instance->get_parent(), "remove_child", cshape);
  170. }
  171. }
  172. }
  173. ur->commit_action();
  174. }
  175. void MeshInstance3DEditor::_menu_option(int p_option) {
  176. Ref<Mesh> mesh = node->get_mesh();
  177. if (mesh.is_null()) {
  178. err_dialog->set_text(TTR("Mesh is empty!"));
  179. err_dialog->popup_centered();
  180. return;
  181. }
  182. switch (p_option) {
  183. case MENU_OPTION_CREATE_COLLISION_SHAPE: {
  184. shape_dialog->popup_centered();
  185. } break;
  186. case MENU_OPTION_CREATE_NAVMESH: {
  187. navigation_mesh_dialog->popup_centered(Vector2(200, 90));
  188. } break;
  189. case MENU_OPTION_CREATE_OUTLINE_MESH: {
  190. outline_dialog->popup_centered(Vector2(200, 90));
  191. } break;
  192. case MENU_OPTION_CREATE_DEBUG_TANGENTS: {
  193. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  194. ur->create_action(TTR("Create Debug Tangents"));
  195. MeshInstance3D *tangents = node->create_debug_tangents_node();
  196. if (tangents) {
  197. Node *owner = get_tree()->get_edited_scene_root();
  198. ur->add_do_reference(tangents);
  199. ur->add_do_method(node, "add_child", tangents, true);
  200. ur->add_do_method(tangents, "set_owner", owner);
  201. ur->add_undo_method(node, "remove_child", tangents);
  202. }
  203. ur->commit_action();
  204. } break;
  205. case MENU_OPTION_CREATE_UV2: {
  206. Ref<Mesh> mesh2 = node->get_mesh();
  207. if (mesh.is_null()) {
  208. err_dialog->set_text(TTR("No mesh to unwrap."));
  209. err_dialog->popup_centered();
  210. return;
  211. }
  212. // Test if we are allowed to unwrap this mesh resource.
  213. String path = mesh2->get_path();
  214. int srpos = path.find("::");
  215. if (srpos != -1) {
  216. String base = path.substr(0, srpos);
  217. if (ResourceLoader::get_resource_type(base) == "PackedScene") {
  218. if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
  219. err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it does not belong to the edited scene. Make it unique first."));
  220. err_dialog->popup_centered();
  221. return;
  222. }
  223. } else {
  224. if (FileAccess::exists(path + ".import")) {
  225. err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it belongs to another resource which was imported from another file type. Make it unique first."));
  226. err_dialog->popup_centered();
  227. return;
  228. }
  229. }
  230. } else {
  231. if (FileAccess::exists(path + ".import")) {
  232. err_dialog->set_text(TTR("Mesh cannot unwrap UVs because it was imported from another file type. Make it unique first."));
  233. err_dialog->popup_centered();
  234. return;
  235. }
  236. }
  237. Ref<PrimitiveMesh> primitive_mesh = mesh2;
  238. if (primitive_mesh.is_valid()) {
  239. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  240. ur->create_action(TTR("Unwrap UV2"));
  241. ur->add_do_method(*primitive_mesh, "set_add_uv2", true);
  242. ur->add_undo_method(*primitive_mesh, "set_add_uv2", primitive_mesh->get_add_uv2());
  243. ur->commit_action();
  244. } else {
  245. Ref<ArrayMesh> array_mesh = mesh2;
  246. if (array_mesh.is_null()) {
  247. err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh."));
  248. err_dialog->popup_centered();
  249. return;
  250. }
  251. // Preemptively evaluate common fail cases for lightmap unwrapping.
  252. {
  253. if (array_mesh->get_blend_shape_count() > 0) {
  254. err_dialog->set_text(TTR("Can't unwrap mesh with blend shapes."));
  255. err_dialog->popup_centered();
  256. return;
  257. }
  258. for (int i = 0; i < array_mesh->get_surface_count(); i++) {
  259. Mesh::PrimitiveType primitive = array_mesh->surface_get_primitive_type(i);
  260. if (primitive != Mesh::PRIMITIVE_TRIANGLES) {
  261. err_dialog->set_text(TTR("Only triangles are supported for lightmap unwrap."));
  262. err_dialog->popup_centered();
  263. return;
  264. }
  265. uint64_t format = array_mesh->surface_get_format(i);
  266. if (!(format & Mesh::ArrayFormat::ARRAY_FORMAT_NORMAL)) {
  267. err_dialog->set_text(TTR("Normals are required for lightmap unwrap."));
  268. err_dialog->popup_centered();
  269. return;
  270. }
  271. }
  272. }
  273. Ref<ArrayMesh> unwrapped_mesh = array_mesh->duplicate(false);
  274. Error err = unwrapped_mesh->lightmap_unwrap(node->get_global_transform());
  275. if (err != OK) {
  276. err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?"));
  277. err_dialog->popup_centered();
  278. return;
  279. }
  280. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  281. ur->create_action(TTR("Unwrap UV2"));
  282. ur->add_do_method(node, "set_mesh", unwrapped_mesh);
  283. ur->add_do_reference(node);
  284. ur->add_do_reference(array_mesh.ptr());
  285. ur->add_undo_method(node, "set_mesh", array_mesh);
  286. ur->add_undo_reference(unwrapped_mesh.ptr());
  287. ur->commit_action();
  288. }
  289. } break;
  290. case MENU_OPTION_DEBUG_UV1: {
  291. Ref<Mesh> mesh2 = node->get_mesh();
  292. if (mesh2.is_null()) {
  293. err_dialog->set_text(TTR("No mesh to debug."));
  294. err_dialog->popup_centered();
  295. return;
  296. }
  297. _create_uv_lines(0);
  298. } break;
  299. case MENU_OPTION_DEBUG_UV2: {
  300. Ref<Mesh> mesh2 = node->get_mesh();
  301. if (mesh2.is_null()) {
  302. err_dialog->set_text(TTR("No mesh to debug."));
  303. err_dialog->popup_centered();
  304. return;
  305. }
  306. _create_uv_lines(1);
  307. } break;
  308. }
  309. }
  310. struct MeshInstance3DEditorEdgeSort {
  311. Vector2 a;
  312. Vector2 b;
  313. static uint32_t hash(const MeshInstance3DEditorEdgeSort &p_edge) {
  314. uint32_t h = hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.a));
  315. return hash_fmix32(hash_murmur3_one_32(HashMapHasherDefault::hash(p_edge.b), h));
  316. }
  317. bool operator==(const MeshInstance3DEditorEdgeSort &p_b) const {
  318. return a == p_b.a && b == p_b.b;
  319. }
  320. MeshInstance3DEditorEdgeSort() {}
  321. MeshInstance3DEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) {
  322. if (p_a < p_b) {
  323. a = p_a;
  324. b = p_b;
  325. } else {
  326. b = p_a;
  327. a = p_b;
  328. }
  329. }
  330. };
  331. void MeshInstance3DEditor::_create_uv_lines(int p_layer) {
  332. Ref<Mesh> mesh = node->get_mesh();
  333. ERR_FAIL_COND(mesh.is_null());
  334. HashSet<MeshInstance3DEditorEdgeSort, MeshInstance3DEditorEdgeSort> edges;
  335. uv_lines.clear();
  336. for (int i = 0; i < mesh->get_surface_count(); i++) {
  337. if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
  338. continue;
  339. }
  340. Array a = mesh->surface_get_arrays(i);
  341. Vector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
  342. if (uv.size() == 0) {
  343. err_dialog->set_text(vformat(TTR("Mesh has no UV in layer %d."), p_layer + 1));
  344. err_dialog->popup_centered();
  345. return;
  346. }
  347. const Vector2 *r = uv.ptr();
  348. Vector<int> indices = a[Mesh::ARRAY_INDEX];
  349. const int *ri = nullptr;
  350. int ic;
  351. if (indices.size()) {
  352. ic = indices.size();
  353. ri = indices.ptr();
  354. } else {
  355. ic = uv.size();
  356. }
  357. for (int j = 0; j < ic; j += 3) {
  358. for (int k = 0; k < 3; k++) {
  359. MeshInstance3DEditorEdgeSort edge;
  360. if (ri) {
  361. edge.a = r[ri[j + k]];
  362. edge.b = r[ri[j + ((k + 1) % 3)]];
  363. } else {
  364. edge.a = r[j + k];
  365. edge.b = r[j + ((k + 1) % 3)];
  366. }
  367. if (edges.has(edge)) {
  368. continue;
  369. }
  370. uv_lines.push_back(edge.a);
  371. uv_lines.push_back(edge.b);
  372. edges.insert(edge);
  373. }
  374. }
  375. }
  376. debug_uv_dialog->popup_centered();
  377. }
  378. void MeshInstance3DEditor::_debug_uv_draw() {
  379. if (uv_lines.size() == 0) {
  380. return;
  381. }
  382. debug_uv->set_clip_contents(true);
  383. debug_uv->draw_rect(
  384. Rect2(Vector2(), debug_uv->get_size()),
  385. get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor)));
  386. // Draw an outline to represent the UV2's beginning and end area (useful on Black OLED theme).
  387. // Top-left coordinate needs to be `(1, 1)` to prevent `clip_contents` from clipping the top and left lines.
  388. debug_uv->draw_rect(
  389. Rect2(Vector2(1, 1), debug_uv->get_size() - Vector2(1, 1)),
  390. get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
  391. false,
  392. Math::round(EDSCALE));
  393. for (int x = 1; x <= 7; x++) {
  394. debug_uv->draw_line(
  395. Vector2(debug_uv->get_size().x * 0.125 * x, 0),
  396. Vector2(debug_uv->get_size().x * 0.125 * x, debug_uv->get_size().y),
  397. get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
  398. Math::round(EDSCALE));
  399. }
  400. for (int y = 1; y <= 7; y++) {
  401. debug_uv->draw_line(
  402. Vector2(0, debug_uv->get_size().y * 0.125 * y),
  403. Vector2(debug_uv->get_size().x, debug_uv->get_size().y * 0.125 * y),
  404. get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125),
  405. Math::round(EDSCALE));
  406. }
  407. debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
  408. // Use a translucent color to allow overlapping triangles to be visible.
  409. // Divide line width by the drawing scale set above, so that line width is consistent regardless of dialog size.
  410. // Aspect ratio is preserved by the parent AspectRatioContainer, so we only need to check the X size which is always equal to Y.
  411. debug_uv->draw_multiline(
  412. uv_lines,
  413. get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.5),
  414. Math::round(EDSCALE) / debug_uv->get_size().x);
  415. }
  416. void MeshInstance3DEditor::_create_navigation_mesh() {
  417. Ref<Mesh> mesh = node->get_mesh();
  418. if (mesh.is_null()) {
  419. return;
  420. }
  421. Ref<NavigationMesh> nmesh = memnew(NavigationMesh);
  422. if (nmesh.is_null()) {
  423. return;
  424. }
  425. nmesh->create_from_mesh(mesh);
  426. NavigationRegion3D *nmi = memnew(NavigationRegion3D);
  427. nmi->set_navigation_mesh(nmesh);
  428. Node *owner = get_tree()->get_edited_scene_root();
  429. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  430. ur->create_action(TTR("Create Navigation Mesh"));
  431. ur->add_do_method(node, "add_child", nmi, true);
  432. ur->add_do_method(nmi, "set_owner", owner);
  433. ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), nmi);
  434. ur->add_do_reference(nmi);
  435. ur->add_undo_method(node, "remove_child", nmi);
  436. ur->commit_action();
  437. }
  438. void MeshInstance3DEditor::_create_outline_mesh() {
  439. Ref<Mesh> mesh = node->get_mesh();
  440. if (mesh.is_null()) {
  441. err_dialog->set_text(TTR("MeshInstance3D lacks a Mesh."));
  442. err_dialog->popup_centered();
  443. return;
  444. }
  445. if (mesh->get_surface_count() == 0) {
  446. err_dialog->set_text(TTR("Mesh has no surface to create outlines from."));
  447. err_dialog->popup_centered();
  448. return;
  449. } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
  450. err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES."));
  451. err_dialog->popup_centered();
  452. return;
  453. }
  454. Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value());
  455. if (mesho.is_null()) {
  456. err_dialog->set_text(TTR("Could not create outline."));
  457. err_dialog->popup_centered();
  458. return;
  459. }
  460. MeshInstance3D *mi = memnew(MeshInstance3D);
  461. mi->set_mesh(mesho);
  462. Node *owner = get_tree()->get_edited_scene_root();
  463. EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
  464. ur->create_action(TTR("Create Outline"));
  465. ur->add_do_method(node, "add_child", mi, true);
  466. ur->add_do_method(mi, "set_owner", owner);
  467. ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), mi);
  468. ur->add_do_reference(mi);
  469. ur->add_undo_method(node, "remove_child", mi);
  470. ur->commit_action();
  471. }
  472. void MeshInstance3DEditor::_notification(int p_what) {
  473. switch (p_what) {
  474. case NOTIFICATION_THEME_CHANGED: {
  475. options->set_button_icon(get_editor_theme_icon(SNAME("MeshInstance3D")));
  476. } break;
  477. }
  478. }
  479. MeshInstance3DEditor::MeshInstance3DEditor() {
  480. options = memnew(MenuButton);
  481. options->set_text(TTR("Mesh"));
  482. options->set_switch_on_hover(true);
  483. Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
  484. options->get_popup()->add_item(TTR("Create Collision Shape..."), MENU_OPTION_CREATE_COLLISION_SHAPE);
  485. options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
  486. options->get_popup()->add_separator();
  487. options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
  488. options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a static outline mesh. The outline mesh will have its normals flipped automatically.\nThis can be used instead of the StandardMaterial Grow property when using that property isn't possible."));
  489. options->get_popup()->add_item(TTR("Create Debug Tangents"), MENU_OPTION_CREATE_DEBUG_TANGENTS);
  490. options->get_popup()->add_separator();
  491. options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
  492. options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
  493. options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2);
  494. options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MeshInstance3DEditor::_menu_option));
  495. outline_dialog = memnew(ConfirmationDialog);
  496. outline_dialog->set_title(TTR("Create Outline Mesh"));
  497. outline_dialog->set_ok_button_text(TTR("Create"));
  498. VBoxContainer *outline_dialog_vbc = memnew(VBoxContainer);
  499. outline_dialog->add_child(outline_dialog_vbc);
  500. //outline_dialog->set_child_rect(outline_dialog_vbc);
  501. outline_size = memnew(SpinBox);
  502. outline_size->set_min(0.001);
  503. outline_size->set_max(1024);
  504. outline_size->set_step(0.001);
  505. outline_size->set_value(0.05);
  506. outline_dialog_vbc->add_margin_child(TTR("Outline Size:"), outline_size);
  507. add_child(outline_dialog);
  508. outline_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_outline_mesh));
  509. shape_dialog = memnew(ConfirmationDialog);
  510. shape_dialog->set_title(TTR("Create Collision Shape"));
  511. shape_dialog->set_ok_button_text(TTR("Create"));
  512. VBoxContainer *shape_dialog_vbc = memnew(VBoxContainer);
  513. shape_dialog->add_child(shape_dialog_vbc);
  514. Label *l = memnew(Label);
  515. l->set_text(TTR("Collision Shape placement"));
  516. shape_dialog_vbc->add_child(l);
  517. shape_placement = memnew(OptionButton);
  518. shape_placement->set_h_size_flags(SIZE_EXPAND_FILL);
  519. shape_placement->add_item(TTR("Sibling"), SHAPE_PLACEMENT_SIBLING);
  520. shape_placement->set_item_tooltip(-1, TTR("Creates collision shapes as Sibling."));
  521. shape_placement->add_item(TTR("Static Body Child"), SHAPE_PLACEMENT_STATIC_BODY_CHILD);
  522. shape_placement->set_item_tooltip(-1, TTR("Creates a StaticBody3D as child and assigns collision shapes to it."));
  523. shape_dialog_vbc->add_child(shape_placement);
  524. l = memnew(Label);
  525. l->set_text(TTR("Collision Shape Type"));
  526. shape_dialog_vbc->add_child(l);
  527. shape_type = memnew(OptionButton);
  528. shape_type->set_h_size_flags(SIZE_EXPAND_FILL);
  529. shape_type->add_item(TTR("Trimesh"), SHAPE_TYPE_TRIMESH);
  530. shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
  531. shape_type->add_item(TTR("Single Convex"), SHAPE_TYPE_SINGLE_CONVEX);
  532. shape_type->set_item_tooltip(-1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
  533. shape_type->add_item(TTR("Simplified Convex"), SHAPE_TYPE_SIMPLIFIED_CONVEX);
  534. shape_type->set_item_tooltip(-1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy."));
  535. shape_type->add_item(TTR("Multiple Convex"), SHAPE_TYPE_MULTIPLE_CONVEX);
  536. shape_type->set_item_tooltip(-1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision."));
  537. shape_dialog_vbc->add_child(shape_type);
  538. add_child(shape_dialog);
  539. shape_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_collision_shape));
  540. err_dialog = memnew(AcceptDialog);
  541. add_child(err_dialog);
  542. debug_uv_dialog = memnew(AcceptDialog);
  543. debug_uv_dialog->set_title(TTR("UV Channel Debug"));
  544. add_child(debug_uv_dialog);
  545. debug_uv_arc = memnew(AspectRatioContainer);
  546. debug_uv_dialog->add_child(debug_uv_arc);
  547. debug_uv = memnew(Control);
  548. debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE);
  549. debug_uv->connect(SceneStringName(draw), callable_mp(this, &MeshInstance3DEditor::_debug_uv_draw));
  550. debug_uv_arc->add_child(debug_uv);
  551. navigation_mesh_dialog = memnew(ConfirmationDialog);
  552. navigation_mesh_dialog->set_title(TTR("Create NavigationMesh"));
  553. navigation_mesh_dialog->set_ok_button_text(TTR("Create"));
  554. VBoxContainer *navigation_mesh_dialog_vbc = memnew(VBoxContainer);
  555. navigation_mesh_dialog->add_child(navigation_mesh_dialog_vbc);
  556. Label *navigation_mesh_l = memnew(Label);
  557. navigation_mesh_l->set_text(TTR("Before converting a rendering mesh to a navigation mesh, please verify:\n\n- The mesh is two-dimensional.\n- The mesh has no surface overlap.\n- The mesh has no self-intersection.\n- The mesh surfaces have indices.\n\nIf the mesh does not fulfill these requirements, the pathfinding will be broken."));
  558. navigation_mesh_dialog_vbc->add_child(navigation_mesh_l);
  559. add_child(navigation_mesh_dialog);
  560. navigation_mesh_dialog->connect(SceneStringName(confirmed), callable_mp(this, &MeshInstance3DEditor::_create_navigation_mesh));
  561. }
  562. void MeshInstance3DEditorPlugin::edit(Object *p_object) {
  563. {
  564. MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_object);
  565. if (mi) {
  566. mesh_editor->edit(mi);
  567. return;
  568. }
  569. }
  570. Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
  571. Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
  572. if (mne.is_valid() && edited_scene) {
  573. for (int i = 0; i < mne->get_node_count(); i++) {
  574. MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(edited_scene->get_node(mne->get_node(i)));
  575. if (mi) {
  576. mesh_editor->edit(mi);
  577. return;
  578. }
  579. }
  580. }
  581. mesh_editor->edit(nullptr);
  582. }
  583. bool MeshInstance3DEditorPlugin::handles(Object *p_object) const {
  584. if (Object::cast_to<MeshInstance3D>(p_object)) {
  585. return true;
  586. }
  587. Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
  588. Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
  589. if (mne.is_valid() && edited_scene) {
  590. bool has_mesh = false;
  591. for (int i = 0; i < mne->get_node_count(); i++) {
  592. if (Object::cast_to<MeshInstance3D>(edited_scene->get_node(mne->get_node(i)))) {
  593. if (has_mesh) {
  594. return true;
  595. } else {
  596. has_mesh = true;
  597. }
  598. }
  599. }
  600. }
  601. return false;
  602. }
  603. void MeshInstance3DEditorPlugin::make_visible(bool p_visible) {
  604. if (p_visible) {
  605. mesh_editor->options->show();
  606. } else {
  607. mesh_editor->options->hide();
  608. mesh_editor->edit(nullptr);
  609. }
  610. }
  611. MeshInstance3DEditorPlugin::MeshInstance3DEditorPlugin() {
  612. mesh_editor = memnew(MeshInstance3DEditor);
  613. EditorNode::get_singleton()->get_gui_base()->add_child(mesh_editor);
  614. mesh_editor->options->hide();
  615. }
  616. MeshInstance3DEditorPlugin::~MeshInstance3DEditorPlugin() {
  617. }