Skip to content

Commit

Permalink
Camera controls focus damping (#7313)
Browse files Browse the repository at this point in the history
* Added separate focusing damping

* Adjusted default focus Damping

* Updated focus damping description
  • Loading branch information
kpal81xd authored Jan 29, 2025
1 parent be27bc1 commit 292802d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 33 deletions.
12 changes: 12 additions & 0 deletions examples/src/examples/camera/multi.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
link: { observer, path: 'attr.enableFly' }
})
),
jsx(
LabelGroup,
{ text: 'Focus damping' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'attr.focusDamping' },
min: 0,
max: 0.999,
step: 0.001,
precision: 3
})
),
jsx(
LabelGroup,
{ text: 'Pitch range' },
Expand Down
1 change: 1 addition & 0 deletions examples/src/examples/camera/multi.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ data.set('attr', [
'enableOrbit',
'enablePan',
'enableFly',
'focusDamping',
'pitchRange',
'rotateSpeed',
'rotateDamping',
Expand Down
12 changes: 12 additions & 0 deletions examples/src/examples/camera/orbit.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
link: { observer, path: 'attr.enablePan' }
})
),
jsx(
LabelGroup,
{ text: 'Focus damping' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'attr.focusDamping' },
min: 0,
max: 0.999,
step: 0.001,
precision: 3
})
),
jsx(
LabelGroup,
{ text: 'Pitch range' },
Expand Down
1 change: 1 addition & 0 deletions examples/src/examples/camera/orbit.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ data.set('example', {

data.set('attr', [
'enablePan',
'focusDamping',
'pitchRange',
'rotateSpeed',
'rotateDamping',
Expand Down
4 changes: 3 additions & 1 deletion examples/src/examples/misc/editor.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ app.root.addChild(camera);
const cameraControls = /** @type {CameraControls} */ (camera.script.create(CameraControls, {
properties: {
focusPoint: pc.Vec3.ZERO,
sceneSize: 5
sceneSize: 5,
rotateDamping: 0,
moveDamping: 0
}
}));
app.on('gizmo:pointer', (/** @type {boolean} */ hasPointer) => {
Expand Down
118 changes: 86 additions & 32 deletions scripts/esm/camera-controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ const tmpQ1 = new Quat();
const tmpR1 = new Ray();
const tmpP1 = new Plane();

/** @type {AddEventListenerOptions & EventListenerOptions} */
const PASSIVE = { passive: false };
const ZOOM_SCALE_SCENE_MULT = 10;
const EPSILON = 0.0001;

/**
* Calculate the lerp rate.
Expand Down Expand Up @@ -55,7 +57,7 @@ class CameraControls extends Script {

/**
* @private
* @type {CameraComponent}
* @type {CameraComponent | null}
*/
_camera = null;

Expand Down Expand Up @@ -155,6 +157,12 @@ class CameraControls extends Script {
*/
_moving = false;

/**
* @type {boolean}
* @private
*/
_focusing = false;

/**
* @type {Record<string, boolean>}
* @private
Expand Down Expand Up @@ -222,6 +230,15 @@ class CameraControls extends Script {
*/
enableFly = true;

/**
* @attribute
* @title Focus Damping
* @description The damping applied when calling {@link CameraControls#focus}. A higher value means
* more damping. A value of 0 means no damping.
* @type {number}
*/
focusDamping = 0.98;

/**
* @attribute
* @title Rotate Speed
Expand All @@ -236,7 +253,7 @@ class CameraControls extends Script {
* @description The rotation damping. A higher value means more damping. A value of 0 means no damping.
* @type {number}
*/
rotateDamping = 0.97;
rotateDamping = 0.98;

/**
* @attribute
Expand Down Expand Up @@ -334,7 +351,9 @@ class CameraControls extends Script {

const camera = this._camera;
this.detach();
this.attach(camera);
if (camera) {
this.attach(camera);
}
}

get element() {
Expand Down Expand Up @@ -547,6 +566,11 @@ class CameraControls extends Script {
const startFly = this._isStartFly(event);
const startOrbit = this._isStartOrbit(event);

if (this._focusing) {
this._cancelSmoothTransform();
this._focusing = false;
}

if (startTouchPan) {
// start touch pan
this._lastPinchDist = this._getPinchDist();
Expand Down Expand Up @@ -768,6 +792,11 @@ class CameraControls extends Script {

// clamp movement if locked
if (this._moving) {
if (this._focusing) {
this._cancelSmoothTransform();
this._focusing = false;
}

this._clampPosition(this._origin);
}
}
Expand Down Expand Up @@ -874,12 +903,34 @@ class CameraControls extends Script {
* @param {number} dt - The delta time.
*/
_smoothTransform(dt) {
const ar = dt === -1 ? 1 : lerpRate(this.rotateDamping, dt);
const am = dt === -1 ? 1 : lerpRate(this.moveDamping, dt);
const ar = dt === -1 ? 1 : lerpRate(this._focusing ? this.focusDamping : this.rotateDamping, dt);
const am = dt === -1 ? 1 : lerpRate(this._focusing ? this.focusDamping : this.moveDamping, dt);
this._angles.x = math.lerp(this._angles.x, this._dir.x, ar);
this._angles.y = math.lerp(this._angles.y, this._dir.y, ar);
this._position.lerp(this._position, this._origin, am);
this._baseTransform.setTRS(this._position, tmpQ1.setFromEulerAngles(this._angles), Vec3.ONE);

const focusDelta = this._position.distance(this._origin) +
Math.abs(this._angles.x - this._dir.x) +
Math.abs(this._angles.y - this._dir.y);
if (this._focusing && focusDelta < EPSILON) {
this._focusing = false;
}
}

/**
* @private
*/
_cancelSmoothZoom() {
this._cameraDist = this._zoomDist;
}

/**
* @private
*/
_cancelSmoothTransform() {
this._origin.copy(this._position);
this._dir.set(this._angles.x, this._angles.y);
}

/**
Expand All @@ -894,8 +945,8 @@ class CameraControls extends Script {
/**
* Focus the camera on a point.
*
* @param {Vec3} point - The point.
* @param {Vec3} [start] - The start.
* @param {Vec3} point - The focus point.
* @param {Vec3} [start] - The camera start position.
* @param {boolean} [smooth] - Whether to smooth the focus.
*/
focus(point, start, smooth = true) {
Expand All @@ -905,35 +956,39 @@ class CameraControls extends Script {
if (this._flying) {
return;
}
if (!start) {
this._origin.copy(point);
if (!smooth) {
this._position.copy(point);
}
return;
}

tmpV1.sub2(start, point);
const elev = Math.atan2(tmpV1.y, Math.sqrt(tmpV1.x * tmpV1.x + tmpV1.z * tmpV1.z)) * math.RAD_TO_DEG;
const azim = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG;
this._clampAngles(this._dir.set(-elev, azim));
if (start) {
tmpV1.sub2(start, point);
const elev = Math.atan2(tmpV1.y, Math.sqrt(tmpV1.x * tmpV1.x + tmpV1.z * tmpV1.z)) * math.RAD_TO_DEG;
const azim = Math.atan2(tmpV1.x, tmpV1.z) * math.RAD_TO_DEG;
this._clampAngles(this._dir.set(-elev, azim));

this._origin.copy(point);
this._origin.copy(point);

this._cameraTransform.setTranslate(0, 0, 0);
this._cameraTransform.setTranslate(0, 0, 0);

const pos = this.entity.getPosition();
const rot = this.entity.getRotation();
this._baseTransform.setTRS(pos, rot, Vec3.ONE);
const pos = this.entity.getPosition();
const rot = this.entity.getRotation();
this._baseTransform.setTRS(pos, rot, Vec3.ONE);

this._zoomDist = this._clampZoom(tmpV1.length());
this._zoomDist = this._clampZoom(tmpV1.length());

if (!smooth) {
this._smoothZoom(-1);
this._smoothTransform(-1);
if (!smooth) {
this._smoothZoom(-1);
this._smoothTransform(-1);
}

this._updateTransform();
} else {
this._origin.copy(point);
if (!smooth) {
this._position.copy(point);
}
}

this._updateTransform();
if (smooth) {
this._focusing = true;
}
}

/**
Expand All @@ -957,7 +1012,7 @@ class CameraControls extends Script {
* @param {number} [zoomDist] - The zoom distance.
* @param {boolean} [smooth] - Whether to smooth the refocus.
*/
refocus(point, start = null, zoomDist, smooth = true) {
refocus(point, start, zoomDist, smooth = true) {
if (typeof zoomDist === 'number') {
this.resetZoom(zoomDist, smooth);
}
Expand Down Expand Up @@ -1003,9 +1058,8 @@ class CameraControls extends Script {

this._camera = null;

this._dir.x = this._angles.x;
this._dir.y = this._angles.y;
this._origin.copy(this._position);
this._cancelSmoothZoom();
this._cancelSmoothTransform();

this._pointerEvents.clear();
this._lastPinchDist = -1;
Expand Down

0 comments on commit 292802d

Please sign in to comment.