Initialize Trellis project guidelines

This commit is contained in:
2026-08-03 10:56:21 +08:00
parent 7f4ebe8fb7
commit 4c5981c555
166 changed files with 28564 additions and 0 deletions

View File

@@ -0,0 +1,219 @@
# PreviewCesium 预览层
> 覆盖 `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()` 里加进注入的 JSONJS 侧从 `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` 会在相机快速移动时
读到过期值,不节流会拖慢帧率。
### 单资产 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` | 快速移动时读到过期值 |
| 诊断刷新不节流 | 拖慢帧率 |
| 再开一个全局变量 | 已有 `window.osmPreview` |
| 用 `file://` 打开 | fetch 被拦,卡在加载中 |
---
## 相关
- [CLI 与阶段](../pipeline/cli-and-stages.md)`cesium` / `preview` 阶段如何生成这些文件
- [资产生成](../blender/asset-generation.md)GLB 里的材质为什么要单独调色
- README「实验车辆巡航」节面向使用者的说明