Path: blob/master/editor/scene/3d/polygon_3d_editor_plugin.cpp
9902 views
/**************************************************************************/1/* polygon_3d_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 "polygon_3d_editor_plugin.h"3132#include "core/input/input.h"33#include "core/math/geometry_2d.h"34#include "core/os/keyboard.h"35#include "editor/editor_node.h"36#include "editor/editor_string_names.h"37#include "editor/editor_undo_redo_manager.h"38#include "editor/scene/3d/node_3d_editor_plugin.h"39#include "editor/scene/canvas_item_editor_plugin.h"40#include "editor/settings/editor_settings.h"41#include "scene/3d/camera_3d.h"4243void Polygon3DEditor::_notification(int p_what) {44switch (p_what) {45case NOTIFICATION_READY: {46button_create->set_button_icon(get_editor_theme_icon(SNAME("Edit")));47button_edit->set_button_icon(get_editor_theme_icon(SNAME("MovePoint")));48button_edit->set_pressed(true);49get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed));5051} break;5253case NOTIFICATION_PROCESS: {54if (!node) {55return;56}5758if (_get_depth() != prev_depth) {59_polygon_draw();60prev_depth = _get_depth();61}6263} break;64}65}6667void Polygon3DEditor::_node_removed(Node *p_node) {68if (p_node == node) {69node = nullptr;70if (imgeom->get_parent() == p_node) {71p_node->remove_child(imgeom);72}73hide();74set_process(false);75}76}7778void Polygon3DEditor::_menu_option(int p_option) {79switch (p_option) {80case MODE_CREATE: {81mode = MODE_CREATE;82button_create->set_pressed(true);83button_edit->set_pressed(false);84} break;85case MODE_EDIT: {86mode = MODE_EDIT;87button_create->set_pressed(false);88button_edit->set_pressed(true);89} break;90}91}9293void Polygon3DEditor::_wip_close() {94Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;95ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");96EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();97undo_redo->create_action(TTR("Create Polygon3D"));98undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));99undo_redo->add_do_method(obj, "set_polygon", wip);100undo_redo->add_do_method(this, "_polygon_draw");101undo_redo->add_undo_method(this, "_polygon_draw");102wip.clear();103wip_active = false;104mode = MODE_EDIT;105button_edit->set_pressed(true);106button_create->set_pressed(false);107edited_point = -1;108undo_redo->commit_action();109}110111EditorPlugin::AfterGUIInput Polygon3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {112if (!node) {113return EditorPlugin::AFTER_GUI_INPUT_PASS;114}115116Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;117Transform3D gt = node->get_global_transform();118Transform3D gi = gt.affine_inverse();119float depth = _get_depth() * 0.5;120Vector3 n = gt.basis.get_column(2).normalized();121Plane p(n, gt.origin + n * depth);122123Ref<InputEventMouseButton> mb = p_event;124125if (mb.is_valid()) {126Vector2 gpoint = mb->get_position();127Vector3 ray_from = p_camera->project_ray_origin(gpoint);128Vector3 ray_dir = p_camera->project_ray_normal(gpoint);129130Vector3 spoint;131132if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {133return EditorPlugin::AFTER_GUI_INPUT_PASS;134}135136spoint = gi.xform(spoint);137138Vector2 cpoint(spoint.x, spoint.y);139140//DO NOT snap here, it's confusing in 3D for adding points.141//Let the snap happen when the point is being moved, instead.142//cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);143144PackedVector2Array poly = _get_polygon();145146//first check if a point is to be added (segment split)147real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");148149switch (mode) {150case MODE_CREATE: {151if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {152if (!wip_active) {153wip.clear();154wip.push_back(cpoint);155wip_active = true;156edited_point_pos = cpoint;157snap_ignore = false;158_polygon_draw();159edited_point = 1;160return EditorPlugin::AFTER_GUI_INPUT_STOP;161} else {162if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {163//wip closed164_wip_close();165166return EditorPlugin::AFTER_GUI_INPUT_STOP;167} else {168wip.push_back(cpoint);169edited_point = wip.size();170snap_ignore = false;171_polygon_draw();172return EditorPlugin::AFTER_GUI_INPUT_STOP;173}174}175} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {176_wip_close();177}178179} break;180181case MODE_EDIT: {182if (mb->get_button_index() == MouseButton::LEFT) {183if (mb->is_pressed()) {184if (mb->is_command_or_control_pressed()) {185if (poly.size() < 3) {186EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();187undo_redo->create_action(TTR("Edit Poly"));188undo_redo->add_undo_method(obj, "set_polygon", poly);189poly.push_back(cpoint);190undo_redo->add_do_method(obj, "set_polygon", poly);191undo_redo->add_do_method(this, "_polygon_draw");192undo_redo->add_undo_method(this, "_polygon_draw");193undo_redo->commit_action();194return EditorPlugin::AFTER_GUI_INPUT_STOP;195}196197//search edges198int closest_idx = -1;199Vector2 closest_pos;200real_t closest_dist = 1e10;201for (int i = 0; i < poly.size(); i++) {202const Vector2 segment_a = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));203const Vector2 segment_b = p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, poly[(i + 1) % poly.size()].y, depth)));204205Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, segment_a, segment_b);206if (cp.distance_squared_to(segment_a) < CMP_EPSILON2 || cp.distance_squared_to(segment_b) < CMP_EPSILON2) {207continue; //not valid to reuse point208}209210real_t d = cp.distance_to(gpoint);211if (d < closest_dist && d < grab_threshold) {212closest_dist = d;213closest_pos = cp;214closest_idx = i;215}216}217218if (closest_idx >= 0) {219pre_move_edit = poly;220poly.insert(closest_idx + 1, cpoint);221edited_point = closest_idx + 1;222edited_point_pos = cpoint;223_set_polygon(poly);224_polygon_draw();225snap_ignore = true;226227return EditorPlugin::AFTER_GUI_INPUT_STOP;228}229} else {230//look for points to move231232int closest_idx = -1;233Vector2 closest_pos;234real_t closest_dist = 1e10;235for (int i = 0; i < poly.size(); i++) {236Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));237238real_t d = cp.distance_to(gpoint);239if (d < closest_dist && d < grab_threshold) {240closest_dist = d;241closest_pos = cp;242closest_idx = i;243}244}245246if (closest_idx >= 0) {247pre_move_edit = poly;248edited_point = closest_idx;249edited_point_pos = poly[closest_idx];250_polygon_draw();251snap_ignore = false;252return EditorPlugin::AFTER_GUI_INPUT_STOP;253}254}255} else {256snap_ignore = false;257258if (edited_point != -1) {259//apply260261ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);262poly.write[edited_point] = edited_point_pos;263EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();264undo_redo->create_action(TTR("Edit Poly"));265undo_redo->add_do_method(obj, "set_polygon", poly);266undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);267undo_redo->add_do_method(this, "_polygon_draw");268undo_redo->add_undo_method(this, "_polygon_draw");269undo_redo->commit_action();270271edited_point = -1;272return EditorPlugin::AFTER_GUI_INPUT_STOP;273}274}275}276if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {277int closest_idx = -1;278Vector2 closest_pos;279real_t closest_dist = 1e10;280for (int i = 0; i < poly.size(); i++) {281Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, poly[i].y, depth)));282283real_t d = cp.distance_to(gpoint);284if (d < closest_dist && d < grab_threshold) {285closest_dist = d;286closest_pos = cp;287closest_idx = i;288}289}290291if (closest_idx >= 0) {292EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();293undo_redo->create_action(TTR("Edit Poly (Remove Point)"));294undo_redo->add_undo_method(obj, "set_polygon", poly);295poly.remove_at(closest_idx);296undo_redo->add_do_method(obj, "set_polygon", poly);297undo_redo->add_do_method(this, "_polygon_draw");298undo_redo->add_undo_method(this, "_polygon_draw");299undo_redo->commit_action();300return EditorPlugin::AFTER_GUI_INPUT_STOP;301}302}303304} break;305}306}307308Ref<InputEventMouseMotion> mm = p_event;309310if (mm.is_valid()) {311if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {312Vector2 gpoint = mm->get_position();313314Vector3 ray_from = p_camera->project_ray_origin(gpoint);315Vector3 ray_dir = p_camera->project_ray_normal(gpoint);316317Vector3 spoint;318319if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {320return EditorPlugin::AFTER_GUI_INPUT_PASS;321}322323spoint = gi.xform(spoint);324325Vector2 cpoint(spoint.x, spoint.y);326327if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {328snap_ignore = false;329}330331if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) {332cpoint = cpoint.snappedf(Node3DEditor::get_singleton()->get_translate_snap());333}334edited_point_pos = cpoint;335336_polygon_draw();337}338}339340return EditorPlugin::AFTER_GUI_INPUT_PASS;341}342343float Polygon3DEditor::_get_depth() {344Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;345ERR_FAIL_NULL_V_MSG(obj, 0.0f, "Edited object is not valid.");346347if (bool(obj->call("_has_editable_3d_polygon_no_depth"))) {348return 0.0f;349}350351return float(obj->call("get_depth"));352}353354PackedVector2Array Polygon3DEditor::_get_polygon() {355Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;356ERR_FAIL_NULL_V_MSG(obj, PackedVector2Array(), "Edited object is not valid.");357return PackedVector2Array(obj->call("get_polygon"));358}359360void Polygon3DEditor::_set_polygon(const PackedVector2Array &p_poly) {361Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;362ERR_FAIL_NULL_MSG(obj, "Edited object is not valid.");363obj->call("set_polygon", p_poly);364}365366void Polygon3DEditor::_polygon_draw() {367if (!node) {368return;369}370371PackedVector2Array poly;372373if (wip_active) {374poly = wip;375} else {376poly = _get_polygon();377}378379float depth = _get_depth() * 0.5;380381m->clear_surfaces();382imesh->clear_surfaces();383imgeom->set_material_override(line_material);384imesh->surface_begin(Mesh::PRIMITIVE_LINES);385386Rect2 rect;387388for (int i = 0; i < poly.size(); i++) {389Vector2 p, p2;390p = i == edited_point ? edited_point_pos : poly[i];391if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) {392p2 = edited_point_pos;393} else {394p2 = poly[(i + 1) % poly.size()];395}396397if (i == 0) {398rect.position = p;399} else {400rect.expand_to(p);401}402403Vector3 point = Vector3(p.x, p.y, depth);404Vector3 next_point = Vector3(p2.x, p2.y, depth);405406imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));407imesh->surface_add_vertex(point);408imesh->surface_set_color(Color(1, 0.3, 0.1, 0.8));409imesh->surface_add_vertex(next_point);410411//Color col=Color(1,0.3,0.1,0.8);412//vpc->draw_line(point,next_point,col,2);413//vpc->draw_texture(handle,point-handle->get_size()*0.5);414}415416rect = rect.grow(1);417418AABB r;419r.position.x = rect.position.x;420r.position.y = rect.position.y;421r.position.z = depth;422r.size.x = rect.size.x;423r.size.y = rect.size.y;424r.size.z = 0;425426imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));427imesh->surface_add_vertex(r.position);428imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));429imesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0));430imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));431imesh->surface_add_vertex(r.position);432imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));433imesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0));434435imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));436imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));437imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));438imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0));439imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));440imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0));441imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));442imesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0));443444imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));445imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));446imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));447imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0));448imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));449imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0));450imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));451imesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0));452453imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));454imesh->surface_add_vertex(r.position + r.size);455imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));456imesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0));457imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));458imesh->surface_add_vertex(r.position + r.size);459imesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2));460imesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0));461462imesh->surface_end();463464if (poly.is_empty()) {465return;466}467468Array a;469a.resize(Mesh::ARRAY_MAX);470Vector<Vector3> va;471{472va.resize(poly.size());473Vector3 *w = va.ptrw();474for (int i = 0; i < poly.size(); i++) {475Vector2 p;476p = i == edited_point ? edited_point_pos : poly[i];477478Vector3 point = Vector3(p.x, p.y, depth);479w[i] = point;480}481}482a[Mesh::ARRAY_VERTEX] = va;483m->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);484m->surface_set_material(0, handle_material);485}486487void Polygon3DEditor::edit(Node *p_node) {488if (p_node) {489node = Object::cast_to<Node3D>(p_node);490node_resource = node->call("_get_editable_3d_polygon_resource");491492if (node_resource.is_valid()) {493node_resource->connect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));494}495//Enable the pencil tool if the polygon is empty496if (_get_polygon().is_empty()) {497_menu_option(MODE_CREATE);498}499wip.clear();500wip_active = false;501edited_point = -1;502if (imgeom->get_parent()) {503imgeom->reparent(p_node, false);504} else {505p_node->add_child(imgeom);506}507_polygon_draw();508set_process(true);509prev_depth = -1;510511} else {512node = nullptr;513if (node_resource.is_valid()) {514node_resource->disconnect_changed(callable_mp(this, &Polygon3DEditor::_polygon_draw));515}516node_resource.unref();517518if (imgeom->get_parent()) {519imgeom->get_parent()->remove_child(imgeom);520}521522set_process(false);523}524}525526void Polygon3DEditor::_bind_methods() {527ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);528}529530Polygon3DEditor::Polygon3DEditor() {531node = nullptr;532533button_create = memnew(Button);534button_create->set_theme_type_variation(SceneStringName(FlatButton));535button_create->set_tooltip_text(TTRC("Create Polygon"));536add_child(button_create);537button_create->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_CREATE));538button_create->set_toggle_mode(true);539540button_edit = memnew(Button);541button_edit->set_theme_type_variation(SceneStringName(FlatButton));542button_edit->set_tooltip_text(TTRC("Edit Polygon"));543add_child(button_edit);544button_edit->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_EDIT));545button_edit->set_toggle_mode(true);546547mode = MODE_EDIT;548wip_active = false;549imgeom = memnew(MeshInstance3D);550imesh.instantiate();551imgeom->set_mesh(imesh);552imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));553554line_material.instantiate();555line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);556line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);557line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);558line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);559line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);560line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);561line_material->set_albedo(Color(1, 1, 1));562563handle_material.instantiate();564handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);565handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);566handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);567handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);568handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);569handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);570handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);571Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));572handle_material->set_point_size(handle->get_width());573handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);574575pointsm = memnew(MeshInstance3D);576imgeom->add_child(pointsm);577m.instantiate();578pointsm->set_mesh(m);579pointsm->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));580581snap_ignore = false;582}583584Polygon3DEditor::~Polygon3DEditor() {585memdelete(imgeom);586}587588void Polygon3DEditorPlugin::edit(Object *p_object) {589polygon_editor->edit(Object::cast_to<Node>(p_object));590}591592bool Polygon3DEditorPlugin::handles(Object *p_object) const {593return Object::cast_to<Node3D>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));594}595596void Polygon3DEditorPlugin::make_visible(bool p_visible) {597if (p_visible) {598polygon_editor->show();599} else {600polygon_editor->hide();601polygon_editor->edit(nullptr);602}603}604605Polygon3DEditorPlugin::Polygon3DEditorPlugin() {606polygon_editor = memnew(Polygon3DEditor);607Node3DEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);608609polygon_editor->hide();610}611612613