@@ -295,6 +295,7 @@ void EditorTransformGizmoND::_update_gizmo_transform() {
295295 set_visible (false );
296296 } else {
297297 set_visible (true );
298+ sum_transform->set_dimension (sum_transform->get_dimension ()); // Make sure the matrix is square.
298299 sum_transform = sum_transform->divide_scalar (double (transform_count));
299300 if (_is_use_local_rotation) {
300301 // Scale/shear/skew can mess with the rotation gizmo, so get rid of it.
@@ -583,7 +584,7 @@ void EditorTransformGizmoND::_end_transformation() {
583584 }
584585 // Create an undo/redo action for the transformation.
585586 const String action = _get_transform_part_simple_action_name (_current_transformation);
586- _undo_redo->create_action (action + String (" 4D nodes with gizmo" ));
587+ _undo_redo->create_action (action + String (" ND nodes with gizmo" ));
587588 const int size = _selected_top_nodes.size ();
588589 for (int i = 0 ; i < size; i++) {
589590 NodeND *node_nd = Object::cast_to<NodeND>(_selected_top_nodes[i]);
@@ -660,7 +661,12 @@ void EditorTransformGizmoND::_process_transform(const VectorN &p_local_ray_origi
660661 // The above position changes happen relative to the visual gizmo mesh holder, but
661662 // we want them relative to the gizmo itself. Scale/rotation should not be adjusted.
662663 transform_change->set_origin (_old_mesh_holder_transform->xform (transform_change->get_origin ()));
663- Ref<TransformND> new_transform = _old_gizmo_transform->compose_square (transform_change);
664+ transform_change->set_dimension (transform_change->get_dimension ()); // Make sure the matrix is square.
665+ Ref<TransformND> new_transform = _snap_settings->snap_transform_change (_old_gizmo_transform, transform_change);
666+ // Special case: Only in move mode, ignore any snapping that happened to the basis.
667+ if (_current_transformation == TRANSFORM_MOVE_AXIS || _current_transformation == TRANSFORM_MOVE_PLANE) {
668+ new_transform->set_basis (_old_gizmo_transform->get_basis ());
669+ }
664670 set_transform (new_transform);
665671 // We want the global diff so we can apply it from the left on the global transform of all selected nodes.
666672 // Without this, the transforms would be relative to each node (ex: moving on X moves on each node's X axis).
@@ -669,6 +675,11 @@ void EditorTransformGizmoND::_process_transform(const VectorN &p_local_ray_origi
669675 NodeND *node_nd = Object::cast_to<NodeND>(_selected_top_nodes[i]);
670676 if (node_nd != nullptr ) {
671677 node_nd->set_global_transform (transform_change->compose_square (_selected_top_node_old_transforms[i]));
678+ if (_snap_settings->get_snap_final_values ()) {
679+ node_nd->set_transform (_snap_settings->snap_single_transform (node_nd->get_transform ()));
680+ }
681+ // Note: The keep mode takes priority over snapping, so we apply it after snapping.
682+ // In most cases these won't conflict, but in an edge case where they do, keep mode wins.
672683 switch (_keep_mode) {
673684 case KeepMode::FREEFORM: {
674685 // Do nothing.
@@ -832,10 +843,10 @@ bool EditorTransformGizmoND::gizmo_mouse_input(const Ref<InputEventMouse> &p_mou
832843 const Ref<TransformND> global_to_local = _old_gizmo_transform->compose_square (_old_mesh_holder_transform)->inverse ();
833844 const VectorN local_ray_origin = global_to_local->xform (ray_origin);
834845 const VectorN local_ray_direction = global_to_local->xform_basis (ray_direction);
835- return gizmo_mouse_raycast (p_mouse_event, local_ray_origin, local_ray_direction);
846+ return _gizmo_mouse_raycast (p_mouse_event, p_camera , local_ray_origin, local_ray_direction);
836847}
837848
838- bool EditorTransformGizmoND::gizmo_mouse_raycast (const Ref<InputEventMouse> &p_mouse_event, const VectorN &p_local_ray_origin, const VectorN &p_local_ray_direction) {
849+ bool EditorTransformGizmoND::_gizmo_mouse_raycast (const Ref<InputEventMouse> &p_mouse_event, const CameraND *p_camera , const VectorN &p_local_ray_origin, const VectorN &p_local_ray_direction) {
839850 Ref<InputEventMouseButton> mouse_button = p_mouse_event;
840851 if (mouse_button.is_valid ()) {
841852 if (mouse_button->get_button_index () != MOUSE_BUTTON_LEFT) {
@@ -850,6 +861,8 @@ bool EditorTransformGizmoND::gizmo_mouse_raycast(const Ref<InputEventMouse> &p_m
850861 _primary_axis = new_primary_axis;
851862 _secondary_axis = new_secondary_axis;
852863 _begin_transformation (p_local_ray_origin, p_local_ray_direction);
864+ const double dist = VectorND::distance_to (_old_gizmo_transform->get_origin (), p_camera->get_global_position ());
865+ _snap_settings->set_camera_distance (dist);
853866 }
854867 } else if (!mouse_button->is_pressed () && _current_transformation != TRANSFORM_NONE) {
855868 // If we are transforming something and the user releases the click, end the transformation.
@@ -901,6 +914,7 @@ void EditorTransformGizmoND::setup(EditorMainScreenND *p_editor_main_screen, Edi
901914 _mesh_holder = memnew (NodeND);
902915 _mesh_holder->set_name (StringName (" GizmoMeshHolderND" ));
903916 add_child (_mesh_holder);
917+ _snap_settings = memnew (EditorTransformSnapSettingsND);
904918 RenderingServerND::get_singleton ()->connect (StringName (" pre_render" ), callable_mp (this , &EditorTransformGizmoND::_on_rendering_server_pre_render));
905919 EditorInterface::get_singleton ()->get_inspector ()->connect (StringName (" property_edited" ), callable_mp (this , &EditorTransformGizmoND::_on_editor_inspector_property_edited));
906920
@@ -909,3 +923,9 @@ void EditorTransformGizmoND::setup(EditorMainScreenND *p_editor_main_screen, Edi
909923 _undo_redo = p_undo_redo_manager;
910924 p_undo_redo_manager->connect (StringName (" version_changed" ), callable_mp (this , &EditorTransformGizmoND::_on_undo_redo_version_changed));
911925}
926+
927+ EditorTransformGizmoND::~EditorTransformGizmoND () {
928+ if (_snap_settings != nullptr ) {
929+ memdelete (_snap_settings);
930+ }
931+ }
0 commit comments