Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gizmo fixes #6763

Merged
merged 11 commits into from
Jun 28, 2024
3 changes: 2 additions & 1 deletion examples/src/examples/misc/gizmos.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ data.set('camera', {
});
const camera = new pc.Entity('camera');
camera.addComponent('camera', {
clearColor: new pc.Color(0.1, 0.1, 0.1)
clearColor: new pc.Color(0.1, 0.1, 0.1),
farClip: 1000
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why won't the example work with the default far clip?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does it you can just see the the axis lines clipping when zooming out

});
camera.addComponent('script');
const orbitCamera = camera.script.create('orbitCamera');
Expand Down
2 changes: 1 addition & 1 deletion src/extras/gizmo/axis-shapes.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const SHADER = {
// store z/w for later use in fragment shader
vZW = gl_Position.zw;
// disable depth clipping
gl_Position.z = 0.0;
// gl_Position.z = 0.0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks cleaner when clipping with this so would prefer to have it in but the selection area and rendered area do not align

}`,
frag: /* glsl */`
precision highp float;
Expand Down
135 changes: 75 additions & 60 deletions src/extras/gizmo/gizmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,6 @@ class Gizmo extends EventHandler {
*/
static EVENT_RENDERUPDATE = 'render:update';

/**
* Internal device start size.
*
* @type {number}
* @private
*/
_deviceStartSize;

/**
* Internal version of the gizmo size. Defaults to 1.
*
Expand Down Expand Up @@ -254,40 +246,18 @@ class Gizmo extends EventHandler {

this._app = app;
this._device = app.graphicsDevice;
this._deviceStartSize = Math.max(this._device.width, this._device.height);
this._camera = camera;
this._layer = layer;

this._createGizmo();
this.root = new Entity('gizmo');
this._app.root.addChild(this.root);
this.root.enabled = false;

this._updateScale();

this._onPointerDown = (e) => {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
const selection = this._getSelection(e.offsetX, e.offsetY);
if (selection[0]) {
e.preventDefault();
}
this.fire(Gizmo.EVENT_POINTERDOWN, e.offsetX, e.offsetY, selection[0]);
};
this._onPointerMove = (e) => {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
const selection = this._getSelection(e.offsetX, e.offsetY);
if (selection[0]) {
e.preventDefault();
}
this.fire(Gizmo.EVENT_POINTERMOVE, e.offsetX, e.offsetY, selection[0]);
};
this._onPointerUp = (e) => {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
this.fire(Gizmo.EVENT_POINTERUP);
};
this._onPointerDown = this._onPointerDown.bind(this);
this._onPointerMove = this._onPointerMove.bind(this);
this._onPointerUp = this._onPointerUp.bind(this);

this._device.canvas.addEventListener('pointerdown', this._onPointerDown);
this._device.canvas.addEventListener('pointermove', this._onPointerMove);
Expand Down Expand Up @@ -341,19 +311,49 @@ class Gizmo extends EventHandler {
return this._size;
}

_getProjFrustumWidth() {
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
const dist = tmpV1.copy(gizmoPos).sub(cameraPos).dot(this._camera.entity.forward);
return dist * Math.tan(0.5 * this._camera.fov * math.DEG_TO_RAD);
/**
* @param {PointerEvent} e - The pointer event.
* @private
*/
_onPointerDown(e) {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
const selection = this._getSelection(e.offsetX, e.offsetY);
if (selection[0]) {
e.preventDefault();
}
this.fire(Gizmo.EVENT_POINTERDOWN, e.offsetX, e.offsetY, selection[0]);
}

_createGizmo() {
this.root = new Entity('gizmo');
this._app.root.addChild(this.root);
this.root.enabled = false;
/**
* @param {PointerEvent} e - The pointer event.
* @private
*/
_onPointerMove(e) {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
const selection = this._getSelection(e.offsetX, e.offsetY);
if (selection[0]) {
e.preventDefault();
}
this.fire(Gizmo.EVENT_POINTERMOVE, e.offsetX, e.offsetY, selection[0]);
}

/**
* @private
*/
_onPointerUp() {
if (!this.root.enabled || document.pointerLockElement) {
return;
}
this.fire(Gizmo.EVENT_POINTERUP);
}

/**
* @protected
*/
_updatePosition() {
tmpV1.set(0, 0, 0);
for (let i = 0; i < this.nodes.length; i++) {
Expand All @@ -366,6 +366,9 @@ class Gizmo extends EventHandler {
this.fire(Gizmo.EVENT_POSITIONUPDATE, tmpV1);
}

/**
* @protected
*/
_updateRotation() {
tmpV1.set(0, 0, 0);
if (this._coordSpace === GIZMO_LOCAL && this.nodes.length !== 0) {
Expand All @@ -376,13 +379,15 @@ class Gizmo extends EventHandler {
this.fire(Gizmo.EVENT_ROTATIONUPDATE, tmpV1);
}

/**
* @protected
*/
_updateScale() {
if (this._camera.projection === PROJECTION_PERSPECTIVE) {
let canvasMult = 1;
if (this._device.width > 0 && this._device.height > 0) {
canvasMult = this._deviceStartSize / Math.min(this._device.width, this._device.height);
}
this._scale = this._getProjFrustumWidth() * canvasMult * PERS_SCALE_RATIO;
const gizmoPos = this.root.getPosition();
const cameraPos = this._camera.entity.getPosition();
const dist = gizmoPos.distance(cameraPos);
this._scale = Math.tan(0.5 * this._camera.fov * math.DEG_TO_RAD) * dist * PERS_SCALE_RATIO;
} else {
this._scale = this._camera.orthoHeight * ORTHO_SCALE_RATIO;
}
Expand All @@ -392,27 +397,37 @@ class Gizmo extends EventHandler {
this.fire(Gizmo.EVENT_SCALEUPDATE, this._scale);
}

/**
* @param {number} x - The x coordinate.
* @param {number} y - The y coordinate.
* @returns {import('../../scene/mesh-instance.js').MeshInstance[]} - The mesh instances.
* @private
*/
_getSelection(x, y) {
const start = this._camera.screenToWorld(x, y, 1);
const start = this._camera.screenToWorld(x, y, this._camera.nearClip);
const end = this._camera.screenToWorld(x, y, this._camera.farClip);
const dir = end.clone().sub(start).normalize();

const selection = [];
for (let i = 0; i < this.intersectData.length; i++) {
const { triData, parent, meshInstances } = this.intersectData[i];
const wtm = parent.getWorldTransform().clone();
const parentTM = parent.getWorldTransform();
for (let j = 0; j < triData.length; j++) {
const { tris, ptm, priority } = triData[j];
tmpM1.copy(wtm).mul(ptm);
tmpM2.copy(tmpM1).invert();
tmpM2.transformPoint(start, tmpR1.origin);
tmpM2.transformVector(dir, tmpR1.direction);
tmpR1.direction.normalize();
const { tris, transform, priority } = triData[j];

// combine node world transform with transform of tri relative to parent
const triWTM = tmpM1.copy(parentTM).mul(transform);
const invTriWTM = tmpM2.copy(triWTM).invert();

const ray = tmpR1;
invTriWTM.transformPoint(start, ray.origin);
invTriWTM.transformVector(dir, ray.direction);
ray.direction.normalize();

for (let k = 0; k < tris.length; k++) {
if (tris[k].intersectsRay(tmpR1, tmpV1)) {
if (tris[k].intersectsRay(ray, tmpV1)) {
selection.push({
dist: tmpM1.transformPoint(tmpV1).sub(start).length(),
dist: triWTM.transformPoint(tmpV1).sub(start).length(),
meshInstances: meshInstances,
priority: priority
});
Expand Down
Loading