fix(simulation): reset camera input on layer switch
This commit is contained in:
@@ -78,10 +78,21 @@ export interface SimulationRuntime {
|
||||
|
||||
type DesktopCameraInput = {
|
||||
read(): { mouse: number[] } & Record<string, number[]>;
|
||||
_pointerId?: number;
|
||||
_button?: number[];
|
||||
_keyNow?: number[];
|
||||
_keyPrev?: number[];
|
||||
};
|
||||
|
||||
type CameraController = ScriptType & Pick<CameraControls, "reset" | "focus"> & {
|
||||
_desktopInput?: DesktopCameraInput;
|
||||
_state?: {
|
||||
axis: Vec3;
|
||||
shift: number;
|
||||
ctrl: number;
|
||||
mouse: number[];
|
||||
touches: number;
|
||||
};
|
||||
};
|
||||
|
||||
const HOME_POSITION = new Vec3(0, 1, 0);
|
||||
@@ -243,6 +254,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
||||
}
|
||||
|
||||
async setViewMode(mode: SimulationViewMode): Promise<void> {
|
||||
this.resetCameraInteraction();
|
||||
this.viewMode = mode;
|
||||
this.applyViewMode();
|
||||
try {
|
||||
@@ -277,6 +289,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
||||
}
|
||||
|
||||
home(): void {
|
||||
this.resetCameraInteraction();
|
||||
if (this.cameraController) {
|
||||
this.cameraController.reset(HOME_FOCUS, HOME_POSITION);
|
||||
} else if (this.camera) {
|
||||
@@ -287,6 +300,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
||||
}
|
||||
|
||||
focusBounds(): void {
|
||||
this.resetCameraInteraction();
|
||||
const bounds = this.visualEntity?.gsplat?.customAabb;
|
||||
const focus = bounds?.center.clone() ?? new Vec3(0, 0, 0);
|
||||
const radius = bounds?.halfExtents.length() ?? 3;
|
||||
@@ -356,6 +370,37 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
private resetCameraInteraction(): void {
|
||||
const controller = this.cameraController;
|
||||
const canvas = this.canvas;
|
||||
if (!controller || !canvas) return;
|
||||
const input = controller._desktopInput;
|
||||
const pointerId = input?._pointerId ?? -1;
|
||||
if (pointerId >= 0 && canvas.hasPointerCapture(pointerId)) {
|
||||
try {
|
||||
canvas.releasePointerCapture(pointerId);
|
||||
} catch {
|
||||
// The browser may already have released capture after a trackpad gesture.
|
||||
}
|
||||
}
|
||||
if (input) {
|
||||
input._pointerId = -1;
|
||||
input._button?.fill(0);
|
||||
input._keyNow?.fill(0);
|
||||
input._keyPrev?.fill(0);
|
||||
input.read();
|
||||
}
|
||||
controller.fire("state");
|
||||
const state = controller._state;
|
||||
if (state) {
|
||||
state.axis.set(0, 0, 0);
|
||||
state.shift = 0;
|
||||
state.ctrl = 0;
|
||||
state.mouse.fill(0);
|
||||
state.touches = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private applyQuality(): void {
|
||||
const gsplat = this.visualEntity?.gsplat;
|
||||
const profile = QUALITY_PROFILES[this.quality];
|
||||
|
||||
@@ -76,6 +76,9 @@ test("PlayCanvas owns the realtime scene graph without an iframe or React entity
|
||||
assert.match(runtime, /selected\.horizontal[^\n]+frame\.mouse\[0\] \*= -1/);
|
||||
assert.match(runtime, /selected\.vertical[^\n]+frame\.mouse\[1\] \*= -1/);
|
||||
assert.match(runtime, /setCameraInversion/);
|
||||
assert.match(runtime, /async setViewMode\([\s\S]*this\.resetCameraInteraction\(\);[\s\S]*this\.viewMode = mode/);
|
||||
assert.match(runtime, /input\._pointerId = -1/);
|
||||
assert.match(runtime, /state\.mouse\.fill\(0\)/);
|
||||
assert.match(runtime, /new Asset\([\s\S]*"container"/);
|
||||
assert.match(runtime, /instantiateRenderEntity/);
|
||||
assert.match(runtime, /applyWorldTransform/);
|
||||
|
||||
Reference in New Issue
Block a user