绘制 API
把自绘模型交给渲染管线。两种坐标系、两个绘制目标,先分清再动手。
两种绘制调用
| 类型 | 坐标系 | 用途 |
|---|---|---|
ClusterDrawCall | 车厢本地 | 跟着车厢走的东西 —— 车内设备、外部标识 |
WorldDrawCall | 世界绝对 | 不跟车走的东西 |
两者都通过全局同名类构造:
var call = new ClusterDrawCall(model, pose);
model 是 ModelCluster 或 DynamicModelHolder,
pose 是 Matrix4f 变换矩阵。
提交到车厢
| 方法 | 说明 |
|---|---|
addCarModel(car, model, pose) | 把模型挂到第 car 节车厢 |
addCarSound(car, sound, position, volume, pitch) | 在指定车厢位置播声音 |
addConnModel(car, model, pose) | 挂到车厢连接处 |
drawConnStretchTexture(car, texture) | 在连接处拉伸贴一张图 |
car 从 0 开始。
编组数用 train.trainCars() 取,越界会被忽略。绘制调用表
ctx 上按车厢维护了一组 DrawCallMap,行为与 Map 一致:
| 方法 | 说明 |
|---|---|
put(key, drawCall) | 登记一个绘制调用,key 任意 |
get(key) | 取回 |
remove(key) | 移除 |
containsKey(key) | 是否存在 |
clear() | 清空 |
size() | 数量 |
keySet() | 全部 key |
values() | 全部绘制调用 |
用 key 复用比每帧重建划算得多:
function render(ctx, state, train) {
if (!train.shouldRender()) return;
// 站名没变就不重建模型
var name = train.nextStationName();
if (state.builtFor !== name) {
state.builtFor = name;
state.model = buildSignModel(name);
}
}
模型类
| 全局名 | 说明 |
|---|---|
RawMesh | 可编辑网格。支持平移、旋转、缩放等变换 |
RawMeshBuilder | 逐面构建网格 |
RawModel | 由多个 RawMesh 组成的模型 |
ModelCluster | 烘焙后的模型,上传到 GPU,绘制开销最低 |
DynamicModelHolder | 可替换内容的模型容器 |
RawMesh 的旋转方法接受的是角度,不是弧度。
传 Math.PI 只会转 3.14 度。性能
- 不要每帧构建模型。 把结果缓存进
state,只在数据变化时重建。 - 能烘焙成
ModelCluster就烘焙。 它上传 GPU 一次,之后每帧近乎免费。 - 先判
shouldRender()。 车不在视野内直接返回。 - 远景降级。 用
shouldRenderDetail()决定要不要画细节。
开客户端配置 enableRenderDiagnostics 可在日志看到各阶段耗时与模型去重统计。