238 lines
9.0 KiB
Markdown
238 lines
9.0 KiB
Markdown
# Preview:Cesium 预览层
|
||
|
||
> 覆盖 `scripts/lib/cesium-preview.js`(672 行)与 `cesium-preview.css`(230 行)。
|
||
> 运行时:浏览器。全仓唯一的 DOM 环境。
|
||
|
||
---
|
||
|
||
## 定位
|
||
|
||
预览层是**验证性的,不是产物本身**。它加载 `cesium` 阶段导出的 `.glb` + `.json`,
|
||
用来确认资产在真实 Cesium 里的样子。改这一层**不会**改变 Blender/GLB 主资产。
|
||
|
||
车辆巡航同理——README 里写明它是"用于验证高精度巡航可用性的预览层功能"。
|
||
|
||
---
|
||
|
||
## 没有构建步骤
|
||
|
||
```
|
||
scripts/lib/cesium-preview.js ─── 原样 copyFileSync ──▶ outputs/<area>/cesium-preview.js
|
||
scripts/lib/cesium-preview.css ─── 原样 copyFileSync ──▶ outputs/<area>/cesium-preview.css
|
||
(build-area.js:328-335)
|
||
<area>-cesium-preview.html ─── 模板字符串生成 ────▶ 同目录
|
||
(build-area.js:697)
|
||
```
|
||
|
||
所以:**没有打包、没有转译、没有 npm 依赖、没有模块系统**。浏览器直接吃。
|
||
写代码时只能用目标浏览器原生支持的语法,`Cesium` 从 CDN 全局引入。
|
||
|
||
整个文件是一个 IIFE + `"use strict"`(`cesium-preview.js:1-2`)。
|
||
|
||
---
|
||
|
||
## 参数注入
|
||
|
||
JS 不硬编码任何文件名,全部从 HTML 注入的全局对象读:
|
||
|
||
```js
|
||
const config = window.OSM_ASSET_PREVIEW_CONFIG || {}; // :4
|
||
// config.areaId / .glbName / .metadataName / .routeName / .vehicleModelName
|
||
```
|
||
|
||
生成侧在 `build-area.js:697 cesiumPreviewHtml()`,注入时**必须转义**:
|
||
|
||
| 场景 | 用 |
|
||
|---|---|
|
||
| HTML 文本/属性 | `escapeHtml()`(`build-area.js:759`) |
|
||
| `<script>` 里的 JSON | `escapeScriptJson()`(`:767`) |
|
||
|
||
`|| {}` 的兜底不能删——它让 JS 在没有配置块时也不至于在第一行就崩。
|
||
|
||
**加一个新的可配置项**:`cesiumPreviewHtml()` 里加进注入的 JSON,JS 侧从 `config` 读,
|
||
两边都要动。
|
||
|
||
---
|
||
|
||
## 加载流程
|
||
|
||
`main()`(`:30-52`)的顺序是刻意的:
|
||
|
||
```
|
||
setLoadingMessage("Loading scene")
|
||
→ fetchJson(metadata) 必需,失败即终止
|
||
→ fetchOptionalJson(route) 可选,失败降级
|
||
→ scenePlacement(metadata)
|
||
→ createViewer()
|
||
setLoadingMessage("Loading model")
|
||
→ loadSceneAssets() 逐个资产加载,失败收集不中断
|
||
→ addVehicleCruises() / createCameraPresets()
|
||
→ buildAssetToggles() / bindRuntimeControls() / startDiagnostics()
|
||
→ cameras.overview()
|
||
→ baseStatus = summaryText(...)
|
||
setLoadingMessage("Preparing view")
|
||
→ await waitForStableFrames() 等画面稳定
|
||
→ document.body.classList.add("scene-ready") ← CSS 靠这个类收起遮罩
|
||
→ window.osmPreview = {...}
|
||
```
|
||
|
||
### 三档失败语义
|
||
|
||
这一层的错误处理分得很清楚,**新增加载逻辑时要先想清楚落在哪一档**:
|
||
|
||
| 档 | 做法 | 出处 |
|
||
|---|---|---|
|
||
| **必需** | `fetchJson` 直接抛,预览起不来 | `:55-62` |
|
||
| **可选** | `fetchOptionalJson` 捕获 → `console.warn` → 返回 `null` | `:63-73` |
|
||
| **部分** | 逐条收集失败,汇总到诊断面板,其余照常显示 | `:163-165` |
|
||
|
||
两段注释把理由写清楚了:
|
||
|
||
> The route file is an extra on top of the scene, not a precondition for it.
|
||
> A missing or unreadable route costs the cruise controls, not the preview.
|
||
|
||
> One broken entry in metadata.assets should not blank the whole preview, so
|
||
> failures are collected and surfaced in the diagnostics panel instead.
|
||
|
||
### `scene-ready` 是加载态的唯一开关
|
||
|
||
`waitForStableFrames()`(`:107`)等若干帧稳定后才加 `scene-ready` 类,CSS 据此
|
||
收起遮罩。**不要改成定时器或 `load` 事件**——材质编译完成之前画面是花的。
|
||
|
||
### `window.osmPreview` 是唯一对外句柄
|
||
|
||
```js
|
||
// Handle for the browser console and for headless checks: everything else
|
||
// in here is closed over by the IIFE and unreachable from outside. :50-51
|
||
window.osmPreview = { viewer, metadata, placement, assets, cruise, cameras };
|
||
```
|
||
|
||
调试和无头检查都靠它。**加新的顶层对象就往这里挂**,不要再开新全局。
|
||
|
||
---
|
||
|
||
## Viewer 配置:一切都关掉
|
||
|
||
`createViewer()`(`:74-97`)把 Cesium 的默认 UI 和地球全部关闭:
|
||
|
||
```js
|
||
animation, timeline, baseLayerPicker, geocoder,
|
||
navigationHelpButton, sceneModePicker, infoBox,
|
||
selectionIndicator, baseLayer ← 全 false
|
||
globe.show = false ← 不显示地球
|
||
skyAtmosphere / skyBox / sun / moon ← 全 false
|
||
backgroundColor = globe.baseColor = "#d9e0e2" ← PREVIEW_BACKGROUND
|
||
```
|
||
|
||
理由:这是**看单个园区资产**的预览,不是地图应用。留着地球和大气会干扰对
|
||
材质与几何的判断,也让背景色不可控。
|
||
|
||
`depthTestAgainstTerrain = false` —— 没有地形,开着只会让模型被裁。
|
||
|
||
保留的只有 `homeButton` 和 `fullscreenButton`。
|
||
|
||
---
|
||
|
||
## DOM 与状态
|
||
|
||
### DOM 句柄集中在顶部
|
||
|
||
`:5-20` 一次性取完全部元素引用,**不在函数里现查**。新增控件时加在这一批里。
|
||
|
||
### status 的两类消息
|
||
|
||
```js
|
||
// Status carries two kinds of message: the scene summary, which is what the
|
||
// panel should read whenever nothing else is going on, and transient notes
|
||
// from a control the user just touched. Keep the summary so the transient
|
||
// note can be replaced instead of destroying it. :21-24
|
||
let baseStatus = "";
|
||
```
|
||
|
||
`baseStatus` 存场景摘要,瞬时提示用完要能回到它。**加新的瞬时提示时不要直接覆写
|
||
`baseStatus`**。
|
||
|
||
### 诊断读数跟渲染循环,不跟定时器
|
||
|
||
```js
|
||
// Camera-dependent readouts have to track the camera, so refresh off the
|
||
// render loop rather than a fixed timer, throttled to stay off the hot path. :589-590
|
||
```
|
||
|
||
相机相关的读数必须跟着渲染循环刷新并**做节流**。用 `setInterval` 会在相机快速移动时
|
||
读到过期值,不节流会拖慢帧率。
|
||
|
||
### 车辆巡航热路径要少分配
|
||
|
||
车辆巡航是预览层里唯一持续动画的功能。`scripts/lib/cesium-preview.js` 的
|
||
`routeOrientation()` 和 `createChaseFollow()` 每帧都会运行,里面的 `JulianDate`、
|
||
`HeadingPitchRoll`、`Quaternion`、`Matrix4` 等临时对象必须在闭包外复用,不要在
|
||
`CallbackProperty` 或 `viewer.clock.onTick` 回调里 `new`。
|
||
|
||
预览页面加载的是 30MB+ GLB,动画时 GPU 已经很忙;巡航辅助线也要避免额外透明合成。
|
||
路线 polyline 使用不透明 `Color` 和 `arcType: Cesium.ArcType.NONE`,不要为了视觉效果
|
||
随手改回半透明 geodesic 线。
|
||
|
||
不要默认降低 `viewer.resolutionScale` 或关闭 antialias/MSAA 来换取巡航流畅度。
|
||
这会让用户误判材质和模型质量。若确实需要性能模式,应做成显式开关,而不是默认牺牲
|
||
预览清晰度。
|
||
|
||
### 单资产 vs 多资产的开关
|
||
|
||
```js
|
||
// A single-asset scene keeps the plain "Scene" checkbox; a multi-asset one
|
||
// gets a child checkbox per model with "Scene" acting as the master. :194-195
|
||
```
|
||
|
||
`buildAssetToggles()` 按资产数量决定 UI 形态,`syncSceneMaster()` 维护主从关系。
|
||
|
||
---
|
||
|
||
## 坐标系
|
||
|
||
GLB 停留在**局部 ENU 坐标系**(X 东、Y 北、Z 上),靠伴生 JSON 的放置信息配合
|
||
`Cesium.Transforms.eastNorthUpToFixedFrame` 摆到地球上
|
||
(`blender/export_cesium.py:8-9`)。
|
||
|
||
`scenePlacement(metadata)`(`:131`)负责这一步。**改动导出侧的坐标约定必须同步改这里。**
|
||
|
||
---
|
||
|
||
## 本地预览必须走 HTTP
|
||
|
||
```bash
|
||
cd outputs/<area-id>
|
||
python3 -m http.server 8765
|
||
# → http://localhost:8765/<area-id>-cesium-preview.html
|
||
```
|
||
|
||
`file://` 会被浏览器的同源策略拦掉 `fetch`,页面停在加载遮罩上。
|
||
|
||
---
|
||
|
||
## 反模式
|
||
|
||
| 反模式 | 后果 |
|
||
|---|---|
|
||
| 引入需要打包/转译的语法或 npm 依赖 | 没有构建步骤,直接跑不起来 |
|
||
| 在 JS 里硬编码 `.glb` / `.json` 文件名 | 换区域就失效,绕过 config 注入 |
|
||
| 注入 HTML 时不转义 | 区域名带特殊字符就破页面 |
|
||
| 把可选资源当必需资源加载 | 缺一个路线文件整个预览打不开 |
|
||
| 单个资产加载失败就中断全部 | 一条坏 metadata 让预览全白 |
|
||
| 用定时器代替 `waitForStableFrames` | 遮罩在材质编译完成前就收起,画面是花的 |
|
||
| 相机读数用 `setInterval` | 快速移动时读到过期值 |
|
||
| 诊断刷新不节流 | 拖慢帧率 |
|
||
| 在车辆每帧回调里创建 `JulianDate` / HPR / 矩阵对象 | 巡航时触发 GC 抖动 |
|
||
| 默认打开半透明路线辅助线 | 增加透明合成成本,主场景可见时车辆更不顺 |
|
||
| 默认降低 `resolutionScale` 或关抗锯齿 | 预览变糊,无法判断材质/模型质量 |
|
||
| 再开一个全局变量 | 已有 `window.osmPreview` |
|
||
| 用 `file://` 打开 | fetch 被拦,卡在加载中 |
|
||
|
||
---
|
||
|
||
## 相关
|
||
|
||
- [CLI 与阶段](../pipeline/cli-and-stages.md):`cesium` / `preview` 阶段如何生成这些文件
|
||
- [资产生成](../blender/asset-generation.md):GLB 里的材质为什么要单独调色
|
||
- README「实验:车辆巡航」节:面向使用者的说明
|