mesh_instance_3d_editor_plugin.cpp 26 KB

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