Merge pull request #53684 from TokageItLab/orthogonal-mode

This commit is contained in:
Rémi Verschelde 2022-01-05 16:05:40 +01:00 committed by GitHub
commit 6af77c7b09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 164 additions and 87 deletions

View file

@ -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);

View file

@ -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

View file

@ -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);
} }

View file

@ -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 {

View file

@ -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: {

View file

@ -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 (p_local) { if (_edit.snap || spatial_editor->is_snap_enabled()) {
Basis g = p_original.basis.orthonormalized(); p_motion.snap(Vector3(p_extra, p_extra, p_extra));
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;
} else {
Transform3D base = Transform3D(Basis(), _edit.center);
if (_edit.snap || spatial_editor->is_snap_enabled()) {
p_motion.snap(Vector3(p_extra, p_extra, p_extra));
}
Transform3D global_t;
global_t.basis.scale(p_motion + Vector3(1, 1, 1));
return base * (global_t * (base.inverse() * p_original));
} }
Transform3D s;
if (p_local) {
s.basis = p_original_local.basis.scaled_local(p_motion + Vector3(1, 1, 1));
s.origin = p_original_local.origin;
} else {
s.basis.scale(p_motion + Vector3(1, 1, 1));
Transform3D base = Transform3D(Basis(), _edit.center);
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();
}
}
return s;
} }
case TRANSFORM_TRANSLATE: { case TRANSFORM_TRANSLATE: {
if (_edit.snap || spatial_editor->is_snap_enabled()) {
p_motion.snap(Vector3(p_extra, p_extra, p_extra));
}
if (p_local) { if (p_local) {
if (_edit.snap || spatial_editor->is_snap_enabled()) { p_motion = p_original.basis.xform(p_motion);
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);
}
} else {
if (_edit.snap || spatial_editor->is_snap_enabled()) {
p_motion.snap(Vector3(p_extra, p_extra, p_extra));
}
} }
// 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'");
@ -1480,6 +1474,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();
@ -1783,30 +1778,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); 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), _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:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0); 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), _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:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1); 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), _edit.center); plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
plane_mv = true; plane_mv = true;
break; break;
} }
@ -1857,6 +1852,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) {
@ -1877,14 +1873,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);
} }
} }
@ -1904,27 +1900,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;
} }
@ -1956,6 +1952,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) {
@ -1976,12 +1973,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);
} }
} }
@ -2000,15 +1997,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:
@ -2063,14 +2060,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);
} }
} }
@ -3676,8 +3673,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) {
@ -3694,18 +3689,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));
} }
@ -4915,7 +4918,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++;
} }
@ -4940,7 +4942,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++;
} }
@ -5815,6 +5816,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) {
@ -5852,16 +5859,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);

View file

@ -399,7 +399,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);

View file

@ -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();
} }