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