Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/src/examples/gizmos/transform-rotate.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Object.assign(cc, {
rotateDamping: 0.97,
moveDamping: 0.97,
zoomDamping: 0.97,
pitchRange: new pc.Vec2(-89.99, 89.99),
pitchRange: new pc.Vec2(-89.999, 89.999),
zoomRange: new pc.Vec2(2, 10),
enableFly: false
});
Expand Down
2 changes: 1 addition & 1 deletion examples/src/examples/gizmos/transform-scale.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Object.assign(cc, {
rotateDamping: 0.97,
moveDamping: 0.97,
zoomDamping: 0.97,
pitchRange: new pc.Vec2(-89.99, 89.99),
pitchRange: new pc.Vec2(-89.999, 89.999),
zoomRange: new pc.Vec2(2, 10),
enableFly: false
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Object.assign(cc, {
rotateDamping: 0.97,
moveDamping: 0.97,
zoomDamping: 0.97,
pitchRange: new pc.Vec2(-89.99, 89.99),
pitchRange: new pc.Vec2(-89.999, 89.999),
zoomRange: new pc.Vec2(2, 10),
enableFly: false
});
Expand Down
5 changes: 5 additions & 0 deletions src/extras/gizmo/mesh-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class MeshLine {
this.entity.setLocalPosition(dir.mulScalar(0.5 * length).add(from));
this.entity.setLocalScale(this._thickness * scale, length, this._thickness * scale);
}

destroy() {
this._material.destroy();
this.entity.destroy();
}
}

export { MeshLine };
115 changes: 58 additions & 57 deletions src/extras/gizmo/rotate-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@ const point = new Vec3();
const v1 = new Vec3();
const v2 = new Vec3();
const v3 = new Vec3();
const v4 = new Vec3();
const q1 = new Quat();
const q2 = new Quat();
const color = new Color();

// constants
const ROTATE_FACING_EPSILON = 0.1;
const RING_FACING_EPSILON = 1e-4;
const AXES = /** @type {('x' | 'y' | 'z')[]} */ (['x', 'y', 'z']);

Expand Down Expand Up @@ -122,14 +120,6 @@ class RotateGizmo extends TransformGizmo {
*/
_selectionStartAngle = 0;

/**
* Internal selection screen point in 2D space.
*
* @type {Vec2}
* @private
*/
_selectionScreenPoint = new Vec2();

/**
* Internal mapping from each attached node to their starting rotation in local space.
*
Expand All @@ -154,6 +144,22 @@ class RotateGizmo extends TransformGizmo {
*/
_nodeOffsets = new Map();

/**
* Internal vector for storing the mouse position in screen space.
*
* @type {Vec2}
* @private
*/
_screenPos = new Vec2();

/**
* Internal vector for storing the mouse start position in screen space.
*
* @type {Vec2}
* @private
*/
_screenStartPos = new Vec2();

/**
* Internal vector for the start point of the guide line angle.
*
Expand Down Expand Up @@ -222,12 +228,13 @@ class RotateGizmo extends TransformGizmo {
line.entity.enabled = false;
});

this.on(TransformGizmo.EVENT_TRANSFORMSTART, (point, x, y) => {
this.on(TransformGizmo.EVENT_TRANSFORMSTART, (_point, x, y) => {
// store start screen point
this._selectionScreenPoint.set(x, y);
this._screenPos.set(x, y);
this._screenStartPos.set(x, y);

// store start angle
this._selectionStartAngle = this._calculateArcAngle(point, x, y);
this._selectionStartAngle = this._calculateArcAngle(x, y);

// store initial node rotations
this._storeNodeRotations();
Expand All @@ -248,17 +255,19 @@ class RotateGizmo extends TransformGizmo {
return;
}

// update screen point
this._screenPos.set(x, y);

if (axis === 'xyz') {
// calculate angle axis and delta and update node rotations
const facingDir = v1.copy(this.facingDir);
const delta = v2.copy(point).sub(this._selectionStartPoint);
const angleAxis = v1.cross(facingDir, delta).normalize();

const angleDelta = screen.set(x, y).distance(this._selectionScreenPoint);
const angleDelta = this._screenPos.distance(this._screenStartPos);
this._setNodeRotations(axis, angleAxis, angleDelta);
} else {
// calculate angle axis and delta and update node rotations
let angleDelta = this._calculateArcAngle(point, x, y) - this._selectionStartAngle;
let angleDelta = this._calculateArcAngle(x, y) - this._selectionStartAngle;
if (this.snap) {
angleDelta = Math.round(angleDelta / this.snapIncrement) * this.snapIncrement;
}
Expand All @@ -269,7 +278,6 @@ class RotateGizmo extends TransformGizmo {
this._updateGuidePoints(angleDelta);
this._angleGuide(true);
}

});

this.on(TransformGizmo.EVENT_TRANSFORMEND, () => {
Expand Down Expand Up @@ -633,13 +641,12 @@ class RotateGizmo extends TransformGizmo {
}

/**
* @param {Vec3} point - The point.
* @param {number} x - The x coordinate.
* @param {number} y - The y coordinate.
* @returns {number} The angle.
* @protected
*/
_calculateArcAngle(point, x, y) {
_calculateArcAngle(x, y) {
const gizmoPos = this.root.getLocalPosition();

const axis = this._selectedAxis;
Expand All @@ -649,49 +656,34 @@ class RotateGizmo extends TransformGizmo {
let angle = 0;

// arc angle
const facingDir = v2.copy(this.facingDir);
const facingDir = this.facingDir;
const facingDot = plane.normal.dot(facingDir);
if (this.orbitRotation || (1 - Math.abs(facingDot)) < ROTATE_FACING_EPSILON) {
// plane facing camera so based on mouse position around gizmo
v1.sub2(point, gizmoPos);

switch (axis) {
case 'x': {
// convert to local space
q1.copy(this._rootStartRot).invert().transformVector(v1, v1);
angle = Math.atan2(v1.z, v1.y) * math.RAD_TO_DEG;
break;
}
case 'y': {
// convert to local space
q1.copy(this._rootStartRot).invert().transformVector(v1, v1);
angle = Math.atan2(v1.x, v1.z) * math.RAD_TO_DEG;
break;
}
case 'z': {
// convert to local space
q1.copy(this._rootStartRot).invert().transformVector(v1, v1);
angle = Math.atan2(v1.y, v1.x) * math.RAD_TO_DEG;
break;
}
case 'f': {
// convert to camera space
q1.copy(this._camera.entity.getRotation()).invert().transformVector(v1, v1);
angle = Math.sign(facingDot) * Math.atan2(v1.y, v1.x) * math.RAD_TO_DEG;
break;
}
}
if (this.orbitRotation) {
// convert gizmo position to screen space
const screenPos = this._camera.worldToScreen(gizmoPos, v1);

// calculate angle based on mouse position around gizmo
const dir = screen.set(x - screenPos.x, y - screenPos.y).normalize();
angle = Math.sign(facingDot) * Math.atan2(-dir.y, dir.x) * math.RAD_TO_DEG;
} else {
// convert rotation axis to screen space
v1.copy(gizmoPos);
v2.cross(plane.normal, facingDir).normalize().add(gizmoPos);
this._camera.worldToScreen(gizmoPos, v2);
if (axis === 'f' || facingDot > 1 - RING_FACING_EPSILON) {
// determine which size of the ring the mouse is on to flip rotation direction
v1.set(
this._screenStartPos.y >= v2.y ? 1 : -1,
this._screenStartPos.x >= v2.x ? -1 : 1,
0
).normalize();
} else {
// calculate projection vector in world space for rotation axis
const projDir = v1.cross(plane.normal, facingDir).normalize();

// convert world space vectors to screen space
this._camera.worldToScreen(v1, v3);
this._camera.worldToScreen(v2, v4);
// convert to screen space
this._camera.worldToScreen(projDir.add(gizmoPos), v3);
v1.sub2(v3, v2).normalize();
}

// angle is dot product with mouse position
v1.sub2(v4, v3).normalize();
v2.set(x, y, 0);
angle = v1.dot(v2);
}
Expand Down Expand Up @@ -735,6 +727,15 @@ class RotateGizmo extends TransformGizmo {

this._shapesLookAtCamera();
}

/**
* @override
*/
destroy() {
this._guideAngleLines.forEach(line => line.destroy());

super.destroy();
}
}

export { RotateGizmo };
24 changes: 6 additions & 18 deletions src/extras/gizmo/scale-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,36 +447,24 @@ class ScaleGizmo extends TransformGizmo {
// axes
let dot = cameraDir.dot(this.root.right);
this._shapes.x.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.x.flipped = dot < 0;
}
this._shapes.x.flipped = this.flipAxes && dot < 0;
dot = cameraDir.dot(this.root.up);
this._shapes.y.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.y.flipped = dot < 0;
}
this._shapes.y.flipped = this.flipAxes && dot < 0;
dot = cameraDir.dot(this.root.forward);
this._shapes.z.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.z.flipped = dot > 0;
}
this._shapes.z.flipped = this.flipAxes && dot > 0;

// planes
v1.cross(cameraDir, this.root.right);
this._shapes.yz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.yz.flipped = v2.set(0, +(v1.dot(this.root.forward) < 0), +(v1.dot(this.root.up) < 0));
}
this._shapes.yz.flipped = this.flipPlanes ? v2.set(0, +(v1.dot(this.root.forward) < 0), +(v1.dot(this.root.up) < 0)) : Vec3.ZERO;
v1.cross(cameraDir, this.root.forward);
this._shapes.xy.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.xy.flipped = v2.set(+(v1.dot(this.root.up) < 0), +(v1.dot(this.root.right) > 0), 0);
}
this._shapes.xy.flipped = this.flipPlanes ? v2.set(+(v1.dot(this.root.up) < 0), +(v1.dot(this.root.right) > 0), 0) : Vec3.ZERO;
v1.cross(cameraDir, this.root.up);
this._shapes.xz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.xz.flipped = v2.set(+(v1.dot(this.root.forward) > 0), 0, +(v1.dot(this.root.right) > 0));
}
this._shapes.xz.flipped = this.flipPlanes ? v2.set(+(v1.dot(this.root.forward) > 0), 0, +(v1.dot(this.root.right) > 0)) : Vec3.ZERO;
}

/**
Expand Down
24 changes: 6 additions & 18 deletions src/extras/gizmo/translate-gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,36 +427,24 @@ class TranslateGizmo extends TransformGizmo {
// axes
let dot = cameraDir.dot(this.root.right);
this._shapes.x.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.x.flipped = dot < 0;
}
this._shapes.x.flipped = this.flipAxes && dot < 0;
dot = cameraDir.dot(this.root.up);
this._shapes.y.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.y.flipped = dot < 0;
}
this._shapes.y.flipped = this.flipAxes && dot < 0;
dot = cameraDir.dot(this.root.forward);
this._shapes.z.entity.enabled = 1 - Math.abs(dot) > GLANCE_EPSILON;
if (this.flipAxes) {
this._shapes.z.flipped = dot > 0;
}
this._shapes.z.flipped = this.flipAxes && dot > 0;

// planes
v1.cross(cameraDir, this.root.right);
this._shapes.yz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.yz.flipped = v2.set(0, +(v1.dot(this.root.forward) < 0), +(v1.dot(this.root.up) < 0));
}
this._shapes.yz.flipped = this.flipPlanes ? v2.set(0, +(v1.dot(this.root.forward) < 0), +(v1.dot(this.root.up) < 0)) : Vec3.ZERO;
v1.cross(cameraDir, this.root.forward);
this._shapes.xy.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.xy.flipped = v2.set(+(v1.dot(this.root.up) < 0), +(v1.dot(this.root.right) > 0), 0);
}
this._shapes.xy.flipped = this.flipPlanes ? v2.set(+(v1.dot(this.root.up) < 0), +(v1.dot(this.root.right) > 0), 0) : Vec3.ZERO;
v1.cross(cameraDir, this.root.up);
this._shapes.xz.entity.enabled = 1 - v1.length() > GLANCE_EPSILON;
if (this.flipPlanes) {
this._shapes.xz.flipped = v2.set(+(v1.dot(this.root.forward) > 0), 0, +(v1.dot(this.root.right) > 0));
}
this._shapes.xz.flipped = this.flipPlanes ? v2.set(+(v1.dot(this.root.forward) > 0), 0, +(v1.dot(this.root.right) > 0)) : Vec3.ZERO;
}

/**
Expand Down