@@ -116,6 +116,13 @@ constexpr real_t GIZMO_VIEW_ROTATION_SIZE = 1.25;
116116constexpr real_t GIZMO_SCALE_OFFSET = GIZMO_CIRCLE_SIZE + 0.3 ;
117117constexpr real_t GIZMO_ARROW_OFFSET = GIZMO_CIRCLE_SIZE + 0.3 ;
118118
119+ constexpr real_t TRACKBALL_SENSITIVITY = 0.005 ;
120+ constexpr int TRACKBALL_SPHERE_RINGS = 16 ;
121+ constexpr int TRACKBALL_SPHERE_SECTORS = 32 ;
122+ constexpr real_t TRACKBALL_HIGHLIGHT_ALPHA = 0.01 ;
123+ constexpr int GIZMO_HIGHLIGHT_AXIS_VIEW_ROTATION = 15 ;
124+ constexpr int GIZMO_HIGHLIGHT_AXIS_TRACKBALL = 16 ;
125+
119126constexpr real_t ZOOM_FREELOOK_MIN = 0.01 ;
120127constexpr real_t ZOOM_FREELOOK_MULTIPLIER = 1.08 ;
121128constexpr real_t ZOOM_FREELOOK_INDICATOR_DELAY_S = 1.5 ;
@@ -1400,6 +1407,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
14001407 if (spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_ROTATE ) {
14011408 int col_axis = -1 ;
14021409 bool view_rotation_selected = false ;
1410+ bool trackball_selected = false ;
14031411
14041412 Vector3 hit_position;
14051413 Vector3 hit_normal;
@@ -1451,12 +1459,14 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
14511459 if (Math::abs (distance_ray_to_center - view_rotation_radius) < circumference_tolerance &&
14521460 ray_length_to_center > 0 ) {
14531461 view_rotation_selected = true ;
1462+ } else if (distance_ray_to_center < gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH ) && ray_length_to_center > 0 ) {
1463+ trackball_selected = true ;
14541464 }
14551465 }
14561466
14571467 if (view_rotation_selected) {
14581468 if (p_highlight_only) {
1459- spatial_editor->select_gizmo_highlight_axis (15 );
1469+ spatial_editor->select_gizmo_highlight_axis (GIZMO_HIGHLIGHT_AXIS_VIEW_ROTATION );
14601470 } else {
14611471 _edit.mode = TRANSFORM_ROTATE ;
14621472 _compute_edit (p_screenpos);
@@ -1467,6 +1477,21 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
14671477 _edit.gizmo_initiated = true ;
14681478 }
14691479 return true ;
1480+ } else if (trackball_selected) {
1481+ if (p_highlight_only) {
1482+ spatial_editor->select_gizmo_highlight_axis (GIZMO_HIGHLIGHT_AXIS_TRACKBALL );
1483+ } else {
1484+ _edit.mode = TRANSFORM_ROTATE ;
1485+ _compute_edit (p_screenpos);
1486+ _edit.plane = TRANSFORM_VIEW ;
1487+ _edit.is_trackball = true ;
1488+ _edit.show_rotation_line = false ;
1489+ _edit.accumulated_rotation_angle = 0.0 ;
1490+ _edit.rotation_axis = _get_camera_normal ();
1491+ _edit.gizmo_initiated = true ;
1492+ spatial_editor->select_gizmo_highlight_axis (-1 );
1493+ }
1494+ return true ;
14701495 } else if (col_axis != -1 ) {
14711496 if (p_highlight_only) {
14721497 spatial_editor->select_gizmo_highlight_axis (col_axis + 3 );
@@ -3749,7 +3774,7 @@ void Node3DEditorViewport::_draw() {
37493774 break ;
37503775 }
37513776
3752- if (_is_rotation_arc_visible () && !_edit.initial_click_vector .is_zero_approx ()) {
3777+ if (!_edit. is_trackball && _is_rotation_arc_visible () && !_edit.initial_click_vector .is_zero_approx ()) {
37533778 Vector3 up = _edit.rotation_axis ;
37543779 Vector3 right = _edit.initial_click_vector ;
37553780
@@ -4456,6 +4481,16 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
44564481 RS::get_singleton ()->instance_geometry_set_flag (rotate_gizmo_instance[i], RS ::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING , true );
44574482 RS::get_singleton ()->instance_geometry_set_flag (rotate_gizmo_instance[i], RS ::INSTANCE_FLAG_USE_BAKED_LIGHT , false );
44584483 }
4484+
4485+ // Create trackball sphere instance
4486+ trackball_sphere_instance = RS::get_singleton ()->instance_create ();
4487+ RS::get_singleton ()->instance_set_base (trackball_sphere_instance, spatial_editor->get_trackball_sphere_gizmo ()->get_rid ());
4488+ RS::get_singleton ()->instance_set_scenario (trackball_sphere_instance, get_tree ()->get_root ()->get_world_3d ()->get_scenario ());
4489+ RS::get_singleton ()->instance_set_visible (trackball_sphere_instance, false );
4490+ RS::get_singleton ()->instance_geometry_set_cast_shadows_setting (trackball_sphere_instance, RS ::SHADOW_CASTING_SETTING_OFF );
4491+ RS::get_singleton ()->instance_set_layer_mask (trackball_sphere_instance, layer);
4492+ RS::get_singleton ()->instance_geometry_set_flag (trackball_sphere_instance, RS ::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING , true );
4493+ RS::get_singleton ()->instance_geometry_set_flag (trackball_sphere_instance, RS ::INSTANCE_FLAG_USE_BAKED_LIGHT , false );
44594494}
44604495
44614496void Node3DEditorViewport::_finish_gizmo_instances () {
@@ -4470,6 +4505,8 @@ void Node3DEditorViewport::_finish_gizmo_instances() {
44704505 }
44714506 // Rotation white outline
44724507 RS::get_singleton ()->free_rid (rotate_gizmo_instance[3 ]);
4508+
4509+ RS::get_singleton ()->free_rid (trackball_sphere_instance);
44734510}
44744511
44754512void Node3DEditorViewport::_toggle_camera_preview (bool p_activate) {
@@ -4608,6 +4645,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
46084645
46094646 bool keep_gizmo_visible = arc_visible && ((local_coords && (show_gizmo_flags & Node3DEditor::TRANSFORM_MODE_LOCAL )) || (!local_coords && (show_gizmo_flags & Node3DEditor::TRANSFORM_MODE_GLOBAL )));
46104647 bool hide_gizmo_during_rotation = arc_visible && !keep_gizmo_visible;
4648+ bool hide_gizmo_during_trackball = (_edit.mode == TRANSFORM_ROTATE && _edit.is_trackball );
46114649
46124650 int arc_replaces_ring = -1 ;
46134651 if (keep_gizmo_visible) {
@@ -4629,7 +4667,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
46294667 }
46304668 }
46314669
4632- bool show_gizmo = spatial_editor->is_gizmo_visible () && !_edit.instant && transform_gizmo_visible && !collision_reposition && !hide_gizmo_during_rotation;
4670+ bool show_gizmo = spatial_editor->is_gizmo_visible () && !_edit.instant && transform_gizmo_visible && !collision_reposition && !hide_gizmo_during_rotation && !hide_gizmo_during_trackball ;
46334671 bool show_rotate_gizmo = show_gizmo && (spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_ROTATE );
46344672
46354673 for (int i = 0 ; i < 3 ; i++) {
@@ -4660,7 +4698,12 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
46604698 RenderingServer::get_singleton ()->instance_set_transform (rotate_gizmo_instance[3 ], view_rotation_xform);
46614699 RenderingServer::get_singleton ()->instance_set_visible (rotate_gizmo_instance[3 ], show_rotate_gizmo && arc_replaces_ring != 3 );
46624700
4663- bool show_axes = spatial_editor->is_gizmo_visible () && _edit.mode != TRANSFORM_NONE ;
4701+ bool can_show_trackball = spatial_editor->is_gizmo_visible () && !_edit.instant && transform_gizmo_visible && !collision_reposition && !hide_gizmo_during_rotation;
4702+ bool show_trackball_sphere = can_show_trackball && (spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode () == Node3DEditor::TOOL_MODE_ROTATE ) && !hide_gizmo_during_trackball;
4703+ RenderingServer::get_singleton ()->instance_set_transform (trackball_sphere_instance, view_rotation_xform);
4704+ RenderingServer::get_singleton ()->instance_set_visible (trackball_sphere_instance, show_trackball_sphere);
4705+
4706+ bool show_axes = spatial_editor->is_gizmo_visible () && _edit.mode != TRANSFORM_NONE && !hide_gizmo_during_trackball;
46644707 RenderingServer *rs = RenderingServer::get_singleton ();
46654708 rs->instance_set_visible (axis_gizmo_instance[0 ], show_axes && (_edit.plane == TRANSFORM_X_AXIS || _edit.plane == TRANSFORM_XY || _edit.plane == TRANSFORM_XZ ));
46664709 rs->instance_set_visible (axis_gizmo_instance[1 ], show_axes && (_edit.plane == TRANSFORM_Y_AXIS || _edit.plane == TRANSFORM_XY || _edit.plane == TRANSFORM_YZ ));
@@ -5867,6 +5910,36 @@ void Node3DEditorViewport::update_transform(bool p_shift) {
58675910 plane = Plane (_get_camera_normal (), _edit.center );
58685911 }
58695912
5913+ if (_edit.is_trackball ) {
5914+ Vector2 motion_delta = _edit.mouse_pos - _edit.original_mouse_pos ;
5915+ real_t sensitivity = TRACKBALL_SENSITIVITY * EDSCALE ;
5916+ Vector2 rotation_input = motion_delta * sensitivity;
5917+
5918+ Transform3D cam_transform = to_camera_transform (cursor);
5919+ Vector3 cam_right = cam_transform.basis .get_column (0 ).normalized ();
5920+ Vector3 cam_up = cam_transform.basis .get_column (1 ).normalized ();
5921+ Vector3 rotation_axis = cam_up * rotation_input.x + cam_right * rotation_input.y ;
5922+
5923+ real_t rotation_angle = rotation_axis.length ();
5924+ if (rotation_angle > 0 .0f ) {
5925+ rotation_axis /= rotation_angle;
5926+
5927+ bool snapping = _edit.snap || spatial_editor->is_snap_enabled ();
5928+ if (snapping) {
5929+ double snap_step = spatial_editor->get_rotate_snap ();
5930+ double angle_deg = Math::rad_to_deg (rotation_angle);
5931+ angle_deg = Math::snapped (angle_deg, snap_step);
5932+ rotation_angle = Math::deg_to_rad (angle_deg);
5933+ }
5934+
5935+ double angle_deg = Math::rad_to_deg (rotation_angle);
5936+ set_message (vformat (TTR (" Rotating %s degrees." ), String::num (angle_deg, 2 )));
5937+
5938+ apply_transform (rotation_axis, rotation_angle);
5939+ }
5940+ break ;
5941+ }
5942+
58705943 Vector3 local_axis;
58715944 Vector3 global_axis;
58725945 switch (_edit.plane ) {
@@ -6029,6 +6102,7 @@ void Node3DEditorViewport::finish_transform() {
60296102 _edit.numeric_input = 0 ;
60306103 _edit.numeric_next_decimal = 0 ;
60316104 _edit.numeric_negate = false ;
6105+ _edit.is_trackball = false ;
60326106 _edit.initial_click_vector = Vector3 ();
60336107 _edit.previous_rotation_vector = Vector3 ();
60346108 _edit.accumulated_rotation_angle = 0.0 ;
@@ -6840,12 +6914,15 @@ void Node3DEditor::select_gizmo_highlight_axis(int p_axis) {
68406914 for (int i = 0 ; i < 4 ; i++) {
68416915 bool highlight;
68426916 if (i == 3 ) {
6843- highlight = (p_axis == 15 );
6917+ highlight = (p_axis == GIZMO_HIGHLIGHT_AXIS_VIEW_ROTATION );
68446918 } else {
68456919 highlight = (i + 3 ) == p_axis;
68466920 }
68476921 rotate_gizmo[i]->surface_set_material (0 , highlight ? rotate_gizmo_color_hl[i] : rotate_gizmo_color[i]);
68486922 }
6923+
6924+ bool highlight_trackball = (p_axis == GIZMO_HIGHLIGHT_AXIS_TRACKBALL );
6925+ trackball_sphere_gizmo->surface_set_material (0 , highlight_trackball ? trackball_sphere_material_hl : trackball_sphere_material);
68496926}
68506927
68516928void Node3DEditor::update_transform_gizmo () {
@@ -8228,6 +8305,62 @@ void fragment() {
82288305 }
82298306 }
82308307
8308+ // Create trackball sphere
8309+ {
8310+ trackball_sphere_gizmo.instantiate ();
8311+ Ref<SurfaceTool> surftool;
8312+ surftool.instantiate ();
8313+ surftool->begin (Mesh::PRIMITIVE_TRIANGLES );
8314+
8315+ const int sphere_rings = TRACKBALL_SPHERE_RINGS ;
8316+ const int sphere_sectors = TRACKBALL_SPHERE_SECTORS ;
8317+ const real_t sphere_radius = GIZMO_CIRCLE_SIZE ;
8318+
8319+ for (int r = 0 ; r <= sphere_rings; ++r) {
8320+ for (int s = 0 ; s <= sphere_sectors; ++s) {
8321+ real_t ring_angle = Math::PI * r / sphere_rings;
8322+ real_t sector_angle = 2.0 * Math::PI * s / sphere_sectors;
8323+
8324+ Vector3 vertex (
8325+ sphere_radius * Math::sin (ring_angle) * Math::cos (sector_angle),
8326+ sphere_radius * Math::cos (ring_angle),
8327+ sphere_radius * Math::sin (ring_angle) * Math::sin (sector_angle));
8328+
8329+ surftool->set_normal (vertex.normalized ());
8330+ surftool->add_vertex (vertex);
8331+ }
8332+ }
8333+
8334+ for (int r = 0 ; r < sphere_rings; ++r) {
8335+ for (int s = 0 ; s < sphere_sectors; ++s) {
8336+ int current = r * (sphere_sectors + 1 ) + s;
8337+ int next = current + sphere_sectors + 1 ;
8338+
8339+ surftool->add_index (current);
8340+ surftool->add_index (next);
8341+ surftool->add_index (current + 1 );
8342+
8343+ surftool->add_index (current + 1 );
8344+ surftool->add_index (next);
8345+ surftool->add_index (next + 1 );
8346+ }
8347+ }
8348+
8349+ trackball_sphere_material.instantiate ();
8350+ trackball_sphere_material->set_shading_mode (StandardMaterial3D::SHADING_MODE_UNSHADED );
8351+ trackball_sphere_material->set_flag (StandardMaterial3D::FLAG_DISABLE_FOG , true );
8352+ trackball_sphere_material->set_transparency (StandardMaterial3D::TRANSPARENCY_ALPHA );
8353+ trackball_sphere_material->set_cull_mode (StandardMaterial3D::CULL_DISABLED );
8354+ trackball_sphere_material->set_albedo (Color (1.0 , 1.0 , 1.0 , 0.0 ));
8355+ trackball_sphere_material->set_flag (StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST , true );
8356+
8357+ trackball_sphere_material_hl = trackball_sphere_material->duplicate ();
8358+ trackball_sphere_material_hl->set_albedo (Color (1.0 , 1.0 , 1.0 , TRACKBALL_HIGHLIGHT_ALPHA ));
8359+
8360+ surftool->set_material (trackball_sphere_material);
8361+ surftool->commit (trackball_sphere_gizmo);
8362+ }
8363+
82318364 _generate_selection_boxes ();
82328365}
82338366
0 commit comments