mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-01-23 17:47:17 +01:00
Added Path2D and Polygon2D Nodes and their editord to the Paint module.
This commit is contained in:
parent
8bf5c70f50
commit
3424e44400
637
modules/paint/nodes/curve_2d/editor/path_2d_editor_plugin.cpp
Normal file
637
modules/paint/nodes/curve_2d/editor/path_2d_editor_plugin.cpp
Normal file
@ -0,0 +1,637 @@
|
||||
/*************************************************************************/
|
||||
/* path_2d_editor_plugin.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "path_2d_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/math/transform_2d.h"
|
||||
#include "core/input/input_event.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/object/undo_redo.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "scene/2d/path_2d.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/tool_button.h"
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/curve.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
void Path2DEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
//button_create->set_icon( get_theme_icon("Edit","EditorIcons"));
|
||||
//button_edit->set_icon( get_theme_icon("MovePoint","EditorIcons"));
|
||||
//set_pressed_button(button_edit);
|
||||
//button_edit->set_pressed(true);
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_PHYSICS_PROCESS: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
void Path2DEditor::_node_removed(Node *p_node) {
|
||||
if (p_node == node) {
|
||||
node = nullptr;
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!node->is_visible_in_tree()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!node->get_curve().is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid()) {
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
|
||||
|
||||
Vector2 gpoint = mb->get_position();
|
||||
Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
|
||||
|
||||
if (mb->is_pressed() && action == ACTION_NONE) {
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
for (int i = 0; i < curve->get_point_count(); i++) {
|
||||
real_t dist_to_p = gpoint.distance_to(xform.xform(curve->get_point_position(i)));
|
||||
real_t dist_to_p_out = gpoint.distance_to(xform.xform(curve->get_point_position(i) + curve->get_point_out(i)));
|
||||
real_t dist_to_p_in = gpoint.distance_to(xform.xform(curve->get_point_position(i) + curve->get_point_in(i)));
|
||||
|
||||
// Check for point movement start (for point + in/out controls).
|
||||
if (mb->get_button_index() == BUTTON_LEFT) {
|
||||
if (mode == MODE_EDIT && !mb->get_shift() && dist_to_p < grab_threshold) {
|
||||
// Points can only be moved in edit mode.
|
||||
|
||||
action = ACTION_MOVING_POINT;
|
||||
action_point = i;
|
||||
moving_from = curve->get_point_position(i);
|
||||
moving_screen_from = gpoint;
|
||||
return true;
|
||||
} else if (mode == MODE_EDIT || mode == MODE_EDIT_CURVE) {
|
||||
// In/out controls can be moved in multiple modes.
|
||||
if (dist_to_p_out < grab_threshold && i < (curve->get_point_count() - 1)) {
|
||||
action = ACTION_MOVING_OUT;
|
||||
action_point = i;
|
||||
moving_from = curve->get_point_out(i);
|
||||
moving_screen_from = gpoint;
|
||||
orig_in_length = curve->get_point_in(action_point).length();
|
||||
return true;
|
||||
} else if (dist_to_p_in < grab_threshold && i > 0) {
|
||||
action = ACTION_MOVING_IN;
|
||||
action_point = i;
|
||||
moving_from = curve->get_point_in(i);
|
||||
moving_screen_from = gpoint;
|
||||
orig_out_length = curve->get_point_out(action_point).length();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for point deletion.
|
||||
if ((mb->get_button_index() == BUTTON_RIGHT && mode == MODE_EDIT) || (mb->get_button_index() == BUTTON_LEFT && mode == MODE_DELETE)) {
|
||||
if (dist_to_p < grab_threshold) {
|
||||
undo_redo->create_action(TTR("Remove Point from Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "remove_point", i);
|
||||
undo_redo->add_undo_method(curve.ptr(), "add_point", curve->get_point_position(i), curve->get_point_in(i), curve->get_point_out(i), i);
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
return true;
|
||||
} else if (dist_to_p_out < grab_threshold) {
|
||||
undo_redo->create_action(TTR("Remove Out-Control from Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_out", i, Vector2());
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_out", i, curve->get_point_out(i));
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
return true;
|
||||
} else if (dist_to_p_in < grab_threshold) {
|
||||
undo_redo->create_action(TTR("Remove In-Control from Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_in", i, Vector2());
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_in", i, curve->get_point_in(i));
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for point creation.
|
||||
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && ((mb->get_command() && mode == MODE_EDIT) || mode == MODE_CREATE)) {
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
undo_redo->create_action(TTR("Add Point to Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "add_point", cpoint);
|
||||
undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count());
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
|
||||
action = ACTION_MOVING_POINT;
|
||||
action_point = curve->get_point_count() - 1;
|
||||
moving_from = curve->get_point_position(action_point);
|
||||
moving_screen_from = gpoint;
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for segment split.
|
||||
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && mode == MODE_EDIT && on_edge) {
|
||||
Vector2 gpoint2 = mb->get_position();
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
int insertion_point = -1;
|
||||
float mbLength = curve->get_closest_offset(xform.affine_inverse().xform(gpoint2));
|
||||
int len = curve->get_point_count();
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
float compareLength = curve->get_closest_offset(curve->get_point_position(i + 1));
|
||||
if (mbLength >= curve->get_closest_offset(curve->get_point_position(i)) && mbLength <= compareLength) {
|
||||
insertion_point = i;
|
||||
}
|
||||
}
|
||||
if (insertion_point == -1) {
|
||||
insertion_point = curve->get_point_count() - 2;
|
||||
}
|
||||
|
||||
undo_redo->create_action(TTR("Split Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint2), Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
|
||||
undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1);
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
|
||||
action = ACTION_MOVING_POINT;
|
||||
action_point = insertion_point + 1;
|
||||
moving_from = curve->get_point_position(action_point);
|
||||
moving_screen_from = gpoint2;
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
|
||||
on_edge = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for point movement completion.
|
||||
if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && action != ACTION_NONE) {
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
|
||||
switch (action) {
|
||||
case ACTION_NONE:
|
||||
// N/A, handled in above condition.
|
||||
break;
|
||||
|
||||
case ACTION_MOVING_POINT: {
|
||||
undo_redo->create_action(TTR("Move Point in Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from);
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_IN: {
|
||||
undo_redo->create_action(TTR("Move In-Control in Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, new_pos);
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, moving_from);
|
||||
|
||||
if (mirror_handle_angle) {
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
|
||||
}
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_OUT: {
|
||||
undo_redo->create_action(TTR("Move Out-Control in Curve"));
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, new_pos);
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, moving_from);
|
||||
|
||||
if (mirror_handle_angle) {
|
||||
undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
|
||||
undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
|
||||
}
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
action = ACTION_NONE;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
if (mm.is_valid()) {
|
||||
if (action == ACTION_NONE && mode == MODE_EDIT) {
|
||||
// Handle Edge Follow
|
||||
bool old_edge = on_edge;
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
|
||||
Vector2 gpoint = mm->get_position();
|
||||
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
if (curve == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (curve->get_point_count() < 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find edge
|
||||
edge_point = xform.xform(curve->get_closest_point(xform.affine_inverse().xform(mm->get_position())));
|
||||
on_edge = false;
|
||||
if (edge_point.distance_to(gpoint) <= grab_threshold) {
|
||||
on_edge = true;
|
||||
}
|
||||
// However, if near a control point or its in-out handles then not on edge
|
||||
int len = curve->get_point_count();
|
||||
for (int i = 0; i < len; i++) {
|
||||
Vector2 pp = curve->get_point_position(i);
|
||||
Vector2 p = xform.xform(pp);
|
||||
if (p.distance_to(gpoint) <= grab_threshold) {
|
||||
on_edge = false;
|
||||
break;
|
||||
}
|
||||
p = xform.xform(pp + curve->get_point_in(i));
|
||||
if (p.distance_to(gpoint) <= grab_threshold) {
|
||||
on_edge = false;
|
||||
break;
|
||||
}
|
||||
p = xform.xform(pp + curve->get_point_out(i));
|
||||
if (p.distance_to(gpoint) <= grab_threshold) {
|
||||
on_edge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (on_edge || old_edge != on_edge) {
|
||||
canvas_item_editor->update_viewport();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action != ACTION_NONE) {
|
||||
// Handle point/control movement.
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
|
||||
Vector2 gpoint = mm->get_position();
|
||||
Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())));
|
||||
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
|
||||
|
||||
switch (action) {
|
||||
case ACTION_NONE:
|
||||
// N/A, handled in above condition.
|
||||
break;
|
||||
|
||||
case ACTION_MOVING_POINT: {
|
||||
curve->set_point_position(action_point, cpoint);
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_IN: {
|
||||
curve->set_point_in(action_point, new_pos);
|
||||
|
||||
if (mirror_handle_angle) {
|
||||
curve->set_point_out(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
|
||||
}
|
||||
} break;
|
||||
|
||||
case ACTION_MOVING_OUT: {
|
||||
curve->set_point_out(action_point, new_pos);
|
||||
|
||||
if (mirror_handle_angle) {
|
||||
curve->set_point_in(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
||||
if (!node || !node->is_visible_in_tree() || !node->get_curve().is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
|
||||
|
||||
const Ref<Texture> path_sharp_handle = get_theme_icon("EditorPathSharpHandle", "EditorIcons");
|
||||
const Ref<Texture> path_smooth_handle = get_theme_icon("EditorPathSmoothHandle", "EditorIcons");
|
||||
// Both handle icons must be of the same size
|
||||
const Size2 handle_size = path_sharp_handle->get_size();
|
||||
|
||||
const Ref<Texture> curve_handle = get_theme_icon("EditorCurveHandle", "EditorIcons");
|
||||
const Size2 curve_handle_size = curve_handle->get_size();
|
||||
|
||||
Ref<Curve2D> curve = node->get_curve();
|
||||
|
||||
int len = curve->get_point_count();
|
||||
Control *vpc = canvas_item_editor->get_viewport_control();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
Vector2 point = xform.xform(curve->get_point_position(i));
|
||||
// Determines the point icon to be used
|
||||
bool smooth = false;
|
||||
|
||||
if (i < len - 1) {
|
||||
Vector2 pointout = xform.xform(curve->get_point_position(i) + curve->get_point_out(i));
|
||||
if (point != pointout) {
|
||||
smooth = true;
|
||||
// Draw the line with a dark and light color to be visible on all backgrounds
|
||||
vpc->draw_line(point, pointout, Color(0, 0, 0, 0.5), Math::round(EDSCALE), true);
|
||||
vpc->draw_line(point, pointout, Color(1, 1, 1, 0.5), Math::round(EDSCALE), true);
|
||||
vpc->draw_texture_rect(curve_handle, Rect2(pointout - curve_handle_size * 0.5, curve_handle_size), false, Color(1, 1, 1, 0.75));
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
Vector2 pointin = xform.xform(curve->get_point_position(i) + curve->get_point_in(i));
|
||||
if (point != pointin) {
|
||||
smooth = true;
|
||||
// Draw the line with a dark and light color to be visible on all backgrounds
|
||||
vpc->draw_line(point, pointin, Color(0, 0, 0, 0.5), Math::round(EDSCALE), true);
|
||||
vpc->draw_line(point, pointin, Color(1, 1, 1, 0.5), Math::round(EDSCALE), true);
|
||||
vpc->draw_texture_rect(curve_handle, Rect2(pointin - curve_handle_size * 0.5, curve_handle_size), false, Color(1, 1, 1, 0.75));
|
||||
}
|
||||
}
|
||||
|
||||
vpc->draw_texture_rect(
|
||||
smooth ? path_smooth_handle : path_sharp_handle,
|
||||
Rect2(point - handle_size * 0.5, handle_size),
|
||||
false);
|
||||
}
|
||||
|
||||
if (on_edge) {
|
||||
Ref<Texture> add_handle = get_theme_icon("EditorHandleAdd", "EditorIcons");
|
||||
p_overlay->draw_texture(add_handle, edge_point - add_handle->get_size() * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
void Path2DEditor::_node_visibility_changed() {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_item_editor->update_viewport();
|
||||
}
|
||||
|
||||
void Path2DEditor::edit(Node *p_path2d) {
|
||||
if (!canvas_item_editor) {
|
||||
canvas_item_editor = CanvasItemEditor::get_singleton();
|
||||
}
|
||||
|
||||
if (p_path2d) {
|
||||
node = Object::cast_to<Path2D>(p_path2d);
|
||||
if (!node->is_connected("visibility_changed", this, "_node_visibility_changed")) {
|
||||
node->connect("visibility_changed", this, "_node_visibility_changed");
|
||||
}
|
||||
|
||||
} else {
|
||||
// node may have been deleted at this point
|
||||
if (node && node->is_connected("visibility_changed", this, "_node_visibility_changed")) {
|
||||
node->disconnect("visibility_changed", this, "_node_visibility_changed");
|
||||
}
|
||||
node = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Path2DEditor::_bind_methods() {
|
||||
//ClassDB::bind_method(D_METHOD("_menu_option"),&Path2DEditor::_menu_option);
|
||||
ClassDB::bind_method(D_METHOD("_node_visibility_changed"), &Path2DEditor::_node_visibility_changed);
|
||||
ClassDB::bind_method(D_METHOD("_mode_selected"), &Path2DEditor::_mode_selected);
|
||||
ClassDB::bind_method(D_METHOD("_handle_option_pressed"), &Path2DEditor::_handle_option_pressed);
|
||||
}
|
||||
|
||||
void Path2DEditor::_mode_selected(int p_mode) {
|
||||
if (p_mode == MODE_CREATE) {
|
||||
curve_create->set_pressed(true);
|
||||
curve_edit->set_pressed(false);
|
||||
curve_edit_curve->set_pressed(false);
|
||||
curve_del->set_pressed(false);
|
||||
} else if (p_mode == MODE_EDIT) {
|
||||
curve_create->set_pressed(false);
|
||||
curve_edit->set_pressed(true);
|
||||
curve_edit_curve->set_pressed(false);
|
||||
curve_del->set_pressed(false);
|
||||
} else if (p_mode == MODE_EDIT_CURVE) {
|
||||
curve_create->set_pressed(false);
|
||||
curve_edit->set_pressed(false);
|
||||
curve_edit_curve->set_pressed(true);
|
||||
curve_del->set_pressed(false);
|
||||
} else if (p_mode == MODE_DELETE) {
|
||||
curve_create->set_pressed(false);
|
||||
curve_edit->set_pressed(false);
|
||||
curve_edit_curve->set_pressed(false);
|
||||
curve_del->set_pressed(true);
|
||||
} else if (p_mode == ACTION_CLOSE) {
|
||||
//?
|
||||
|
||||
if (!node->get_curve().is_valid()) {
|
||||
return;
|
||||
}
|
||||
if (node->get_curve()->get_point_count() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 begin = node->get_curve()->get_point_position(0);
|
||||
Vector2 end = node->get_curve()->get_point_position(node->get_curve()->get_point_count() - 1);
|
||||
if (begin.distance_to(end) < CMP_EPSILON) {
|
||||
return;
|
||||
}
|
||||
|
||||
undo_redo->create_action(TTR("Remove Point from Curve"));
|
||||
undo_redo->add_do_method(node->get_curve().ptr(), "add_point", begin);
|
||||
undo_redo->add_undo_method(node->get_curve().ptr(), "remove_point", node->get_curve()->get_point_count());
|
||||
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
|
||||
undo_redo->commit_action();
|
||||
return;
|
||||
}
|
||||
|
||||
mode = Mode(p_mode);
|
||||
}
|
||||
|
||||
void Path2DEditor::_handle_option_pressed(int p_option) {
|
||||
PopupMenu *pm;
|
||||
pm = handle_menu->get_popup();
|
||||
|
||||
switch (p_option) {
|
||||
case HANDLE_OPTION_ANGLE: {
|
||||
bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
|
||||
mirror_handle_angle = !is_checked;
|
||||
pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
|
||||
pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
|
||||
} break;
|
||||
case HANDLE_OPTION_LENGTH: {
|
||||
bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
|
||||
mirror_handle_length = !is_checked;
|
||||
pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Path2DEditor::Path2DEditor(EditorNode *p_editor) {
|
||||
canvas_item_editor = nullptr;
|
||||
editor = p_editor;
|
||||
undo_redo = editor->get_undo_redo();
|
||||
mirror_handle_angle = true;
|
||||
mirror_handle_length = true;
|
||||
on_edge = false;
|
||||
|
||||
mode = MODE_EDIT;
|
||||
action = ACTION_NONE;
|
||||
|
||||
base_hb = memnew(HBoxContainer);
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(base_hb);
|
||||
|
||||
sep = memnew(VSeparator);
|
||||
base_hb->add_child(sep);
|
||||
curve_edit = memnew(ToolButton);
|
||||
curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveEdit", "EditorIcons"));
|
||||
curve_edit->set_toggle_mode(true);
|
||||
curve_edit->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
|
||||
curve_edit->connect("pressed", this, "_mode_selected", varray(MODE_EDIT));
|
||||
base_hb->add_child(curve_edit);
|
||||
curve_edit_curve = memnew(ToolButton);
|
||||
curve_edit_curve->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCurve", "EditorIcons"));
|
||||
curve_edit_curve->set_toggle_mode(true);
|
||||
curve_edit_curve->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_edit_curve->set_tooltip(TTR("Select Control Points (Shift+Drag)"));
|
||||
curve_edit_curve->connect("pressed", this, "_mode_selected", varray(MODE_EDIT_CURVE));
|
||||
base_hb->add_child(curve_edit_curve);
|
||||
curve_create = memnew(ToolButton);
|
||||
curve_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCreate", "EditorIcons"));
|
||||
curve_create->set_toggle_mode(true);
|
||||
curve_create->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_create->set_tooltip(TTR("Add Point (in empty space)"));
|
||||
curve_create->connect("pressed", this, "_mode_selected", varray(MODE_CREATE));
|
||||
base_hb->add_child(curve_create);
|
||||
curve_del = memnew(ToolButton);
|
||||
curve_del->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveDelete", "EditorIcons"));
|
||||
curve_del->set_toggle_mode(true);
|
||||
curve_del->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_del->set_tooltip(TTR("Delete Point"));
|
||||
curve_del->connect("pressed", this, "_mode_selected", varray(MODE_DELETE));
|
||||
base_hb->add_child(curve_del);
|
||||
curve_close = memnew(ToolButton);
|
||||
curve_close->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveClose", "EditorIcons"));
|
||||
curve_close->set_focus_mode(Control::FOCUS_NONE);
|
||||
curve_close->set_tooltip(TTR("Close Curve"));
|
||||
curve_close->connect("pressed", this, "_mode_selected", varray(ACTION_CLOSE));
|
||||
base_hb->add_child(curve_close);
|
||||
|
||||
PopupMenu *menu;
|
||||
|
||||
handle_menu = memnew(MenuButton);
|
||||
handle_menu->set_text(TTR("Options"));
|
||||
base_hb->add_child(handle_menu);
|
||||
|
||||
menu = handle_menu->get_popup();
|
||||
menu->add_check_item(TTR("Mirror Handle Angles"));
|
||||
menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
|
||||
menu->add_check_item(TTR("Mirror Handle Lengths"));
|
||||
menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
|
||||
menu->connect("id_pressed", this, "_handle_option_pressed");
|
||||
|
||||
base_hb->hide();
|
||||
|
||||
curve_edit->set_pressed(true);
|
||||
}
|
||||
|
||||
void Path2DEditorPlugin::edit(Object *p_object) {
|
||||
path2d_editor->edit(Object::cast_to<Node>(p_object));
|
||||
}
|
||||
|
||||
bool Path2DEditorPlugin::handles(Object *p_object) const {
|
||||
return p_object->is_class("Path2D");
|
||||
}
|
||||
|
||||
void Path2DEditorPlugin::make_visible(bool p_visible) {
|
||||
if (p_visible) {
|
||||
path2d_editor->show();
|
||||
path2d_editor->base_hb->show();
|
||||
|
||||
} else {
|
||||
path2d_editor->hide();
|
||||
path2d_editor->base_hb->hide();
|
||||
path2d_editor->edit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Path2DEditorPlugin::Path2DEditorPlugin(EditorNode *p_node) {
|
||||
editor = p_node;
|
||||
path2d_editor = memnew(Path2DEditor(p_node));
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(path2d_editor);
|
||||
path2d_editor->hide();
|
||||
}
|
||||
|
||||
Path2DEditorPlugin::~Path2DEditorPlugin() {
|
||||
}
|
146
modules/paint/nodes/curve_2d/editor/path_2d_editor_plugin.h
Normal file
146
modules/paint/nodes/curve_2d/editor/path_2d_editor_plugin.h
Normal file
@ -0,0 +1,146 @@
|
||||
#ifndef PATH_2D_EDITOR_PLUGIN_H
|
||||
#define PATH_2D_EDITOR_PLUGIN_H
|
||||
/*************************************************************************/
|
||||
/* path_2d_editor_plugin.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
|
||||
class CanvasItemEditor;
|
||||
class Control;
|
||||
class EditorNode;
|
||||
class InputEvent;
|
||||
class MenuButton;
|
||||
class Node;
|
||||
class Panel;
|
||||
class Path2D;
|
||||
class Separator;
|
||||
class ToolButton;
|
||||
class UndoRedo;
|
||||
|
||||
class Path2DEditor : public HBoxContainer {
|
||||
GDCLASS(Path2DEditor, HBoxContainer);
|
||||
|
||||
UndoRedo *undo_redo;
|
||||
|
||||
CanvasItemEditor *canvas_item_editor;
|
||||
EditorNode *editor;
|
||||
Panel *panel;
|
||||
Path2D *node;
|
||||
|
||||
HBoxContainer *base_hb;
|
||||
Separator *sep;
|
||||
|
||||
enum Mode {
|
||||
MODE_CREATE,
|
||||
MODE_EDIT,
|
||||
MODE_EDIT_CURVE,
|
||||
MODE_DELETE,
|
||||
ACTION_CLOSE
|
||||
};
|
||||
|
||||
Mode mode;
|
||||
ToolButton *curve_create;
|
||||
ToolButton *curve_edit;
|
||||
ToolButton *curve_edit_curve;
|
||||
ToolButton *curve_del;
|
||||
ToolButton *curve_close;
|
||||
MenuButton *handle_menu;
|
||||
|
||||
bool mirror_handle_angle;
|
||||
bool mirror_handle_length;
|
||||
bool on_edge;
|
||||
|
||||
enum HandleOption {
|
||||
HANDLE_OPTION_ANGLE,
|
||||
HANDLE_OPTION_LENGTH
|
||||
};
|
||||
|
||||
enum Action {
|
||||
|
||||
ACTION_NONE,
|
||||
ACTION_MOVING_POINT,
|
||||
ACTION_MOVING_IN,
|
||||
ACTION_MOVING_OUT,
|
||||
};
|
||||
|
||||
Action action;
|
||||
int action_point;
|
||||
Point2 moving_from;
|
||||
Point2 moving_screen_from;
|
||||
float orig_in_length;
|
||||
float orig_out_length;
|
||||
Vector2 edge_point;
|
||||
|
||||
void _mode_selected(int p_mode);
|
||||
void _handle_option_pressed(int p_option);
|
||||
|
||||
void _node_visibility_changed();
|
||||
friend class Path2DEditorPlugin;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
void _node_removed(Node *p_node);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
bool forward_gui_input(const Ref<InputEvent> &p_event);
|
||||
void forward_canvas_draw_over_viewport(Control *p_overlay);
|
||||
void edit(Node *p_path2d);
|
||||
Path2DEditor(EditorNode *p_editor);
|
||||
};
|
||||
|
||||
class Path2DEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(Path2DEditorPlugin, EditorPlugin);
|
||||
|
||||
Path2DEditor *path2d_editor;
|
||||
EditorNode *editor;
|
||||
|
||||
public:
|
||||
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return path2d_editor->forward_gui_input(p_event); }
|
||||
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) { path2d_editor->forward_canvas_draw_over_viewport(p_overlay); }
|
||||
|
||||
virtual String get_name() const { return "Path2D"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
virtual void edit(Object *p_object);
|
||||
virtual bool handles(Object *p_object) const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
|
||||
Path2DEditorPlugin(EditorNode *p_node);
|
||||
~Path2DEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // PATH_2D_EDITOR_PLUGIN_H
|
412
modules/paint/nodes/curve_2d/path_2d.cpp
Normal file
412
modules/paint/nodes/curve_2d/path_2d.cpp
Normal file
@ -0,0 +1,412 @@
|
||||
/*************************************************************************/
|
||||
/* path_2d.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "path_2d.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "scene/resources/curve.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_scale.h"
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Rect2 Path2D::_edit_get_rect() const {
|
||||
if (!curve.is_valid() || curve->get_point_count() == 0) {
|
||||
return Rect2(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
Rect2 aabb = Rect2(curve->get_point_position(0), Vector2(0, 0));
|
||||
|
||||
for (int i = 0; i < curve->get_point_count(); i++) {
|
||||
for (int j = 0; j <= 8; j++) {
|
||||
real_t frac = j / 8.0;
|
||||
Vector2 p = curve->interpolate(i, frac);
|
||||
aabb.expand_to(p);
|
||||
}
|
||||
}
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
bool Path2D::_edit_use_rect() const {
|
||||
return curve.is_valid() && curve->get_point_count() != 0;
|
||||
}
|
||||
|
||||
bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
if (curve.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < curve->get_point_count(); i++) {
|
||||
Vector2 s[2];
|
||||
s[0] = curve->get_point_position(i);
|
||||
|
||||
for (int j = 1; j <= 8; j++) {
|
||||
real_t frac = j / 8.0;
|
||||
s[1] = curve->interpolate(i, frac);
|
||||
|
||||
Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s);
|
||||
if (p.distance_to(p_point) <= p_tolerance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
s[0] = s[1];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Path2D::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_DRAW && curve.is_valid()) {
|
||||
//draw the curve!!
|
||||
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (curve->get_point_count() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
const float line_width = 2 * EDSCALE;
|
||||
#else
|
||||
const float line_width = 2;
|
||||
#endif
|
||||
const Color color = Color(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
_cached_draw_pts.resize(curve->get_point_count() * 8);
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < curve->get_point_count(); i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
real_t frac = j * (1.0 / 8.0);
|
||||
Vector2 p = curve->interpolate(i, frac);
|
||||
_cached_draw_pts.set(count++, p);
|
||||
}
|
||||
}
|
||||
|
||||
draw_polyline(_cached_draw_pts, color, line_width, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Path2D::_curve_changed() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void Path2D::set_curve(const Ref<Curve2D> &p_curve) {
|
||||
if (curve.is_valid()) {
|
||||
curve->disconnect("changed", this, "_curve_changed");
|
||||
}
|
||||
|
||||
curve = p_curve;
|
||||
|
||||
if (curve.is_valid()) {
|
||||
curve->connect("changed", this, "_curve_changed");
|
||||
}
|
||||
|
||||
_curve_changed();
|
||||
}
|
||||
|
||||
Ref<Curve2D> Path2D::get_curve() const {
|
||||
return curve;
|
||||
}
|
||||
|
||||
void Path2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path2D::set_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_curve"), &Path2D::get_curve);
|
||||
ClassDB::bind_method(D_METHOD("_curve_changed"), &Path2D::_curve_changed);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D"), "set_curve", "get_curve");
|
||||
}
|
||||
|
||||
Path2D::Path2D() {
|
||||
set_curve(Ref<Curve2D>(memnew(Curve2D))); //create one by default
|
||||
set_self_modulate(Color(0.5, 0.6, 1.0, 0.7));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PathFollow2D::_update_transform() {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Curve2D> c = path->get_curve();
|
||||
if (!c.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
float path_length = c->get_baked_length();
|
||||
if (path_length == 0) {
|
||||
return;
|
||||
}
|
||||
Vector2 pos = c->interpolate_baked(offset, cubic);
|
||||
|
||||
if (rotate) {
|
||||
float ahead = offset + lookahead;
|
||||
|
||||
if (loop && ahead >= path_length) {
|
||||
// If our lookahead will loop, we need to check if the path is closed.
|
||||
int point_count = c->get_point_count();
|
||||
if (point_count > 0) {
|
||||
Vector2 start_point = c->get_point_position(0);
|
||||
Vector2 end_point = c->get_point_position(point_count - 1);
|
||||
if (start_point == end_point) {
|
||||
// Since the path is closed we want to 'smooth off'
|
||||
// the corner at the start/end.
|
||||
// So we wrap the lookahead back round.
|
||||
ahead = Math::fmod(ahead, path_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 ahead_pos = c->interpolate_baked(ahead, cubic);
|
||||
|
||||
Vector2 tangent_to_curve;
|
||||
if (ahead_pos == pos) {
|
||||
// This will happen at the end of non-looping or non-closed paths.
|
||||
// We'll try a look behind instead, in order to get a meaningful angle.
|
||||
tangent_to_curve =
|
||||
(pos - c->interpolate_baked(offset - lookahead, cubic)).normalized();
|
||||
} else {
|
||||
tangent_to_curve = (ahead_pos - pos).normalized();
|
||||
}
|
||||
|
||||
Vector2 normal_of_curve = -tangent_to_curve.tangent();
|
||||
|
||||
pos += tangent_to_curve * h_offset;
|
||||
pos += normal_of_curve * v_offset;
|
||||
|
||||
set_rotation(tangent_to_curve.angle());
|
||||
|
||||
} else {
|
||||
pos.x += h_offset;
|
||||
pos.y += v_offset;
|
||||
}
|
||||
|
||||
set_position(pos);
|
||||
}
|
||||
|
||||
void PathFollow2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
path = Object::cast_to<Path2D>(get_parent());
|
||||
if (path) {
|
||||
_update_transform();
|
||||
}
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
path = nullptr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void PathFollow2D::set_cubic_interpolation(bool p_enable) {
|
||||
cubic = p_enable;
|
||||
}
|
||||
|
||||
bool PathFollow2D::get_cubic_interpolation() const {
|
||||
return cubic;
|
||||
}
|
||||
|
||||
void PathFollow2D::_validate_property(PropertyInfo &property) const {
|
||||
if (property.name == "offset") {
|
||||
float max = 10000;
|
||||
if (path && path->get_curve().is_valid()) {
|
||||
max = path->get_curve()->get_baked_length();
|
||||
}
|
||||
|
||||
property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater";
|
||||
}
|
||||
}
|
||||
|
||||
String PathFollow2D::get_configuration_warning() const {
|
||||
if (!is_visible_in_tree() || !is_inside_tree()) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String warning = Node2D::get_configuration_warning();
|
||||
if (!Object::cast_to<Path2D>(get_parent())) {
|
||||
if (warning != String()) {
|
||||
warning += "\n\n";
|
||||
}
|
||||
warning += TTR("PathFollow2D only works when set as a child of a Path2D node.");
|
||||
}
|
||||
|
||||
return warning;
|
||||
}
|
||||
|
||||
void PathFollow2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow2D::set_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_offset"), &PathFollow2D::get_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow2D::set_h_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow2D::get_h_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow2D::set_v_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow2D::get_v_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow2D::set_unit_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow2D::get_unit_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_rotate", "enable"), &PathFollow2D::set_rotate);
|
||||
ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow2D::set_cubic_interpolation);
|
||||
ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow2D::get_cubic_interpolation);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow2D::set_loop);
|
||||
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow2D::has_loop);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);
|
||||
ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser,or_greater"), "set_offset", "get_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotate"), "set_rotate", "is_rotating");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead");
|
||||
}
|
||||
|
||||
void PathFollow2D::set_offset(float p_offset) {
|
||||
ERR_FAIL_COND(!isfinite(p_offset));
|
||||
offset = p_offset;
|
||||
if (path) {
|
||||
if (path->get_curve().is_valid()) {
|
||||
float path_length = path->get_curve()->get_baked_length();
|
||||
|
||||
if (loop) {
|
||||
offset = Math::fposmod(offset, path_length);
|
||||
if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) {
|
||||
offset = path_length;
|
||||
}
|
||||
} else {
|
||||
offset = CLAMP(offset, 0, path_length);
|
||||
}
|
||||
}
|
||||
|
||||
_update_transform();
|
||||
}
|
||||
_change_notify("offset");
|
||||
_change_notify("unit_offset");
|
||||
}
|
||||
|
||||
void PathFollow2D::set_h_offset(float p_h_offset) {
|
||||
h_offset = p_h_offset;
|
||||
if (path) {
|
||||
_update_transform();
|
||||
}
|
||||
}
|
||||
|
||||
float PathFollow2D::get_h_offset() const {
|
||||
return h_offset;
|
||||
}
|
||||
|
||||
void PathFollow2D::set_v_offset(float p_v_offset) {
|
||||
v_offset = p_v_offset;
|
||||
if (path) {
|
||||
_update_transform();
|
||||
}
|
||||
}
|
||||
|
||||
float PathFollow2D::get_v_offset() const {
|
||||
return v_offset;
|
||||
}
|
||||
|
||||
float PathFollow2D::get_offset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
void PathFollow2D::set_unit_offset(float p_unit_offset) {
|
||||
if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
|
||||
set_offset(p_unit_offset * path->get_curve()->get_baked_length());
|
||||
}
|
||||
}
|
||||
|
||||
float PathFollow2D::get_unit_offset() const {
|
||||
if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
|
||||
return get_offset() / path->get_curve()->get_baked_length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PathFollow2D::set_lookahead(float p_lookahead) {
|
||||
lookahead = p_lookahead;
|
||||
}
|
||||
|
||||
float PathFollow2D::get_lookahead() const {
|
||||
return lookahead;
|
||||
}
|
||||
|
||||
void PathFollow2D::set_rotate(bool p_rotate) {
|
||||
rotate = p_rotate;
|
||||
_update_transform();
|
||||
}
|
||||
|
||||
bool PathFollow2D::is_rotating() const {
|
||||
return rotate;
|
||||
}
|
||||
|
||||
void PathFollow2D::set_loop(bool p_loop) {
|
||||
loop = p_loop;
|
||||
}
|
||||
|
||||
bool PathFollow2D::has_loop() const {
|
||||
return loop;
|
||||
}
|
||||
|
||||
PathFollow2D::PathFollow2D() {
|
||||
offset = 0;
|
||||
h_offset = 0;
|
||||
v_offset = 0;
|
||||
path = nullptr;
|
||||
rotate = true;
|
||||
cubic = true;
|
||||
loop = true;
|
||||
lookahead = 4;
|
||||
}
|
114
modules/paint/nodes/curve_2d/path_2d.h
Normal file
114
modules/paint/nodes/curve_2d/path_2d.h
Normal file
@ -0,0 +1,114 @@
|
||||
#ifndef PATH_2D_H
|
||||
#define PATH_2D_H
|
||||
/*************************************************************************/
|
||||
/* path_2d.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class Curve2D;
|
||||
|
||||
class Path2D : public Node2D {
|
||||
GDCLASS(Path2D, Node2D);
|
||||
|
||||
Ref<Curve2D> curve;
|
||||
Vector<Vector2> _cached_draw_pts;
|
||||
|
||||
void _curve_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const;
|
||||
virtual bool _edit_use_rect() const;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
|
||||
#endif
|
||||
|
||||
void set_curve(const Ref<Curve2D> &p_curve);
|
||||
Ref<Curve2D> get_curve() const;
|
||||
|
||||
Path2D();
|
||||
};
|
||||
|
||||
class PathFollow2D : public Node2D {
|
||||
GDCLASS(PathFollow2D, Node2D);
|
||||
|
||||
public:
|
||||
private:
|
||||
Path2D *path;
|
||||
real_t offset;
|
||||
real_t h_offset;
|
||||
real_t v_offset;
|
||||
real_t lookahead;
|
||||
bool cubic;
|
||||
bool loop;
|
||||
bool rotate;
|
||||
|
||||
void _update_transform();
|
||||
|
||||
protected:
|
||||
virtual void _validate_property(PropertyInfo &property) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_offset(float p_offset);
|
||||
float get_offset() const;
|
||||
|
||||
void set_h_offset(float p_h_offset);
|
||||
float get_h_offset() const;
|
||||
|
||||
void set_v_offset(float p_v_offset);
|
||||
float get_v_offset() const;
|
||||
|
||||
void set_unit_offset(float p_unit_offset);
|
||||
float get_unit_offset() const;
|
||||
|
||||
void set_lookahead(float p_lookahead);
|
||||
float get_lookahead() const;
|
||||
|
||||
void set_loop(bool p_loop);
|
||||
bool has_loop() const;
|
||||
|
||||
void set_rotate(bool p_rotate);
|
||||
bool is_rotating() const;
|
||||
|
||||
void set_cubic_interpolation(bool p_enable);
|
||||
bool get_cubic_interpolation() const;
|
||||
|
||||
String get_configuration_warning() const;
|
||||
|
||||
PathFollow2D();
|
||||
};
|
||||
|
||||
#endif // PATH_2D_H
|
1531
modules/paint/nodes/polygon_2d/editor/polygon_2d_editor_plugin.cpp
Normal file
1531
modules/paint/nodes/polygon_2d/editor/polygon_2d_editor_plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
198
modules/paint/nodes/polygon_2d/editor/polygon_2d_editor_plugin.h
Normal file
198
modules/paint/nodes/polygon_2d/editor/polygon_2d_editor_plugin.h
Normal file
@ -0,0 +1,198 @@
|
||||
#ifndef POLYGON_2D_EDITOR_PLUGIN_H
|
||||
#define POLYGON_2D_EDITOR_PLUGIN_H
|
||||
/*************************************************************************/
|
||||
/* polygon_2d_editor_plugin.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "editor/plugins/abstract_polygon_2d_editor.h"
|
||||
|
||||
#include "core/variant/array.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/object/object.h"
|
||||
#include "core/containers/pool_vector.h"
|
||||
#include "core/object/reference.h"
|
||||
#include "core/containers/vector.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class Button;
|
||||
class EditorNode;
|
||||
class HScrollBar;
|
||||
class HSlider;
|
||||
class InputEvent;
|
||||
class Label;
|
||||
class MenuButton;
|
||||
class Node2D;
|
||||
class Node;
|
||||
class Panel;
|
||||
class Polygon2D;
|
||||
class ScrollContainer;
|
||||
class SpinBox;
|
||||
class TextureRect;
|
||||
class ToolButton;
|
||||
class VBoxContainer;
|
||||
class VScrollBar;
|
||||
|
||||
class Polygon2DEditor : public AbstractPolygon2DEditor {
|
||||
GDCLASS(Polygon2DEditor, AbstractPolygon2DEditor);
|
||||
|
||||
enum Mode {
|
||||
MODE_EDIT_UV = MODE_CONT,
|
||||
UVEDIT_POLYGON_TO_UV,
|
||||
UVEDIT_UV_TO_POLYGON,
|
||||
UVEDIT_UV_CLEAR,
|
||||
UVEDIT_GRID_SETTINGS
|
||||
};
|
||||
|
||||
enum UVMode {
|
||||
UV_MODE_CREATE,
|
||||
UV_MODE_CREATE_INTERNAL,
|
||||
UV_MODE_REMOVE_INTERNAL,
|
||||
UV_MODE_EDIT_POINT,
|
||||
UV_MODE_MOVE,
|
||||
UV_MODE_ROTATE,
|
||||
UV_MODE_SCALE,
|
||||
UV_MODE_ADD_POLYGON,
|
||||
UV_MODE_REMOVE_POLYGON,
|
||||
UV_MODE_PAINT_WEIGHT,
|
||||
UV_MODE_CLEAR_WEIGHT,
|
||||
UV_MODE_MAX
|
||||
};
|
||||
|
||||
ToolButton *uv_edit_mode[4];
|
||||
Ref<ButtonGroup> uv_edit_group;
|
||||
|
||||
Polygon2D *node;
|
||||
|
||||
UVMode uv_mode;
|
||||
AcceptDialog *uv_edit;
|
||||
ToolButton *uv_button[UV_MODE_MAX];
|
||||
ToolButton *b_snap_enable;
|
||||
ToolButton *b_snap_grid;
|
||||
Panel *uv_edit_draw;
|
||||
HSlider *uv_zoom;
|
||||
SpinBox *uv_zoom_value;
|
||||
HScrollBar *uv_hscroll;
|
||||
VScrollBar *uv_vscroll;
|
||||
MenuButton *uv_menu;
|
||||
TextureRect *uv_icon_zoom;
|
||||
|
||||
VBoxContainer *bone_scroll_main_vb;
|
||||
ScrollContainer *bone_scroll;
|
||||
VBoxContainer *bone_scroll_vb;
|
||||
Button *sync_bones;
|
||||
HSlider *bone_paint_strength;
|
||||
SpinBox *bone_paint_radius;
|
||||
Label *bone_paint_radius_label;
|
||||
bool bone_painting;
|
||||
int bone_painting_bone;
|
||||
PoolVector<float> prev_weights;
|
||||
Vector2 bone_paint_pos;
|
||||
AcceptDialog *grid_settings;
|
||||
|
||||
void _sync_bones();
|
||||
void _update_bone_list();
|
||||
|
||||
Vector2 uv_draw_ofs;
|
||||
float uv_draw_zoom;
|
||||
PoolVector<Vector2> points_prev;
|
||||
PoolVector<Vector2> uv_create_uv_prev;
|
||||
PoolVector<Vector2> uv_create_poly_prev;
|
||||
PoolVector<Color> uv_create_colors_prev;
|
||||
int uv_create_prev_internal_vertices;
|
||||
Array uv_create_bones_prev;
|
||||
Array polygons_prev;
|
||||
|
||||
Vector2 uv_create_to;
|
||||
int point_drag_index;
|
||||
bool uv_drag;
|
||||
bool uv_create;
|
||||
Vector<int> polygon_create;
|
||||
UVMode uv_move_current;
|
||||
Vector2 uv_drag_from;
|
||||
bool updating_uv_scroll;
|
||||
|
||||
AcceptDialog *error;
|
||||
|
||||
ToolButton *button_uv;
|
||||
|
||||
bool use_snap;
|
||||
bool snap_show_grid;
|
||||
Vector2 snap_offset;
|
||||
Vector2 snap_step;
|
||||
|
||||
virtual void _menu_option(int p_option);
|
||||
|
||||
void _cancel_editing();
|
||||
void _update_polygon_editing_state();
|
||||
|
||||
void _uv_scroll_changed(float);
|
||||
void _uv_input(const Ref<InputEvent> &p_input);
|
||||
void _uv_draw();
|
||||
void _uv_mode(int p_mode);
|
||||
|
||||
void _set_use_snap(bool p_use);
|
||||
void _set_show_grid(bool p_show);
|
||||
void _set_snap_off_x(float p_val);
|
||||
void _set_snap_off_y(float p_val);
|
||||
void _set_snap_step_x(float p_val);
|
||||
void _set_snap_step_y(float p_val);
|
||||
|
||||
void _uv_edit_mode_select(int p_mode);
|
||||
void _uv_edit_popup_hide();
|
||||
void _bone_paint_selected(int p_index);
|
||||
|
||||
int _get_polygon_count() const;
|
||||
|
||||
protected:
|
||||
virtual Node2D *_get_node() const;
|
||||
virtual void _set_node(Node *p_polygon);
|
||||
|
||||
virtual Vector2 _get_offset(int p_idx) const;
|
||||
|
||||
virtual bool _has_uv() const { return true; };
|
||||
virtual void _commit_action();
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
Vector2 snap_point(Vector2 p_target) const;
|
||||
|
||||
public:
|
||||
Polygon2DEditor(EditorNode *p_editor);
|
||||
};
|
||||
|
||||
class Polygon2DEditorPlugin : public AbstractPolygon2DEditorPlugin {
|
||||
GDCLASS(Polygon2DEditorPlugin, AbstractPolygon2DEditorPlugin);
|
||||
|
||||
public:
|
||||
Polygon2DEditorPlugin(EditorNode *p_node);
|
||||
};
|
||||
|
||||
#endif // POLYGON_2D_EDITOR_PLUGIN_H
|
690
modules/paint/nodes/polygon_2d/polygon_2d.cpp
Normal file
690
modules/paint/nodes/polygon_2d/polygon_2d.cpp
Normal file
@ -0,0 +1,690 @@
|
||||
/*************************************************************************/
|
||||
/* polygon_2d.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "polygon_2d.h"
|
||||
|
||||
#include "core/math/geometry.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
#ifdef MODULE_SKELETON_2D_ENABLED
|
||||
#include "modules/skeleton_2d/nodes/skeleton_2d.h"
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Dictionary Polygon2D::_edit_get_state() const {
|
||||
Dictionary state = Node2D::_edit_get_state();
|
||||
state["offset"] = offset;
|
||||
return state;
|
||||
}
|
||||
|
||||
void Polygon2D::_edit_set_state(const Dictionary &p_state) {
|
||||
Node2D::_edit_set_state(p_state);
|
||||
set_offset(p_state["offset"]);
|
||||
}
|
||||
|
||||
void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) {
|
||||
set_position(get_transform().xform(p_pivot));
|
||||
set_offset(get_offset() - p_pivot);
|
||||
}
|
||||
|
||||
Point2 Polygon2D::_edit_get_pivot() const {
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
bool Polygon2D::_edit_use_pivot() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect2 Polygon2D::_edit_get_rect() const {
|
||||
if (rect_cache_dirty) {
|
||||
int l = polygon.size();
|
||||
PoolVector<Vector2>::Read r = polygon.read();
|
||||
item_rect = Rect2();
|
||||
for (int i = 0; i < l; i++) {
|
||||
Vector2 pos = r[i] + offset;
|
||||
if (i == 0) {
|
||||
item_rect.position = pos;
|
||||
} else {
|
||||
item_rect.expand_to(pos);
|
||||
}
|
||||
}
|
||||
rect_cache_dirty = false;
|
||||
}
|
||||
|
||||
return item_rect;
|
||||
}
|
||||
|
||||
bool Polygon2D::_edit_use_rect() const {
|
||||
return polygon.size() > 0;
|
||||
}
|
||||
|
||||
bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
Vector<Vector2> polygon2d = Variant(polygon);
|
||||
if (internal_vertices > 0) {
|
||||
polygon2d.resize(polygon2d.size() - internal_vertices);
|
||||
}
|
||||
return Geometry::is_point_in_polygon(p_point - get_offset(), polygon2d);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Polygon2D::_skeleton_bone_setup_changed() {
|
||||
update();
|
||||
}
|
||||
|
||||
void Polygon2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
// Must re-establish any existing links with skeletons on re-entering the tree.
|
||||
update();
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
// Always detach skeleton when exiting the tree, so skeletons don't inform
|
||||
// Polygon2Ds outside the tree that they have moved (this would be useless work).
|
||||
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
|
||||
} break;
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (polygon.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODULE_SKELETON_2D_ENABLED
|
||||
Skeleton2D *skeleton_node = nullptr;
|
||||
if (has_node(skeleton)) {
|
||||
skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton));
|
||||
}
|
||||
|
||||
ObjectID new_skeleton_id = 0;
|
||||
|
||||
if (skeleton_node) {
|
||||
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
|
||||
new_skeleton_id = skeleton_node->get_instance_id();
|
||||
|
||||
// Sync the offset transform between the Polygon2D and the skeleton.
|
||||
// This is needed for accurate culling in VisualServer.
|
||||
Transform2D global_xform_skel = skeleton_node->get_global_transform();
|
||||
Transform2D global_xform_poly = get_global_transform();
|
||||
|
||||
// find the difference
|
||||
Transform2D global_xform_offset = global_xform_skel.affine_inverse() * global_xform_poly;
|
||||
RS::get_singleton()->canvas_item_set_skeleton_relative_xform(get_canvas_item(), global_xform_offset);
|
||||
|
||||
} else {
|
||||
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
|
||||
}
|
||||
|
||||
if (new_skeleton_id != current_skeleton_id) {
|
||||
Object *old_skeleton = ObjectDB::get_instance(current_skeleton_id);
|
||||
if (old_skeleton) {
|
||||
old_skeleton->disconnect("bone_setup_changed", this, "_skeleton_bone_setup_changed");
|
||||
}
|
||||
|
||||
if (skeleton_node) {
|
||||
skeleton_node->connect("bone_setup_changed", this, "_skeleton_bone_setup_changed");
|
||||
}
|
||||
|
||||
current_skeleton_id = new_skeleton_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vector<Vector2> points;
|
||||
Vector<Vector2> uvs;
|
||||
Vector<int> bones;
|
||||
Vector<float> weights;
|
||||
|
||||
int len = polygon.size();
|
||||
if ((invert || polygons.size() == 0) && internal_vertices > 0) {
|
||||
//if no polygons are around, internal vertices must not be drawn, else let them be
|
||||
len -= internal_vertices;
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
points.resize(len);
|
||||
|
||||
{
|
||||
PoolVector<Vector2>::Read polyr = polygon.read();
|
||||
for (int i = 0; i < len; i++) {
|
||||
points.write[i] = polyr[i] + offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (invert) {
|
||||
Rect2 bounds;
|
||||
int highest_idx = -1;
|
||||
float highest_y = -1e20;
|
||||
float sum = 0;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i == 0) {
|
||||
bounds.position = points[i];
|
||||
} else {
|
||||
bounds.expand_to(points[i]);
|
||||
}
|
||||
if (points[i].y > highest_y) {
|
||||
highest_idx = i;
|
||||
highest_y = points[i].y;
|
||||
}
|
||||
int ni = (i + 1) % len;
|
||||
sum += (points[ni].x - points[i].x) * (points[ni].y + points[i].y);
|
||||
}
|
||||
|
||||
bounds = bounds.grow(invert_border);
|
||||
|
||||
Vector2 ep[7] = {
|
||||
Vector2(points[highest_idx].x, points[highest_idx].y + invert_border),
|
||||
Vector2(bounds.position + bounds.size),
|
||||
Vector2(bounds.position + Vector2(bounds.size.x, 0)),
|
||||
Vector2(bounds.position),
|
||||
Vector2(bounds.position + Vector2(0, bounds.size.y)),
|
||||
Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y + invert_border),
|
||||
Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y),
|
||||
};
|
||||
|
||||
if (sum > 0) {
|
||||
SWAP(ep[1], ep[4]);
|
||||
SWAP(ep[2], ep[3]);
|
||||
SWAP(ep[5], ep[0]);
|
||||
SWAP(ep[6], points.write[highest_idx]);
|
||||
}
|
||||
|
||||
points.resize(points.size() + 7);
|
||||
for (int i = points.size() - 1; i >= highest_idx + 7; i--) {
|
||||
points.write[i] = points[i - 7];
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
points.write[highest_idx + i + 1] = ep[i];
|
||||
}
|
||||
|
||||
len = points.size();
|
||||
}
|
||||
|
||||
if (texture.is_valid()) {
|
||||
Transform2D texmat(tex_rot, tex_ofs);
|
||||
texmat.scale(tex_scale);
|
||||
Size2 tex_size = texture->get_size();
|
||||
|
||||
uvs.resize(len);
|
||||
|
||||
if (points.size() == uv.size()) {
|
||||
PoolVector<Vector2>::Read uvr = uv.read();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
uvs.write[i] = texmat.xform(uvr[i]) / tex_size;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
uvs.write[i] = texmat.xform(points[i]) / tex_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MODULE_SKELETON_2D_ENABLED
|
||||
if (skeleton_node && !invert && bone_weights.size()) {
|
||||
//a skeleton is set! fill indices and weights
|
||||
int vc = len;
|
||||
bones.resize(vc * 4);
|
||||
weights.resize(vc * 4);
|
||||
|
||||
int *bonesw = bones.ptrw();
|
||||
float *weightsw = weights.ptrw();
|
||||
|
||||
for (int i = 0; i < vc * 4; i++) {
|
||||
bonesw[i] = 0;
|
||||
weightsw[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bone_weights.size(); i++) {
|
||||
if (bone_weights[i].weights.size() != points.size()) {
|
||||
continue; //different number of vertices, sorry not using.
|
||||
}
|
||||
if (!skeleton_node->has_node(bone_weights[i].path)) {
|
||||
continue; //node does not exist
|
||||
}
|
||||
Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path));
|
||||
if (!bone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int bone_index = bone->get_index_in_skeleton();
|
||||
PoolVector<float>::Read r = bone_weights[i].weights.read();
|
||||
for (int j = 0; j < vc; j++) {
|
||||
if (r[j] == 0.0) {
|
||||
continue; //weight is unpainted, skip
|
||||
}
|
||||
//find an index with a weight
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (weightsw[j * 4 + k] < r[j]) {
|
||||
//this is less than this weight, insert weight!
|
||||
for (int l = 3; l > k; l--) {
|
||||
weightsw[j * 4 + l] = weightsw[j * 4 + l - 1];
|
||||
bonesw[j * 4 + l] = bonesw[j * 4 + l - 1];
|
||||
}
|
||||
weightsw[j * 4 + k] = r[j];
|
||||
bonesw[j * 4 + k] = bone_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//normalize the weights
|
||||
for (int i = 0; i < vc; i++) {
|
||||
float tw = 0;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
tw += weightsw[i * 4 + j];
|
||||
}
|
||||
if (tw == 0) {
|
||||
continue; //unpainted, do nothing
|
||||
}
|
||||
|
||||
//normalize
|
||||
for (int j = 0; j < 4; j++) {
|
||||
weightsw[i * 4 + j] /= tw;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Vector<Color> colors;
|
||||
if (vertex_colors.size() == points.size()) {
|
||||
colors.resize(len);
|
||||
PoolVector<Color>::Read color_r = vertex_colors.read();
|
||||
for (int i = 0; i < len; i++) {
|
||||
colors.write[i] = color_r[i];
|
||||
}
|
||||
} else {
|
||||
colors.push_back(color);
|
||||
}
|
||||
|
||||
// Vector<int> indices = Geometry::triangulate_polygon(points);
|
||||
// RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
|
||||
|
||||
if (invert || polygons.size() == 0) {
|
||||
Vector<int> indices = Geometry::triangulate_polygon(points);
|
||||
if (indices.size()) {
|
||||
RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, RID(), antialiased);
|
||||
}
|
||||
} else {
|
||||
//draw individual polygons
|
||||
Vector<int> total_indices;
|
||||
for (int i = 0; i < polygons.size(); i++) {
|
||||
PoolVector<int> src_indices = polygons[i];
|
||||
int ic = src_indices.size();
|
||||
if (ic < 3) {
|
||||
continue;
|
||||
}
|
||||
PoolVector<int>::Read r = src_indices.read();
|
||||
|
||||
Vector<Vector2> tmp_points;
|
||||
tmp_points.resize(ic);
|
||||
|
||||
for (int j = 0; j < ic; j++) {
|
||||
int idx = r[j];
|
||||
ERR_CONTINUE(idx < 0 || idx >= points.size());
|
||||
tmp_points.write[j] = points[r[j]];
|
||||
}
|
||||
Vector<int> indices = Geometry::triangulate_polygon(tmp_points);
|
||||
int ic2 = indices.size();
|
||||
const int *r2 = indices.ptr();
|
||||
|
||||
int bic = total_indices.size();
|
||||
total_indices.resize(bic + ic2);
|
||||
int *w2 = total_indices.ptrw();
|
||||
|
||||
for (int j = 0; j < ic2; j++) {
|
||||
w2[j + bic] = r[r2[j]];
|
||||
}
|
||||
}
|
||||
|
||||
if (total_indices.size()) {
|
||||
RS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, RID(), antialiased);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) {
|
||||
polygon = p_polygon;
|
||||
rect_cache_dirty = true;
|
||||
update();
|
||||
}
|
||||
|
||||
PoolVector<Vector2> Polygon2D::get_polygon() const {
|
||||
return polygon;
|
||||
}
|
||||
|
||||
void Polygon2D::set_internal_vertex_count(int p_count) {
|
||||
internal_vertices = p_count;
|
||||
}
|
||||
|
||||
int Polygon2D::get_internal_vertex_count() const {
|
||||
return internal_vertices;
|
||||
}
|
||||
|
||||
void Polygon2D::set_uv(const PoolVector<Vector2> &p_uv) {
|
||||
uv = p_uv;
|
||||
update();
|
||||
}
|
||||
|
||||
PoolVector<Vector2> Polygon2D::get_uv() const {
|
||||
return uv;
|
||||
}
|
||||
|
||||
void Polygon2D::set_polygons(const Array &p_polygons) {
|
||||
polygons = p_polygons;
|
||||
update();
|
||||
}
|
||||
|
||||
Array Polygon2D::get_polygons() const {
|
||||
return polygons;
|
||||
}
|
||||
|
||||
void Polygon2D::set_color(const Color &p_color) {
|
||||
color = p_color;
|
||||
update();
|
||||
}
|
||||
Color Polygon2D::get_color() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void Polygon2D::set_vertex_colors(const PoolVector<Color> &p_colors) {
|
||||
vertex_colors = p_colors;
|
||||
update();
|
||||
}
|
||||
PoolVector<Color> Polygon2D::get_vertex_colors() const {
|
||||
return vertex_colors;
|
||||
}
|
||||
|
||||
void Polygon2D::set_texture(const Ref<Texture> &p_texture) {
|
||||
texture = p_texture;
|
||||
|
||||
/*if (texture.is_valid()) {
|
||||
uint32_t flags=texture->get_flags();
|
||||
flags&=~Texture::FLAG_REPEAT;
|
||||
if (tex_tile)
|
||||
flags|=Texture::FLAG_REPEAT;
|
||||
|
||||
texture->set_flags(flags);
|
||||
}*/
|
||||
update();
|
||||
}
|
||||
Ref<Texture> Polygon2D::get_texture() const {
|
||||
return texture;
|
||||
}
|
||||
|
||||
void Polygon2D::set_texture_offset(const Vector2 &p_offset) {
|
||||
tex_ofs = p_offset;
|
||||
update();
|
||||
}
|
||||
Vector2 Polygon2D::get_texture_offset() const {
|
||||
return tex_ofs;
|
||||
}
|
||||
|
||||
void Polygon2D::set_texture_rotation(float p_rot) {
|
||||
tex_rot = p_rot;
|
||||
update();
|
||||
}
|
||||
float Polygon2D::get_texture_rotation() const {
|
||||
return tex_rot;
|
||||
}
|
||||
|
||||
void Polygon2D::set_texture_rotation_degrees(float p_rot) {
|
||||
set_texture_rotation(Math::deg2rad(p_rot));
|
||||
}
|
||||
float Polygon2D::get_texture_rotation_degrees() const {
|
||||
return Math::rad2deg(get_texture_rotation());
|
||||
}
|
||||
|
||||
void Polygon2D::set_texture_scale(const Size2 &p_scale) {
|
||||
tex_scale = p_scale;
|
||||
update();
|
||||
}
|
||||
Size2 Polygon2D::get_texture_scale() const {
|
||||
return tex_scale;
|
||||
}
|
||||
|
||||
void Polygon2D::set_invert(bool p_invert) {
|
||||
invert = p_invert;
|
||||
update();
|
||||
}
|
||||
bool Polygon2D::get_invert() const {
|
||||
return invert;
|
||||
}
|
||||
|
||||
void Polygon2D::set_antialiased(bool p_antialiased) {
|
||||
antialiased = p_antialiased;
|
||||
update();
|
||||
}
|
||||
bool Polygon2D::get_antialiased() const {
|
||||
return antialiased;
|
||||
}
|
||||
|
||||
void Polygon2D::set_invert_border(float p_invert_border) {
|
||||
invert_border = p_invert_border;
|
||||
update();
|
||||
}
|
||||
float Polygon2D::get_invert_border() const {
|
||||
return invert_border;
|
||||
}
|
||||
|
||||
void Polygon2D::set_offset(const Vector2 &p_offset) {
|
||||
offset = p_offset;
|
||||
rect_cache_dirty = true;
|
||||
update();
|
||||
_change_notify("offset");
|
||||
}
|
||||
|
||||
Vector2 Polygon2D::get_offset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Polygon2D::add_bone(const NodePath &p_path, const PoolVector<float> &p_weights) {
|
||||
Bone bone;
|
||||
bone.path = p_path;
|
||||
bone.weights = p_weights;
|
||||
bone_weights.push_back(bone);
|
||||
}
|
||||
int Polygon2D::get_bone_count() const {
|
||||
return bone_weights.size();
|
||||
}
|
||||
NodePath Polygon2D::get_bone_path(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, bone_weights.size(), NodePath());
|
||||
return bone_weights[p_index].path;
|
||||
}
|
||||
PoolVector<float> Polygon2D::get_bone_weights(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, bone_weights.size(), PoolVector<float>());
|
||||
return bone_weights[p_index].weights;
|
||||
}
|
||||
void Polygon2D::erase_bone(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, bone_weights.size());
|
||||
bone_weights.remove(p_idx);
|
||||
}
|
||||
|
||||
void Polygon2D::clear_bones() {
|
||||
bone_weights.clear();
|
||||
}
|
||||
|
||||
void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) {
|
||||
ERR_FAIL_INDEX(p_index, bone_weights.size());
|
||||
bone_weights.write[p_index].weights = p_weights;
|
||||
update();
|
||||
}
|
||||
void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) {
|
||||
ERR_FAIL_INDEX(p_index, bone_weights.size());
|
||||
bone_weights.write[p_index].path = p_path;
|
||||
update();
|
||||
}
|
||||
|
||||
Array Polygon2D::_get_bones() const {
|
||||
Array bones;
|
||||
for (int i = 0; i < get_bone_count(); i++) {
|
||||
// Convert path property to String to avoid errors due to invalid node path in editor,
|
||||
// because it's relative to the Skeleton2D node and not Polygon2D.
|
||||
bones.push_back(String(get_bone_path(i)));
|
||||
bones.push_back(get_bone_weights(i));
|
||||
}
|
||||
return bones;
|
||||
}
|
||||
void Polygon2D::_set_bones(const Array &p_bones) {
|
||||
ERR_FAIL_COND(p_bones.size() & 1);
|
||||
clear_bones();
|
||||
for (int i = 0; i < p_bones.size(); i += 2) {
|
||||
// Convert back from String to NodePath.
|
||||
add_bone(NodePath(p_bones[i].operator String()), p_bones[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon2D::set_skeleton(const NodePath &p_skeleton) {
|
||||
if (skeleton == p_skeleton) {
|
||||
return;
|
||||
}
|
||||
skeleton = p_skeleton;
|
||||
update();
|
||||
}
|
||||
|
||||
NodePath Polygon2D::get_skeleton() const {
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
void Polygon2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon);
|
||||
ClassDB::bind_method(D_METHOD("get_polygon"), &Polygon2D::get_polygon);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_uv", "uv"), &Polygon2D::set_uv);
|
||||
ClassDB::bind_method(D_METHOD("get_uv"), &Polygon2D::get_uv);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_color", "color"), &Polygon2D::set_color);
|
||||
ClassDB::bind_method(D_METHOD("get_color"), &Polygon2D::get_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_polygons", "polygons"), &Polygon2D::set_polygons);
|
||||
ClassDB::bind_method(D_METHOD("get_polygons"), &Polygon2D::get_polygons);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_vertex_colors", "vertex_colors"), &Polygon2D::set_vertex_colors);
|
||||
ClassDB::bind_method(D_METHOD("get_vertex_colors"), &Polygon2D::get_vertex_colors);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Polygon2D::set_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_texture"), &Polygon2D::get_texture);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &Polygon2D::set_texture_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_offset"), &Polygon2D::get_texture_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture_rotation", "texture_rotation"), &Polygon2D::set_texture_rotation);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_rotation"), &Polygon2D::get_texture_rotation);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture_rotation_degrees", "texture_rotation"), &Polygon2D::set_texture_rotation_degrees);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_rotation_degrees"), &Polygon2D::get_texture_rotation_degrees);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &Polygon2D::set_texture_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_scale"), &Polygon2D::get_texture_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_invert", "invert"), &Polygon2D::set_invert);
|
||||
ClassDB::bind_method(D_METHOD("get_invert"), &Polygon2D::get_invert);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &Polygon2D::set_antialiased);
|
||||
ClassDB::bind_method(D_METHOD("get_antialiased"), &Polygon2D::get_antialiased);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_invert_border", "invert_border"), &Polygon2D::set_invert_border);
|
||||
ClassDB::bind_method(D_METHOD("get_invert_border"), &Polygon2D::get_invert_border);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Polygon2D::set_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_offset"), &Polygon2D::get_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_bone", "path", "weights"), &Polygon2D::add_bone);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_count"), &Polygon2D::get_bone_count);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_path", "index"), &Polygon2D::get_bone_path);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_weights", "index"), &Polygon2D::get_bone_weights);
|
||||
ClassDB::bind_method(D_METHOD("erase_bone", "index"), &Polygon2D::erase_bone);
|
||||
ClassDB::bind_method(D_METHOD("clear_bones"), &Polygon2D::clear_bones);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_path", "index", "path"), &Polygon2D::set_bone_path);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_weights", "index", "weights"), &Polygon2D::set_bone_weights);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &Polygon2D::set_skeleton);
|
||||
ClassDB::bind_method(D_METHOD("get_skeleton"), &Polygon2D::get_skeleton);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_internal_vertex_count", "internal_vertex_count"), &Polygon2D::set_internal_vertex_count);
|
||||
ClassDB::bind_method(D_METHOD("get_internal_vertex_count"), &Polygon2D::get_internal_vertex_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_bones", "bones"), &Polygon2D::_set_bones);
|
||||
ClassDB::bind_method(D_METHOD("_get_bones"), &Polygon2D::_get_bones);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_skeleton_bone_setup_changed"), &Polygon2D::_skeleton_bone_setup_changed);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
|
||||
ADD_GROUP("Texture", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
|
||||
ADD_GROUP("Texture", "texture_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_offset"), "set_texture_offset", "get_texture_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale", PROPERTY_HINT_LINK), "set_texture_scale", "get_texture_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
|
||||
ADD_GROUP("Skeleton", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton");
|
||||
|
||||
ADD_GROUP("Invert", "invert_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1"), "set_invert_border", "get_invert_border");
|
||||
|
||||
ADD_GROUP("Data", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
|
||||
}
|
||||
|
||||
Polygon2D::Polygon2D() {
|
||||
invert = false;
|
||||
invert_border = 100;
|
||||
antialiased = false;
|
||||
tex_rot = 0;
|
||||
tex_tile = true;
|
||||
tex_scale = Vector2(1, 1);
|
||||
color = Color(1, 1, 1);
|
||||
rect_cache_dirty = true;
|
||||
internal_vertices = 0;
|
||||
current_skeleton_id = 0;
|
||||
}
|
||||
|
||||
Polygon2D::~Polygon2D() {
|
||||
// Most definitely don't want to leave references to this deleted canvas item
|
||||
// in the skeleton.
|
||||
if (get_canvas_item().is_valid()) {
|
||||
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
|
||||
}
|
||||
}
|
153
modules/paint/nodes/polygon_2d/polygon_2d.h
Normal file
153
modules/paint/nodes/polygon_2d/polygon_2d.h
Normal file
@ -0,0 +1,153 @@
|
||||
#ifndef POLYGON_2D_H
|
||||
#define POLYGON_2D_H
|
||||
|
||||
/*************************************************************************/
|
||||
/* polygon_2d.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class Polygon2D : public Node2D {
|
||||
GDCLASS(Polygon2D, Node2D);
|
||||
|
||||
PoolVector<Vector2> polygon;
|
||||
PoolVector<Vector2> uv;
|
||||
PoolVector<Color> vertex_colors;
|
||||
Array polygons;
|
||||
int internal_vertices;
|
||||
|
||||
struct Bone {
|
||||
NodePath path;
|
||||
PoolVector<float> weights;
|
||||
};
|
||||
|
||||
Vector<Bone> bone_weights;
|
||||
|
||||
Color color;
|
||||
Ref<Texture> texture;
|
||||
Size2 tex_scale;
|
||||
Vector2 tex_ofs;
|
||||
bool tex_tile;
|
||||
float tex_rot;
|
||||
bool invert;
|
||||
float invert_border;
|
||||
bool antialiased;
|
||||
|
||||
Vector2 offset;
|
||||
mutable bool rect_cache_dirty;
|
||||
mutable Rect2 item_rect;
|
||||
|
||||
NodePath skeleton;
|
||||
ObjectID current_skeleton_id;
|
||||
|
||||
Array _get_bones() const;
|
||||
void _set_bones(const Array &p_bones);
|
||||
|
||||
void _skeleton_bone_setup_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual Dictionary _edit_get_state() const;
|
||||
virtual void _edit_set_state(const Dictionary &p_state);
|
||||
|
||||
virtual void _edit_set_pivot(const Point2 &p_pivot);
|
||||
virtual Point2 _edit_get_pivot() const;
|
||||
virtual bool _edit_use_pivot() const;
|
||||
virtual Rect2 _edit_get_rect() const;
|
||||
virtual bool _edit_use_rect() const;
|
||||
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
|
||||
#endif
|
||||
|
||||
void set_polygon(const PoolVector<Vector2> &p_polygon);
|
||||
PoolVector<Vector2> get_polygon() const;
|
||||
|
||||
void set_internal_vertex_count(int p_count);
|
||||
int get_internal_vertex_count() const;
|
||||
|
||||
void set_uv(const PoolVector<Vector2> &p_uv);
|
||||
PoolVector<Vector2> get_uv() const;
|
||||
|
||||
void set_polygons(const Array &p_polygons);
|
||||
Array get_polygons() const;
|
||||
|
||||
void set_color(const Color &p_color);
|
||||
Color get_color() const;
|
||||
|
||||
void set_vertex_colors(const PoolVector<Color> &p_colors);
|
||||
PoolVector<Color> get_vertex_colors() const;
|
||||
|
||||
void set_texture(const Ref<Texture> &p_texture);
|
||||
Ref<Texture> get_texture() const;
|
||||
|
||||
void set_texture_offset(const Vector2 &p_offset);
|
||||
Vector2 get_texture_offset() const;
|
||||
|
||||
void set_texture_rotation(float p_rot);
|
||||
float get_texture_rotation() const;
|
||||
|
||||
void set_texture_rotation_degrees(float p_rot);
|
||||
float get_texture_rotation_degrees() const;
|
||||
|
||||
void set_texture_scale(const Size2 &p_scale);
|
||||
Size2 get_texture_scale() const;
|
||||
|
||||
void set_invert(bool p_invert);
|
||||
bool get_invert() const;
|
||||
|
||||
void set_antialiased(bool p_antialiased);
|
||||
bool get_antialiased() const;
|
||||
|
||||
void set_invert_border(float p_invert_border);
|
||||
float get_invert_border() const;
|
||||
|
||||
void set_offset(const Vector2 &p_offset);
|
||||
Vector2 get_offset() const;
|
||||
|
||||
void add_bone(const NodePath &p_path = NodePath(), const PoolVector<float> &p_weights = PoolVector<float>());
|
||||
int get_bone_count() const;
|
||||
NodePath get_bone_path(int p_index) const;
|
||||
PoolVector<float> get_bone_weights(int p_index) const;
|
||||
void erase_bone(int p_idx);
|
||||
void clear_bones();
|
||||
void set_bone_weights(int p_index, const PoolVector<float> &p_weights);
|
||||
void set_bone_path(int p_index, const NodePath &p_path);
|
||||
|
||||
void set_skeleton(const NodePath &p_skeleton);
|
||||
NodePath get_skeleton() const;
|
||||
|
||||
Polygon2D();
|
||||
virtual ~Polygon2D();
|
||||
};
|
||||
|
||||
#endif // POLYGON_2D_H
|
Loading…
Reference in New Issue
Block a user