1 /*************************************************************************/
2 /* mesh_instance_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 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
31 #include "mesh_instance_editor_plugin.h"
32
33 #include "editor/editor_scale.h"
34 #include "scene/3d/collision_shape.h"
35 #include "scene/3d/navigation_mesh.h"
36 #include "scene/3d/physics_body.h"
37 #include "scene/gui/box_container.h"
38 #include "spatial_editor_plugin.h"
39
_node_removed(Node * p_node)40 void MeshInstanceEditor::_node_removed(Node *p_node) {
41
42 if (p_node == node) {
43 node = NULL;
44 options->hide();
45 }
46 }
47
edit(MeshInstance * p_mesh)48 void MeshInstanceEditor::edit(MeshInstance *p_mesh) {
49
50 node = p_mesh;
51 }
52
_menu_option(int p_option)53 void MeshInstanceEditor::_menu_option(int p_option) {
54
55 Ref<Mesh> mesh = node->get_mesh();
56 if (mesh.is_null()) {
57 err_dialog->set_text(TTR("Mesh is empty!"));
58 err_dialog->popup_centered_minsize();
59 return;
60 }
61
62 switch (p_option) {
63 case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
64
65 EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
66 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
67
68 List<Node *> selection = editor_selection->get_selected_node_list();
69
70 if (selection.empty()) {
71 Ref<Shape> shape = mesh->create_trimesh_shape();
72 if (shape.is_null()) {
73 err_dialog->set_text(TTR("Couldn't create a Trimesh collision shape."));
74 err_dialog->popup_centered_minsize();
75 return;
76 }
77
78 CollisionShape *cshape = memnew(CollisionShape);
79 cshape->set_shape(shape);
80 StaticBody *body = memnew(StaticBody);
81 body->add_child(cshape);
82
83 Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
84
85 ur->create_action(TTR("Create Static Trimesh Body"));
86 ur->add_do_method(node, "add_child", body);
87 ur->add_do_method(body, "set_owner", owner);
88 ur->add_do_method(cshape, "set_owner", owner);
89 ur->add_do_reference(body);
90 ur->add_undo_method(node, "remove_child", body);
91 ur->commit_action();
92 return;
93 }
94
95 ur->create_action(TTR("Create Static Trimesh Body"));
96
97 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
98
99 MeshInstance *instance = Object::cast_to<MeshInstance>(E->get());
100 if (!instance)
101 continue;
102
103 Ref<Mesh> m = instance->get_mesh();
104 if (m.is_null())
105 continue;
106
107 Ref<Shape> shape = m->create_trimesh_shape();
108 if (shape.is_null())
109 continue;
110
111 CollisionShape *cshape = memnew(CollisionShape);
112 cshape->set_shape(shape);
113 StaticBody *body = memnew(StaticBody);
114 body->add_child(cshape);
115
116 Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner();
117
118 ur->add_do_method(instance, "add_child", body);
119 ur->add_do_method(body, "set_owner", owner);
120 ur->add_do_method(cshape, "set_owner", owner);
121 ur->add_do_reference(body);
122 ur->add_undo_method(instance, "remove_child", body);
123 }
124
125 ur->commit_action();
126
127 } break;
128
129 case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: {
130
131 if (node == get_tree()->get_edited_scene_root()) {
132 err_dialog->set_text(TTR("This doesn't work on scene root!"));
133 err_dialog->popup_centered_minsize();
134 return;
135 }
136
137 Ref<Shape> shape = mesh->create_trimesh_shape();
138 if (shape.is_null())
139 return;
140
141 CollisionShape *cshape = memnew(CollisionShape);
142 cshape->set_shape(shape);
143 cshape->set_transform(node->get_transform());
144
145 Node *owner = node->get_owner();
146
147 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
148
149 ur->create_action(TTR("Create Trimesh Static Shape"));
150
151 ur->add_do_method(node->get_parent(), "add_child", cshape);
152 ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
153 ur->add_do_method(cshape, "set_owner", owner);
154 ur->add_do_reference(cshape);
155 ur->add_undo_method(node->get_parent(), "remove_child", cshape);
156 ur->commit_action();
157 } break;
158 case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: {
159
160 if (node == get_tree()->get_edited_scene_root()) {
161 err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root."));
162 err_dialog->popup_centered_minsize();
163 return;
164 }
165
166 Ref<Shape> shape = mesh->create_convex_shape();
167
168 if (shape.is_null()) {
169 err_dialog->set_text(TTR("Couldn't create a single convex collision shape."));
170 err_dialog->popup_centered_minsize();
171 return;
172 }
173 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
174
175 ur->create_action(TTR("Create Single Convex Shape"));
176
177 CollisionShape *cshape = memnew(CollisionShape);
178 cshape->set_shape(shape);
179 cshape->set_transform(node->get_transform());
180
181 Node *owner = node->get_owner();
182
183 ur->add_do_method(node->get_parent(), "add_child", cshape);
184 ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
185 ur->add_do_method(cshape, "set_owner", owner);
186 ur->add_do_reference(cshape);
187 ur->add_undo_method(node->get_parent(), "remove_child", cshape);
188
189 ur->commit_action();
190
191 } break;
192 case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: {
193
194 if (node == get_tree()->get_edited_scene_root()) {
195 err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root."));
196 err_dialog->popup_centered_minsize();
197 return;
198 }
199
200 Vector<Ref<Shape> > shapes = mesh->convex_decompose();
201
202 if (!shapes.size()) {
203 err_dialog->set_text(TTR("Couldn't create any collision shapes."));
204 err_dialog->popup_centered_minsize();
205 return;
206 }
207 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
208
209 ur->create_action(TTR("Create Multiple Convex Shapes"));
210
211 for (int i = 0; i < shapes.size(); i++) {
212
213 CollisionShape *cshape = memnew(CollisionShape);
214 cshape->set_shape(shapes[i]);
215 cshape->set_transform(node->get_transform());
216
217 Node *owner = node->get_owner();
218
219 ur->add_do_method(node->get_parent(), "add_child", cshape);
220 ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
221 ur->add_do_method(cshape, "set_owner", owner);
222 ur->add_do_reference(cshape);
223 ur->add_undo_method(node->get_parent(), "remove_child", cshape);
224 }
225 ur->commit_action();
226
227 } break;
228
229 case MENU_OPTION_CREATE_NAVMESH: {
230
231 Ref<NavigationMesh> nmesh = memnew(NavigationMesh);
232
233 if (nmesh.is_null())
234 return;
235
236 nmesh->create_from_mesh(mesh);
237 NavigationMeshInstance *nmi = memnew(NavigationMeshInstance);
238 nmi->set_navigation_mesh(nmesh);
239
240 Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
241
242 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
243 ur->create_action(TTR("Create Navigation Mesh"));
244
245 ur->add_do_method(node, "add_child", nmi);
246 ur->add_do_method(nmi, "set_owner", owner);
247
248 ur->add_do_reference(nmi);
249 ur->add_undo_method(node, "remove_child", nmi);
250 ur->commit_action();
251 } break;
252
253 case MENU_OPTION_CREATE_OUTLINE_MESH: {
254
255 outline_dialog->popup_centered(Vector2(200, 90));
256 } break;
257 case MENU_OPTION_CREATE_UV2: {
258
259 Ref<ArrayMesh> mesh2 = node->get_mesh();
260 if (!mesh2.is_valid()) {
261 err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh."));
262 err_dialog->popup_centered_minsize();
263 return;
264 }
265
266 Error err = mesh2->lightmap_unwrap(node->get_global_transform());
267 if (err != OK) {
268 err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?"));
269 err_dialog->popup_centered_minsize();
270 return;
271 }
272
273 } break;
274 case MENU_OPTION_DEBUG_UV1: {
275 Ref<Mesh> mesh2 = node->get_mesh();
276 if (!mesh2.is_valid()) {
277 err_dialog->set_text(TTR("No mesh to debug."));
278 err_dialog->popup_centered_minsize();
279 return;
280 }
281 _create_uv_lines(0);
282 } break;
283 case MENU_OPTION_DEBUG_UV2: {
284 Ref<Mesh> mesh2 = node->get_mesh();
285 if (!mesh2.is_valid()) {
286 err_dialog->set_text(TTR("No mesh to debug."));
287 err_dialog->popup_centered_minsize();
288 return;
289 }
290 _create_uv_lines(1);
291 } break;
292 }
293 }
294
295 struct MeshInstanceEditorEdgeSort {
296
297 Vector2 a;
298 Vector2 b;
299
operator <MeshInstanceEditorEdgeSort300 bool operator<(const MeshInstanceEditorEdgeSort &p_b) const {
301 if (a == p_b.a)
302 return b < p_b.b;
303 else
304 return a < p_b.a;
305 }
306
MeshInstanceEditorEdgeSortMeshInstanceEditorEdgeSort307 MeshInstanceEditorEdgeSort() {}
MeshInstanceEditorEdgeSortMeshInstanceEditorEdgeSort308 MeshInstanceEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) {
309 if (p_a < p_b) {
310 a = p_a;
311 b = p_b;
312 } else {
313 b = p_a;
314 a = p_b;
315 }
316 }
317 };
318
_create_uv_lines(int p_layer)319 void MeshInstanceEditor::_create_uv_lines(int p_layer) {
320
321 Ref<Mesh> mesh = node->get_mesh();
322 ERR_FAIL_COND(!mesh.is_valid());
323
324 Set<MeshInstanceEditorEdgeSort> edges;
325 uv_lines.clear();
326 for (int i = 0; i < mesh->get_surface_count(); i++) {
327 if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
328 continue;
329 Array a = mesh->surface_get_arrays(i);
330
331 PoolVector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
332 if (uv.size() == 0) {
333 err_dialog->set_text(TTR("Model has no UV in this layer"));
334 err_dialog->popup_centered_minsize();
335 return;
336 }
337
338 PoolVector<Vector2>::Read r = uv.read();
339
340 PoolVector<int> indices = a[Mesh::ARRAY_INDEX];
341 PoolVector<int>::Read ri;
342
343 int ic;
344 bool use_indices;
345
346 if (indices.size()) {
347 ic = indices.size();
348 ri = indices.read();
349 use_indices = true;
350 } else {
351 ic = uv.size();
352 use_indices = false;
353 }
354
355 for (int j = 0; j < ic; j += 3) {
356
357 for (int k = 0; k < 3; k++) {
358
359 MeshInstanceEditorEdgeSort edge;
360 if (use_indices) {
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
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
378 debug_uv_dialog->popup_centered_minsize();
379 }
380
_debug_uv_draw()381 void MeshInstanceEditor::_debug_uv_draw() {
382
383 if (uv_lines.size() == 0)
384 return;
385
386 debug_uv->set_clip_contents(true);
387 debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), Color(0.2, 0.2, 0.0));
388 debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
389 debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7));
390 }
391
_create_outline_mesh()392 void MeshInstanceEditor::_create_outline_mesh() {
393
394 Ref<Mesh> mesh = node->get_mesh();
395 if (mesh.is_null()) {
396 err_dialog->set_text(TTR("MeshInstance lacks a Mesh!"));
397 err_dialog->popup_centered_minsize();
398 return;
399 }
400
401 if (mesh->get_surface_count() == 0) {
402 err_dialog->set_text(TTR("Mesh has not surface to create outlines from!"));
403 err_dialog->popup_centered_minsize();
404 return;
405 } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
406 err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!"));
407 err_dialog->popup_centered_minsize();
408 return;
409 }
410
411 Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value());
412
413 if (mesho.is_null()) {
414 err_dialog->set_text(TTR("Could not create outline!"));
415 err_dialog->popup_centered_minsize();
416 return;
417 }
418
419 MeshInstance *mi = memnew(MeshInstance);
420 mi->set_mesh(mesho);
421 Node *owner = node->get_owner();
422 if (get_tree()->get_edited_scene_root() == node) {
423 owner = node;
424 }
425
426 UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
427
428 ur->create_action(TTR("Create Outline"));
429
430 ur->add_do_method(node, "add_child", mi);
431 ur->add_do_method(mi, "set_owner", owner);
432
433 ur->add_do_reference(mi);
434 ur->add_undo_method(node, "remove_child", mi);
435 ur->commit_action();
436 }
437
_bind_methods()438 void MeshInstanceEditor::_bind_methods() {
439
440 ClassDB::bind_method("_menu_option", &MeshInstanceEditor::_menu_option);
441 ClassDB::bind_method("_create_outline_mesh", &MeshInstanceEditor::_create_outline_mesh);
442 ClassDB::bind_method("_debug_uv_draw", &MeshInstanceEditor::_debug_uv_draw);
443 }
444
MeshInstanceEditor()445 MeshInstanceEditor::MeshInstanceEditor() {
446
447 options = memnew(MenuButton);
448 options->set_switch_on_hover(true);
449 SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
450
451 options->set_text(TTR("Mesh"));
452 options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance", "EditorIcons"));
453
454 options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY);
455 options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a StaticBody and assigns a polygon-based collision shape to it automatically.\nThis is the most accurate (but slowest) option for collision detection."));
456 options->get_popup()->add_separator();
457 options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE);
458 options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection."));
459 options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE);
460 options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection."));
461 options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES);
462 options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between the two above options."));
463 options->get_popup()->add_separator();
464 options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
465 options->get_popup()->add_separator();
466 options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
467 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 SpatialMaterial Grow property when using that property isn't possible."));
468 options->get_popup()->add_separator();
469 options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
470 options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
471 options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2);
472
473 options->get_popup()->connect("id_pressed", this, "_menu_option");
474
475 outline_dialog = memnew(ConfirmationDialog);
476 outline_dialog->set_title(TTR("Create Outline Mesh"));
477 outline_dialog->get_ok()->set_text(TTR("Create"));
478
479 VBoxContainer *outline_dialog_vbc = memnew(VBoxContainer);
480 outline_dialog->add_child(outline_dialog_vbc);
481 //outline_dialog->set_child_rect(outline_dialog_vbc);
482
483 outline_size = memnew(SpinBox);
484 outline_size->set_min(0.001);
485 outline_size->set_max(1024);
486 outline_size->set_step(0.001);
487 outline_size->set_value(0.05);
488 outline_dialog_vbc->add_margin_child(TTR("Outline Size:"), outline_size);
489
490 add_child(outline_dialog);
491 outline_dialog->connect("confirmed", this, "_create_outline_mesh");
492
493 err_dialog = memnew(AcceptDialog);
494 add_child(err_dialog);
495
496 debug_uv_dialog = memnew(AcceptDialog);
497 debug_uv_dialog->set_title(TTR("UV Channel Debug"));
498 add_child(debug_uv_dialog);
499 debug_uv = memnew(Control);
500 debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE);
501 debug_uv->connect("draw", this, "_debug_uv_draw");
502 debug_uv_dialog->add_child(debug_uv);
503 }
504
edit(Object * p_object)505 void MeshInstanceEditorPlugin::edit(Object *p_object) {
506
507 mesh_editor->edit(Object::cast_to<MeshInstance>(p_object));
508 }
509
handles(Object * p_object) const510 bool MeshInstanceEditorPlugin::handles(Object *p_object) const {
511
512 return p_object->is_class("MeshInstance");
513 }
514
make_visible(bool p_visible)515 void MeshInstanceEditorPlugin::make_visible(bool p_visible) {
516
517 if (p_visible) {
518 mesh_editor->options->show();
519 } else {
520
521 mesh_editor->options->hide();
522 mesh_editor->edit(NULL);
523 }
524 }
525
MeshInstanceEditorPlugin(EditorNode * p_node)526 MeshInstanceEditorPlugin::MeshInstanceEditorPlugin(EditorNode *p_node) {
527
528 editor = p_node;
529 mesh_editor = memnew(MeshInstanceEditor);
530 editor->get_viewport()->add_child(mesh_editor);
531
532 mesh_editor->options->hide();
533 }
534
~MeshInstanceEditorPlugin()535 MeshInstanceEditorPlugin::~MeshInstanceEditorPlugin() {
536 }
537