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 |
导出 | 重新导出控制器、输入辅助函数和轨道实用工具。 |
数据流
CameraController 存储状态向量(旋转、平移、缩放、中心点、轨道上向量)。每帧 update():
1. 构建轨道基向量 (calculateOrbitBasis)。
2. 应用对数距离缩放以进行缩放。
3. 应用平移以调整轨道中心。
4. 应用带有极点保护的偏航/俯仰/翻滚。
5. 通过 lookAtW2C 重新计算相机位置和旋转四元数。
6. 衰减状态向量 (applyDecay) 以实现平滑运动。
集成
- Camera 模块 – 控制器修改
PerspectiveCamera.positionV和rotationQ。投影数据保持不变,以便渲染器/预处理可以重用它。 - 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)。
相关文档
- 架构 (Architecture) – 轨道数学推导、控制器状态机和衰减调整。
- API 参考 (API Reference) – 控制器构造函数、输入辅助函数和导出的实用程序类型。
- 相机模块 (Camera Module) – 描述控制器修改的
PerspectiveCamera接口。 - Three 集成 (Three Integration) – 展示 UI 层如何将 DOM 事件连接到控制系统。