mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-15 06:12:18 +00:00
PageScene: extract cameraMath + browserChecks modules
Pure-function helpers carved out of FreeCam in preparation for the hook rewrite: - engine/cameraMath.ts: HeldMoveKeys / HeldRotateKeys types, moveVectorFromKeys, rotationVectorFromKeys, panFromDrag, zoomFromWheel, lookAtFromCamera, lerpVelocity. No DOM access, no signals. - engine/browserChecks.ts: isSafariOrWebKit, hasApplePay, isSafari, isPointerLockSupported. Unrelated to camera math; were just hitchhiking in free_cam.ts. free_cam.ts re-exports the browser checks for back-compat with keybinds_controls.ts; both consumers will switch to the direct imports when free_cam.ts is deleted in the next commit.
This commit is contained in:
1 parent
61c4936c29
commit
718b8261fd
3 files changed
+138
-22
No files matched your search
@@ -0,0 +1,20 @@
|
||||
// Browser feature detection helpers used by the input subsystem.
|
||||
// Pointer Lock behaves differently on Safari/WebKit, so the input
|
||||
// hooks fall back to drag-based control there.
|
||||
|
||||
export const isSafariOrWebKit = (): boolean => {
|
||||
const ua = navigator.userAgent;
|
||||
const isWebKit =
|
||||
/AppleWebKit/i.test(ua) && !/Chrome|Chromium|Edg|OPR|CriOS|FxiOS/i.test(ua);
|
||||
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(ua);
|
||||
return isSafari || isWebKit;
|
||||
};
|
||||
|
||||
export const hasApplePay = (): boolean => {
|
||||
// @ts-expect-error Apple Pay is not defined in TypeScript
|
||||
return !!window.ApplePaySession;
|
||||
};
|
||||
|
||||
export const isSafari = (): boolean => isSafariOrWebKit() || hasApplePay();
|
||||
|
||||
export const isPointerLockSupported = (): boolean => !isSafari();
|
||||
@@ -0,0 +1,109 @@
|
||||
import { MathUtils, Vector3 } from "three";
|
||||
import type { PerspectiveCamera } from "three";
|
||||
|
||||
// Pure math used by the free-cam viewport controls.
|
||||
// No DOM access, no event listeners, no signals — every function
|
||||
// takes inputs and returns values. The input hooks own the listener
|
||||
// lifecycle and the per-frame loop; this module owns the formulas.
|
||||
|
||||
// Held movement keys (WASD + QE). Stored as 0/1 so update steps can
|
||||
// just sum them in opposite directions.
|
||||
export interface HeldMoveKeys {
|
||||
forward: 0 | 1;
|
||||
back: 0 | 1;
|
||||
left: 0 | 1;
|
||||
right: 0 | 1;
|
||||
up: 0 | 1;
|
||||
down: 0 | 1;
|
||||
}
|
||||
|
||||
// Held rotation keys (arrow keys + roll). Same encoding.
|
||||
export interface HeldRotateKeys {
|
||||
pitchUp: 0 | 1;
|
||||
pitchDown: 0 | 1;
|
||||
yawLeft: 0 | 1;
|
||||
yawRight: 0 | 1;
|
||||
rollLeft: 0 | 1;
|
||||
rollRight: 0 | 1;
|
||||
}
|
||||
|
||||
export const emptyMoveKeys = (): HeldMoveKeys => ({
|
||||
forward: 0,
|
||||
back: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
up: 0,
|
||||
down: 0,
|
||||
});
|
||||
|
||||
export const emptyRotateKeys = (): HeldRotateKeys => ({
|
||||
pitchUp: 0,
|
||||
pitchDown: 0,
|
||||
yawLeft: 0,
|
||||
yawRight: 0,
|
||||
rollLeft: 0,
|
||||
rollRight: 0,
|
||||
});
|
||||
|
||||
// Per-frame movement direction (in camera-local space). Multiply by
|
||||
// (delta * movementSpeed) before applying.
|
||||
export const moveVectorFromKeys = (
|
||||
keys: HeldMoveKeys,
|
||||
autoForward = false,
|
||||
): Vector3 => {
|
||||
const forward = keys.forward || (autoForward && !keys.back) ? 1 : 0;
|
||||
return new Vector3(
|
||||
-keys.left + keys.right,
|
||||
-keys.down + keys.up,
|
||||
-forward + keys.back,
|
||||
);
|
||||
};
|
||||
|
||||
// Per-frame rotation deltas. Already pre-scaled by rollSpeed.
|
||||
export const rotationVectorFromKeys = (
|
||||
keys: HeldRotateKeys,
|
||||
rollSpeed: number,
|
||||
): Vector3 =>
|
||||
new Vector3(
|
||||
-keys.pitchDown + keys.pitchUp,
|
||||
-keys.yawRight + keys.yawLeft,
|
||||
-keys.rollRight + keys.rollLeft,
|
||||
).multiplyScalar(rollSpeed);
|
||||
|
||||
// Mouse-drag pan amounts (in camera-local x/y, before applying).
|
||||
// dx/dy are pointer deltas in pixels; speed scales them.
|
||||
export const panFromDrag = (
|
||||
dx: number,
|
||||
dy: number,
|
||||
speed: number,
|
||||
): { x: number; y: number } => ({
|
||||
x: -dx * (speed * 0.01),
|
||||
y: dy * (speed * 0.01),
|
||||
});
|
||||
|
||||
// Wheel-event zoom amount along camera-local z.
|
||||
export const zoomFromWheel = (deltaY: number): number => deltaY / 120;
|
||||
|
||||
// World-space lookAt point one unit ahead of the camera along its
|
||||
// current orientation. Used to update camera entries in the store
|
||||
// after a free-cam move so the camera entry's "lookAt" stays in
|
||||
// sync with the rendered orientation.
|
||||
export const lookAtFromCamera = (camera: PerspectiveCamera): Vector3 => {
|
||||
const lookAt = new Vector3(0, 0, -1);
|
||||
lookAt.applyQuaternion(camera.quaternion);
|
||||
lookAt.add(camera.position);
|
||||
return lookAt;
|
||||
};
|
||||
|
||||
// Smooth a velocity vector toward a target with a per-component lerp.
|
||||
// Returns the same `velocity` instance for chaining.
|
||||
export const lerpVelocity = (
|
||||
velocity: Vector3,
|
||||
target: Vector3,
|
||||
smoothing: number,
|
||||
): Vector3 => {
|
||||
velocity.x = MathUtils.lerp(velocity.x, target.x, smoothing);
|
||||
velocity.y = MathUtils.lerp(velocity.y, target.y, smoothing);
|
||||
velocity.z = MathUtils.lerp(velocity.z, target.z, smoothing);
|
||||
return velocity;
|
||||
};
|
||||
@@ -9,28 +9,15 @@ import * as THREE from "three";
|
||||
import { cameras, selectedCameraId } from "~/pages/PageScene/signals/camera";
|
||||
import { isPromptBoxFocused } from "~/pages/PageScene/signals/promptBox";
|
||||
|
||||
|
||||
export const isSafariOrWebKit = () => {
|
||||
const ua = navigator.userAgent;
|
||||
|
||||
const isWebKit = /AppleWebKit/i.test(ua) && !/Chrome|Chromium|Edg|OPR|CriOS|FxiOS/i.test(ua);
|
||||
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(ua);
|
||||
|
||||
return isSafari || isWebKit;
|
||||
};
|
||||
|
||||
export const hasApplePay = () => {
|
||||
// @ts-expect-error Apple Pay is not defined in TypeScript
|
||||
return !!window.ApplePaySession;
|
||||
}
|
||||
|
||||
export const isSafari = () => {
|
||||
return isSafariOrWebKit() || hasApplePay();
|
||||
}
|
||||
|
||||
export const isPointerLockSupported = () => {
|
||||
return !isSafari();
|
||||
}
|
||||
// Re-exported here for back-compat with keybinds_controls.ts; the
|
||||
// definitions live in browserChecks.ts and will be imported directly
|
||||
// once Phase 6 deletes this file.
|
||||
export {
|
||||
isSafariOrWebKit,
|
||||
hasApplePay,
|
||||
isSafari,
|
||||
isPointerLockSupported,
|
||||
} from "./browserChecks";
|
||||
|
||||
class FreeCam extends EventDispatcher {
|
||||
object: THREE.PerspectiveCamera;
|
||||
|
||||
Reference in new issue
Block a user