1 /*************************************************************************/
2 /*  multimesh_editor_plugin.cpp                                          */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 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 #include "multimesh_editor_plugin.h"
31 #include "scene/3d/mesh_instance.h"
32 #include "scene/gui/box_container.h"
33 #include "spatial_editor_plugin.h"
34 
_node_removed(Node * p_node)35 void MultiMeshEditor::_node_removed(Node *p_node) {
36 
37 	if (p_node == node) {
38 		node = NULL;
39 		hide();
40 	}
41 }
42 
_populate()43 void MultiMeshEditor::_populate() {
44 
45 	if (!node)
46 		return;
47 
48 	Ref<Mesh> mesh;
49 
50 	if (mesh_source->get_text() == "") {
51 
52 		Ref<MultiMesh> multimesh;
53 		multimesh = node->get_multimesh();
54 		if (multimesh.is_null()) {
55 
56 			err_dialog->set_text(TTR("No mesh source specified (and no MultiMesh set in node)."));
57 			err_dialog->popup_centered_minsize();
58 			return;
59 		}
60 		if (multimesh->get_mesh().is_null()) {
61 
62 			err_dialog->set_text(TTR("No mesh source specified (and MultiMesh contains no Mesh)."));
63 			err_dialog->popup_centered_minsize();
64 			return;
65 		}
66 
67 		mesh = multimesh->get_mesh();
68 	} else {
69 
70 		Node *ms_node = node->get_node(mesh_source->get_text());
71 
72 		if (!ms_node) {
73 
74 			err_dialog->set_text(TTR("Mesh source is invalid (invalid path)."));
75 			err_dialog->popup_centered_minsize();
76 			return;
77 		}
78 
79 		MeshInstance *ms_instance = ms_node->cast_to<MeshInstance>();
80 
81 		if (!ms_instance) {
82 
83 			err_dialog->set_text(TTR("Mesh source is invalid (not a MeshInstance)."));
84 			err_dialog->popup_centered_minsize();
85 			return;
86 		}
87 
88 		mesh = ms_instance->get_mesh();
89 
90 		if (mesh.is_null()) {
91 
92 			err_dialog->set_text(TTR("Mesh source is invalid (contains no Mesh resource)."));
93 			err_dialog->popup_centered_minsize();
94 			return;
95 		}
96 	}
97 
98 	if (surface_source->get_text() == "") {
99 
100 		err_dialog->set_text(TTR("No surface source specified."));
101 		err_dialog->popup_centered_minsize();
102 		return;
103 	}
104 
105 	Node *ss_node = node->get_node(surface_source->get_text());
106 
107 	if (!ss_node) {
108 
109 		err_dialog->set_text(TTR("Surface source is invalid (invalid path)."));
110 		err_dialog->popup_centered_minsize();
111 		return;
112 	}
113 
114 	GeometryInstance *ss_instance = ss_node->cast_to<MeshInstance>();
115 
116 	if (!ss_instance) {
117 
118 		err_dialog->set_text(TTR("Surface source is invalid (no geometry)."));
119 		err_dialog->popup_centered_minsize();
120 		return;
121 	}
122 
123 	Transform geom_xform = node->get_global_transform().affine_inverse() * ss_instance->get_global_transform();
124 
125 	DVector<Face3> geometry = ss_instance->get_faces(VisualInstance::FACES_SOLID);
126 
127 	if (geometry.size() == 0) {
128 
129 		err_dialog->set_text(TTR("Surface source is invalid (no faces)."));
130 		err_dialog->popup_centered_minsize();
131 		return;
132 	}
133 
134 	//make all faces local
135 
136 	int gc = geometry.size();
137 	DVector<Face3>::Write w = geometry.write();
138 
139 	for (int i = 0; i < gc; i++) {
140 		for (int j = 0; j < 3; j++) {
141 			w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);
142 		}
143 	}
144 
145 	w = DVector<Face3>::Write();
146 #if 0
147 	node->get_multimesh()->set_instance_count(populate_amount->get_val());
148 	node->populate_parent(populate_rotate_random->get_val(),populate_tilt_random->get_val(),populate_scale_random->get_val(),populate_scale->get_val());
149 
150 
151 	ERR_EXPLAIN("Parent is not of type VisualInstance.");
152 	ERR_FAIL_COND(!get_parent() || !get_parent()->is_type("VisualInstance"));
153 
154 	ERR_EXPLAIN("Multimesh not present.");
155 	ERR_FAIL_COND(multimesh.is_null());
156 
157 	VisualInstance *vi = get_parent()->cast_to<VisualInstance>();
158 	ERR_EXPLAIN("Parent is not of type VisualInstance, can't be populated.");
159 	ERR_FAIL_COND(!vi);
160 
161 #endif
162 	DVector<Face3> faces = geometry;
163 	ERR_EXPLAIN(TTR("Parent has no solid faces to populate."));
164 	int facecount = faces.size();
165 	ERR_FAIL_COND(!facecount);
166 
167 	DVector<Face3>::Read r = faces.read();
168 
169 	float area_accum = 0;
170 	Map<float, int> triangle_area_map;
171 	for (int i = 0; i < facecount; i++) {
172 
173 		float area = r[i].get_area();
174 		if (area < CMP_EPSILON)
175 			continue;
176 		triangle_area_map[area_accum] = i;
177 		area_accum += area;
178 	}
179 
180 	ERR_EXPLAIN(TTR("Couldn't map area."));
181 	ERR_FAIL_COND(triangle_area_map.size() == 0);
182 	ERR_EXPLAIN(TTR("Couldn't map area."));
183 	ERR_FAIL_COND(area_accum == 0);
184 
185 	Ref<MultiMesh> multimesh = memnew(MultiMesh);
186 	multimesh->set_mesh(mesh);
187 
188 	int instance_count = populate_amount->get_val();
189 
190 	multimesh->set_instance_count(instance_count);
191 
192 	float _tilt_random = populate_tilt_random->get_val();
193 	float _rotate_random = populate_rotate_random->get_val();
194 	float _scale_random = populate_scale_random->get_val();
195 	float _scale = populate_scale->get_val();
196 	int axis = populate_axis->get_selected();
197 
198 	Transform axis_xform;
199 	if (axis == Vector3::AXIS_Z) {
200 		axis_xform.rotate(Vector3(1, 0, 0), Math_PI * 0.5);
201 	}
202 	if (axis == Vector3::AXIS_X) {
203 		axis_xform.rotate(Vector3(0, 0, 1), Math_PI * 0.5);
204 	}
205 
206 	for (int i = 0; i < instance_count; i++) {
207 
208 		float areapos = Math::random(0, area_accum);
209 
210 		Map<float, int>::Element *E = triangle_area_map.find_closest(areapos);
211 		ERR_FAIL_COND(!E)
212 		int index = E->get();
213 		ERR_FAIL_INDEX(index, facecount);
214 
215 		// ok FINALLY get face
216 		Face3 face = r[index];
217 		//now compute some position inside the face...
218 
219 		Vector3 pos = face.get_random_point_inside();
220 		Vector3 normal = face.get_plane().normal;
221 		Vector3 op_axis = (face.vertex[0] - face.vertex[1]).normalized();
222 
223 		Transform xform;
224 
225 		xform.set_look_at(pos, pos + op_axis, normal);
226 		xform = xform * axis_xform;
227 
228 		Matrix3 post_xform;
229 
230 		post_xform.rotate(xform.basis.get_axis(0), Math::random(-_tilt_random, _tilt_random) * Math_PI);
231 		post_xform.rotate(xform.basis.get_axis(2), Math::random(-_tilt_random, _tilt_random) * Math_PI);
232 		post_xform.rotate(xform.basis.get_axis(1), Math::random(-_rotate_random, _rotate_random) * Math_PI);
233 		xform.basis = post_xform * xform.basis;
234 		//xform.basis.orthonormalize();
235 
236 		xform.basis.scale(Vector3(1, 1, 1) * (_scale + Math::random(-_scale_random, _scale_random)));
237 
238 		multimesh->set_instance_transform(i, xform);
239 		multimesh->set_instance_color(i, Color(1, 1, 1, 1));
240 	}
241 
242 	multimesh->generate_aabb();
243 
244 	node->set_multimesh(multimesh);
245 }
246 
_browsed(const NodePath & p_path)247 void MultiMeshEditor::_browsed(const NodePath &p_path) {
248 
249 	NodePath path = node->get_path_to(get_node(p_path));
250 
251 	if (browsing_source)
252 		mesh_source->set_text(path);
253 	else
254 		surface_source->set_text(path);
255 }
256 
_menu_option(int p_option)257 void MultiMeshEditor::_menu_option(int p_option) {
258 
259 	switch (p_option) {
260 
261 		case MENU_OPTION_POPULATE: {
262 
263 			if (_last_pp_node != node) {
264 
265 				surface_source->set_text("..");
266 				mesh_source->set_text("..");
267 				populate_axis->select(1);
268 				populate_rotate_random->set_val(0);
269 				populate_tilt_random->set_val(0);
270 				populate_scale_random->set_val(0);
271 				populate_scale->set_val(1);
272 				populate_amount->set_val(128);
273 
274 				_last_pp_node = node;
275 			}
276 			populate_dialog->popup_centered(Size2(250, 380));
277 
278 		} break;
279 	}
280 }
281 
edit(MultiMeshInstance * p_multimesh)282 void MultiMeshEditor::edit(MultiMeshInstance *p_multimesh) {
283 
284 	node = p_multimesh;
285 }
286 
_browse(bool p_source)287 void MultiMeshEditor::_browse(bool p_source) {
288 
289 	browsing_source = p_source;
290 	std->get_scene_tree()->set_marked(node, false);
291 	std->popup_centered_ratio();
292 	if (p_source)
293 		std->set_title(TTR("Select a Source Mesh:"));
294 	else
295 		std->set_title(TTR("Select a Target Surface:"));
296 }
297 
_bind_methods()298 void MultiMeshEditor::_bind_methods() {
299 
300 	ObjectTypeDB::bind_method("_menu_option", &MultiMeshEditor::_menu_option);
301 	ObjectTypeDB::bind_method("_populate", &MultiMeshEditor::_populate);
302 	ObjectTypeDB::bind_method("_browsed", &MultiMeshEditor::_browsed);
303 	ObjectTypeDB::bind_method("_browse", &MultiMeshEditor::_browse);
304 }
305 
MultiMeshEditor()306 MultiMeshEditor::MultiMeshEditor() {
307 
308 	options = memnew(MenuButton);
309 	SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
310 
311 	options->set_text("MultiMesh");
312 	options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MultiMeshInstance", "EditorIcons"));
313 
314 	options->get_popup()->add_item(TTR("Populate Surface"));
315 	options->get_popup()->connect("item_pressed", this, "_menu_option");
316 
317 	populate_dialog = memnew(ConfirmationDialog);
318 	populate_dialog->set_title(TTR("Populate MultiMesh"));
319 	add_child(populate_dialog);
320 
321 	VBoxContainer *vbc = memnew(VBoxContainer);
322 	populate_dialog->add_child(vbc);
323 	populate_dialog->set_child_rect(vbc);
324 
325 	HBoxContainer *hbc = memnew(HBoxContainer);
326 
327 	surface_source = memnew(LineEdit);
328 	hbc->add_child(surface_source);
329 	surface_source->set_h_size_flags(SIZE_EXPAND_FILL);
330 	Button *b = memnew(Button);
331 	hbc->add_child(b);
332 	b->set_text("..");
333 	b->connect("pressed", this, "_browse", make_binds(false));
334 
335 	vbc->add_margin_child(TTR("Target Surface:"), hbc);
336 
337 	hbc = memnew(HBoxContainer);
338 	mesh_source = memnew(LineEdit);
339 	hbc->add_child(mesh_source);
340 	mesh_source->set_h_size_flags(SIZE_EXPAND_FILL);
341 	b = memnew(Button);
342 	hbc->add_child(b);
343 	b->set_text("..");
344 	vbc->add_margin_child(TTR("Source Mesh:"), hbc);
345 	b->connect("pressed", this, "_browse", make_binds(true));
346 
347 	populate_axis = memnew(OptionButton);
348 	populate_axis->add_item(TTR("X-Axis"));
349 	populate_axis->add_item(TTR("Y-Axis"));
350 	populate_axis->add_item(TTR("Z-Axis"));
351 	populate_axis->select(2);
352 	vbc->add_margin_child(TTR("Mesh Up Axis:"), populate_axis);
353 
354 	populate_rotate_random = memnew(HSlider);
355 	populate_rotate_random->set_max(1);
356 	populate_rotate_random->set_step(0.01);
357 	vbc->add_margin_child(TTR("Random Rotation:"), populate_rotate_random);
358 
359 	populate_tilt_random = memnew(HSlider);
360 	populate_tilt_random->set_max(1);
361 	populate_tilt_random->set_step(0.01);
362 	vbc->add_margin_child(TTR("Random Tilt:"), populate_tilt_random);
363 
364 	populate_scale_random = memnew(SpinBox);
365 	populate_scale_random->set_min(0);
366 	populate_scale_random->set_max(1);
367 	populate_scale_random->set_val(0);
368 
369 	vbc->add_margin_child(TTR("Random Scale:"), populate_scale_random);
370 
371 	populate_scale = memnew(SpinBox);
372 	populate_scale->set_min(0.001);
373 	populate_scale->set_max(4096);
374 	populate_scale->set_val(1);
375 
376 	vbc->add_margin_child(TTR("Scale:"), populate_scale);
377 
378 	populate_amount = memnew(SpinBox);
379 	populate_amount->set_anchor(MARGIN_RIGHT, ANCHOR_END);
380 	populate_amount->set_begin(Point2(20, 232));
381 	populate_amount->set_end(Point2(5, 237));
382 	populate_amount->set_min(1);
383 	populate_amount->set_max(65536);
384 	populate_amount->set_val(128);
385 	vbc->add_margin_child(TTR("Amount:"), populate_amount);
386 
387 	populate_dialog->get_ok()->set_text(TTR("Populate"));
388 
389 	populate_dialog->get_ok()->connect("pressed", this, "_populate");
390 	std = memnew(SceneTreeDialog);
391 	populate_dialog->add_child(std);
392 	std->connect("selected", this, "_browsed");
393 
394 	_last_pp_node = NULL;
395 
396 	err_dialog = memnew(AcceptDialog);
397 	add_child(err_dialog);
398 }
399 
edit(Object * p_object)400 void MultiMeshEditorPlugin::edit(Object *p_object) {
401 
402 	multimesh_editor->edit(p_object->cast_to<MultiMeshInstance>());
403 }
404 
handles(Object * p_object) const405 bool MultiMeshEditorPlugin::handles(Object *p_object) const {
406 
407 	return p_object->is_type("MultiMeshInstance");
408 }
409 
make_visible(bool p_visible)410 void MultiMeshEditorPlugin::make_visible(bool p_visible) {
411 
412 	if (p_visible) {
413 		multimesh_editor->options->show();
414 	} else {
415 
416 		multimesh_editor->options->hide();
417 		multimesh_editor->edit(NULL);
418 	}
419 }
420 
MultiMeshEditorPlugin(EditorNode * p_node)421 MultiMeshEditorPlugin::MultiMeshEditorPlugin(EditorNode *p_node) {
422 
423 	editor = p_node;
424 	multimesh_editor = memnew(MultiMeshEditor);
425 	editor->get_viewport()->add_child(multimesh_editor);
426 
427 	multimesh_editor->options->hide();
428 }
429 
~MultiMeshEditorPlugin()430 MultiMeshEditorPlugin::~MultiMeshEditorPlugin() {
431 }
432