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