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