Add rotation widget to 3D viewport

This commit is contained in:
jfons 2019-10-23 11:46:26 +02:00 committed by JFonS
parent 69c1805735
commit 5b4251695a
3 changed files with 325 additions and 16 deletions

View File

@ -256,6 +256,10 @@ void editor_register_fonts(Ref<Theme> p_theme) {
MAKE_DEFAULT_FONT(df_rulers, 8 * EDSCALE); MAKE_DEFAULT_FONT(df_rulers, 8 * EDSCALE);
p_theme->set_font("rulers", "EditorFonts", df_rulers); p_theme->set_font("rulers", "EditorFonts", df_rulers);
// Rotation widget font
MAKE_DEFAULT_FONT(df_rotation_control, 14 * EDSCALE);
p_theme->set_font("rotation_control", "EditorFonts", df_rotation_control);
// Code font // Code font
MAKE_SOURCE_FONT(df_code, int(EDITOR_GET("interface/editor/code_font_size")) * EDSCALE); MAKE_SOURCE_FONT(df_code, int(EDITOR_GET("interface/editor/code_font_size")) * EDSCALE);
p_theme->set_font("source", "EditorFonts", df_code); p_theme->set_font("source", "EditorFonts", df_code);

View File

@ -76,6 +76,177 @@
#define MIN_FOV 0.01 #define MIN_FOV 0.01
#define MAX_FOV 179 #define MAX_FOV 179
void ViewportRotationControl::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
axis_menu_options.clear();
axis_menu_options.push_back(SpatialEditorViewport::VIEW_RIGHT);
axis_menu_options.push_back(SpatialEditorViewport::VIEW_TOP);
axis_menu_options.push_back(SpatialEditorViewport::VIEW_FRONT);
axis_menu_options.push_back(SpatialEditorViewport::VIEW_LEFT);
axis_menu_options.push_back(SpatialEditorViewport::VIEW_BOTTOM);
axis_menu_options.push_back(SpatialEditorViewport::VIEW_REAR);
axis_colors.clear();
axis_colors.push_back(get_color("axis_x_color", "Editor"));
axis_colors.push_back(get_color("axis_y_color", "Editor"));
axis_colors.push_back(get_color("axis_z_color", "Editor"));
update();
if (!is_connected("mouse_exited", this, "_on_mouse_exited")) {
connect("mouse_exited", this, "_on_mouse_exited");
}
}
if (p_what == NOTIFICATION_DRAW && viewport != nullptr) {
_draw();
}
}
void ViewportRotationControl::_draw() {
Vector2i center = get_size() / 2.0;
float radius = get_size().x / 2.0;
if (focused_axis > -2 || orbiting) {
draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25));
}
Vector<Axis2D> axis_to_draw;
_get_sorted_axis(axis_to_draw);
for (int i = 0; i < axis_to_draw.size(); ++i) {
_draw_axis(axis_to_draw[i]);
}
}
void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) {
bool focused = focused_axis == p_axis.axis;
bool positive = p_axis.axis < 3;
bool front = (Math::abs(p_axis.z_axis) <= 0.001 && positive) || p_axis.z_axis > 0.001;
int direction = p_axis.axis % 3;
Color axis_color = axis_colors[direction];
if (!front) {
axis_color = axis_color.darkened(0.4);
}
Color c = focused ? Color(0.9, 0.9, 0.9) : axis_color;
if (positive) {
Vector2i center = get_size() / 2.0;
draw_line(center, p_axis.screen_point, c, 1.5 * EDSCALE, true);
}
if (front) {
String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z");
draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c);
draw_char(get_font("rotation_control", "EditorFonts"), p_axis.screen_point + Vector2(-4.0, 5.0) * EDSCALE, axis_name, "", Color(0.3, 0.3, 0.3));
} else {
draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * (0.55 + (0.2 * (1.0 + p_axis.z_axis))), c);
}
}
void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) {
Vector2i center = get_size() / 2.0;
float radius = get_size().x / 2.0;
float axis_radius = radius - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE;
Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse();
for (int i = 0; i < 3; ++i) {
Vector3 axis_3d = camera_basis.get_axis(i);
Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * axis_radius;
if (Math::abs(axis_3d.z) < 1.0) {
Axis2D pos_axis;
pos_axis.axis = i;
pos_axis.screen_point = center + axis_vector;
pos_axis.z_axis = axis_3d.z;
r_axis.push_back(pos_axis);
Axis2D neg_axis;
neg_axis.axis = i + 3;
neg_axis.screen_point = center - axis_vector;
neg_axis.z_axis = -axis_3d.z;
r_axis.push_back(neg_axis);
} else {
// Special case when the camera is aligned with one axis
Axis2D axis;
axis.axis = i + (axis_3d.z < 0 ? 0 : 3);
axis.screen_point = center;
axis.z_axis = 1.0;
r_axis.push_back(axis);
}
}
r_axis.sort_custom<Axis2DCompare>();
}
void ViewportRotationControl::_gui_input(Ref<InputEvent> p_event) {
const Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
Vector2 pos = mb->get_position();
if (mb->is_pressed()) {
if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
orbiting = true;
}
} else {
if (focused_axis > -1) {
viewport->_menu_option(axis_menu_options[focused_axis]);
_update_focus();
}
orbiting = false;
}
}
const Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (orbiting) {
viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm));
focused_axis = -1;
} else {
_update_focus();
}
}
}
void ViewportRotationControl::_update_focus() {
int original_focus = focused_axis;
focused_axis = -2;
Vector2 mouse_pos = get_local_mouse_position();
if (mouse_pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
focused_axis = -1;
}
Vector<Axis2D> axes;
_get_sorted_axis(axes);
for (int i = 0; i < axes.size(); i++) {
const Axis2D &axis = axes[i];
if (mouse_pos.distance_to(axis.screen_point) < AXIS_CIRCLE_RADIUS) {
focused_axis = axis.axis;
}
}
if (focused_axis != original_focus) {
update();
}
}
void ViewportRotationControl::_on_mouse_exited() {
focused_axis = -2;
update();
}
void ViewportRotationControl::set_viewport(SpatialEditorViewport *p_viewport) {
viewport = p_viewport;
}
void ViewportRotationControl::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input);
ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &ViewportRotationControl::_on_mouse_exited);
}
void SpatialEditorViewport::_update_camera(float p_interp_delta) { void SpatialEditorViewport::_update_camera(float p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
@ -104,6 +275,13 @@ void SpatialEditorViewport::_update_camera(float p_interp_delta) {
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) {
camera_cursor.x_rot = cursor.x_rot;
}
if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) {
camera_cursor.y_rot = cursor.y_rot;
}
Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1)); Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1));
camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance; camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance;
@ -131,6 +309,13 @@ void SpatialEditorViewport::_update_camera(float p_interp_delta) {
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia))); camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) {
camera_cursor.x_rot = cursor.x_rot;
}
if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) {
camera_cursor.y_rot = cursor.y_rot;
}
camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia))); camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia)));
camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia))); camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia)));
} }
@ -157,12 +342,16 @@ void SpatialEditorViewport::_update_camera(float p_interp_delta) {
camera->set_global_transform(to_camera_transform(camera_cursor)); camera->set_global_transform(to_camera_transform(camera_cursor));
if (orthogonal) if (orthogonal) {
camera->set_orthogonal(2 * cursor.distance, 0.1, 8192); float half_fov = Math::deg2rad(get_fov()) / 2.0;
else float height = 2.0 * cursor.distance * Math::tan(half_fov);
camera->set_orthogonal(height, 0.1, 8192);
} else {
camera->set_perspective(get_fov(), get_znear(), get_zfar()); camera->set_perspective(get_fov(), get_znear(), get_zfar());
}
update_transform_gizmo_view(); update_transform_gizmo_view();
rotation_control->update();
} }
} }
@ -545,6 +734,10 @@ void SpatialEditorViewport::_update_name() {
String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective");
if (auto_orthogonal) {
view_mode += " [auto]";
}
if (name != "") if (name != "")
view_menu->set_text(name + " " + view_mode); view_menu->set_text(name + " " + view_mode);
else else
@ -1813,7 +2006,6 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
// Orthgonal mode doesn't work in freelook. // Orthgonal mode doesn't work in freelook.
if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) {
_menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL); _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL);
_update_name();
} }
if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) { if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) {
_menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW); _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW);
@ -1913,6 +2105,10 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con
return; return;
} }
if (orthogonal && auto_orthogonal) {
_menu_option(VIEW_PERSPECTIVE);
}
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
@ -1938,6 +2134,10 @@ void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, cons
return; return;
} }
if (orthogonal && auto_orthogonal) {
_menu_option(VIEW_PERSPECTIVE);
}
real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
@ -2129,6 +2329,7 @@ void SpatialEditorViewport::_notification(int p_what) {
set_freelook_active(false); set_freelook_active(false);
} }
call_deferred("update_transform_gizmo_view"); call_deferred("update_transform_gizmo_view");
rotation_control->set_visible(EditorSettings::get_singleton()->get("editors/3d/navigation/show_viewport_rotation_gizmo"));
} }
if (p_what == NOTIFICATION_RESIZED) { if (p_what == NOTIFICATION_RESIZED) {
@ -2505,6 +2706,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.x_rot = Math_PI / 2.0; cursor.x_rot = Math_PI / 2.0;
set_message(TTR("Top View."), 2); set_message(TTR("Top View."), 2);
name = TTR("Top"); name = TTR("Top");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2514,6 +2716,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.x_rot = -Math_PI / 2.0; cursor.x_rot = -Math_PI / 2.0;
set_message(TTR("Bottom View."), 2); set_message(TTR("Bottom View."), 2);
name = TTR("Bottom"); name = TTR("Bottom");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2523,6 +2726,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.y_rot = Math_PI / 2.0; cursor.y_rot = Math_PI / 2.0;
set_message(TTR("Left View."), 2); set_message(TTR("Left View."), 2);
name = TTR("Left"); name = TTR("Left");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2532,6 +2736,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.y_rot = -Math_PI / 2.0; cursor.y_rot = -Math_PI / 2.0;
set_message(TTR("Right View."), 2); set_message(TTR("Right View."), 2);
name = TTR("Right"); name = TTR("Right");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2541,6 +2746,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.y_rot = 0; cursor.y_rot = 0;
set_message(TTR("Front View."), 2); set_message(TTR("Front View."), 2);
name = TTR("Front"); name = TTR("Front");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2550,6 +2756,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
cursor.y_rot = Math_PI; cursor.y_rot = Math_PI;
set_message(TTR("Rear View."), 2); set_message(TTR("Rear View."), 2);
name = TTR("Rear"); name = TTR("Rear");
_set_auto_orthogonal();
_update_name(); _update_name();
} break; } break;
@ -2647,6 +2854,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false);
orthogonal = false; orthogonal = false;
auto_orthogonal = false;
call_deferred("update_transform_gizmo_view"); call_deferred("update_transform_gizmo_view");
_update_name(); _update_name();
@ -2656,10 +2864,22 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true);
orthogonal = true; orthogonal = true;
auto_orthogonal = false;
call_deferred("update_transform_gizmo_view"); call_deferred("update_transform_gizmo_view");
_update_name(); _update_name();
} break; } break;
case VIEW_AUTO_ORTHOGONAL: {
int idx = view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL);
bool current = view_menu->get_popup()->is_item_checked(idx);
current = !current;
view_menu->get_popup()->set_item_checked(idx, current);
if (auto_orthogonal) {
auto_orthogonal = false;
_update_name();
}
} break;
case VIEW_LOCK_ROTATION: { case VIEW_LOCK_ROTATION: {
int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION);
@ -2781,6 +3001,13 @@ void SpatialEditorViewport::_menu_option(int p_option) {
} }
} }
void SpatialEditorViewport::_set_auto_orthogonal() {
if (!orthogonal && view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL))) {
_menu_option(VIEW_ORTHOGONAL);
auto_orthogonal = true;
}
}
void SpatialEditorViewport::_preview_exited_scene() { void SpatialEditorViewport::_preview_exited_scene() {
preview_camera->disconnect("toggled", this, "_toggle_camera_preview"); preview_camera->disconnect("toggled", this, "_toggle_camera_preview");
@ -2989,6 +3216,18 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) {
else else
_menu_option(VIEW_PERSPECTIVE); _menu_option(VIEW_PERSPECTIVE);
} }
if (p_state.has("view_name")) {
name = p_state["view_name"];
_update_name();
}
if (p_state.has("auto_orthogonal")) {
auto_orthogonal = p_state["auto_orthogonal"];
_update_name();
}
if (p_state.has("auto_orthogonal_enabled")) {
bool enabled = p_state["auto_orthogonal_enabled"];
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), enabled);
}
if (p_state.has("display_mode")) { if (p_state.has("display_mode")) {
int display = p_state["display_mode"]; int display = p_state["display_mode"];
@ -3083,6 +3322,9 @@ Dictionary SpatialEditorViewport::get_state() const {
d["distance"] = cursor.distance; d["distance"] = cursor.distance;
d["use_environment"] = camera->get_environment().is_valid(); d["use_environment"] = camera->get_environment().is_valid();
d["use_orthogonal"] = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL; d["use_orthogonal"] = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
d["view_name"] = name;
d["auto_orthogonal"] = auto_orthogonal;
d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL));
if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL))) if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL)))
d["display_mode"] = VIEW_DISPLAY_NORMAL; d["display_mode"] = VIEW_DISPLAY_NORMAL;
else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME))) else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME)))
@ -3131,6 +3373,7 @@ void SpatialEditorViewport::_bind_methods() {
void SpatialEditorViewport::reset() { void SpatialEditorViewport::reset() {
orthogonal = false; orthogonal = false;
auto_orthogonal = false;
lock_rotation = false; lock_rotation = false;
message_time = 0; message_time = 0;
message = ""; message = "";
@ -3532,6 +3775,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
clicked = 0; clicked = 0;
clicked_includes_current = false; clicked_includes_current = false;
orthogonal = false; orthogonal = false;
auto_orthogonal = false;
lock_rotation = false; lock_rotation = false;
message_time = 0; message_time = 0;
zoom_indicator_delay = 0.0; zoom_indicator_delay = 0.0;
@ -3581,6 +3825,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE); view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
view_menu->get_popup()->add_check_item(TTR("Auto Orthogonal Enabled"), VIEW_AUTO_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), true);
view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION);
view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_separator();
@ -3662,16 +3908,6 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
surface->add_child(info_label); surface->add_child(info_label);
info_label->hide(); info_label->hide();
fps_label = memnew(Label);
fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN);
fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance."));
fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show.
surface->add_child(fps_label);
fps_label->hide();
cinema_label = memnew(Label); cinema_label = memnew(Label);
cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
cinema_label->set_h_grow_direction(GROW_DIRECTION_END); cinema_label->set_h_grow_direction(GROW_DIRECTION_END);
@ -3691,6 +3927,28 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
locked_label->set_text(TTR("View Rotation Locked")); locked_label->set_text(TTR("View Rotation Locked"));
locked_label->hide(); locked_label->hide();
top_right_vbox = memnew(VBoxContainer);
top_right_vbox->set_anchors_and_margins_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 2.0 * EDSCALE);
top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN);
rotation_control = memnew(ViewportRotationControl);
rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE);
rotation_control->set_h_size_flags(SIZE_SHRINK_END);
rotation_control->set_viewport(this);
top_right_vbox->add_child(rotation_control);
fps_label = memnew(Label);
fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN);
fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance."));
fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show.
top_right_vbox->add_child(fps_label);
fps_label->hide();
surface->add_child(top_right_vbox);
accept = NULL; accept = NULL;
freelook_active = false; freelook_active = false;
@ -5969,6 +6227,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,1024,1")); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,1024,1"));
EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.4); EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.4);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::REAL, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01")); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::REAL, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"));
EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true);
over_gizmo_handle = -1; over_gizmo_handle = -1;
} }

View File

@ -33,6 +33,7 @@
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_plugin.h" #include "editor/editor_plugin.h"
#include "editor/editor_scale.h"
#include "scene/3d/immediate_geometry.h" #include "scene/3d/immediate_geometry.h"
#include "scene/3d/light.h" #include "scene/3d/light.h"
#include "scene/3d/visual_instance.h" #include "scene/3d/visual_instance.h"
@ -42,6 +43,7 @@ class Camera;
class SpatialEditor; class SpatialEditor;
class EditorSpatialGizmoPlugin; class EditorSpatialGizmoPlugin;
class ViewportContainer; class ViewportContainer;
class SpatialEditorViewport;
class EditorSpatialGizmo : public SpatialGizmo { class EditorSpatialGizmo : public SpatialGizmo {
@ -138,10 +140,48 @@ public:
~EditorSpatialGizmo(); ~EditorSpatialGizmo();
}; };
class ViewportRotationControl : public Control {
GDCLASS(ViewportRotationControl, Control);
struct Axis2D {
Vector2 screen_point;
float z_axis = -99.0;
int axis = -1;
};
struct Axis2DCompare {
_FORCE_INLINE_ bool operator()(const Axis2D &l, const Axis2D &r) const {
return l.z_axis < r.z_axis;
}
};
SpatialEditorViewport *viewport = nullptr;
Vector<Color> axis_colors;
Vector<int> axis_menu_options;
bool orbiting = false;
int focused_axis = -2;
const float AXIS_CIRCLE_RADIUS = 8.0f * EDSCALE;
protected:
static void _bind_methods();
void _notification(int p_what);
void _gui_input(Ref<InputEvent> p_event);
void _draw();
void _draw_axis(const Axis2D &p_axis);
void _get_sorted_axis(Vector<Axis2D> &r_axis);
void _update_focus();
void _on_mouse_exited();
public:
void set_viewport(SpatialEditorViewport *p_viewport);
};
class SpatialEditorViewport : public Control { class SpatialEditorViewport : public Control {
GDCLASS(SpatialEditorViewport, Control); GDCLASS(SpatialEditorViewport, Control);
friend class SpatialEditor; friend class SpatialEditor;
friend class ViewportRotationControl;
enum { enum {
VIEW_TOP, VIEW_TOP,
@ -168,7 +208,8 @@ class SpatialEditorViewport : public Control {
VIEW_DISPLAY_OVERDRAW, VIEW_DISPLAY_OVERDRAW,
VIEW_DISPLAY_SHADELESS, VIEW_DISPLAY_SHADELESS,
VIEW_LOCK_ROTATION, VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW VIEW_CINEMATIC_PREVIEW,
VIEW_AUTO_ORTHOGONAL
}; };
public: public:
@ -188,6 +229,7 @@ private:
int index; int index;
String name; String name;
void _menu_option(int p_option); void _menu_option(int p_option);
void _set_auto_orthogonal();
Spatial *preview_node; Spatial *preview_node;
AABB *preview_bounds; AABB *preview_bounds;
Vector<String> selected_files; Vector<String> selected_files;
@ -211,6 +253,7 @@ private:
Camera *camera; Camera *camera;
bool transforming; bool transforming;
bool orthogonal; bool orthogonal;
bool auto_orthogonal;
bool lock_rotation; bool lock_rotation;
float gizmo_scale; float gizmo_scale;
@ -219,10 +262,13 @@ private:
TextureRect *crosshair; TextureRect *crosshair;
Label *info_label; Label *info_label;
Label *fps_label;
Label *cinema_label; Label *cinema_label;
Label *locked_label; Label *locked_label;
VBoxContainer *top_right_vbox;
ViewportRotationControl *rotation_control;
Label *fps_label;
struct _RayResult { struct _RayResult {
Spatial *item; Spatial *item;