mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 11:31:27 +00:00
ui-pagescene: snapshot+restore camera quat across pointer-lock unlock to stop rotation snap
When the user left/middle-click drags to rotate the camera and releases, Chromium synthesizes one final `mousemove` while exiting pointer lock so it can reposition the cursor back to where the user grabbed it. At that exact moment, Three's PointerLockControls still has `isLocked === true` (the flag flips after the unlock event dispatches), so its mousemove handler reads the synthesized `movementX/Y` as user input and rotates the camera by it — producing a visible lurch back by roughly the inverse of the user's last drag delta. Capture the camera quaternion in `MouseControls.onMouseUp` immediately before `lockControls.unlock()`. In a real (non-debug) `unlock` listener attached at editor init, copy the snapshot back into the camera and call `updateMatrixWorld()`. By the time `unlock` fires, the synthetic move has already been applied, so overwriting is the surgical fix at the boundaries we actually own — no Three.js subclassing, no per-frame state machine. Trace that confirmed the cause: [mc] up button=1 locked=true quat=(-0.2684, 0.1394, 0.0393, 0.9524) [cam-frame] quat=(-0.2684, 0.1394, 0.0393, 0.9524) [plc] unlock quat=(-0.2180, 0.5112, 0.1358, 0.8202)
This commit is contained in:
1 parent
1ea94e4b42
commit
edd900d29b
2 files changed
+34
No files matched your search
@@ -425,6 +425,25 @@ class Editor {
|
||||
this.cameraController.camera,
|
||||
this.renderer.domElement,
|
||||
);
|
||||
// Restore the quaternion captured at mouseup. Chromium synthesizes
|
||||
// one final `mousemove` while exiting pointer lock so it can
|
||||
// reposition the cursor; PLC reads that synthesized delta and
|
||||
// rotates the camera by it because `isLocked` is still true at
|
||||
// that moment. By the time the `unlock` event fires here, that
|
||||
// synthetic move has already been applied — overwriting it with
|
||||
// the pre-unlock snapshot keeps the camera at the orientation the
|
||||
// user actually released on.
|
||||
this.cameraController.lockControls.addEventListener("unlock", () => {
|
||||
const snap = this.mouse_controls?.quatAtUnlockRequest;
|
||||
const cam = this.cameraController.camera;
|
||||
if (snap && cam) {
|
||||
cam.quaternion.copy(snap);
|
||||
cam.updateMatrixWorld();
|
||||
}
|
||||
if (this.mouse_controls) {
|
||||
this.mouse_controls.quatAtUnlockRequest = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
// FreeCam math + listeners now live in hooks/useFreeCam.ts; the
|
||||
// editor reads `cameraController.freeCamState` (set by that hook) on
|
||||
|
||||
@@ -130,6 +130,14 @@ export class MouseControls {
|
||||
private isBoneDragged: boolean = false;
|
||||
private ignoreNextClick: boolean = false;
|
||||
|
||||
// Snapshot of the camera quaternion taken in onMouseUp right before
|
||||
// lockControls.unlock(). The unlock listener in editor.ts restores
|
||||
// this value to neutralize the synthetic mousemove Chromium fires
|
||||
// while pointer-lock is releasing — that synthetic move would
|
||||
// otherwise be processed by PointerLockControls and lurch the camera
|
||||
// back by the cursor's "trip home" delta, producing a visible snap.
|
||||
quatAtUnlockRequest: THREE.Quaternion | null = null;
|
||||
|
||||
// Deps held for read callbacks + bus emissions throughout the class.
|
||||
// Constructor still copies primitive fields onto `this.x` for the
|
||||
// existing internals; new code reads through `this.deps` directly.
|
||||
@@ -273,6 +281,13 @@ export class MouseControls {
|
||||
|
||||
onMouseUp(event: any) {
|
||||
if (event.button === 0 || event.button === 1) {
|
||||
// Snapshot the quaternion the user actually ended on. The unlock
|
||||
// listener in editor.ts will restore this once Chromium's
|
||||
// synthetic-mousemove window has closed, so the camera doesn't
|
||||
// lurch by the cursor's trip-home delta.
|
||||
if (this.camera) {
|
||||
this.quatAtUnlockRequest = this.camera.quaternion.clone();
|
||||
}
|
||||
this.lockControls?.unlock();
|
||||
this.isMouseClicked = false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user