Skip to content

Commit

Permalink
Gizmo rotation fix (playcanvas#7068)
Browse files Browse the repository at this point in the history
* Added facing direction for correct rotation orientation in orthographic space

* minor refactor

* Fixed facing for screen to point rotation

* Fixed rotation direction

* Added comments to shapes look at camera

* Inverted forward to use facing

* Lint fix
  • Loading branch information
kpal81xd authored Oct 25, 2024
1 parent 54fd4fe commit 63bfc8c
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 63 deletions.
8 changes: 5 additions & 3 deletions src/extras/gizmo/gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,11 +342,13 @@ class Gizmo extends EventHandler {
* @type {Vec3}
* @protected
*/
get forward() {
get facing() {
if (this._camera.projection === PROJECTION_PERSPECTIVE) {
return tmpV2.copy(this.root.getPosition()).sub(this._camera.entity.getPosition()).normalize();
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
return tmpV2.sub2(cameraPos, gizmoPos).normalize();
}
return tmpV2.copy(this._camera.entity.forward);
return tmpV2.copy(this._camera.entity.forward).mulScalar(-1);
}

/**
Expand Down
39 changes: 16 additions & 23 deletions src/extras/gizmo/rotate-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,13 @@ class RotateGizmo extends TransformGizmo {
* @private
*/
_updateGuidePoints(angleDelta) {
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
const axis = this._selectedAxis;
const isFacing = axis === GIZMOAXIS_FACE;

tmpV1.set(0, 0, 0);
if (isFacing) {
tmpV1.sub2(cameraPos, gizmoPos).normalize();
tmpV1.copy(this.facing);
} else {
tmpV1.set(0, 0, 0);
tmpV1[axis] = 1;
this._rootStartRot.transformVector(tmpV1, tmpV1);
}
Expand Down Expand Up @@ -349,29 +347,24 @@ class RotateGizmo extends TransformGizmo {
* @private
*/
_shapesLookAtCamera() {
// face shape
if (this._camera.projection === PROJECTION_PERSPECTIVE) {
this._shapes.face.entity.lookAt(this._camera.entity.getPosition());
this._shapes.face.entity.rotateLocal(90, 0, 0);
} else {
tmpQ1.copy(this._camera.entity.getRotation());
tmpQ1.getEulerAngles(tmpV1);
tmpQ1.copy(this._camera.entity.getRotation()).getEulerAngles(tmpV1);
this._shapes.face.entity.setEulerAngles(tmpV1);
this._shapes.face.entity.rotateLocal(-90, 0, 0);
}

if (this._camera.projection === PROJECTION_PERSPECTIVE) {
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
tmpV1.sub2(cameraPos, gizmoPos).normalize();
} else {
tmpV1.copy(this._camera.entity.forward).mulScalar(-1);
}
tmpQ1.copy(this.root.getRotation()).invert().transformVector(tmpV1, tmpV1);
let angle = Math.atan2(tmpV1.z, tmpV1.y) * math.RAD_TO_DEG;
// axes shapes
const facingDir = tmpV1.copy(this.facing);
tmpQ1.copy(this.root.getRotation()).invert().transformVector(facingDir, facingDir);
let angle = Math.atan2(facingDir.z, facingDir.y) * math.RAD_TO_DEG;
this._shapes.x.entity.setLocalEulerAngles(0, angle - 90, -90);
angle = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG;
angle = Math.atan2(facingDir.x, facingDir.z) * math.RAD_TO_DEG;
this._shapes.y.entity.setLocalEulerAngles(0, angle, 0);
angle = Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG;
angle = Math.atan2(facingDir.y, facingDir.x) * math.RAD_TO_DEG;
this._shapes.z.entity.setLocalEulerAngles(90, 0, angle + 90);
}

Expand Down Expand Up @@ -411,12 +404,11 @@ class RotateGizmo extends TransformGizmo {
*/
_setNodeRotations(axis, angleDelta) {
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
const isFacing = axis === GIZMOAXIS_FACE;
for (let i = 0; i < this.nodes.length; i++) {
const node = this.nodes[i];
if (isFacing) {
tmpV1.copy(cameraPos).sub(gizmoPos).normalize();
tmpV1.copy(this._camera.entity.forward).mulScalar(-1);
} else {
tmpV1.set(0, 0, 0);
tmpV1[axis] = 1;
Expand Down Expand Up @@ -476,15 +468,16 @@ class RotateGizmo extends TransformGizmo {
plane.intersectsRay(ray, point);

// calculate angle
const facingDir = tmpV1.sub2(ray.origin, gizmoPos).normalize();
const facingDir = tmpV2.copy(this.facing);
const facingDot = plane.normal.dot(facingDir);
if (axis === GIZMOAXIS_FACE || Math.abs(facingDot) > FACING_THRESHOLD) {
// plane facing camera so based on mouse position around gizmo
tmpQ1.copy(this._camera.entity.getRotation()).invert();
tmpV1.sub2(point, gizmoPos);

// transform point so it's facing the camera
tmpV1.sub2(point, gizmoPos);
tmpQ1.transformVector(tmpV1, tmpV1);
tmpQ1.copy(this._camera.entity.getRotation()).invert().transformVector(tmpV1, tmpV1);

// calculate angle
angle = Math.sign(facingDot) * Math.atan2(tmpV1.y, tmpV1.x) * math.RAD_TO_DEG;
} else {

Expand Down
33 changes: 16 additions & 17 deletions src/extras/gizmo/scale-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,41 +372,40 @@ class ScaleGizmo extends TransformGizmo {
* @private
*/
_shapesLookAtCamera() {
const forward = this.forward;
let dot = forward.dot(this.root.right);
const facingDir = this.facing;

// axes
let dot = facingDir.dot(this.root.right);
this._shapes.x.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.x.flipped = dot > 0;
this._shapes.x.flipped = dot < 0;
}

dot = forward.dot(this.root.up);
dot = facingDir.dot(this.root.up);
this._shapes.y.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.y.flipped = dot > 0;
this._shapes.y.flipped = dot < 0;
}

dot = forward.dot(this.root.forward);
dot = facingDir.dot(this.root.forward);
this._shapes.z.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.z.flipped = dot < 0;
this._shapes.z.flipped = dot > 0;
}

tmpV1.cross(forward, this.root.right);
// planes
tmpV1.cross(facingDir, this.root.right);
this._shapes.yz.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) > 0), +(tmpV1.dot(this.root.up) > 0));
this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) < 0), +(tmpV1.dot(this.root.up) < 0));
}

tmpV1.cross(forward, this.root.forward);
tmpV1.cross(facingDir, this.root.forward);
this._shapes.xy.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) > 0), +(tmpV1.dot(this.root.right) < 0), 0);
this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) < 0), +(tmpV1.dot(this.root.right) > 0), 0);
}

tmpV1.cross(forward, this.root.up);
tmpV1.cross(facingDir, this.root.up);
this._shapes.xz.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) < 0), 0, +(tmpV1.dot(this.root.right) < 0));
this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) > 0), 0, +(tmpV1.dot(this.root.right) > 0));
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/extras/gizmo/transform-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,9 +562,7 @@ class TransformGizmo extends Gizmo {
* @protected
*/
_createPlane(axis, isFacing, isLine) {
const cameraPos = this._camera.entity.getPosition();

const facingDir = tmpV1.sub2(cameraPos, this._rootStartPos).normalize();
const facingDir = tmpV1.copy(this.facing);
const normal = tmpP1.normal.set(0, 0, 0);

if (isFacing) {
Expand Down
33 changes: 16 additions & 17 deletions src/extras/gizmo/translate-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,41 +361,40 @@ class TranslateGizmo extends TransformGizmo {
* @private
*/
_shapesLookAtCamera() {
const forward = this.forward;
let dot = forward.dot(this.root.right);
const facingDir = this.facing;

// axes
let dot = facingDir.dot(this.root.right);
this._shapes.x.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.x.flipped = dot > 0;
this._shapes.x.flipped = dot < 0;
}

dot = forward.dot(this.root.up);
dot = facingDir.dot(this.root.up);
this._shapes.y.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.y.flipped = dot > 0;
this._shapes.y.flipped = dot < 0;
}

dot = forward.dot(this.root.forward);
dot = facingDir.dot(this.root.forward);
this._shapes.z.entity.enabled = Math.abs(dot) < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.z.flipped = dot < 0;
this._shapes.z.flipped = dot > 0;
}

tmpV1.cross(forward, this.root.right);
// planes
tmpV1.cross(facingDir, this.root.right);
this._shapes.yz.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) > 0), +(tmpV1.dot(this.root.up) > 0));
this._shapes.yz.flipped = tmpV2.set(0, +(tmpV1.dot(this.root.forward) < 0), +(tmpV1.dot(this.root.up) < 0));
}

tmpV1.cross(forward, this.root.forward);
tmpV1.cross(facingDir, this.root.forward);
this._shapes.xy.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) > 0), +(tmpV1.dot(this.root.right) < 0), 0);
this._shapes.xy.flipped = tmpV2.set(+(tmpV1.dot(this.root.up) < 0), +(tmpV1.dot(this.root.right) > 0), 0);
}

tmpV1.cross(forward, this.root.up);
tmpV1.cross(facingDir, this.root.up);
this._shapes.xz.entity.enabled = tmpV1.length() < GLANCE_EPSILON;
if (this.flipShapes) {
this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) < 0), 0, +(tmpV1.dot(this.root.right) < 0));
this._shapes.xz.flipped = tmpV2.set(+(tmpV1.dot(this.root.forward) > 0), 0, +(tmpV1.dot(this.root.right) > 0));
}
}

Expand Down

0 comments on commit 63bfc8c

Please sign in to comment.