diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 67079a69657..2e8ae1a286d 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1041,24 +1041,40 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { int col_axis = -1; - float col_d = 1e20; - for (int i = 0; i < 3; i++) { - Plane plane(gt.basis.get_axis(i).normalized(), gt.origin); - Vector3 r; - if (!plane.intersects_ray(ray_pos, ray, &r)) { - continue; + Vector3 hit_position; + Vector3 hit_normal; + real_t ray_length = gt.origin.distance_to(ray_pos) + (GIZMO_CIRCLE_SIZE * gizmo_scale) * 4.0f; + if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * ray_length, gt.origin, gizmo_scale * (GIZMO_CIRCLE_SIZE), &hit_position, &hit_normal)) { + if (hit_normal.dot(_get_camera_normal()) < 0.05) { + hit_position = gt.xform_inv(hit_position).abs(); + int min_axis = hit_position.min_axis_index(); + if (hit_position[min_axis] < gizmo_scale * GIZMO_RING_HALF_WIDTH) { + col_axis = min_axis; + } } + } - const real_t dist = r.distance_to(gt.origin); - const Vector3 r_dir = (r - gt.origin).normalized(); + if (col_axis == -1) { + float col_d = 1e20; - if (_get_camera_normal().dot(r_dir) <= 0.005) { - if (dist > gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gizmo_scale * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { - const real_t d = ray_pos.distance_to(r); - if (d < col_d) { - col_d = d; - col_axis = i; + for (int i = 0; i < 3; i++) { + Plane plane(gt.basis.get_axis(i).normalized(), gt.origin); + Vector3 r; + if (!plane.intersects_ray(ray_pos, ray, &r)) { + continue; + } + + const real_t dist = r.distance_to(gt.origin); + const Vector3 r_dir = (r - gt.origin).normalized(); + + if (_get_camera_normal().dot(r_dir) <= 0.005) { + if (dist > gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gizmo_scale * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) { + const real_t d = ray_pos.distance_to(r); + if (d < col_d) { + col_d = d; + col_axis = i; + } } } } @@ -2730,7 +2746,7 @@ void Node3DEditorViewport::_draw() { font->draw_string(ci, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1)); } - if (_edit.mode == TRANSFORM_ROTATE) { + if (_edit.mode == TRANSFORM_ROTATE && _edit.show_rotation_line) { Point2 center = _point_to_screen(_edit.center); Color handle_color; @@ -4278,24 +4294,23 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { } break; case TRANSFORM_ROTATE: { - Plane plane; - Vector3 axis; + Plane plane = Plane(_get_camera_normal(), _edit.center); + Vector3 local_axis; + Vector3 global_axis; switch (_edit.plane) { case TRANSFORM_VIEW: - plane = Plane(_get_camera_normal(), _edit.center); + // local_axis unused + global_axis = _get_camera_normal(); break; case TRANSFORM_X_AXIS: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center); - axis = Vector3(1, 0, 0); + local_axis = Vector3(1, 0, 0); break; case TRANSFORM_Y_AXIS: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center); - axis = Vector3(0, 1, 0); + local_axis = Vector3(0, 1, 0); break; case TRANSFORM_Z_AXIS: - plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center); - axis = Vector3(0, 0, 1); + local_axis = Vector3(0, 0, 1); break; case TRANSFORM_YZ: case TRANSFORM_XZ: @@ -4303,6 +4318,10 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { break; } + if (_edit.plane != TRANSFORM_VIEW) { + global_axis = spatial_editor->get_gizmo_transform().basis.xform(local_axis).normalized(); + } + Vector3 intersection; if (!plane.intersects_ray(ray_pos, ray, &intersection)) { break; @@ -4313,10 +4332,22 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { break; } - Vector3 y_axis = (click - _edit.center).normalized(); - Vector3 x_axis = plane.normal.cross(y_axis).normalized(); + static const float orthogonal_threshold = Math::cos(Math::deg2rad(87.0f)); + bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold; - double angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center)); + double angle = 0.0f; + if (axis_is_orthogonal) { + _edit.show_rotation_line = false; + Vector3 projection_axis = plane.normal.cross(global_axis); + Vector3 delta = intersection - click; + float projection = delta.dot(projection_axis); + angle = (projection * (Math_PI / 2.0f)) / (gizmo_scale * GIZMO_CIRCLE_SIZE); + } else { + _edit.show_rotation_line = true; + Vector3 click_axis = (click - _edit.center).normalized(); + Vector3 current_axis = (intersection - _edit.center).normalized(); + angle = click_axis.signed_angle_to(current_axis, global_axis); + } if (_edit.snap || spatial_editor->is_snap_enabled()) { snap = spatial_editor->get_rotate_snap(); @@ -4344,7 +4375,7 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { continue; } - Vector3 compute_axis = local_coords ? axis : plane.normal; + Vector3 compute_axis = local_coords ? local_axis : global_axis; if (se->gizmo.is_valid()) { for (KeyValue &GE : se->subgizmos) { Transform3D xform = GE.value; @@ -4377,6 +4408,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito _edit.mode = TRANSFORM_NONE; _edit.plane = TRANSFORM_VIEW; _edit.snap = true; + _edit.show_rotation_line = true; _edit.instant = false; _edit.gizmo_handle = -1; _edit.gizmo_handle_secondary = false; diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 5c5980eb88a..bbe56155708 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -311,6 +311,7 @@ private: Point2 mouse_pos; Point2 original_mouse_pos; bool snap = false; + bool show_rotation_line = false; Ref gizmo; int gizmo_handle = 0; bool gizmo_handle_secondary = false;