跳转至

Controls模块

src/controls/ 容纳了 Visionary 的 输入 → 相机 管线。它暴露了轨道式控制(默认 CameraController)、FPS 式控制器、纯输入辅助函数和轨道数学实用工具。代码反映了 Rust/Spark 技术栈的行为,并直接插入相机模块 (PerspectiveCamera)。

职责

  • 将 DOM 事件(鼠标/键盘/滚轮)转换为平滑的轨道运行、平移和缩放运动。
  • 维护数值稳定的基向量,使相机永远不会在极点附近“翻转”。
  • 支持替代控制方案(目前是一个实验性的 FPSController)。
  • 提供无状态的辅助函数,以便 UI 层可以生成自定义交互而无需触及控制器内部。

组件

组件 路径 目的
CameraController controller.ts WebGPU 查看器使用的默认轨道控制器。累积输入状态并更新 PerspectiveCamera
FPSController fps-controller.ts 具有惯性、可选地面/飞行模式的 WASD + 鼠标视角控制器。
input.ts 纯函数 将键盘/鼠标/滚轮增量转换为移动向量 (amount, rotation, shift, scroll) 的纯函数。
orbit.ts 数学辅助 轨道基向量计算、对数缩放、平移、带极点保护的旋转、lookAtW2C
base-controller.ts 接口 两个控制器共享的接口 (IController);定义了 update, processKeyboard, processMouse, processScroll
index.ts 导出 重新导出控制器、输入辅助函数和轨道实用工具。

数据流

UI 事件 → input.ts → 控制器状态 → orbit.ts 数学运算 → 相机位置/旋转

CameraController 存储状态向量(旋转、平移、缩放、中心点、轨道上向量)。每帧 update(): 1. 构建轨道基向量 (calculateOrbitBasis)。 2. 应用对数距离缩放以进行缩放。 3. 应用平移以调整轨道中心。 4. 应用带有极点保护的偏航/俯仰/翻滚。 5. 通过 lookAtW2C 重新计算相机位置和旋转四元数。 6. 衰减状态向量 (applyDecay) 以实现平滑运动。

集成

  • Camera 模块 – 控制器修改 PerspectiveCamera.positionVrotationQ。投影数据保持不变,以便渲染器/预处理可以重用它。
  • Renderer/Preprocess – 控制器更新相机后,渲染器上传新的视图/投影矩阵,预处理步骤使用相同的相机接口。
  • UI 层UIController(在 src/app 中)将 DOM 事件连接到控制器方法:processKeyboard, processMouse, processScroll,并更新 leftMousePressed/rightMousePressed 标志。

使用示例

轨道控制器

import { CameraController } from 'src/controls';
const controller = new CameraController(0.2, 0.1);
const camera = PerspectiveCamera.default();

function frame(dt: number) {
  controller.update(camera, dt);
  renderer.prepareMulti(...); // camera 现在包含新的 view/proj
}

附加事件:

canvas.addEventListener('mousedown', (e) => {
  if (e.button === 0) controller.leftMousePressed = true;
  if (e.button === 2) controller.rightMousePressed = true;
});
canvas.addEventListener('mouseup', () => {
  controller.leftMousePressed = controller.rightMousePressed = false;
});
canvas.addEventListener('mousemove', (e) => controller.processMouse(e.movementX, e.movementY));
window.addEventListener('keydown', (e) => controller.processKeyboard(e.code, true));
window.addEventListener('keyup',   (e) => controller.processKeyboard(e.code, false));
canvas.addEventListener('wheel', (e) => controller.processScroll(e.deltaY));

FPS 控制器

import { FPSController } from 'src/controls';
const fps = new FPSController(5.0, 0.002);

function frame(dt: number) {
  fps.update(camera, dt);
}

FPSController 自动监听文档级别的按键事件,并在 leftMousePressed 为真时使用鼠标增量。调用 fps.setFlyMode(false) 可将移动限制在 XZ 平面。

轨道数学一览

  • 基向量 (Basis vectors): forward = normalize(center - cameraPos), yawAxis = 将 orbitUp 投影到与 forward 正交的平面, right = forward × yawAxis。每一帧重新正交化。
  • 对数缩放 (Log zoom): 距离更新通过 exp(log(dist) + scroll * dt * 10 * speed) 进行,因此缩放在任何尺度下感觉都是一致的。
  • 极点保护 (Pole protection): 当 forward 在 5° 以内接近 yawAxis 时,俯仰旋转会被钳制,防止万向节锁。
  • 衰减 (Decay): 旋转/平移/缩放值按 pow(0.8, dt * 60) 缩小,并在微小时归零。

注意事项

  • 所有数学运算都使用 gl-matrix。复用临时向量以避免 GC 压力。
  • 控制器暴露 resetUp, resetOrientation, 和 getControllerType(),供宿主应用程序在轨道/FPS 模式之间切换。
  • 辅助函数 (processKeyboardInput, processMouseInput, processScrollInput, calculateOrbitBasis 等) 已导出,因此自定义控制器可以在相同的原语上构建。

深入了解数学原理请参阅 架构 (Architecture),方法签名请参阅 API 参考 (API Reference)

相关文档