summaryrefslogtreecommitdiffstats
path: root/editor/plugins/path_3d_editor_plugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins/path_3d_editor_plugin.cpp')
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp197
1 files changed, 145 insertions, 52 deletions
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index e4522f5a03..1cffdb6454 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "node_3d_editor_plugin.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/menu_button.h"
#include "scene/resources/curve.h"
@@ -111,7 +112,7 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con
// Primary handles: position.
if (!p_secondary) {
Vector3 inters;
- // Special cas for primary handle, the handle id equals control point id.
+ // Special case for primary handle, the handle id equals control point id.
const int idx = p_id;
if (p.intersects_ray(ray_from, ray_dir, &inters)) {
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
@@ -200,6 +201,22 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ // Primary handles: position.
+ if (!p_secondary && !Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
+ // Special case for primary handle, the handle id equals control point id.
+ const int idx = p_id;
+ if (p_cancel) {
+ c->set_point_position(idx, p_restore);
+ return;
+ }
+ ur->create_action(TTR("Set Curve Point Position"));
+ ur->add_do_method(c.ptr(), "set_point_position", idx, c->get_point_position(idx));
+ ur->add_undo_method(c.ptr(), "set_point_position", idx, p_restore);
+ ur->commit_action();
+
+ return;
+ }
+
// Secondary handles: in, out, tilt.
const HandleInfo info = _secondary_handles_info[p_id];
const int idx = info.point_idx;
@@ -263,6 +280,7 @@ void Path3DGizmo::redraw() {
Ref<StandardMaterial3D> path_thin_material = gizmo_plugin->get_material("path_thin_material", this);
Ref<StandardMaterial3D> path_tilt_material = gizmo_plugin->get_material("path_tilt_material", this);
Ref<StandardMaterial3D> path_tilt_muted_material = gizmo_plugin->get_material("path_tilt_muted_material", this);
+ Ref<StandardMaterial3D> handles_material = gizmo_plugin->get_material("handles");
Ref<StandardMaterial3D> sec_handles_material = gizmo_plugin->get_material("sec_handles");
Ref<Curve3D> c = path->get_curve();
@@ -340,56 +358,50 @@ void Path3DGizmo::redraw() {
if (Path3DEditorPlugin::singleton->get_edited_path() == path) {
PackedVector3Array handle_lines;
PackedVector3Array tilt_handle_lines;
+ PackedVector3Array primary_handle_points;
PackedVector3Array secondary_handle_points;
PackedInt32Array collected_secondary_handle_ids; // Avoid shadowing member on Node3DEditorGizmo.
_secondary_handles_info.resize(c->get_point_count() * 3);
for (int idx = 0; idx < c->get_point_count(); idx++) {
+ // Collect primary-handles.
const Vector3 pos = c->get_point_position(idx);
- bool is_current_point_selected = is_subgizmo_selected(idx);
- bool is_previous_point_selected = is_subgizmo_selected(idx - 1);
- bool is_following_point_selected = is_subgizmo_selected(idx + 1);
+ primary_handle_points.append(pos);
HandleInfo info;
info.point_idx = idx;
// Collect in-handles except for the first point.
- if (idx > 0 && (is_current_point_selected || is_previous_point_selected)) {
+ if (idx > 0 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
const Vector3 in = c->get_point_in(idx);
- // Display in-handles only when they are "initialized".
- if (in.length_squared() > 0) {
- info.type = HandleType::HANDLE_TYPE_IN;
- const int handle_idx = idx * 3 + 0;
- collected_secondary_handle_ids.append(handle_idx);
- _secondary_handles_info.write[handle_idx] = info;
+ info.type = HandleType::HANDLE_TYPE_IN;
+ const int handle_idx = idx * 3 + 0;
+ collected_secondary_handle_ids.append(handle_idx);
+ _secondary_handles_info.write[handle_idx] = info;
- secondary_handle_points.append(pos + in);
- handle_lines.append(pos);
- handle_lines.append(pos + in);
- }
+ secondary_handle_points.append(pos + in);
+ handle_lines.append(pos);
+ handle_lines.append(pos + in);
}
// Collect out-handles except for the last point.
- if (idx < c->get_point_count() - 1 && (is_current_point_selected || is_following_point_selected)) {
+ if (idx < c->get_point_count() - 1 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) {
const Vector3 out = c->get_point_out(idx);
- // Display out-handles only when they are "initialized".
- if (out.length_squared() > 0) {
- info.type = HandleType::HANDLE_TYPE_OUT;
- const int handle_idx = idx * 3 + 1;
- collected_secondary_handle_ids.append(handle_idx);
- _secondary_handles_info.write[handle_idx] = info;
+ info.type = HandleType::HANDLE_TYPE_OUT;
+ const int handle_idx = idx * 3 + 1;
+ collected_secondary_handle_ids.append(handle_idx);
+ _secondary_handles_info.write[handle_idx] = info;
- secondary_handle_points.append(pos + out);
- handle_lines.append(pos);
- handle_lines.append(pos + out);
- }
+ secondary_handle_points.append(pos + out);
+ handle_lines.append(pos);
+ handle_lines.append(pos + out);
}
// Collect tilt-handles.
- if (is_current_point_selected || is_previous_point_selected || is_following_point_selected) {
+ if (Path3DEditorPlugin::singleton->curve_edit_tilt->is_pressed()) {
// Tilt handle.
{
info.type = HandleType::HANDLE_TYPE_TILT;
@@ -419,7 +431,7 @@ void Path3DGizmo::redraw() {
const Vector3 edge = sin(a) * side + cos(a) * up;
disk.append(pos + edge * disk_size);
}
- add_vertices(disk, is_current_point_selected ? path_tilt_material : path_tilt_muted_material, Mesh::PRIMITIVE_LINE_STRIP);
+ add_vertices(disk, path_tilt_material, Mesh::PRIMITIVE_LINE_STRIP);
}
}
}
@@ -432,6 +444,9 @@ void Path3DGizmo::redraw() {
add_lines(tilt_handle_lines, path_tilt_material);
}
+ if (!Path3DEditorPlugin::singleton->curve_edit->is_pressed() && primary_handle_points.size()) {
+ add_handles(primary_handle_points, handles_material);
+ }
if (secondary_handle_points.size()) {
add_handles(secondary_handle_points, sec_handles_material, collected_secondary_handle_ids, false, true);
}
@@ -453,6 +468,12 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path, float p_disk_size) {
// Connecting to a signal once, rather than plaguing the implementation with calls to `Node3DEditor::update_transform_gizmo`.
path->connect("curve_changed", callable_mp(this, &Path3DGizmo::_update_transform_gizmo));
+
+ Path3DEditorPlugin::singleton->curve_edit->connect("pressed", callable_mp(this, &Path3DGizmo::redraw));
+ Path3DEditorPlugin::singleton->curve_edit_curve->connect("pressed", callable_mp(this, &Path3DGizmo::redraw));
+ Path3DEditorPlugin::singleton->curve_create->connect("pressed", callable_mp(this, &Path3DGizmo::redraw));
+ Path3DEditorPlugin::singleton->curve_del->connect("pressed", callable_mp(this, &Path3DGizmo::redraw));
+ Path3DEditorPlugin::singleton->curve_close->connect("pressed", callable_mp(this, &Path3DGizmo::redraw));
}
EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
@@ -623,9 +644,6 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p
}
}
}
- if (curve_edit_curve->is_pressed()) {
- mb->set_shift_pressed(true);
- }
}
return EditorPlugin::AFTER_GUI_INPUT_PASS;
@@ -674,8 +692,11 @@ void Path3DEditorPlugin::make_visible(bool p_visible) {
void Path3DEditorPlugin::_mode_changed(int p_mode) {
curve_create->set_pressed(p_mode == MODE_CREATE);
curve_edit_curve->set_pressed(p_mode == MODE_EDIT_CURVE);
+ curve_edit_tilt->set_pressed(p_mode == MODE_EDIT_TILT);
curve_edit->set_pressed(p_mode == MODE_EDIT);
curve_del->set_pressed(p_mode == MODE_DELETE);
+
+ Node3DEditor::get_singleton()->clear_subgizmo_selection();
}
void Path3DEditorPlugin::_close_curve() {
@@ -715,14 +736,57 @@ void Path3DEditorPlugin::_handle_option_pressed(int p_option) {
}
}
+void Path3DEditorPlugin::_confirm_clear_points() {
+ if (!path || path->get_curve().is_null() || path->get_curve()->get_point_count() == 0) {
+ return;
+ }
+ clear_points_dialog->reset_size();
+ clear_points_dialog->popup_centered();
+}
+
+void Path3DEditorPlugin::_clear_points() {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ PackedVector3Array points = path->get_curve()->get_points().duplicate();
+
+ undo_redo->create_action(TTR("Clear Curve Points"));
+ undo_redo->add_do_method(this, "_clear_curve_points");
+ undo_redo->add_undo_method(this, "_restore_curve_points", points);
+ undo_redo->commit_action();
+}
+
+void Path3DEditorPlugin::_clear_curve_points() {
+ if (!path || path->get_curve().is_null() || path->get_curve()->get_point_count() == 0) {
+ return;
+ }
+ Ref<Curve3D> curve = path->get_curve();
+ curve->clear_points();
+}
+
+void Path3DEditorPlugin::_restore_curve_points(const PackedVector3Array &p_points) {
+ if (!path || path->get_curve().is_null()) {
+ return;
+ }
+ Ref<Curve3D> curve = path->get_curve();
+
+ if (curve->get_point_count() > 0) {
+ curve->clear_points();
+ }
+
+ for (int i = 0; i < p_points.size(); i += 3) {
+ curve->add_point(p_points[i + 2], p_points[i], p_points[i + 1]);
+ }
+}
+
void Path3DEditorPlugin::_update_theme() {
// TODO: Split the EditorPlugin instance from the UI instance and connect this properly.
// See the 2D path editor for inspiration.
curve_edit->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveEdit"), EditorStringName(EditorIcons)));
curve_edit_curve->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveCurve"), EditorStringName(EditorIcons)));
+ curve_edit_tilt->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveTilt"), EditorStringName(EditorIcons)));
curve_create->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveCreate"), EditorStringName(EditorIcons)));
curve_del->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveDelete"), EditorStringName(EditorIcons)));
curve_close->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("CurveClose"), EditorStringName(EditorIcons)));
+ curve_clear_points->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Clear"), EditorStringName(EditorIcons)));
}
void Path3DEditorPlugin::_notification(int p_what) {
@@ -730,6 +794,7 @@ void Path3DEditorPlugin::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
curve_create->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_CREATE));
curve_edit_curve->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_CURVE));
+ curve_edit_tilt->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT_TILT));
curve_edit->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT));
curve_del->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE));
curve_close->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_close_curve));
@@ -747,6 +812,8 @@ void Path3DEditorPlugin::_notification(int p_what) {
}
void Path3DEditorPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_clear_curve_points"), &Path3DEditorPlugin::_clear_curve_points);
+ ClassDB::bind_method(D_METHOD("_restore_curve_points"), &Path3DEditorPlugin::_restore_curve_points);
}
Path3DEditorPlugin *Path3DEditorPlugin::singleton = nullptr;
@@ -761,6 +828,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
Ref<Path3DGizmoPlugin> gizmo_plugin = memnew(Path3DGizmoPlugin(disk_size));
Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
+ path_3d_gizmo_plugin = gizmo_plugin;
topmenu_bar = memnew(HBoxContainer);
topmenu_bar->hide();
@@ -770,16 +838,23 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_edit->set_theme_type_variation("FlatButton");
curve_edit->set_toggle_mode(true);
curve_edit->set_focus_mode(Control::FOCUS_NONE);
- curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
+ curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Click: Select multiple Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
topmenu_bar->add_child(curve_edit);
curve_edit_curve = memnew(Button);
curve_edit_curve->set_theme_type_variation("FlatButton");
curve_edit_curve->set_toggle_mode(true);
curve_edit_curve->set_focus_mode(Control::FOCUS_NONE);
- curve_edit_curve->set_tooltip_text(TTR("Select Control Points (Shift+Drag)"));
+ curve_edit_curve->set_tooltip_text(TTR("Select Control Points") + "\n" + TTR("Shift+Click: Drag out Control Points"));
topmenu_bar->add_child(curve_edit_curve);
+ curve_edit_tilt = memnew(Button);
+ curve_edit_tilt->set_theme_type_variation("FlatButton");
+ curve_edit_tilt->set_toggle_mode(true);
+ curve_edit_tilt->set_focus_mode(Control::FOCUS_NONE);
+ curve_edit_tilt->set_tooltip_text(TTR("Select Tilt Handles"));
+ topmenu_bar->add_child(curve_edit_tilt);
+
curve_create = memnew(Button);
curve_create->set_theme_type_variation("FlatButton");
curve_create->set_toggle_mode(true);
@@ -800,7 +875,18 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_close->set_tooltip_text(TTR("Close Curve"));
topmenu_bar->add_child(curve_close);
- PopupMenu *menu;
+ curve_clear_points = memnew(Button);
+ curve_clear_points->set_theme_type_variation("FlatButton");
+ curve_clear_points->set_focus_mode(Control::FOCUS_NONE);
+ curve_clear_points->set_tooltip_text(TTR("Clear Points"));
+ curve_clear_points->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_confirm_clear_points));
+ topmenu_bar->add_child(curve_clear_points);
+
+ clear_points_dialog = memnew(ConfirmationDialog);
+ clear_points_dialog->set_title(TTR("Please Confirm..."));
+ clear_points_dialog->set_text(TTR("Remove all curve points?"));
+ clear_points_dialog->connect("confirmed", callable_mp(this, &Path3DEditorPlugin::_clear_points));
+ topmenu_bar->add_child(clear_points_dialog);
handle_menu = memnew(MenuButton);
handle_menu->set_flat(false);
@@ -808,6 +894,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
handle_menu->set_text(TTR("Options"));
topmenu_bar->add_child(handle_menu);
+ PopupMenu *menu;
menu = handle_menu->get_popup();
menu->add_check_item(TTR("Mirror Handle Angles"));
menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
@@ -849,11 +936,13 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<StandardMaterial3D> handle_material = get_material("handles", p_gizmo);
PackedVector3Array handles;
- for (int idx = 0; idx < curve->get_point_count(); ++idx) {
- // Collect handles.
- const Vector3 pos = curve->get_point_position(idx);
+ if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
+ for (int idx = 0; idx < curve->get_point_count(); ++idx) {
+ // Collect handles.
+ const Vector3 pos = curve->get_point_position(idx);
- handles.append(pos);
+ handles.append(pos);
+ }
}
if (handles.size()) {
@@ -867,10 +956,12 @@ int Path3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo,
Ref<Curve3D> curve = path->get_curve();
ERR_FAIL_COND_V(curve.is_null(), -1);
- for (int idx = 0; idx < curve->get_point_count(); ++idx) {
- Vector3 pos = path->get_global_transform().xform(curve->get_point_position(idx));
- if (p_camera->unproject_position(pos).distance_to(p_point) < 20) {
- return idx;
+ if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
+ for (int idx = 0; idx < curve->get_point_count(); ++idx) {
+ Vector3 pos = path->get_global_transform().xform(curve->get_point_position(idx));
+ if (p_camera->unproject_position(pos).distance_to(p_point) < 20) {
+ return idx;
+ }
}
}
return -1;
@@ -884,18 +975,20 @@ Vector<int> Path3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGiz
Ref<Curve3D> curve = path->get_curve();
ERR_FAIL_COND_V(curve.is_null(), contained_points);
- for (int idx = 0; idx < curve->get_point_count(); ++idx) {
- Vector3 pos = path->get_global_transform().xform(curve->get_point_position(idx));
- bool is_contained_in_frustum = true;
- for (int i = 0; i < p_frustum.size(); ++i) {
- if (p_frustum[i].distance_to(pos) > 0) {
- is_contained_in_frustum = false;
- break;
+ if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) {
+ for (int idx = 0; idx < curve->get_point_count(); ++idx) {
+ Vector3 pos = path->get_global_transform().xform(curve->get_point_position(idx));
+ bool is_contained_in_frustum = true;
+ for (int i = 0; i < p_frustum.size(); ++i) {
+ if (p_frustum[i].distance_to(pos) > 0) {
+ is_contained_in_frustum = false;
+ break;
+ }
}
- }
- if (is_contained_in_frustum) {
- contained_points.push_back(idx);
+ if (is_contained_in_frustum) {
+ contained_points.push_back(idx);
+ }
}
}