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
36 changes: 31 additions & 5 deletions packages/core/src/core/Worlds/src/simple-camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@ import {
ModelIdMap,
} from "../../../fragments";

/**
* Move the orbit point to an anchored surface without letting the camera
* jump or the zoom decay.
*
* `CameraControls.setOrbitPoint` internally calls `dollyTo(distance)`, which
* clamps the orbit radius to `[minDistance, maxDistance]`. If the anchored
* surface sits closer than `minDistance`, that clamp would snap the camera
* backwards, so the anchor has to be placed with a near-zero `minDistance`.
*
* But `minDistance` is also what stops the zoom from decaying: with
* `dollyToCursor` + `infinityDolly` the wheel step is proportional to the
* orbit radius, and the constant-speed "infinity" push only kicks in once the
* radius reaches `minDistance`. Leaving `minDistance` near zero (as dynamic
* anchoring used to, globally) let the radius shrink toward zero on every
* zoom-in, so each notch moved exponentially less — the zoom felt like it was
* grinding to a halt until a press re-anchored it. So we drop `minDistance`
* only for the placement itself and restore it immediately, keeping the real
* `minDistance` in force for the wheel.
*/
export function setOrbitPoint(controls: CameraControls, point: THREE.Vector3) {
const { minDistance } = controls;
controls.minDistance = 0.01;
controls.setOrbitPoint(
point.x,
point.y,
point.z,
);
controls.minDistance = minDistance;
}

/**
* A basic camera that uses [yomotsu's cameracontrols](https://github.com/yomotsu/camera-controls) to control the camera in 2D and 3D. Check out it's API to find out what features it offers.
*/
Expand Down Expand Up @@ -150,11 +180,7 @@ export class SimpleCamera extends BaseCamera implements Updateable, Disposable {

async setOrbitToItems(items?: ModelIdMap) {
const sphere = await this.getItemsBounding(items);
this.controls.setOrbitPoint(
sphere.center.x,
sphere.center.y,
sphere.center.z,
);
setOrbitPoint(this.controls, sphere.center);
}

/** {@link Updateable.update} */
Expand Down
12 changes: 3 additions & 9 deletions packages/core/src/core/Worlds/src/simple-world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Updateable,
} from "../../Types";
import { Disposer } from "../../Disposer";
import { Worlds } from "..";
import { setOrbitPoint, Worlds } from "..";
import { Raycasters } from "../../Raycasters";

/**
Expand Down Expand Up @@ -70,7 +70,6 @@ export class SimpleWorld<
);
}
if (value) {
if (this.camera.controls) this.camera.controls.minDistance = 0.01;
container.addEventListener("pointerdown", this.onPointerDown);
} else {
container.removeEventListener("pointerdown", this.onPointerDown);
Expand Down Expand Up @@ -120,11 +119,7 @@ export class SimpleWorld<
// snapping around as the pointer crosses gaps between objects.
const planePoint = this.getPlaneAnchor(caster, position);
if (planePoint) {
this.camera.controls.setOrbitPoint(
planePoint.x,
planePoint.y,
planePoint.z,
);
setOrbitPoint(this.camera.controls, planePoint);
}

// Then refine to the real geometry depth once the (asynchronous) pick
Expand All @@ -136,8 +131,7 @@ export class SimpleWorld<
const isStale = requestId !== this._anchorRequestId;
if (isStale || this.isDisposing || !this._dynamicAnchor) return;
if (!result?.point || !this.camera.hasCameraControls()) return;
const { x, y, z } = result.point;
this.camera.controls.setOrbitPoint(x, y, z);
setOrbitPoint(this.camera.controls, result.point);
});
};

Expand Down