Path: blob/master/editor/scene/3d/multimesh_editor_plugin.cpp
9903 views
/**************************************************************************/1/* multimesh_editor_plugin.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "multimesh_editor_plugin.h"3132#include "editor/editor_node.h"33#include "editor/editor_string_names.h"34#include "editor/scene/3d/node_3d_editor_plugin.h"35#include "editor/scene/scene_tree_editor.h"36#include "scene/3d/mesh_instance_3d.h"37#include "scene/gui/box_container.h"38#include "scene/gui/menu_button.h"39#include "scene/gui/option_button.h"4041void MultiMeshEditor::_node_removed(Node *p_node) {42if (p_node == node) {43node = nullptr;44hide();45}46}4748void MultiMeshEditor::_populate() {49if (!node) {50return;51}5253Ref<Mesh> mesh;5455if (mesh_source->get_text().is_empty()) {56Ref<MultiMesh> multimesh;57multimesh = node->get_multimesh();58if (multimesh.is_null()) {59err_dialog->set_text(TTR("No mesh source specified (and no MultiMesh set in node)."));60err_dialog->popup_centered();61return;62}63if (multimesh->get_mesh().is_null()) {64err_dialog->set_text(TTR("No mesh source specified (and MultiMesh contains no Mesh)."));65err_dialog->popup_centered();66return;67}6869mesh = multimesh->get_mesh();70} else {71Node *ms_node = node->get_node(mesh_source->get_text());7273if (!ms_node) {74err_dialog->set_text(TTR("Mesh source is invalid (invalid path)."));75err_dialog->popup_centered();76return;77}7879MeshInstance3D *ms_instance = Object::cast_to<MeshInstance3D>(ms_node);8081if (!ms_instance) {82err_dialog->set_text(TTR("Mesh source is invalid (not a MeshInstance3D)."));83err_dialog->popup_centered();84return;85}8687mesh = ms_instance->get_mesh();8889if (mesh.is_null()) {90err_dialog->set_text(TTR("Mesh source is invalid (contains no Mesh resource)."));91err_dialog->popup_centered();92return;93}94}9596if (surface_source->get_text().is_empty()) {97err_dialog->set_text(TTR("No surface source specified."));98err_dialog->popup_centered();99return;100}101102Node *ss_node = node->get_node(surface_source->get_text());103104if (!ss_node) {105err_dialog->set_text(TTR("Surface source is invalid (invalid path)."));106err_dialog->popup_centered();107return;108}109110MeshInstance3D *ss_instance = Object::cast_to<MeshInstance3D>(ss_node);111112if (!ss_instance || ss_instance->get_mesh().is_null()) {113err_dialog->set_text(TTR("Surface source is invalid (no geometry)."));114err_dialog->popup_centered();115return;116}117118Transform3D geom_xform = node->get_global_transform().affine_inverse() * ss_instance->get_global_transform();119120Vector<Face3> geometry = ss_instance->get_mesh()->get_faces();121122if (geometry.is_empty()) {123err_dialog->set_text(TTR("Surface source is invalid (no faces)."));124err_dialog->popup_centered();125return;126}127128//make all faces local129130int gc = geometry.size();131Face3 *w = geometry.ptrw();132133for (int i = 0; i < gc; i++) {134for (int j = 0; j < 3; j++) {135w[i].vertex[j] = geom_xform.xform(w[i].vertex[j]);136}137}138139Vector<Face3> faces = geometry;140int facecount = faces.size();141ERR_FAIL_COND_MSG(!facecount, "Parent has no solid faces to populate.");142143const Face3 *r = faces.ptr();144145float area_accum = 0;146RBMap<float, int> triangle_area_map;147for (int i = 0; i < facecount; i++) {148float area = r[i].get_area();149if (area < CMP_EPSILON) {150continue;151}152triangle_area_map[area_accum] = i;153area_accum += area;154}155156ERR_FAIL_COND_MSG(triangle_area_map.is_empty(), "Couldn't map area.");157ERR_FAIL_COND_MSG(area_accum == 0, "Couldn't map area.");158159Ref<MultiMesh> multimesh = memnew(MultiMesh);160multimesh->set_mesh(mesh);161162int instance_count = populate_amount->get_value();163164multimesh->set_transform_format(MultiMesh::TRANSFORM_3D);165multimesh->set_use_colors(false);166multimesh->set_instance_count(instance_count);167168float _tilt_random = populate_tilt_random->get_value();169float _rotate_random = populate_rotate_random->get_value();170float _scale_random = populate_scale_random->get_value();171float _scale = populate_scale->get_value();172int axis = populate_axis->get_selected();173174Transform3D axis_xform;175if (axis == Vector3::AXIS_Z) {176axis_xform.rotate(Vector3(1, 0, 0), -Math::PI * 0.5);177}178if (axis == Vector3::AXIS_X) {179axis_xform.rotate(Vector3(0, 0, 1), -Math::PI * 0.5);180}181182for (int i = 0; i < instance_count; i++) {183float areapos = Math::random(0.0f, area_accum);184185RBMap<float, int>::Iterator E = triangle_area_map.find_closest(areapos);186ERR_FAIL_COND(!E);187int index = E->value;188ERR_FAIL_INDEX(index, facecount);189190// ok FINALLY get face191Face3 face = r[index];192//now compute some position inside the face...193194Vector3 pos = face.get_random_point_inside();195Vector3 normal = face.get_plane().normal;196Vector3 op_axis = (face.vertex[0] - face.vertex[1]).normalized();197198Transform3D xform;199200xform.set_look_at(pos, pos + op_axis, normal);201xform = xform * axis_xform;202203Basis post_xform;204205post_xform.rotate(xform.basis.get_column(1), -Math::random(-_rotate_random, _rotate_random) * Math::PI);206post_xform.rotate(xform.basis.get_column(2), -Math::random(-_tilt_random, _tilt_random) * Math::PI);207post_xform.rotate(xform.basis.get_column(0), -Math::random(-_tilt_random, _tilt_random) * Math::PI);208209xform.basis = post_xform * xform.basis;210//xform.basis.orthonormalize();211212xform.basis.scale(Vector3(1, 1, 1) * (_scale + Math::random(-_scale_random, _scale_random)));213214multimesh->set_instance_transform(i, xform);215}216217node->set_multimesh(multimesh);218}219220void MultiMeshEditor::_browsed(const NodePath &p_path) {221NodePath path = node->get_path_to(get_node(p_path));222223if (browsing_source) {224mesh_source->set_text(String(path));225} else {226surface_source->set_text(String(path));227}228}229230void MultiMeshEditor::_menu_option(int p_option) {231switch (p_option) {232case MENU_OPTION_POPULATE: {233if (_last_pp_node != node) {234surface_source->set_text("..");235mesh_source->set_text("..");236populate_axis->select(1);237populate_rotate_random->set_value(0);238populate_tilt_random->set_value(0);239populate_scale_random->set_value(0);240populate_scale->set_value(1);241populate_amount->set_value(128);242243_last_pp_node = node;244}245populate_dialog->popup_centered(Size2(250, 380));246247} break;248}249}250251void MultiMeshEditor::edit(MultiMeshInstance3D *p_multimesh) {252node = p_multimesh;253}254255void MultiMeshEditor::_browse(bool p_source) {256browsing_source = p_source;257Node *browsed_node = nullptr;258if (p_source) {259browsed_node = node->get_node_or_null(mesh_source->get_text());260std->set_title(TTR("Select a Source Mesh:"));261} else {262browsed_node = node->get_node_or_null(surface_source->get_text());263std->set_title(TTR("Select a Target Surface:"));264}265std->popup_scenetree_dialog(browsed_node);266}267268MultiMeshEditor::MultiMeshEditor() {269options = memnew(MenuButton);270options->set_switch_on_hover(true);271Node3DEditor::get_singleton()->add_control_to_menu_panel(options);272273options->set_text("MultiMesh");274options->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MultiMeshInstance3D"), EditorStringName(EditorIcons)));275options->set_flat(false);276options->set_theme_type_variation("FlatMenuButton");277278options->get_popup()->add_item(TTR("Populate Surface"));279options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MultiMeshEditor::_menu_option));280281populate_dialog = memnew(ConfirmationDialog);282populate_dialog->set_title(TTR("Populate MultiMesh"));283add_child(populate_dialog);284285VBoxContainer *vbc = memnew(VBoxContainer);286populate_dialog->add_child(vbc);287//populate_dialog->set_child_rect(vbc);288289HBoxContainer *hbc = memnew(HBoxContainer);290291surface_source = memnew(LineEdit);292hbc->add_child(surface_source);293surface_source->set_h_size_flags(SIZE_EXPAND_FILL);294surface_source->set_accessibility_name(TTRC("Target Surface:"));295Button *b = memnew(Button);296hbc->add_child(b);297b->set_accessibility_name(TTRC("Browse"));298b->set_text("..");299b->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_browse).bind(false));300301vbc->add_margin_child(TTR("Target Surface:"), hbc);302303hbc = memnew(HBoxContainer);304mesh_source = memnew(LineEdit);305hbc->add_child(mesh_source);306mesh_source->set_h_size_flags(SIZE_EXPAND_FILL);307mesh_source->set_accessibility_name(TTRC("Source Mesh:"));308b = memnew(Button);309hbc->add_child(b);310b->set_accessibility_name(TTRC("Browse"));311b->set_text("..");312vbc->add_margin_child(TTR("Source Mesh:"), hbc);313b->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_browse).bind(true));314315populate_axis = memnew(OptionButton);316populate_axis->set_accessibility_name(TTRC("Mesh Up Axis:"));317populate_axis->add_item(TTR("X-Axis"));318populate_axis->add_item(TTR("Y-Axis"));319populate_axis->add_item(TTR("Z-Axis"));320populate_axis->select(2);321vbc->add_margin_child(TTR("Mesh Up Axis:"), populate_axis);322323populate_rotate_random = memnew(HSlider);324populate_rotate_random->set_max(1);325populate_rotate_random->set_step(0.01);326populate_rotate_random->set_accessibility_name(TTRC("Random Rotation:"));327vbc->add_margin_child(TTR("Random Rotation:"), populate_rotate_random);328329populate_tilt_random = memnew(HSlider);330populate_tilt_random->set_max(1);331populate_tilt_random->set_step(0.01);332populate_tilt_random->set_accessibility_name(TTRC("Random Tilt:"));333vbc->add_margin_child(TTR("Random Tilt:"), populate_tilt_random);334335populate_scale_random = memnew(SpinBox);336populate_scale_random->set_min(0);337populate_scale_random->set_max(1);338populate_scale_random->set_value(0);339populate_scale_random->set_step(0.01);340populate_scale_random->set_accessibility_name(TTRC("Random Scale:"));341vbc->add_margin_child(TTR("Random Scale:"), populate_scale_random);342343populate_scale = memnew(SpinBox);344populate_scale->set_min(0.001);345populate_scale->set_max(4096);346populate_scale->set_value(1);347populate_scale->set_step(0.01);348populate_scale->set_accessibility_name(TTRC("Scale:"));349vbc->add_margin_child(TTR("Scale:"), populate_scale);350351populate_amount = memnew(SpinBox);352populate_amount->set_anchor(SIDE_RIGHT, ANCHOR_END);353populate_amount->set_begin(Point2(20, 232));354populate_amount->set_end(Point2(-5, 237));355populate_amount->set_min(1);356populate_amount->set_max(65536);357populate_amount->set_value(128);358populate_amount->set_accessibility_name(TTRC("Amount:"));359vbc->add_margin_child(TTR("Amount:"), populate_amount);360361populate_dialog->set_ok_button_text(TTR("Populate"));362363populate_dialog->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MultiMeshEditor::_populate));364std = memnew(SceneTreeDialog);365Vector<StringName> valid_types;366valid_types.push_back("MeshInstance3D");367std->set_valid_types(valid_types);368populate_dialog->add_child(std);369std->connect("selected", callable_mp(this, &MultiMeshEditor::_browsed));370371_last_pp_node = nullptr;372373err_dialog = memnew(AcceptDialog);374add_child(err_dialog);375}376377void MultiMeshEditorPlugin::edit(Object *p_object) {378multimesh_editor->edit(Object::cast_to<MultiMeshInstance3D>(p_object));379}380381bool MultiMeshEditorPlugin::handles(Object *p_object) const {382return p_object->is_class("MultiMeshInstance3D");383}384385void MultiMeshEditorPlugin::make_visible(bool p_visible) {386if (p_visible) {387multimesh_editor->options->show();388} else {389multimesh_editor->options->hide();390multimesh_editor->edit(nullptr);391}392}393394MultiMeshEditorPlugin::MultiMeshEditorPlugin() {395multimesh_editor = memnew(MultiMeshEditor);396EditorNode::get_singleton()->get_gui_base()->add_child(multimesh_editor);397398multimesh_editor->options->hide();399}400401402