跳转至

Controls API 参考

本参考文档对应 src/controls/ 下的 TypeScript 导出。所有数学运算均使用 gl-matrix 数据类型 (vec2, vec3, quat)。

接口 (Interfaces)

IController

interface IController {
  update(camera: PerspectiveCamera, deltaTime: number): void;
  processKeyboard(code: string, pressed: boolean): boolean;
  processMouse(dx: number, dy: number): void;
  processScroll(delta: number): void;
  leftMousePressed: boolean;
  rightMousePressed: boolean;
  userInput: boolean;
  resetOrientation?(): void;
  getControllerType(): 'orbit' | 'fps';
}

CameraController (轨道) 和 FPSController 都实现了此契约,因此应用程序可以在运行时切换控制器。

类 (Classes)

CameraController

默认使用的轨道控制器。

class CameraController implements IController {
  constructor(speed = 0.2, sensitivity = 0.1);
  center: vec3;
  up: vec3 | null;
  amount: vec3;   // 为键盘运动保留
  shift: vec2;    // 右键平移累加器
  rotation: vec3; // 偏航/俯仰/翻滚 (yaw/pitch/roll) 累加器
  scroll: number; // 对数缩放累加器
  speed: number;
  sensitivity: number;
  leftMousePressed: boolean;
  rightMousePressed: boolean;
  altPressed: boolean;
  userInput: boolean;

  resetUp(u?: vec3): void;
  processKeyboard(code: string, pressed: boolean): boolean;
  processMouse(dx: number, dy: number): void;
  processScroll(delta: number): void;
  update(camera: PerspectiveCamera, deltaTime: number): void;
  resetOrientation(): void;
  getControllerType(): 'orbit';
}
  • resetUp(u) 设置轨道参考的上向量(默认为 WORLD_UP)。
  • processKeyboard/processMouse/processScroll 调用 input.ts 中的辅助函数,并在处理后设置 userInput = true
  • update 运行架构文档中描述的轨道管线,并更新 camera.positionV / camera.rotationQ

FPSController

WASD + 鼠标视角控制器(实验性但已导出)。

class FPSController implements IController {
  constructor(moveSpeed?, rotateSpeed?, scrollSpeed?, moveInertia?, rotateInertia?);
  moveSpeed: number;
  rotateSpeed: number;
  scrollSpeed: number;
  moveInertia: number;
  rotateInertia: number;
  flyMode: boolean;
  enable: boolean;
  leftMousePressed: boolean;
  rightMousePressed: boolean;
  userInput: boolean;

  processKeyboard(code: string, pressed: boolean): boolean;
  processMouse(dx: number, dy: number): void;
  processScroll(delta: number): void;
  update(camera: PerspectiveCamera, deltaTime: number): void;
  resetOrientation(): void;
  getOrientation(): { yaw: number; pitch: number };
  setOrientation(yaw: number, pitch: number): void;
  setFlyMode(enabled: boolean): void;
  getControllerType(): 'fps';
}

FPSController 监听文档级别的按键事件(在其构造函数中),并保持 yaw/pitch 角度以及用于惯性的速度向量。flyMode = false 将移动限制在 XZ 平面上。

输入辅助函数 (input.ts)

processKeyboardInput(code, pressed, amount, rotation, sensitivity)

原地更新 amount (vec3) 和 rotation (vec3) 数组。处理 KeyW/A/S/D, Space, ShiftLeft, KeyQ, KeyE。当识别到按键时返回 true

processMouseInput(dx, dy, leftPressed, rightPressed, rotation, shift)

将鼠标增量添加到 rotation(左键)或 shift(右键)。如果有任何按钮处于活动状态,则返回 true

processScrollInput(delta)

缩放滚轮增量(默认倍数为 3)并返回用于累加的值。

轨道数学 (orbit.ts)

常量

  • WORLD_UP = vec3(0, 1, 0)
  • MIN_POLE_ANGLE = 5°
  • MIN_DISTANCE = 0.1
  • MAX_DISTANCE = 1000
  • EPS = 1e-6

函数

lookAtW2C(forwardWorld: vec3, upWorld: vec3): quat;
projectOntoPlaneNormed(v: vec3, n: vec3, fallback: vec3): vec3;
calculateOrbitBasis(cameraPos: vec3, center: vec3, orbitUp: vec3)
  => { forward: vec3, right: vec3, yawAxis: vec3 };
applyDistanceScaling(cameraPos, center, scroll, deltaTime, speed): number;
applyPanning(center, shift, right, yawAxis, deltaTime, speed, distance): void;
applyRotation(forward, right, yawAxis, yaw, pitch, roll)
  => { forward: vec3, right: vec3, yawAxis: vec3 };
applyDecay(rotation: vec3, shift: vec2, scroll: number, deltaTime: number)
  => { rotation: vec3, shift: vec2, scroll: number };

这些函数是无状态的;控制器传入临时向量并重用结果。

使用片段

import { CameraController } from 'src/controls';
import { PerspectiveCamera } from 'src/camera';

const controller = new CameraController(0.25, 0.12);
const camera = PerspectiveCamera.default();

function frame(dt: number) {
  controller.update(camera, dt);
  renderer.prepareMulti(encoder, queue, [pointCloud], { camera, viewport: [w, h] });
}

鼠标/键盘连接(简化版):

window.addEventListener('keydown', (e) => controller.processKeyboard(e.code, true));
window.addEventListener('keyup',   (e) => controller.processKeyboard(e.code, false));
canvas.addEventListener('mousemove', (e) => controller.processMouse(e.movementX, e.movementY));
canvas.addEventListener('wheel', (e) => controller.processScroll(e.deltaY));

参见 fps-controller.ts 了解具有惯性和 6自由度 (6DoF) 飞行控制的替代设置。