Files
osmWorkflow/.trellis/spec/blender/index.md

151 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BlenderPython 场景生成层
> 覆盖 `blender/**/*.py`。
> 运行时:**两个**——Blender 内嵌 Pythonbpy 层)和系统 Python纯 Python 层)。
> 这条内部边界是本层最重要的结构约束。
---
## 先读哪一篇
| 你要做的事 | 读 |
|---|---|
| 新建模块、挪代码、加一种 OSM 要素 | [模块结构](./module-structure.md) ← **先确认放在哪一层** |
| 改几何构建、材质、实例化、Cesium 调色 | [资产生成](./asset-generation.md) |
| 改 `geom.py` / `osm.py` 或加纯函数 | [测试](./testing.md) |
| 改材质名、动 `MATERIALS` 顺序 | [模块结构 · 材质名对接](./module-structure.md#材质名对接新场景靠自定义属性旧场景靠回退表) |
| 声称"纯重构,产物不变" | [产物一致性指南](../guides/artifact-parity-guide.md) |
---
## 依赖分层
```
┌─────────────────────────────────────────────────────────┐
│ 纯 Python 层 —— 无 bpy系统 python 可跑可测 │
│ │
│ osmassets/osm.py OSM XML 解析 + 局部米制投影 │
│ osmassets/geom.py 平面几何(米制) │
│ osmassets/catalog.py 图层与材质的声明 │
│ │
│ ▲ blender/tests/test_pure.py 覆盖这一层51 个用例) │
└─────────────────────────────────────────────────────────┘
▲ 只能单向依赖
┌─────────────────────────────────────────────────────────┐
│ bpy 层 —— 只在 Blender 内运行,无单元测试 │
│ │
│ osmassets/mesh.py MeshBatch 等几何构建 │
│ osmassets/materials.py 材质构建(消费 catalog 的声明) │
│ osmassets/tree.py 树实例化 │
│ osmassets/water.py grass.py scrub.py │
│ osmassets/features.py 要素分发注册 │
│ osmassets/building.py fountain.py roads.py 要素装配 │
│ │
│ generate_scene.py export_cesium.py 两个入口 │
│ tools/scene_digest.py 结构摘要工具 │
│ │
│ ▲ 回归防线是 parity 校验,不是单元测试 │
└─────────────────────────────────────────────────────────┘
```
**在纯 Python 层里 `import bpy` 会静默废掉整个测试套件**——它不会失败,
而是 import 阶段就崩,看起来像环境问题。
**推论**:能挪进纯 Python 层的逻辑就挪。一个函数只要不碰 `bpy`,放进 `geom.py`
就立刻获得被测试覆盖的资格。
---
## 两个入口
| | `generate_scene.py` | `export_cesium.py` |
|---|---|---|
| 行数 | 895 | 647 |
| 调用 | `--background --factory-startup --python` | `--background --factory-startup --python` |
| 输入 | `--osm` + `--geojson`(可选) | `--blend` |
| 输出 | `--output`(.blend)、`--render`(.png) | `--glb``--metadata`(.json) |
| 完成标记 | `SCENE_DONE` | `CESIUM_EXPORT_DONE` |
| 由谁调起 | `build-area.js``blender` 阶段 | `build-area.js``cesium` 阶段 |
两个 stdout 标记是 [parity 契约](../guides/artifact-parity-guide.md)的一部分
`scripts/parity.js:121` 解析它们),**改动打印格式等于改动契约**。
### 后台阶段使用 `--factory-startup`
它屏蔽本机 Blender 的 preferences 和 addon保证场景生成与 Cesium 导出不受用户配置影响。
副作用是脚本自己的目录不在 `sys.path` 上,所以两个入口开头都有那段
`sys.path.insert` 样板 + `# noqa: E402`——**不是可以整理掉的坏味道**。
---
## 场景构建的输入约定
`generate_scene.py:9-12` 记录了一个容易踩的坑:
> **范围只认 OSM 的 `bounds` 元素**不用全部节点算包围盒。OSM 导出可能带上
> 请求范围之外的 relation 成员,用全部节点会得到一个大得离谱的模型。
`--geojson` 是可选的。给了就用 osm2streets 的精细道路面、人行道、车道标线、
斑马线;不给则回退到简单的 OSM `highway` 折线(`generate_scene.py:14-16`
回退逻辑在 `:849`)。
植被映射(`generate_scene.py:18-20`
| OSM 标签 | 产物 |
|---|---|
| `natural=tree`(节点) | 单棵树 |
| `natural=tree_row`way | 等距成排的树 |
| `landuse=grass` | 绿地 + 可选草簇散布 |
| `natural=scrub` | 低矮灌木覆盖 |
| `amenity=fountain` | 低模喷泉水池 |
---
## 三条贯穿全层的约定
1. **构建必须确定性**
`index` 的纯函数(无理数周期 / 黄金角)或显式 `seed` 代替 RNG
绝不用无种子 `random` 或时间量。重建同一片区域必须得到相同结构,
否则 parity 校验永久红。见[资产生成](./asset-generation.md#确定性用无理数周期代替-rng)。
2. **压低对象数**
`MeshBatch` 批处理 + 树实例化共享 datablock。GLB 要在浏览器里跑,
节点数和贴图数都是硬成本。
3. **退化输入返回空,不抛异常**
一个坏多边形不该中断整片区域的构建。要素模块 `len(ring) < 3` 直接返回 0
`MeshBatch` 静默 return`geom.py` 的函数返回 `[]`
---
## 文件速查
| 文件 | 行数 | 层 |
|---|---|---|
| `generate_scene.py` | 895 | bpy · 入口 |
| `export_cesium.py` | 647 | bpy · 入口 |
| `osmassets/tree.py` | 318 | bpy |
| `osmassets/geom.py` | 241 | 纯 |
| `osmassets/materials.py` | 232 | bpy |
| `osmassets/catalog.py` | 197 | 纯 |
| `osmassets/mesh.py` | 126 | bpy |
| `osmassets/osm.py` | 89 | 纯 |
| `osmassets/features.py` | 20 | bpy · 分发 |
| `osmassets/building.py` / `fountain.py` / `roads.py` | 56 / 49 / 40 | bpy |
| `osmassets/water.py` / `grass.py` / `scrub.py` | 15 / 22 / 13 | bpy |
| `tools/scene_digest.py` | 171 | bpy · 工具 |
| `tests/test_pure.py` | 372 | 纯 · 测试 |
`blender/README.md` 是面向使用者的运行说明,与本目录互补——**用法写那边,
改法写这边**。
---
## 技术选型现状
- **Blender 4.x**`bpy` + `mathutils``export_cesium.py` 额外用 `numpy`Blender 自带)
- **纯 Python 层只用标准库**`math``json``os``xml.etree`
这是它能用系统 python 跑的前提——**不要给它加第三方依赖**
- **无类型标注、无 lint 配置**。保持现状;引入工具链是独立决定
- **`unittest` 而非 pytest**,零依赖跑得起来