Path: blob/master/editor/scene/2d/polygon_2d_editor_plugin.cpp
20912 views
/**************************************************************************/1/* polygon_2d_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_2d_editor_plugin.h"3132#include "core/input/input_event.h"33#include "core/math/geometry_2d.h"34#include "editor/docks/editor_dock.h"35#include "editor/docks/editor_dock_manager.h"36#include "editor/editor_node.h"37#include "editor/editor_undo_redo_manager.h"38#include "editor/gui/editor_zoom_widget.h"39#include "editor/scene/canvas_item_editor_plugin.h"40#include "editor/settings/editor_command_palette.h"41#include "editor/settings/editor_settings.h"42#include "editor/themes/editor_scale.h"43#include "scene/2d/skeleton_2d.h"44#include "scene/gui/check_box.h"45#include "scene/gui/dialogs.h"46#include "scene/gui/label.h"47#include "scene/gui/menu_button.h"48#include "scene/gui/panel.h"49#include "scene/gui/scroll_container.h"50#include "scene/gui/separator.h"51#include "scene/gui/slider.h"52#include "scene/gui/spin_box.h"53#include "scene/gui/split_container.h"54#include "scene/gui/view_panner.h"5556Node2D *Polygon2DEditor::_get_node() const {57return node;58}5960void Polygon2DEditor::_set_node(Node *p_polygon) {61CanvasItem *draw = Object::cast_to<CanvasItem>(canvas);62if (node) {63node->disconnect(SceneStringName(draw), callable_mp(draw, &CanvasItem::queue_redraw));64node->disconnect(SceneStringName(draw), callable_mp(this, &Polygon2DEditor::_update_available_modes));65}66node = Object::cast_to<Polygon2D>(p_polygon);67_update_polygon_editing_state();68canvas->queue_redraw();69if (node) {70canvas->set_texture_filter(node->get_texture_filter_in_tree());7172_update_bone_list(node);73_update_available_modes();74if (current_mode == MODE_MAX) {75_select_mode(MODE_POINTS); // Initialize when opening the first time.76}77if (previous_node != node) {78_center_view_on_draw();79}80previous_node = node;81// Whenever polygon gets redrawn, there's possible changes for the editor as well.82node->connect(SceneStringName(draw), callable_mp(draw, &CanvasItem::queue_redraw));83node->connect(SceneStringName(draw), callable_mp(this, &Polygon2DEditor::_update_available_modes));84}85}8687Vector2 Polygon2DEditor::_get_offset(int p_idx) const {88return node->get_offset();89}9091int Polygon2DEditor::_get_polygon_count() const {92if (node->get_internal_vertex_count() > 0) {93return 0; //do not edit if internal vertices exist94} else {95return 1;96}97}9899void Polygon2DEditor::_notification(int p_what) {100switch (p_what) {101case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {102if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {103break;104}105[[fallthrough]];106}107case NOTIFICATION_ENTER_TREE: {108panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));109panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));110} break;111112case NOTIFICATION_READY: {113action_buttons[ACTION_CREATE]->set_button_icon(get_editor_theme_icon(SNAME("Edit")));114action_buttons[ACTION_CREATE_INTERNAL]->set_button_icon(get_editor_theme_icon(SNAME("EditInternal")));115action_buttons[ACTION_REMOVE_INTERNAL]->set_button_icon(get_editor_theme_icon(SNAME("RemoveInternal")));116action_buttons[ACTION_EDIT_POINT]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect")));117action_buttons[ACTION_MOVE]->set_button_icon(get_editor_theme_icon(SNAME("ToolMove")));118action_buttons[ACTION_ROTATE]->set_button_icon(get_editor_theme_icon(SNAME("ToolRotate")));119action_buttons[ACTION_SCALE]->set_button_icon(get_editor_theme_icon(SNAME("ToolScale")));120action_buttons[ACTION_ADD_POLYGON]->set_button_icon(get_editor_theme_icon(SNAME("Edit")));121action_buttons[ACTION_REMOVE_POLYGON]->set_button_icon(get_editor_theme_icon(SNAME("Close")));122action_buttons[ACTION_PAINT_WEIGHT]->set_button_icon(get_editor_theme_icon(SNAME("Bucket")));123action_buttons[ACTION_CLEAR_WEIGHT]->set_button_icon(get_editor_theme_icon(SNAME("Clear")));124125b_snap_grid->set_button_icon(get_editor_theme_icon(SNAME("Grid")));126b_snap_enable->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid")));127128vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE);129hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);130// Avoid scrollbar overlapping.131Size2 hmin = hscroll->get_combined_minimum_size();132Size2 vmin = vscroll->get_combined_minimum_size();133hscroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -vmin.width);134vscroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -hmin.height);135[[fallthrough]];136}137case NOTIFICATION_THEME_CHANGED: {138canvas->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));139bone_scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));140} break;141case NOTIFICATION_VISIBILITY_CHANGED: {142if (is_visible()) {143polygon_edit->make_visible();144} else {145polygon_edit->close();146}147} break;148}149}150151void Polygon2DEditor::_sync_bones() {152Skeleton2D *skeleton = nullptr;153if (!node->has_node(node->get_skeleton())) {154error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));155error->popup_centered();156} else {157Node *sn = node->get_node(node->get_skeleton());158skeleton = Object::cast_to<Skeleton2D>(sn);159}160161Array prev_bones = node->call("_get_bones");162node->clear_bones();163164if (!skeleton) {165error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));166error->popup_centered();167} else {168for (int i = 0; i < skeleton->get_bone_count(); i++) {169NodePath path = skeleton->get_path_to(skeleton->get_bone(i));170Vector<float> weights;171int wc = node->get_polygon().size();172173for (int j = 0; j < prev_bones.size(); j += 2) {174NodePath pvp = prev_bones[j];175Vector<float> pv = prev_bones[j + 1];176if (pvp == path && pv.size() == wc) {177weights = pv;178}179}180181if (weights.is_empty()) { //create them182weights.resize(wc);183float *w = weights.ptrw();184for (int j = 0; j < wc; j++) {185w[j] = 0.0;186}187}188189node->add_bone(path, weights);190}191}192193Array new_bones = node->call("_get_bones");194195EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();196undo_redo->create_action(TTR("Sync Bones"));197undo_redo->add_do_method(node, "_set_bones", new_bones);198undo_redo->add_undo_method(node, "_set_bones", prev_bones);199undo_redo->add_do_method(this, "_update_bone_list", node);200undo_redo->add_undo_method(this, "_update_bone_list", node);201undo_redo->commit_action();202}203204void Polygon2DEditor::_update_bone_list(const Polygon2D *p_for_node) {205ERR_FAIL_NULL(p_for_node);206if (p_for_node != node) {207return;208}209210NodePath selected;211while (bone_scroll_vb->get_child_count()) {212CheckBox *cb = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(0));213if (cb && cb->is_pressed()) {214selected = cb->get_meta("bone_path");215}216memdelete(bone_scroll_vb->get_child(0));217}218219Ref<ButtonGroup> bg;220bg.instantiate();221for (int i = 0; i < node->get_bone_count(); i++) {222CheckBox *cb = memnew(CheckBox);223NodePath np = node->get_bone_path(i);224String name;225if (np.get_name_count()) {226name = np.get_name(np.get_name_count() - 1);227}228if (name.is_empty()) {229name = "Bone " + itos(i);230}231cb->set_text(name);232cb->set_button_group(bg);233cb->set_meta("bone_path", np);234cb->set_focus_mode(FOCUS_NONE);235bone_scroll_vb->add_child(cb);236237if (np == selected || bone_scroll_vb->get_child_count() < 2) {238cb->set_pressed(true);239}240241cb->connect(SceneStringName(pressed), callable_mp(this, &Polygon2DEditor::_bone_paint_selected).bind(i));242}243244canvas->queue_redraw();245}246247void Polygon2DEditor::_bone_paint_selected(int p_index) {248canvas->queue_redraw();249}250251void Polygon2DEditor::_select_mode(int p_mode) {252current_mode = Mode(p_mode);253mode_buttons[current_mode]->set_pressed(true);254for (int i = 0; i < ACTION_MAX; i++) {255action_buttons[i]->hide();256}257bone_scroll_main_vb->hide();258bone_paint_strength->hide();259bone_paint_radius->hide();260bone_paint_radius_label->hide();261switch (current_mode) {262case MODE_POINTS: {263action_buttons[ACTION_CREATE]->show();264action_buttons[ACTION_CREATE_INTERNAL]->show();265action_buttons[ACTION_REMOVE_INTERNAL]->show();266action_buttons[ACTION_EDIT_POINT]->show();267action_buttons[ACTION_MOVE]->show();268action_buttons[ACTION_ROTATE]->show();269action_buttons[ACTION_SCALE]->show();270271if (node->get_polygon().is_empty()) {272_set_action(ACTION_CREATE);273} else {274_set_action(ACTION_EDIT_POINT);275}276} break;277case MODE_POLYGONS: {278action_buttons[ACTION_ADD_POLYGON]->show();279action_buttons[ACTION_REMOVE_POLYGON]->show();280_set_action(ACTION_ADD_POLYGON);281} break;282case MODE_UV: {283if (node->get_uv().size() != node->get_polygon().size()) {284_edit_menu_option(MENU_POLYGON_TO_UV);285}286action_buttons[ACTION_EDIT_POINT]->show();287action_buttons[ACTION_MOVE]->show();288action_buttons[ACTION_ROTATE]->show();289action_buttons[ACTION_SCALE]->show();290_set_action(ACTION_EDIT_POINT);291} break;292case MODE_BONES: {293action_buttons[ACTION_PAINT_WEIGHT]->show();294action_buttons[ACTION_CLEAR_WEIGHT]->show();295_set_action(ACTION_PAINT_WEIGHT);296297bone_scroll_main_vb->show();298bone_paint_strength->show();299bone_paint_radius->show();300bone_paint_radius_label->show();301_update_bone_list(node);302bone_paint_pos = Vector2(-100000, -100000); // Send brush away when switching.303} break;304default:305break;306}307canvas->queue_redraw();308}309310void Polygon2DEditor::_edit_menu_option(int p_option) {311EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();312switch (p_option) {313case MENU_POLYGON_TO_UV: {314Vector<Vector2> points = node->get_polygon();315if (points.is_empty()) {316break;317}318Vector<Vector2> uvs = node->get_uv();319undo_redo->create_action(TTR("Create UV Map"));320undo_redo->add_do_method(node, "set_uv", points);321undo_redo->add_undo_method(node, "set_uv", uvs);322undo_redo->commit_action();323} break;324case MENU_UV_TO_POLYGON: {325Vector<Vector2> points = node->get_polygon();326Vector<Vector2> uvs = node->get_uv();327if (uvs.is_empty()) {328break;329}330331undo_redo->create_action(TTR("Create Polygon"));332undo_redo->add_do_method(node, "set_polygon", uvs);333undo_redo->add_undo_method(node, "set_polygon", points);334undo_redo->commit_action();335} break;336case MENU_UV_CLEAR: {337Vector<Vector2> uvs = node->get_uv();338if (uvs.is_empty()) {339break;340}341undo_redo->create_action(TTR("Create UV Map"));342undo_redo->add_do_method(node, "set_uv", Vector<Vector2>());343undo_redo->add_undo_method(node, "set_uv", uvs);344undo_redo->commit_action();345} break;346case MENU_GRID_SETTINGS: {347grid_settings->popup_centered();348} break;349}350}351352void Polygon2DEditor::_cancel_editing() {353if (is_creating) {354is_dragging = false;355is_creating = false;356node->set_uv(previous_uv);357node->set_polygon(previous_polygon);358node->set_internal_vertex_count(previous_internal_vertices);359node->set_vertex_colors(previous_colors);360node->call("_set_bones", previous_bones);361node->set_polygons(previous_polygons);362363_update_polygon_editing_state();364_update_available_modes();365} else if (is_dragging) {366is_dragging = false;367if (current_mode == MODE_UV) {368node->set_uv(editing_points);369} else if (current_mode == MODE_POINTS) {370node->set_polygon(editing_points);371}372}373374polygon_create.clear();375}376377void Polygon2DEditor::_update_polygon_editing_state() {378if (!_get_node()) {379return;380}381382if (node->get_internal_vertex_count() > 0) {383disable_polygon_editing(true, TTR("Polygon 2D has internal vertices, so it can no longer be edited in the viewport."));384} else {385disable_polygon_editing(false, String());386}387}388389void Polygon2DEditor::_commit_action() {390// Makes that undo/redoing actions made outside of the UV editor still affect its polygon.391EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();392undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "update_viewport");393undo_redo->add_undo_method(CanvasItemEditor::get_singleton(), "update_viewport");394undo_redo->commit_action();395}396397void Polygon2DEditor::_set_use_snap(bool p_use) {398use_snap = p_use;399EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_enabled", p_use);400}401402void Polygon2DEditor::_set_show_grid(bool p_show) {403snap_show_grid = p_show;404EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "show_grid", p_show);405canvas->queue_redraw();406}407408void Polygon2DEditor::_set_snap_off_x(real_t p_val) {409snap_offset.x = p_val;410EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_offset", snap_offset);411canvas->queue_redraw();412}413414void Polygon2DEditor::_set_snap_off_y(real_t p_val) {415snap_offset.y = p_val;416EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_offset", snap_offset);417canvas->queue_redraw();418}419420void Polygon2DEditor::_set_snap_step_x(real_t p_val) {421snap_step.x = p_val;422EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_step", snap_step);423canvas->queue_redraw();424}425426void Polygon2DEditor::_set_snap_step_y(real_t p_val) {427snap_step.y = p_val;428EditorSettings::get_singleton()->set_project_metadata("polygon_2d_uv_editor", "snap_step", snap_step);429canvas->queue_redraw();430}431432void Polygon2DEditor::_set_action(int p_action) {433polygon_create.clear();434is_dragging = false;435is_creating = false;436437selected_action = Action(p_action);438for (int i = 0; i < ACTION_MAX; i++) {439action_buttons[i]->set_pressed(p_action == i);440}441canvas->queue_redraw();442}443444void Polygon2DEditor::_canvas_input(const Ref<InputEvent> &p_input) {445if (!_get_node()) {446return;447}448449if (panner->gui_input(p_input, canvas->get_global_rect())) {450accept_event();451return;452}453454Transform2D mtx;455mtx.columns[2] = -draw_offset * draw_zoom;456mtx.scale_basis(Vector2(draw_zoom, draw_zoom));457458EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();459460Ref<InputEventMouseButton> mb = p_input;461if (mb.is_valid()) {462if (mb->get_button_index() == MouseButton::LEFT) {463if (mb->is_pressed()) {464drag_from = snap_point(mb->get_position());465is_dragging = true;466if (current_mode == MODE_UV) {467editing_points = node->get_uv();468} else {469editing_points = node->get_polygon();470}471472current_action = selected_action;473if (current_action == ACTION_CREATE) {474if (!is_creating) {475editing_points.clear();476Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));477editing_points.push_back(tuv);478create_to = tuv;479point_drag_index = 0;480drag_from = tuv;481is_dragging = true;482is_creating = true;483previous_uv = node->get_uv();484previous_polygon = node->get_polygon();485previous_internal_vertices = node->get_internal_vertex_count();486previous_colors = node->get_vertex_colors();487previous_bones = node->call("_get_bones");488previous_polygons = node->get_polygons();489disable_polygon_editing(false, String());490node->set_polygon(editing_points);491node->set_uv(editing_points);492node->set_internal_vertex_count(0);493494canvas->queue_redraw();495} else {496Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));497498// Close the polygon if selected point is near start. Threshold for closing scaled by zoom level499if (editing_points.size() > 2 && tuv.distance_to(editing_points[0]) < (8 / draw_zoom)) {500undo_redo->create_action(TTR("Create Polygon & UV"));501undo_redo->add_do_method(node, "set_uv", node->get_uv());502undo_redo->add_undo_method(node, "set_uv", previous_uv);503undo_redo->add_do_method(node, "set_polygon", node->get_polygon());504undo_redo->add_undo_method(node, "set_polygon", previous_polygon);505undo_redo->add_do_method(node, "set_internal_vertex_count", 0);506undo_redo->add_undo_method(node, "set_internal_vertex_count", previous_internal_vertices);507undo_redo->add_do_method(node, "set_vertex_colors", Vector<Color>());508undo_redo->add_undo_method(node, "set_vertex_colors", previous_colors);509undo_redo->add_do_method(node, "clear_bones");510undo_redo->add_undo_method(node, "_set_bones", previous_bones);511undo_redo->add_do_method(this, "_update_polygon_editing_state");512undo_redo->add_undo_method(this, "_update_polygon_editing_state");513undo_redo->commit_action();514is_dragging = false;515is_creating = false;516517_update_available_modes();518_set_action(ACTION_EDIT_POINT);519_menu_option(MODE_EDIT);520} else {521editing_points.push_back(tuv);522point_drag_index = editing_points.size() - 1;523drag_from = tuv;524}525node->set_polygon(editing_points);526node->set_uv(editing_points);527}528529CanvasItemEditor::get_singleton()->update_viewport();530}531532if (current_action == ACTION_CREATE_INTERNAL) {533previous_uv = node->get_uv();534previous_polygon = node->get_polygon();535previous_colors = node->get_vertex_colors();536previous_bones = node->call("_get_bones");537int internal_vertices = node->get_internal_vertex_count();538539Vector2 pos = mtx.affine_inverse().xform(snap_point(mb->get_position()));540541previous_polygon.push_back(pos);542previous_uv.push_back(pos);543if (previous_colors.size()) {544previous_colors.push_back(Color(1, 1, 1));545}546547undo_redo->create_action(TTR("Create Internal Vertex"));548undo_redo->add_do_method(node, "set_uv", previous_uv);549undo_redo->add_undo_method(node, "set_uv", node->get_uv());550undo_redo->add_do_method(node, "set_polygon", previous_polygon);551undo_redo->add_undo_method(node, "set_polygon", node->get_polygon());552undo_redo->add_do_method(node, "set_vertex_colors", previous_colors);553undo_redo->add_undo_method(node, "set_vertex_colors", node->get_vertex_colors());554for (int i = 0; i < node->get_bone_count(); i++) {555Vector<float> bonew = node->get_bone_weights(i);556bonew.push_back(0);557undo_redo->add_do_method(node, "set_bone_weights", i, bonew);558undo_redo->add_undo_method(node, "set_bone_weights", i, node->get_bone_weights(i));559}560undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices + 1);561undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices);562undo_redo->add_do_method(this, "_update_polygon_editing_state");563undo_redo->add_undo_method(this, "_update_polygon_editing_state");564undo_redo->commit_action();565}566567if (current_action == ACTION_REMOVE_INTERNAL) {568previous_uv = node->get_uv();569previous_polygon = node->get_polygon();570previous_colors = node->get_vertex_colors();571previous_bones = node->call("_get_bones");572int internal_vertices = node->get_internal_vertex_count();573574if (internal_vertices <= 0) {575return;576}577578const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");579int closest = -1;580real_t closest_dist = Math::INF;581582for (int i = editing_points.size() - 1; i >= editing_points.size() - internal_vertices && closest_dist >= 8; i--) {583Vector2 tuv = mtx.xform(previous_polygon[i]);584const real_t dist = tuv.distance_to(mb->get_position());585if (dist < grab_threshold && dist < closest_dist) {586closest = i;587closest_dist = dist;588}589}590591if (closest == -1) {592return;593}594if (closest == hovered_point) {595hovered_point = -1;596}597598previous_polygon.remove_at(closest);599previous_uv.remove_at(closest);600if (previous_colors.size()) {601previous_colors.remove_at(closest);602}603604undo_redo->create_action(TTR("Remove Internal Vertex"));605undo_redo->add_do_method(node, "set_uv", previous_uv);606undo_redo->add_undo_method(node, "set_uv", node->get_uv());607undo_redo->add_do_method(node, "set_polygon", previous_polygon);608undo_redo->add_undo_method(node, "set_polygon", node->get_polygon());609undo_redo->add_do_method(node, "set_vertex_colors", previous_colors);610undo_redo->add_undo_method(node, "set_vertex_colors", node->get_vertex_colors());611for (int i = 0; i < node->get_bone_count(); i++) {612Vector<float> bonew = node->get_bone_weights(i);613bonew.remove_at(closest);614undo_redo->add_do_method(node, "set_bone_weights", i, bonew);615undo_redo->add_undo_method(node, "set_bone_weights", i, node->get_bone_weights(i));616}617undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices - 1);618undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices);619undo_redo->add_do_method(this, "_update_polygon_editing_state");620undo_redo->add_undo_method(this, "_update_polygon_editing_state");621undo_redo->commit_action();622}623624if (current_action == ACTION_EDIT_POINT) {625if (mb->is_shift_pressed() && mb->is_command_or_control_pressed()) {626current_action = ACTION_SCALE;627} else if (mb->is_shift_pressed()) {628current_action = ACTION_MOVE;629} else if (mb->is_command_or_control_pressed()) {630current_action = ACTION_ROTATE;631}632}633634if (current_action == ACTION_EDIT_POINT) {635const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");636point_drag_index = -1;637real_t closest_dist = Math::INF;638for (int i = editing_points.size() - 1; i >= 0 && closest_dist >= 8; i--) {639const real_t dist = mtx.xform(editing_points[i]).distance_to(mb->get_position());640if (dist < grab_threshold && dist < closest_dist) {641drag_from = mb->get_position();642point_drag_index = i;643closest_dist = dist;644}645}646647if (point_drag_index == -1) {648is_dragging = false;649}650}651652if (current_action == ACTION_ADD_POLYGON) {653const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");654int closest = -1;655real_t closest_dist = Math::INF;656657for (int i = editing_points.size() - 1; i >= 0 && closest_dist >= 8; i--) {658Vector2 tuv = mtx.xform(editing_points[i]);659const real_t dist = tuv.distance_to(mb->get_position());660if (dist < grab_threshold && dist < closest_dist) {661closest = i;662closest_dist = dist;663}664}665666if (closest != -1) {667if (polygon_create.size() && closest == polygon_create[0]) {668//close669if (polygon_create.size() < 3) {670error->set_text(TTR("Invalid Polygon (need 3 different vertices)"));671error->popup_centered();672} else {673Array polygons = node->get_polygons();674polygons = polygons.duplicate(); //copy because its a reference675676//todo, could check whether it already exists?677polygons.push_back(polygon_create);678undo_redo->create_action(TTR("Add Custom Polygon"));679undo_redo->add_do_method(node, "set_polygons", polygons);680undo_redo->add_undo_method(node, "set_polygons", node->get_polygons());681undo_redo->commit_action();682}683684polygon_create.clear();685} else if (!polygon_create.has(closest)) {686//add temporarily if not exists687polygon_create.push_back(closest);688}689}690}691692if (current_action == ACTION_REMOVE_POLYGON) {693Array polygons = node->get_polygons();694polygons = polygons.duplicate(); //copy because its a reference695696int erase_index = -1;697for (int i = polygons.size() - 1; i >= 0; i--) {698Vector<int> points = polygons[i];699Vector<Vector2> polys;700polys.resize(points.size());701for (int j = 0; j < polys.size(); j++) {702int idx = points[j];703if (idx < 0 || idx >= editing_points.size()) {704continue;705}706polys.write[j] = mtx.xform(editing_points[idx]);707}708709if (Geometry2D::is_point_in_polygon(mb->get_position(), polys)) {710erase_index = i;711break;712}713}714715if (erase_index != -1) {716polygons.remove_at(erase_index);717undo_redo->create_action(TTR("Remove Custom Polygon"));718undo_redo->add_do_method(node, "set_polygons", polygons);719undo_redo->add_undo_method(node, "set_polygons", node->get_polygons());720undo_redo->commit_action();721}722}723724if (current_action == ACTION_PAINT_WEIGHT || current_action == ACTION_CLEAR_WEIGHT) {725int bone_selected = -1;726for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {727CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));728if (c && c->is_pressed()) {729bone_selected = i;730break;731}732}733734if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == editing_points.size()) {735prev_weights = node->get_bone_weights(bone_selected);736bone_painting = true;737bone_painting_bone = bone_selected;738}739}740} else {741if (is_dragging && !is_creating) {742if (current_mode == MODE_UV) {743undo_redo->create_action(TTR("Transform UV Map"));744undo_redo->add_do_method(node, "set_uv", node->get_uv());745undo_redo->add_undo_method(node, "set_uv", editing_points);746undo_redo->commit_action();747} else if (current_mode == MODE_POINTS) {748switch (current_action) {749case ACTION_EDIT_POINT:750case ACTION_MOVE:751case ACTION_ROTATE:752case ACTION_SCALE: {753undo_redo->create_action(TTR("Transform Polygon"));754undo_redo->add_do_method(node, "set_polygon", node->get_polygon());755undo_redo->add_undo_method(node, "set_polygon", editing_points);756undo_redo->commit_action();757} break;758default: {759} break;760}761}762763is_dragging = false;764}765766if (bone_painting) {767undo_redo->create_action(TTR("Paint Bone Weights"));768undo_redo->add_do_method(node, "set_bone_weights", bone_painting_bone, node->get_bone_weights(bone_painting_bone));769undo_redo->add_undo_method(node, "set_bone_weights", bone_painting_bone, prev_weights);770undo_redo->commit_action();771bone_painting = false;772}773}774} else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {775_cancel_editing();776777if (bone_painting) {778node->set_bone_weights(bone_painting_bone, prev_weights);779}780781canvas->queue_redraw();782}783}784785Ref<InputEventMouseMotion> mm = p_input;786787if (mm.is_valid()) {788// Highlight a point near the cursor.789if (is_creating) {790if (editing_points.size() > 2 && mtx.affine_inverse().xform(mm->get_position()).distance_to(node->get_polygon()[0]) < (8 / draw_zoom)) {791if (hovered_point != 0) {792hovered_point = 0;793canvas->queue_redraw();794}795} else if (hovered_point == 0) {796hovered_point = -1;797canvas->queue_redraw();798}799}800if (selected_action == ACTION_REMOVE_INTERNAL || selected_action == ACTION_EDIT_POINT || selected_action == ACTION_ADD_POLYGON) {801Vector<Vector2> points;802if (current_mode == MODE_POINTS || current_mode == MODE_POLYGONS) {803points = node->get_polygon();804} else {805points = node->get_uv();806}807int i = points.size() - 1;808for (; i >= 0; i--) {809if (mtx.affine_inverse().xform(mm->get_position()).distance_to(points[i]) < (8 / draw_zoom)) {810if (hovered_point != i) {811hovered_point = i;812canvas->queue_redraw();813}814break;815}816}817if (i == -1 && hovered_point >= 0) {818hovered_point = -1;819canvas->queue_redraw();820}821}822if (is_dragging) {823Vector2 uv_drag_to = mm->get_position();824uv_drag_to = snap_point(uv_drag_to);825Vector2 drag = mtx.affine_inverse().basis_xform(uv_drag_to - drag_from);826827switch (current_action) {828case ACTION_CREATE: {829if (is_creating) {830create_to = mtx.affine_inverse().xform(snap_point(mm->get_position()));831}832} break;833case ACTION_EDIT_POINT: {834Vector<Vector2> uv_new = editing_points;835uv_new.set(point_drag_index, mtx.affine_inverse().xform(snap_point(mm->get_position())));836837if (current_mode == MODE_UV) {838node->set_uv(uv_new);839} else if (current_mode == MODE_POINTS) {840node->set_polygon(uv_new);841}842} break;843case ACTION_MOVE: {844Vector<Vector2> uv_new = editing_points;845for (int i = 0; i < uv_new.size(); i++) {846uv_new.set(i, uv_new[i] + drag);847}848849if (current_mode == MODE_UV) {850node->set_uv(uv_new);851} else if (current_mode == MODE_POINTS) {852node->set_polygon(uv_new);853}854} break;855case ACTION_ROTATE: {856Vector2 center;857Vector<Vector2> uv_new = editing_points;858859for (int i = 0; i < uv_new.size(); i++) {860center += editing_points[i];861}862center /= uv_new.size();863864real_t angle = (drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized());865866for (int i = 0; i < uv_new.size(); i++) {867Vector2 rel = editing_points[i] - center;868rel = rel.rotated(angle);869uv_new.set(i, center + rel);870}871872if (current_mode == MODE_UV) {873node->set_uv(uv_new);874} else if (current_mode == MODE_POINTS) {875node->set_polygon(uv_new);876}877} break;878case ACTION_SCALE: {879Vector2 center;880Vector<Vector2> uv_new = editing_points;881882for (int i = 0; i < uv_new.size(); i++) {883center += editing_points[i];884}885center /= uv_new.size();886887real_t from_dist = drag_from.distance_to(mtx.xform(center));888real_t to_dist = uv_drag_to.distance_to(mtx.xform(center));889if (from_dist < 2) {890break;891}892893real_t scale = to_dist / from_dist;894895for (int i = 0; i < uv_new.size(); i++) {896Vector2 rel = editing_points[i] - center;897rel = rel * scale;898uv_new.set(i, center + rel);899}900901if (current_mode == MODE_UV) {902node->set_uv(uv_new);903} else if (current_mode == MODE_POINTS) {904node->set_polygon(uv_new);905}906} break;907case ACTION_PAINT_WEIGHT:908case ACTION_CLEAR_WEIGHT: {909bone_paint_pos = mm->get_position();910} break;911default: {912}913}914915if (bone_painting) {916Vector<float> painted_weights = node->get_bone_weights(bone_painting_bone);917918{919int pc = painted_weights.size();920real_t amount = bone_paint_strength->get_value();921real_t radius = bone_paint_radius->get_value() * EDSCALE;922923if (selected_action == ACTION_CLEAR_WEIGHT) {924amount = -amount;925}926927float *w = painted_weights.ptrw();928const float *r = prev_weights.ptr();929const Vector2 *rv = editing_points.ptr();930931for (int i = 0; i < pc; i++) {932if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) {933w[i] = CLAMP(r[i] + amount, 0, 1);934}935}936}937938node->set_bone_weights(bone_painting_bone, painted_weights);939}940941canvas->queue_redraw();942CanvasItemEditor::get_singleton()->update_viewport();943} else if (polygon_create.size()) {944create_to = mtx.affine_inverse().xform(mm->get_position());945canvas->queue_redraw();946} else if (selected_action == ACTION_PAINT_WEIGHT || selected_action == ACTION_CLEAR_WEIGHT) {947bone_paint_pos = mm->get_position();948canvas->queue_redraw();949}950}951}952953void Polygon2DEditor::_update_available_modes() {954// Force point editing mode if there's no polygon yet.955if (node->get_polygon().is_empty()) {956if (current_mode != MODE_POINTS) {957_select_mode(MODE_POINTS);958}959mode_buttons[MODE_UV]->set_disabled(true);960mode_buttons[MODE_POLYGONS]->set_disabled(true);961mode_buttons[MODE_BONES]->set_disabled(true);962} else {963mode_buttons[MODE_UV]->set_disabled(false);964mode_buttons[MODE_POLYGONS]->set_disabled(false);965mode_buttons[MODE_BONES]->set_disabled(false);966}967}968969void Polygon2DEditor::_center_view() {970Size2 texture_size;971if (node->get_texture().is_valid()) {972texture_size = node->get_texture()->get_size();973Vector2 zoom_factor = (canvas->get_size() - Vector2(1, 1) * 50 * EDSCALE) / texture_size;974zoom_widget->set_zoom(MIN(zoom_factor.x, zoom_factor.y));975} else {976zoom_widget->set_zoom(EDSCALE);977}978// Recalculate scroll limits.979_update_zoom_and_pan(false);980981Size2 offset = (texture_size - canvas->get_size() / draw_zoom) / 2;982hscroll->set_value_no_signal(offset.x);983vscroll->set_value_no_signal(offset.y);984_update_zoom_and_pan(false);985}986987void Polygon2DEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {988hscroll->set_value_no_signal(hscroll->get_value() - p_scroll_vec.x / draw_zoom);989vscroll->set_value_no_signal(vscroll->get_value() - p_scroll_vec.y / draw_zoom);990_update_zoom_and_pan(false);991}992993void Polygon2DEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {994zoom_widget->set_zoom(draw_zoom * p_zoom_factor);995draw_offset += p_origin / draw_zoom - p_origin / zoom_widget->get_zoom();996hscroll->set_value_no_signal(draw_offset.x);997vscroll->set_value_no_signal(draw_offset.y);998_update_zoom_and_pan(false);999}10001001void Polygon2DEditor::_update_zoom_and_pan(bool p_zoom_at_center) {1002draw_offset = Vector2(hscroll->get_value(), vscroll->get_value());1003real_t previous_zoom = draw_zoom;1004draw_zoom = zoom_widget->get_zoom();1005if (p_zoom_at_center) {1006Vector2 center = canvas->get_size() / 2;1007draw_offset += center / previous_zoom - center / draw_zoom;1008}10091010Point2 min_corner;1011Point2 max_corner;1012if (node->get_texture().is_valid()) {1013max_corner += node->get_texture()->get_size();1014}10151016Vector<Vector2> points = current_mode == MODE_UV ? node->get_uv() : node->get_polygon();1017for (int i = 0; i < points.size(); i++) {1018min_corner = min_corner.min(points[i]);1019max_corner = max_corner.max(points[i]);1020}1021Size2 page_size = canvas->get_size() / draw_zoom;1022Vector2 margin = Vector2(50, 50) * EDSCALE / draw_zoom;1023min_corner -= page_size - margin;1024max_corner += page_size - margin;10251026hscroll->set_block_signals(true);1027hscroll->set_min(min_corner.x);1028hscroll->set_max(max_corner.x);1029hscroll->set_page(page_size.x);1030hscroll->set_value(draw_offset.x);1031hscroll->set_block_signals(false);10321033vscroll->set_block_signals(true);1034vscroll->set_min(min_corner.y);1035vscroll->set_max(max_corner.y);1036vscroll->set_page(page_size.y);1037vscroll->set_value(draw_offset.y);1038vscroll->set_block_signals(false);10391040canvas->queue_redraw();1041}10421043void Polygon2DEditor::_center_view_on_draw(bool p_enabled) {1044if (center_view_on_draw == p_enabled) {1045return;1046}1047center_view_on_draw = p_enabled;1048if (center_view_on_draw) {1049// Ensure that the view is centered even if the canvas is redrawn multiple times in the frame.1050get_tree()->connect("process_frame", callable_mp(this, &Polygon2DEditor::_center_view_on_draw).bind(false), CONNECT_ONE_SHOT);1051}1052}10531054void Polygon2DEditor::_canvas_draw() {1055if (!polygon_edit->is_visible() || !_get_node()) {1056return;1057}1058if (center_view_on_draw) {1059_center_view();1060}10611062Ref<Texture2D> base_tex = node->get_texture();10631064String warning;10651066Transform2D mtx;1067mtx.columns[2] = -draw_offset * draw_zoom;1068mtx.scale_basis(Vector2(draw_zoom, draw_zoom));10691070// Draw texture as a background if editing uvs or no uv mapping exist.1071if (current_mode == MODE_UV || selected_action == ACTION_CREATE || node->get_polygon().is_empty() || node->get_uv().size() != node->get_polygon().size()) {1072if (base_tex.is_valid()) {1073Transform2D texture_transform = Transform2D(node->get_texture_rotation(), node->get_texture_offset());1074texture_transform.scale(node->get_texture_scale());1075texture_transform.affine_invert();1076RS::get_singleton()->canvas_item_add_set_transform(canvas->get_canvas_item(), mtx * texture_transform);1077canvas->draw_texture(base_tex, Point2());1078RS::get_singleton()->canvas_item_add_set_transform(canvas->get_canvas_item(), Transform2D());1079}1080preview_polygon->hide();1081} else {1082preview_polygon->set_transform(mtx);1083// Keep in sync with newly added Polygon2D properties (when relevant).1084preview_polygon->set_texture(node->get_texture());1085preview_polygon->set_texture_offset(node->get_texture_offset());1086preview_polygon->set_texture_rotation(node->get_texture_rotation());1087preview_polygon->set_texture_scale(node->get_texture_scale());1088preview_polygon->set_texture_filter(node->get_texture_filter_in_tree());1089preview_polygon->set_texture_repeat(node->get_texture_repeat_in_tree());1090preview_polygon->set_polygon(node->get_polygon());1091preview_polygon->set_uv(node->get_uv());1092preview_polygon->set_invert(node->get_invert());1093preview_polygon->set_invert_border(node->get_invert_border());1094preview_polygon->set_internal_vertex_count(node->get_internal_vertex_count());1095if (selected_action == ACTION_ADD_POLYGON) {1096preview_polygon->set_polygons(Array());1097} else {1098preview_polygon->set_polygons(node->get_polygons());1099}1100preview_polygon->show();1101}11021103if (snap_show_grid) {1104Color grid_color = Color(1.0, 1.0, 1.0, 0.15);1105Size2 s = canvas->get_size();1106int last_cell = 0;11071108if (snap_step.x != 0) {1109for (int i = 0; i < s.width; i++) {1110int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(i, 0)).x - snap_offset.x) / snap_step.x));1111if (i == 0) {1112last_cell = cell;1113}1114if (last_cell != cell) {1115canvas->draw_line(Point2(i, 0), Point2(i, s.height), grid_color, Math::round(EDSCALE));1116}1117last_cell = cell;1118}1119}11201121if (snap_step.y != 0) {1122for (int i = 0; i < s.height; i++) {1123int cell = Math::fast_ftoi(Math::floor((mtx.affine_inverse().xform(Vector2(0, i)).y - snap_offset.y) / snap_step.y));1124if (i == 0) {1125last_cell = cell;1126}1127if (last_cell != cell) {1128canvas->draw_line(Point2(0, i), Point2(s.width, i), grid_color, Math::round(EDSCALE));1129}1130last_cell = cell;1131}1132}1133}11341135Array polygons = node->get_polygons();11361137Vector<Vector2> uvs;1138if (current_mode == MODE_UV) {1139uvs = node->get_uv();1140} else {1141uvs = node->get_polygon();1142}11431144const float *weight_r = nullptr;11451146if (current_mode == MODE_BONES) {1147int bone_selected = -1;1148for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {1149CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));1150if (c && c->is_pressed()) {1151bone_selected = i;1152break;1153}1154}11551156if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uvs.size()) {1157weight_r = node->get_bone_weights(bone_selected).ptr();1158}1159}11601161// All UV points are sharp, so use the sharp handle icon1162Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle"));11631164Color poly_line_color = Color(0.9, 0.5, 0.5);1165if (polygons.size() || polygon_create.size()) {1166poly_line_color.a *= 0.25;1167}1168Color polygon_line_color = Color(0.5, 0.5, 0.9);1169Color polygon_fill_color = polygon_line_color;1170polygon_fill_color.a *= 0.5;1171Color prev_color = Color(0.5, 0.5, 0.5);11721173int uv_draw_max = uvs.size();11741175uv_draw_max -= node->get_internal_vertex_count();1176if (uv_draw_max < 0) {1177uv_draw_max = 0;1178}11791180for (int i = 0; i < uvs.size(); i++) {1181int next = uv_draw_max > 0 ? (i + 1) % uv_draw_max : 0;11821183if (i < uv_draw_max && is_dragging && current_action == ACTION_EDIT_POINT && EDITOR_GET("editors/polygon_editor/show_previous_outline")) {1184canvas->draw_line(mtx.xform(editing_points[i]), mtx.xform(editing_points[next]), prev_color, Math::round(EDSCALE));1185}11861187Vector2 next_point = uvs[next];1188if (is_creating && i == uvs.size() - 1) {1189next_point = create_to;1190}1191if (i < uv_draw_max) { // If using or creating polygons, do not show outline (will show polygons instead).1192canvas->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), poly_line_color, Math::round(EDSCALE));1193}1194}11951196for (int i = 0; i < polygons.size(); i++) {1197Vector<int> points = polygons[i];1198Vector<Vector2> polypoints;1199for (int j = 0; j < points.size(); j++) {1200int next = (j + 1) % points.size();12011202int idx = points[j];1203int idx_next = points[next];1204if (idx < 0 || idx >= uvs.size()) {1205continue;1206}1207polypoints.push_back(mtx.xform(uvs[idx]));12081209if (idx_next < 0 || idx_next >= uvs.size()) {1210continue;1211}1212canvas->draw_line(mtx.xform(uvs[idx]), mtx.xform(uvs[idx_next]), polygon_line_color, Math::round(EDSCALE));1213}1214if (points.size() >= 3) {1215canvas->draw_colored_polygon(polypoints, polygon_fill_color);1216}1217}12181219if (weight_r) {1220for (int i = 0; i < uvs.size(); i++) {1221Vector2 draw_pos = mtx.xform(uvs[i]);1222float weight = weight_r[i];1223canvas->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0), Math::round(EDSCALE));1224}1225} else {1226Vector2 texture_size_half = handle->get_size() * 0.5;1227Color mod(1, 1, 1);1228Color hovered_mod(0.65, 0.65, 0.65);1229for (int i = 0; i < uv_draw_max; i++) {1230if (i == hovered_point && selected_action != ACTION_REMOVE_INTERNAL) {1231canvas->draw_texture(handle, mtx.xform(uvs[i]) - texture_size_half, hovered_mod);1232} else {1233canvas->draw_texture(handle, mtx.xform(uvs[i]) - texture_size_half, mod);1234}1235}1236// Internal vertices.1237mod = Color(0.6, 0.8, 1);1238hovered_mod = Color(0.35, 0.55, 0.75);1239for (int i = uv_draw_max; i < uvs.size(); i++) {1240if (i == hovered_point) {1241canvas->draw_texture(handle, mtx.xform(uvs[i]) - texture_size_half, hovered_mod);1242} else {1243canvas->draw_texture(handle, mtx.xform(uvs[i]) - texture_size_half, mod);1244}1245}1246}12471248if (polygon_create.size()) {1249for (int i = 0; i < polygon_create.size(); i++) {1250Vector2 from = uvs[polygon_create[i]];1251Vector2 to = (i + 1) < polygon_create.size() ? uvs[polygon_create[i + 1]] : create_to;1252canvas->draw_line(mtx.xform(from), mtx.xform(to), polygon_line_color, Math::round(EDSCALE));1253}1254}12551256if (selected_action == ACTION_PAINT_WEIGHT || selected_action == ACTION_CLEAR_WEIGHT) {1257NodePath bone_path;1258for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {1259CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));1260if (c && c->is_pressed()) {1261bone_path = node->get_bone_path(i);1262break;1263}1264}12651266//draw skeleton1267NodePath skeleton_path = node->get_skeleton();1268Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node_or_null(skeleton_path));1269if (skeleton) {1270Transform2D skeleton_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * skeleton->get_global_transform();1271for (int i = 0; i < skeleton->get_bone_count(); i++) {1272Bone2D *bone = skeleton->get_bone(i);1273if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) {1274continue; //not set1275}12761277bool current = bone_path == skeleton->get_path_to(bone);12781279bool found_child = false;12801281for (int j = 0; j < bone->get_child_count(); j++) {1282Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j));1283if (!n) {1284continue;1285}12861287found_child = true;12881289Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest();1290Transform2D endpoint_xform = bone_xform * n->get_transform();12911292Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);1293canvas->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));1294canvas->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));1295}12961297if (!found_child) {1298//draw normally1299Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest();1300Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)).rotated(bone->get_bone_angle());13011302Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);1303canvas->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));1304canvas->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));1305}1306}1307}13081309//draw paint circle1310canvas->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1));1311}1312}13131314void Polygon2DEditor::_bind_methods() {1315ClassDB::bind_method(D_METHOD("_update_bone_list", "for_node"), &Polygon2DEditor::_update_bone_list);1316ClassDB::bind_method(D_METHOD("_update_polygon_editing_state"), &Polygon2DEditor::_update_polygon_editing_state);1317}13181319Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {1320if (use_snap) {1321p_target.x = Math::snap_scalar((snap_offset.x - draw_offset.x) * draw_zoom, snap_step.x * draw_zoom, p_target.x);1322p_target.y = Math::snap_scalar((snap_offset.y - draw_offset.y) * draw_zoom, snap_step.y * draw_zoom, p_target.y);1323}13241325return p_target;1326}13271328Polygon2DEditor::Polygon2DEditor() {1329snap_offset = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_offset", Vector2());1330// A power-of-two value works better as a default grid size.1331snap_step = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_step", Vector2(8, 8));1332use_snap = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_enabled", false);1333snap_show_grid = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "show_grid", false);13341335selected_action = ACTION_EDIT_POINT;13361337polygon_edit = memnew(EditorDock);1338polygon_edit->set_name(TTRC("Polygon"));1339polygon_edit->set_icon_name("PolygonDock");1340polygon_edit->set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_polygon_2d_bottom_panel", TTRC("Toggle Polygon Dock")));1341polygon_edit->set_default_slot(EditorDock::DOCK_SLOT_BOTTOM);1342polygon_edit->set_available_layouts(EditorDock::DOCK_LAYOUT_HORIZONTAL | EditorDock::DOCK_LAYOUT_FLOATING);1343polygon_edit->set_global(false);1344polygon_edit->set_transient(true);1345EditorDockManager::get_singleton()->add_dock(polygon_edit);1346polygon_edit->close();13471348VBoxContainer *edit_vbox = memnew(VBoxContainer);1349polygon_edit->add_child(edit_vbox);13501351HBoxContainer *toolbar = memnew(HBoxContainer);13521353Ref<ButtonGroup> mode_button_group;1354mode_button_group.instantiate();1355for (int i = 0; i < MODE_MAX; i++) {1356mode_buttons[i] = memnew(Button);1357toolbar->add_child(mode_buttons[i]);1358mode_buttons[i]->set_toggle_mode(true);1359mode_buttons[i]->set_button_group(mode_button_group);1360mode_buttons[i]->connect(SceneStringName(pressed), callable_mp(this, &Polygon2DEditor::_select_mode).bind(i));1361}1362mode_buttons[MODE_POINTS]->set_text(TTR("Points"));1363mode_buttons[MODE_POLYGONS]->set_text(TTR("Polygons"));1364mode_buttons[MODE_UV]->set_text(TTR("UV"));1365mode_buttons[MODE_BONES]->set_text(TTR("Bones"));13661367toolbar->add_child(memnew(VSeparator));13681369edit_vbox->add_child(toolbar);1370for (int i = 0; i < ACTION_MAX; i++) {1371action_buttons[i] = memnew(Button);1372action_buttons[i]->set_theme_type_variation(SceneStringName(FlatButton));1373action_buttons[i]->set_toggle_mode(true);1374toolbar->add_child(action_buttons[i]);1375action_buttons[i]->connect(SceneStringName(pressed), callable_mp(this, &Polygon2DEditor::_set_action).bind(i));1376action_buttons[i]->set_focus_mode(FOCUS_ACCESSIBILITY);1377}13781379action_buttons[ACTION_CREATE]->set_tooltip_text(TTR("Create Polygon"));1380action_buttons[ACTION_CREATE_INTERNAL]->set_tooltip_text(TTR("Create Internal Vertex"));1381action_buttons[ACTION_REMOVE_INTERNAL]->set_tooltip_text(TTR("Remove Internal Vertex"));1382Key key = OS::prefer_meta_over_ctrl() ? Key::META : Key::CTRL;1383// TRANSLATORS: %s is Control or Command key name.1384action_buttons[ACTION_EDIT_POINT]->set_tooltip_text(TTR("Move Points") + "\n" + vformat(TTR("%s: Rotate"), find_keycode_name(key)) + "\n" + TTR("Shift: Move All") + "\n" + vformat(TTR("%s + Shift: Scale"), find_keycode_name(key)));1385action_buttons[ACTION_MOVE]->set_tooltip_text(TTR("Move Polygon"));1386action_buttons[ACTION_ROTATE]->set_tooltip_text(TTR("Rotate Polygon"));1387action_buttons[ACTION_SCALE]->set_tooltip_text(TTR("Scale Polygon"));1388action_buttons[ACTION_ADD_POLYGON]->set_tooltip_text(TTR("Create a custom polygon. Enables custom polygon rendering."));1389action_buttons[ACTION_REMOVE_POLYGON]->set_tooltip_text(TTR("Remove a custom polygon. If none remain, custom polygon rendering is disabled."));1390action_buttons[ACTION_PAINT_WEIGHT]->set_tooltip_text(TTR("Paint weights with specified intensity."));1391action_buttons[ACTION_CLEAR_WEIGHT]->set_tooltip_text(TTR("Unpaint weights with specified intensity."));13921393action_buttons[ACTION_CREATE]->set_accessibility_name(TTRC("Create Polygon"));1394action_buttons[ACTION_CREATE_INTERNAL]->set_accessibility_name(TTRC("Create Internal Vertex"));1395action_buttons[ACTION_REMOVE_INTERNAL]->set_accessibility_name(TTRC("Remove Internal Vertex"));1396action_buttons[ACTION_EDIT_POINT]->set_accessibility_name(TTRC("Move Points"));1397action_buttons[ACTION_MOVE]->set_accessibility_name(TTRC("Move Polygon"));1398action_buttons[ACTION_ROTATE]->set_accessibility_name(TTRC("Rotate Polygon"));1399action_buttons[ACTION_SCALE]->set_accessibility_name(TTRC("Scale Polygon"));1400action_buttons[ACTION_ADD_POLYGON]->set_accessibility_name(TTRC("Create a custom polygon. Enables custom polygon rendering."));1401action_buttons[ACTION_REMOVE_POLYGON]->set_accessibility_name(TTRC("Remove a custom polygon. If none remain, custom polygon rendering is disabled."));1402action_buttons[ACTION_PAINT_WEIGHT]->set_accessibility_name(TTRC("Paint weights with specified intensity."));1403action_buttons[ACTION_CLEAR_WEIGHT]->set_accessibility_name(TTRC("Unpaint weights with specified intensity."));14041405bone_paint_strength = memnew(HSlider);1406toolbar->add_child(bone_paint_strength);1407bone_paint_strength->set_custom_minimum_size(Size2(75 * EDSCALE, 0));1408bone_paint_strength->set_v_size_flags(SIZE_SHRINK_CENTER);1409bone_paint_strength->set_min(0);1410bone_paint_strength->set_max(1);1411bone_paint_strength->set_step(0.01);1412bone_paint_strength->set_value(0.5);1413bone_paint_strength->set_accessibility_name(TTRC("Strength"));14141415bone_paint_radius_label = memnew(Label(TTR("Radius:")));1416toolbar->add_child(bone_paint_radius_label);1417bone_paint_radius = memnew(SpinBox);1418toolbar->add_child(bone_paint_radius);14191420bone_paint_radius->set_min(1);1421bone_paint_radius->set_max(100);1422bone_paint_radius->set_step(1);1423bone_paint_radius->set_value(32);1424bone_paint_radius->set_accessibility_name(TTRC("Radius:"));14251426HSplitContainer *uv_main_hsc = memnew(HSplitContainer);1427edit_vbox->add_child(uv_main_hsc);1428uv_main_hsc->set_v_size_flags(SIZE_EXPAND_FILL);14291430canvas_background = memnew(Panel);1431uv_main_hsc->add_child(canvas_background);1432canvas_background->set_h_size_flags(SIZE_EXPAND_FILL);1433canvas_background->set_custom_minimum_size(Size2(200, 200) * EDSCALE);1434canvas_background->set_clip_contents(true);14351436preview_polygon = memnew(Polygon2D);1437canvas_background->add_child(preview_polygon);14381439canvas = memnew(Control);1440canvas_background->add_child(canvas);1441canvas->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);14421443Control *space = memnew(Control);1444toolbar->add_child(space);1445space->set_h_size_flags(SIZE_EXPAND_FILL);14461447edit_menu = memnew(MenuButton);1448toolbar->add_child(edit_menu);1449edit_menu->set_flat(false);1450edit_menu->set_theme_type_variation("FlatMenuButton");1451edit_menu->set_text(TTR("Edit"));1452edit_menu->get_popup()->add_item(TTR("Copy Polygon to UV"), MENU_POLYGON_TO_UV);1453edit_menu->get_popup()->add_item(TTR("Copy UV to Polygon"), MENU_UV_TO_POLYGON);1454edit_menu->get_popup()->add_separator();1455edit_menu->get_popup()->add_item(TTR("Clear UV"), MENU_UV_CLEAR);1456edit_menu->get_popup()->add_separator();1457edit_menu->get_popup()->add_item(TTR("Grid Settings"), MENU_GRID_SETTINGS);1458edit_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &Polygon2DEditor::_edit_menu_option));14591460toolbar->add_child(memnew(VSeparator));14611462b_snap_enable = memnew(Button);1463b_snap_enable->set_theme_type_variation(SceneStringName(FlatButton));1464toolbar->add_child(b_snap_enable);1465b_snap_enable->set_text(TTR("Snap"));1466b_snap_enable->set_focus_mode(FOCUS_ACCESSIBILITY);1467b_snap_enable->set_toggle_mode(true);1468b_snap_enable->set_pressed(use_snap);1469b_snap_enable->set_tooltip_text(TTR("Enable Snap"));1470b_snap_enable->connect(SceneStringName(toggled), callable_mp(this, &Polygon2DEditor::_set_use_snap));14711472b_snap_grid = memnew(Button);1473b_snap_grid->set_theme_type_variation(SceneStringName(FlatButton));1474toolbar->add_child(b_snap_grid);1475b_snap_grid->set_text(TTR("Grid"));1476b_snap_grid->set_focus_mode(FOCUS_ACCESSIBILITY);1477b_snap_grid->set_toggle_mode(true);1478b_snap_grid->set_pressed(snap_show_grid);1479b_snap_grid->set_tooltip_text(TTR("Show Grid"));1480b_snap_grid->connect(SceneStringName(toggled), callable_mp(this, &Polygon2DEditor::_set_show_grid));14811482grid_settings = memnew(AcceptDialog);1483grid_settings->set_title(TTR("Configure Grid:"));1484polygon_edit->add_child(grid_settings);1485VBoxContainer *grid_settings_vb = memnew(VBoxContainer);1486grid_settings->add_child(grid_settings_vb);14871488SpinBox *sb_off_x = memnew(SpinBox);1489sb_off_x->set_min(-256);1490sb_off_x->set_max(256);1491sb_off_x->set_step(1);1492sb_off_x->set_value(snap_offset.x);1493sb_off_x->set_suffix("px");1494sb_off_x->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_set_snap_off_x));1495sb_off_x->set_accessibility_name(TTRC("Grid Offset X:"));1496grid_settings_vb->add_margin_child(TTR("Grid Offset X:"), sb_off_x);14971498SpinBox *sb_off_y = memnew(SpinBox);1499sb_off_y->set_min(-256);1500sb_off_y->set_max(256);1501sb_off_y->set_step(1);1502sb_off_y->set_value(snap_offset.y);1503sb_off_y->set_suffix("px");1504sb_off_y->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_set_snap_off_y));1505sb_off_y->set_accessibility_name(TTRC("Grid Offset Y:"));1506grid_settings_vb->add_margin_child(TTR("Grid Offset Y:"), sb_off_y);15071508SpinBox *sb_step_x = memnew(SpinBox);1509sb_step_x->set_min(-256);1510sb_step_x->set_max(256);1511sb_step_x->set_step(1);1512sb_step_x->set_value(snap_step.x);1513sb_step_x->set_suffix("px");1514sb_step_x->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_set_snap_step_x));1515sb_step_x->set_accessibility_name(TTRC("Grid Step X:"));1516grid_settings_vb->add_margin_child(TTR("Grid Step X:"), sb_step_x);15171518SpinBox *sb_step_y = memnew(SpinBox);1519sb_step_y->set_min(-256);1520sb_step_y->set_max(256);1521sb_step_y->set_step(1);1522sb_step_y->set_value(snap_step.y);1523sb_step_y->set_suffix("px");1524sb_step_y->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_set_snap_step_y));1525sb_step_y->set_accessibility_name(TTRC("Grid Step Y:"));1526grid_settings_vb->add_margin_child(TTR("Grid Step Y:"), sb_step_y);15271528zoom_widget = memnew(EditorZoomWidget);1529canvas->add_child(zoom_widget);1530zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);1531zoom_widget->connect("zoom_changed", callable_mp(this, &Polygon2DEditor::_update_zoom_and_pan).unbind(1).bind(true));1532zoom_widget->set_shortcut_context(nullptr);15331534vscroll = memnew(VScrollBar);1535vscroll->set_step(0.001);1536canvas->add_child(vscroll);1537vscroll->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_update_zoom_and_pan).unbind(1).bind(false));1538hscroll = memnew(HScrollBar);1539hscroll->set_step(0.001);1540canvas->add_child(hscroll);1541hscroll->connect(SceneStringName(value_changed), callable_mp(this, &Polygon2DEditor::_update_zoom_and_pan).unbind(1).bind(false));15421543bone_scroll_main_vb = memnew(VBoxContainer);1544bone_scroll_main_vb->set_custom_minimum_size(Size2(150 * EDSCALE, 0));1545sync_bones = memnew(Button(TTR("Sync Bones to Polygon")));1546bone_scroll_main_vb->add_child(sync_bones);1547sync_bones->set_h_size_flags(0);1548sync_bones->connect(SceneStringName(pressed), callable_mp(this, &Polygon2DEditor::_sync_bones));1549uv_main_hsc->add_child(bone_scroll_main_vb);1550bone_scroll = memnew(ScrollContainer);1551bone_scroll->set_v_scroll(true);1552bone_scroll->set_h_scroll(false);1553bone_scroll_main_vb->add_child(bone_scroll);1554bone_scroll->set_v_size_flags(SIZE_EXPAND_FILL);1555bone_scroll_vb = memnew(VBoxContainer);1556bone_scroll->add_child(bone_scroll_vb);15571558panner.instantiate();1559panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_pan_callback), callable_mp(this, &Polygon2DEditor::_zoom_callback));15601561canvas->connect(SceneStringName(draw), callable_mp(this, &Polygon2DEditor::_canvas_draw));1562canvas->connect(SceneStringName(gui_input), callable_mp(this, &Polygon2DEditor::_canvas_input));1563canvas->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key));1564canvas->set_focus_mode(FOCUS_CLICK);15651566error = memnew(AcceptDialog);1567add_child(error);1568}15691570Polygon2DEditorPlugin::Polygon2DEditorPlugin() :1571AbstractPolygon2DEditorPlugin(memnew(Polygon2DEditor), "Polygon2D") {1572}157315741575