Browse Source

Fix some gizmo behavior to make more consistent

Silc 'Tokage' Renew 3 years ago
parent
commit
61759da5b3

+ 39 - 0
core/math/basis.cpp

@@ -94,6 +94,18 @@ Basis Basis::orthonormalized() const {
 	return c;
 	return c;
 }
 }
 
 
+void Basis::orthogonalize() {
+	Vector3 scl = get_scale();
+	orthonormalize();
+	scale_local(scl);
+}
+
+Basis Basis::orthogonalized() const {
+	Basis c = *this;
+	c.orthogonalize();
+	return c;
+}
+
 bool Basis::is_orthogonal() const {
 bool Basis::is_orthogonal() const {
 	Basis identity;
 	Basis identity;
 	Basis m = (*this) * transposed();
 	Basis m = (*this) * transposed();
@@ -237,6 +249,24 @@ void Basis::scale_local(const Vector3 &p_scale) {
 	*this = scaled_local(p_scale);
 	*this = scaled_local(p_scale);
 }
 }
 
 
+void Basis::scale_orthogonal(const Vector3 &p_scale) {
+	*this = scaled_orthogonal(p_scale);
+}
+
+Basis Basis::scaled_orthogonal(const Vector3 &p_scale) const {
+	Basis m = *this;
+	Vector3 s = Vector3(-1, -1, -1) + p_scale;
+	Vector3 dots;
+	Basis b;
+	for (int i = 0; i < 3; i++) {
+		for (int j = 0; j < 3; j++) {
+			dots[j] += s[i] * abs(m.get_axis(i).normalized().dot(b.get_axis(j)));
+		}
+	}
+	m.scale_local(Vector3(1, 1, 1) + dots);
+	return m;
+}
+
 float Basis::get_uniform_scale() const {
 float Basis::get_uniform_scale() const {
 	return (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
 	return (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
 }
 }
@@ -931,6 +961,15 @@ void Basis::_set_diagonal(const Vector3 &p_diag) {
 	elements[2][2] = p_diag.z;
 	elements[2][2] = p_diag.z;
 }
 }
 
 
+Basis Basis::lerp(const Basis &p_to, const real_t &p_weight) const {
+	Basis b;
+	b.elements[0] = elements[0].lerp(p_to.elements[0], p_weight);
+	b.elements[1] = elements[1].lerp(p_to.elements[1], p_weight);
+	b.elements[2] = elements[2].lerp(p_to.elements[2], p_weight);
+
+	return b;
+}
+
 Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const {
 Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const {
 	//consider scale
 	//consider scale
 	Quaternion from(*this);
 	Quaternion from(*this);

+ 7 - 0
core/math/basis.h

@@ -123,6 +123,9 @@ public:
 	void scale_local(const Vector3 &p_scale);
 	void scale_local(const Vector3 &p_scale);
 	Basis scaled_local(const Vector3 &p_scale) const;
 	Basis scaled_local(const Vector3 &p_scale) const;
 
 
+	void scale_orthogonal(const Vector3 &p_scale);
+	Basis scaled_orthogonal(const Vector3 &p_scale) const;
+
 	void make_scale_uniform();
 	void make_scale_uniform();
 	float get_uniform_scale() const;
 	float get_uniform_scale() const;
 
 
@@ -168,6 +171,7 @@ public:
 	bool is_diagonal() const;
 	bool is_diagonal() const;
 	bool is_rotation() const;
 	bool is_rotation() const;
 
 
+	Basis lerp(const Basis &p_to, const real_t &p_weight) const;
 	Basis slerp(const Basis &p_to, const real_t &p_weight) const;
 	Basis slerp(const Basis &p_to, const real_t &p_weight) const;
 	void rotate_sh(real_t *p_values);
 	void rotate_sh(real_t *p_values);
 
 
@@ -233,6 +237,9 @@ public:
 	void orthonormalize();
 	void orthonormalize();
 	Basis orthonormalized() const;
 	Basis orthonormalized() const;
 
 
+	void orthogonalize();
+	Basis orthogonalized() const;
+
 #ifdef MATH_CHECKS
 #ifdef MATH_CHECKS
 	bool is_symmetric() const;
 	bool is_symmetric() const;
 #endif
 #endif

+ 22 - 2
core/math/transform_3d.cpp

@@ -80,9 +80,11 @@ void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, con
 	origin = p_eye;
 	origin = p_eye;
 }
 }
 
 
-Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const {
+Transform3D Transform3D::sphere_interpolate_with(const Transform3D &p_transform, real_t p_c) const {
 	/* not sure if very "efficient" but good enough? */
 	/* not sure if very "efficient" but good enough? */
 
 
+	Transform3D interp;
+
 	Vector3 src_scale = basis.get_scale();
 	Vector3 src_scale = basis.get_scale();
 	Quaternion src_rot = basis.get_rotation_quaternion();
 	Quaternion src_rot = basis.get_rotation_quaternion();
 	Vector3 src_loc = origin;
 	Vector3 src_loc = origin;
@@ -91,13 +93,21 @@ Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t
 	Quaternion dst_rot = p_transform.basis.get_rotation_quaternion();
 	Quaternion dst_rot = p_transform.basis.get_rotation_quaternion();
 	Vector3 dst_loc = p_transform.origin;
 	Vector3 dst_loc = p_transform.origin;
 
 
-	Transform3D interp;
 	interp.basis.set_quaternion_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.lerp(dst_scale, p_c));
 	interp.basis.set_quaternion_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.lerp(dst_scale, p_c));
 	interp.origin = src_loc.lerp(dst_loc, p_c);
 	interp.origin = src_loc.lerp(dst_loc, p_c);
 
 
 	return interp;
 	return interp;
 }
 }
 
 
+Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const {
+	Transform3D interp;
+
+	interp.basis = basis.lerp(p_transform.basis, p_c);
+	interp.origin = origin.lerp(p_transform.origin, p_c);
+
+	return interp;
+}
+
 void Transform3D::scale(const Vector3 &p_scale) {
 void Transform3D::scale(const Vector3 &p_scale) {
 	basis.scale(p_scale);
 	basis.scale(p_scale);
 	origin *= p_scale;
 	origin *= p_scale;
@@ -139,6 +149,16 @@ Transform3D Transform3D::orthonormalized() const {
 	return _copy;
 	return _copy;
 }
 }
 
 
+void Transform3D::orthogonalize() {
+	basis.orthogonalize();
+}
+
+Transform3D Transform3D::orthogonalized() const {
+	Transform3D _copy = *this;
+	_copy.orthogonalize();
+	return _copy;
+}
+
 bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
 bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
 	return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
 	return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
 }
 }

+ 3 - 0
core/math/transform_3d.h

@@ -69,6 +69,8 @@ public:
 
 
 	void orthonormalize();
 	void orthonormalize();
 	Transform3D orthonormalized() const;
 	Transform3D orthonormalized() const;
+	void orthogonalize();
+	Transform3D orthogonalized() const;
 	bool is_equal_approx(const Transform3D &p_transform) const;
 	bool is_equal_approx(const Transform3D &p_transform) const;
 
 
 	bool operator==(const Transform3D &p_transform) const;
 	bool operator==(const Transform3D &p_transform) const;
@@ -99,6 +101,7 @@ public:
 	void operator*=(const real_t p_val);
 	void operator*=(const real_t p_val);
 	Transform3D operator*(const real_t p_val) const;
 	Transform3D operator*(const real_t p_val) const;
 
 
+	Transform3D sphere_interpolate_with(const Transform3D &p_transform, real_t p_c) const;
 	Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const;
 	Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const;
 
 
 	_FORCE_INLINE_ Transform3D inverse_xform(const Transform3D &t) const {
 	_FORCE_INLINE_ Transform3D inverse_xform(const Transform3D &t) const {

+ 1 - 1
core/variant/variant_setget.cpp

@@ -2120,7 +2120,7 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &
 		}
 		}
 			return;
 			return;
 		case BASIS: {
 		case BASIS: {
-			r_dst = Transform3D(*a._data._basis).interpolate_with(Transform3D(*b._data._basis), c).basis;
+			r_dst = a._data._basis->lerp(*b._data._basis, c);
 		}
 		}
 			return;
 			return;
 		case TRANSFORM3D: {
 		case TRANSFORM3D: {

+ 76 - 79
editor/plugins/node_3d_editor_plugin.cpp

@@ -954,7 +954,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
 		real_t col_d = 1e20;
 		real_t col_d = 1e20;
 
 
 		for (int i = 0; i < 3; i++) {
 		for (int i = 0; i < 3; i++) {
-			const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gizmo_scale * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5));
+			const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i).normalized() * gizmo_scale * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5));
 			const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE;
 			const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE;
 
 
 			Vector3 r;
 			Vector3 r;
@@ -1058,7 +1058,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
 		float col_d = 1e20;
 		float col_d = 1e20;
 
 
 		for (int i = 0; i < 3; i++) {
 		for (int i = 0; i < 3; i++) {
-			const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gizmo_scale * GIZMO_SCALE_OFFSET;
+			const Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i).normalized() * gizmo_scale * GIZMO_SCALE_OFFSET;
 			const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE;
 			const real_t grabber_radius = gizmo_scale * GIZMO_ARROW_SIZE;
 
 
 			Vector3 r;
 			Vector3 r;
@@ -1138,68 +1138,62 @@ void Node3DEditorViewport::_transform_gizmo_apply(Node3D *p_node, const Transfor
 	}
 	}
 }
 }
 
 
-Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local) {
+Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local, bool p_orthogonal) {
 	switch (p_mode) {
 	switch (p_mode) {
 		case TRANSFORM_SCALE: {
 		case TRANSFORM_SCALE: {
+			if (_edit.snap || spatial_editor->is_snap_enabled()) {
+				p_motion.snap(Vector3(p_extra, p_extra, p_extra));
+			}
+			Transform3D s;
 			if (p_local) {
 			if (p_local) {
-				Basis g = p_original.basis.orthonormalized();
-				Vector3 local_motion = g.inverse().xform(p_motion);
-
-				if (_edit.snap || spatial_editor->is_snap_enabled()) {
-					local_motion.snap(Vector3(p_extra, p_extra, p_extra));
-				}
-
-				Transform3D local_t;
-				local_t.basis = p_original_local.basis.scaled_local(local_motion + Vector3(1, 1, 1));
-				local_t.origin = p_original_local.origin;
-				return local_t;
+				s.basis = p_original_local.basis.scaled_local(p_motion + Vector3(1, 1, 1));
+				s.origin = p_original_local.origin;
 			} else {
 			} else {
+				s.basis.scale(p_motion + Vector3(1, 1, 1));
 				Transform3D base = Transform3D(Basis(), _edit.center);
 				Transform3D base = Transform3D(Basis(), _edit.center);
-				if (_edit.snap || spatial_editor->is_snap_enabled()) {
-					p_motion.snap(Vector3(p_extra, p_extra, p_extra));
+				s = base * (s * (base.inverse() * p_original));
+
+				// Recalculate orthogonalized scale without moving origin.
+				if (p_orthogonal) {
+					s.basis = p_original_local.basis.scaled_orthogonal(p_motion + Vector3(1, 1, 1));
+					// The scaled_orthogonal() does not require orthogonal Basis,
+					// but it may make a bit skew by precision problems.
+					s.basis.orthogonalize();
 				}
 				}
-
-				Transform3D global_t;
-				global_t.basis.scale(p_motion + Vector3(1, 1, 1));
-				return base * (global_t * (base.inverse() * p_original));
 			}
 			}
+
+			return s;
 		}
 		}
 		case TRANSFORM_TRANSLATE: {
 		case TRANSFORM_TRANSLATE: {
-			if (p_local) {
-				if (_edit.snap || spatial_editor->is_snap_enabled()) {
-					Basis g = p_original.basis.orthonormalized();
-					Vector3 local_motion = g.inverse().xform(p_motion);
-					local_motion.snap(Vector3(p_extra, p_extra, p_extra));
-
-					p_motion = g.xform(local_motion);
-				}
+			if (_edit.snap || spatial_editor->is_snap_enabled()) {
+				p_motion.snap(Vector3(p_extra, p_extra, p_extra));
+			}
 
 
-			} else {
-				if (_edit.snap || spatial_editor->is_snap_enabled()) {
-					p_motion.snap(Vector3(p_extra, p_extra, p_extra));
-				}
+			if (p_local) {
+				p_motion = p_original.basis.xform(p_motion);
 			}
 			}
 
 
 			// Apply translation
 			// Apply translation
 			Transform3D t = p_original;
 			Transform3D t = p_original;
 			t.origin += p_motion;
 			t.origin += p_motion;
+
 			return t;
 			return t;
 		}
 		}
 		case TRANSFORM_ROTATE: {
 		case TRANSFORM_ROTATE: {
+			Transform3D r;
+
 			if (p_local) {
 			if (p_local) {
-				Transform3D r;
 				Vector3 axis = p_original_local.basis.xform(p_motion);
 				Vector3 axis = p_original_local.basis.xform(p_motion);
 				r.basis = Basis(axis.normalized(), p_extra) * p_original_local.basis;
 				r.basis = Basis(axis.normalized(), p_extra) * p_original_local.basis;
 				r.origin = p_original_local.origin;
 				r.origin = p_original_local.origin;
-				return r;
 			} else {
 			} else {
-				Transform3D r;
 				Basis local = p_original.basis * p_original_local.basis.inverse();
 				Basis local = p_original.basis * p_original_local.basis.inverse();
 				Vector3 axis = local.xform_inv(p_motion);
 				Vector3 axis = local.xform_inv(p_motion);
 				r.basis = local * Basis(axis.normalized(), p_extra) * p_original_local.basis;
 				r.basis = local * Basis(axis.normalized(), p_extra) * p_original_local.basis;
 				r.origin = Basis(p_motion, p_extra).xform(p_original.origin - _edit.center) + _edit.center;
 				r.origin = Basis(p_motion, p_extra).xform(p_original.origin - _edit.center) + _edit.center;
-				return r;
 			}
 			}
+
+			return r;
 		}
 		}
 		default: {
 		default: {
 			ERR_FAIL_V_MSG(Transform3D(), "Invalid mode in '_compute_transform'");
 			ERR_FAIL_V_MSG(Transform3D(), "Invalid mode in '_compute_transform'");
@@ -1479,6 +1473,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 					_edit.original_mouse_pos = b->get_position();
 					_edit.original_mouse_pos = b->get_position();
 					_edit.snap = spatial_editor->is_snap_enabled();
 					_edit.snap = spatial_editor->is_snap_enabled();
 					_edit.mode = TRANSFORM_NONE;
 					_edit.mode = TRANSFORM_NONE;
+					_edit.original = spatial_editor->get_gizmo_transform(); // To prevent to break when flipping with scale.
 
 
 					bool can_select_gizmos = spatial_editor->get_single_selected_node();
 					bool can_select_gizmos = spatial_editor->get_single_selected_node();
 
 
@@ -1782,30 +1777,30 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 								plane = Plane(_get_camera_normal(), _edit.center);
 								plane = Plane(_get_camera_normal(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_X_AXIS:
 							case TRANSFORM_X_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_Y_AXIS:
 							case TRANSFORM_Y_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_Z_AXIS:
 							case TRANSFORM_Z_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_YZ:
 							case TRANSFORM_YZ:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 							case TRANSFORM_XZ:
 							case TRANSFORM_XZ:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0);
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 							case TRANSFORM_XY:
 							case TRANSFORM_XY:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 						}
 						}
@@ -1856,6 +1851,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 						// This might not be necessary anymore after issue #288 is solved (in 4.0?).
 						// This might not be necessary anymore after issue #288 is solved (in 4.0?).
 						set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
 						set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
 								String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
 								String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+						motion = _edit.original.basis.inverse().xform(motion);
 
 
 						List<Node *> &selection = editor_selection->get_selected_node_list();
 						List<Node *> &selection = editor_selection->get_selected_node_list();
 						for (Node *E : selection) {
 						for (Node *E : selection) {
@@ -1876,14 +1872,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 							if (se->gizmo.is_valid()) {
 							if (se->gizmo.is_valid()) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 									Transform3D xform = GE.value;
 									Transform3D xform = GE.value;
-									Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords);
+									Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
 									if (!local_coords) {
 									if (!local_coords) {
 										new_xform = se->original.affine_inverse() * new_xform;
 										new_xform = se->original.affine_inverse() * new_xform;
 									}
 									}
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 								}
 								}
 							} else {
 							} else {
-								Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords);
+								Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
 								_transform_gizmo_apply(se->sp, new_xform, local_coords);
 								_transform_gizmo_apply(se->sp, new_xform, local_coords);
 							}
 							}
 						}
 						}
@@ -1903,27 +1899,27 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 								plane = Plane(_get_camera_normal(), _edit.center);
 								plane = Plane(_get_camera_normal(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_X_AXIS:
 							case TRANSFORM_X_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_Y_AXIS:
 							case TRANSFORM_Y_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_Z_AXIS:
 							case TRANSFORM_Z_AXIS:
-								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
+								motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_YZ:
 							case TRANSFORM_YZ:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 							case TRANSFORM_XZ:
 							case TRANSFORM_XZ:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 							case TRANSFORM_XY:
 							case TRANSFORM_XY:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
 								plane_mv = true;
 								plane_mv = true;
 								break;
 								break;
 						}
 						}
@@ -1955,6 +1951,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 						motion_snapped.snap(Vector3(snap, snap, snap));
 						motion_snapped.snap(Vector3(snap, snap, snap));
 						set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
 						set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
 								String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
 								String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+						motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion);
 
 
 						List<Node *> &selection = editor_selection->get_selected_node_list();
 						List<Node *> &selection = editor_selection->get_selected_node_list();
 						for (Node *E : selection) {
 						for (Node *E : selection) {
@@ -1975,12 +1972,12 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 							if (se->gizmo.is_valid()) {
 							if (se->gizmo.is_valid()) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 									Transform3D xform = GE.value;
 									Transform3D xform = GE.value;
-									Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords);
+									Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
 									new_xform = se->original.affine_inverse() * new_xform;
 									new_xform = se->original.affine_inverse() * new_xform;
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 								}
 								}
 							} else {
 							} else {
-								Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords);
+								Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
 								_transform_gizmo_apply(se->sp, new_xform, false);
 								_transform_gizmo_apply(se->sp, new_xform, false);
 							}
 							}
 						}
 						}
@@ -1999,15 +1996,15 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 								plane = Plane(_get_camera_normal(), _edit.center);
 								plane = Plane(_get_camera_normal(), _edit.center);
 								break;
 								break;
 							case TRANSFORM_X_AXIS:
 							case TRANSFORM_X_AXIS:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
 								axis = Vector3(1, 0, 0);
 								axis = Vector3(1, 0, 0);
 								break;
 								break;
 							case TRANSFORM_Y_AXIS:
 							case TRANSFORM_Y_AXIS:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
 								axis = Vector3(0, 1, 0);
 								axis = Vector3(0, 1, 0);
 								break;
 								break;
 							case TRANSFORM_Z_AXIS:
 							case TRANSFORM_Z_AXIS:
-								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
+								plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
 								axis = Vector3(0, 0, 1);
 								axis = Vector3(0, 0, 1);
 								break;
 								break;
 							case TRANSFORM_YZ:
 							case TRANSFORM_YZ:
@@ -2062,14 +2059,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 								for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
 									Transform3D xform = GE.value;
 									Transform3D xform = GE.value;
 
 
-									Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords);
+									Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo.
 									if (!local_coords) {
 									if (!local_coords) {
 										new_xform = se->original.affine_inverse() * new_xform;
 										new_xform = se->original.affine_inverse() * new_xform;
 									}
 									}
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 									se->gizmo->set_subgizmo_transform(GE.key, new_xform);
 								}
 								}
 							} else {
 							} else {
-								Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords);
+								Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
 								_transform_gizmo_apply(se->sp, new_xform, local_coords);
 								_transform_gizmo_apply(se->sp, new_xform, local_coords);
 							}
 							}
 						}
 						}
@@ -3671,8 +3668,6 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
 			subviewport_container->get_stretch_shrink();
 			subviewport_container->get_stretch_shrink();
 	Vector3 scale = Vector3(1, 1, 1) * gizmo_scale;
 	Vector3 scale = Vector3(1, 1, 1) * gizmo_scale;
 
 
-	xform.basis.scale(scale);
-
 	// if the determinant is zero, we should disable the gizmo from being rendered
 	// if the determinant is zero, we should disable the gizmo from being rendered
 	// this prevents supplying bad values to the renderer and then having to filter it out again
 	// this prevents supplying bad values to the renderer and then having to filter it out again
 	if (xform.basis.determinant() == 0) {
 	if (xform.basis.determinant() == 0) {
@@ -3689,18 +3684,26 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
 	}
 	}
 
 
 	for (int i = 0; i < 3; i++) {
 	for (int i = 0; i < 3; i++) {
-		RenderingServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform);
+		Transform3D axis_angle = Transform3D();
+		if (xform.basis.get_axis(i).normalized().dot(xform.basis.get_axis((i + 1) % 3).normalized()) < 1.0) {
+			axis_angle = axis_angle.looking_at(xform.basis.get_axis(i).normalized(), xform.basis.get_axis((i + 1) % 3).normalized());
+		}
+		axis_angle.basis.scale(scale);
+		axis_angle.origin = xform.origin;
+		RenderingServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], axis_angle);
 		RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
 		RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
-		RenderingServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform);
+		RenderingServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], axis_angle);
 		RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
 		RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
-		RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform);
+		RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], axis_angle);
 		RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
 		RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
-		RenderingServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
+		RenderingServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], axis_angle);
 		RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
 		RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
-		RenderingServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
+		RenderingServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], axis_angle);
 		RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
 		RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
 	}
 	}
 	// Rotation white outline
 	// Rotation white outline
+	xform.orthonormalize();
+	xform.basis.scale(scale);
 	RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[3], xform);
 	RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[3], xform);
 	RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
 	RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE));
 }
 }
@@ -4862,7 +4865,6 @@ void Node3DEditor::update_transform_gizmo() {
 			gizmo_center += xf.origin;
 			gizmo_center += xf.origin;
 			if (count == 0 && local_gizmo_coords) {
 			if (count == 0 && local_gizmo_coords) {
 				gizmo_basis = xf.basis;
 				gizmo_basis = xf.basis;
-				gizmo_basis.orthonormalize();
 			}
 			}
 			count++;
 			count++;
 		}
 		}
@@ -4887,7 +4889,6 @@ void Node3DEditor::update_transform_gizmo() {
 			gizmo_center += xf.origin;
 			gizmo_center += xf.origin;
 			if (count == 0 && local_gizmo_coords) {
 			if (count == 0 && local_gizmo_coords) {
 				gizmo_basis = xf.basis;
 				gizmo_basis = xf.basis;
-				gizmo_basis.orthonormalize();
 			}
 			}
 			count++;
 			count++;
 		}
 		}
@@ -5762,6 +5763,12 @@ void fragment() {
 	{
 	{
 		//move gizmo
 		//move gizmo
 
 
+		// Inverted zxy.
+		Vector3 ivec = Vector3(0, 0, -1);
+		Vector3 nivec = Vector3(-1, -1, 0);
+		Vector3 ivec2 = Vector3(-1, 0, 0);
+		Vector3 ivec3 = Vector3(0, -1, 0);
+
 		for (int i = 0; i < 3; i++) {
 		for (int i = 0; i < 3; i++) {
 			Color col;
 			Color col;
 			switch (i) {
 			switch (i) {
@@ -5799,16 +5806,6 @@ void fragment() {
 			mat_hl->set_albedo(albedo);
 			mat_hl->set_albedo(albedo);
 			gizmo_color_hl[i] = mat_hl;
 			gizmo_color_hl[i] = mat_hl;
 
 
-			Vector3 ivec;
-			ivec[i] = 1;
-			Vector3 nivec;
-			nivec[(i + 1) % 3] = 1;
-			nivec[(i + 2) % 3] = 1;
-			Vector3 ivec2;
-			ivec2[(i + 1) % 3] = 1;
-			Vector3 ivec3;
-			ivec3[(i + 2) % 3] = 1;
-
 			//translate
 			//translate
 			{
 			{
 				Ref<SurfaceTool> surftool = memnew(SurfaceTool);
 				Ref<SurfaceTool> surftool = memnew(SurfaceTool);

+ 1 - 1
editor/plugins/node_3d_editor_plugin.h

@@ -395,7 +395,7 @@ private:
 
 
 	void _project_settings_changed();
 	void _project_settings_changed();
 
 
-	Transform3D _compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local);
+	Transform3D _compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local, bool p_orthogonal);
 
 
 protected:
 protected:
 	void _notification(int p_what);
 	void _notification(int p_what);

+ 11 - 0
scene/3d/node_3d.cpp

@@ -84,6 +84,9 @@ void Node3D::_notify_dirty() {
 }
 }
 
 
 void Node3D::_update_local_transform() const {
 void Node3D::_update_local_transform() const {
+	if (this->get_rotation_edit_mode() != ROTATION_EDIT_MODE_BASIS) {
+		data.local_transform = data.local_transform.orthogonalized();
+	}
 	data.local_transform.basis.set_euler_scale(data.rotation, data.scale);
 	data.local_transform.basis.set_euler_scale(data.rotation, data.scale);
 
 
 	data.dirty &= ~DIRTY_LOCAL;
 	data.dirty &= ~DIRTY_LOCAL;
@@ -321,6 +324,14 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
 		return;
 		return;
 	}
 	}
 	data.rotation_edit_mode = p_mode;
 	data.rotation_edit_mode = p_mode;
+
+	// Shearing is not allowed except in ROTATION_EDIT_MODE_BASIS.
+	data.dirty |= DIRTY_LOCAL;
+	_propagate_transform_changed(this);
+	if (data.notify_local_transform) {
+		notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+	}
+
 	notify_property_list_changed();
 	notify_property_list_changed();
 }
 }