Files
osmWorkflow/.trellis/spec/blender/module-structure.md

7.6 KiB
Raw Blame History

osmassets 模块结构

适用:在 blender/ 下新增或移动代码。 核心是一条按依赖切的边界——切错了,整个测试套件就跑不起来。


按依赖分包,不按功能

osmassets/__init__.py:3-12 写明了这个包的切分原则:

The package is split by dependency, not by feature: osm and geom are pure Python. They import no bpy and can be run and tested with a plain interpreter. Everything else may touch bpy and only runs inside Blender.

┌─ 纯 Python 层(无 bpy可用系统 python 直接跑和测)
│    osmassets/osm.py     OSM XML 解析 + 局部米制投影
│    osmassets/geom.py    平面几何:裁剪、采样、面积、点在多边形内
│    osmassets/catalog.py 图层与材质的声明(只有 json/os无 bpy
│
└─ bpy 层(只能在 Blender 内运行)
     osmassets/mesh.py       MeshBatch、prism、polyline
     osmassets/materials.py  材质构建
     osmassets/tree.py       树实例化
     osmassets/water.py  grass.py  scrub.py   要素装配
     generate_scene.py  export_cesium.py      两个入口脚本
     tools/scene_digest.py                    结构摘要工具

这条线是几何可测试的唯一前提。 拆分之前,验证 clip_polygonsample_tree_row 的唯一办法是渲染整片区域然后看图(__init__.py:9-12test_pure.py:5-8)。

在纯 Python 模块里写 import bpy 会静默废掉 51 个单元测试——它们不会失败, 而是 import 阶段就崩,看起来像环境问题。

catalog.py 属于纯 Python 层是刻意的:它只声明"是什么",让任何不启动 Blender 的 工具也能读到场景的材质定义(materials.py:3-7)。


各模块职责

模块 职责
osm.py parse_osm() 读 OSM XML → (bounds, ways, points)Projector 局部米制投影;parse_height()tags()
geom.py 平面几何全家桶。输入输出一律是投影后的米,例外只有 geometry_rings / feature_in_bounds(收原始 GeoJSON 的经纬度)
catalog.py ROAD_LAYERSMATERIALSroad_material_specs()check_layers()
mesh.py bpy MeshBatchmake_prismadd_roofadd_wall_paneladd_polyline、集合管理
materials.py bpy catalog 的规格变成真实材质:from_spec()、贴图、程序化噪声、tint、alpha-clip
tree.py bpy 两个 vendored 模型 → 一套可实例化的运行时形状
water.py / grass.py / scrub.py bpy 单一 OSM 要素的装配

geom.py 的两条隐含约定

  • 环是 (x, y) 元组的列表。重复的闭合点"到处容忍但从不要求" geom.py:8-9)——新函数要维持这个宽容度
  • 单位是米,除非函数名另有说明

要素模块的统一形状

water.py / grass.py / scrub.py 三个要素模块签名一致:

def assemble(ring, [way_id,] scene_xmin, scene_xmax, scene_ymin, scene_ymax,
             <颜色/材质...>, [回调...]):
    ring = clip_polygon(ring, scene_xmin, scene_xmax, scene_ymin, scene_ymax)
    if len(ring) < 3:
        return 0[, ...]
    ...
    return <计数>[, <焦点用的点集>]

四条约定:

  1. 自己裁剪。收边界参数而不是收已裁剪的环,不假设调用方做过 (三个 assemble 的第一行都是 clip_polygon
  2. 退化输入返回零,不抛异常len(ring) < 3 直接返回计数 0
  3. 返回计数供调用方汇总统计;需要参与相机取景的还返回点集(grass.py / scrub.pyfocus
  4. 不自己找数据。ring 由 generate_scene.py 传入,模块只负责装配

加一种新 OSM 要素

目标形态(docs/refactor-plan.md新增一个模块 + 注册一行,不改 build()

  1. 新建 osmassets/<feature>.py,写 assemble(...),签名照抄上面
  2. 只 import 需要的:from osmassets.geom import clip_polygonfrom osmassets.mesh import MeshBatch
  3. 材质规格加进 catalog.MATERIALS追加到末尾,顺序决定 GLB 材质索引)
  4. generate_scene.py 的要素分发处加一行调用
  5. 纯几何部分若有新函数,放 geom.pyblender/tests/test_pure.py

两个入口脚本

generate_scene.py (999行) export_cesium.py (624行)
调用 --background --factory-startup --python --background --python
输入 --osm + --geojson --blend
输出 --output(.blend) + --render(.png) --glb + --metadata(.json)
完成标记 SCENE_DONE CESIUM_EXPORT_DONE

完成标记是 parity 契约的一部分parity.js:121 解析它们),改动打印格式等于改动 契约。

sys.path 那段样板不能删

两个脚本开头都有(generate_scene.py:30-35export_cesium.py:19-24

# --factory-startup does not put the script's own directory on sys.path, so the
# osmassets package next to this file is not importable without this.
_HERE = os.path.dirname(os.path.abspath(__file__))
if _HERE not in sys.path:
    sys.path.insert(0, _HERE)

因此其后的 import 全部带 # noqa: E402。这不是可以"整理"掉的坏味道。

CLI 参数解析

两个脚本各有一份 cli_args(),都从 -- 之后取参数:

argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []

export_cesium.py:118-124 对三个必填项逐个 raise RuntimeError必填项显式抛错, 不要给静默默认值——Blender 子进程里一个错的默认路径会写到意想不到的地方。


⚠️ 已知现状:两个入口靠材质名字符串对接

这是当前实际状态,不是设计目标。改动材质名之前必读。

export_cesium.py 不 import catalog(它只 import from osmassets.materials import link_alpha_clip)。它自己维护四张以材质名字符串 为键的覆盖表:

位置
EXPORT_TINTS export_cesium.py:68
EXPORT_METALLIC_OVERRIDES :80
EXPORT_BASE_COLOR_OVERRIDES :86
EXPORT_EMISSION_OVERRIDES :95

后果:

  • catalog.MATERIALS 里的 name 会静默断开这些覆盖。没有任何校验, 材质只是悄悄退回未调过的样子
  • catalog.CESIUM_EXPORTcatalog.py:142是死代码——定义了但全仓无人引用。 它是一次未完成的迁移,不要以为改它会生效
  • "Office White Metal Facade" 在四张表里都有,但 catalog已无此材质 docs/refactor-plan.md 记为缺陷 D1本轮只记录不修

改材质名时:四张表 + catalog.MATERIALS + catalog.ROAD_LAYERS 全部 grep 一遍。


反模式

反模式 后果
osm.py / geom.py / catalog.pyimport bpy 51 个单元测试整体崩,且像环境问题
按功能而非依赖新建模块(把几何和 bpy 混在一起) 该几何从此不可测
删掉 sys.path.insert 样板或 # noqa: E402 Blender 里 import 不到 osmassets
要素模块假设 ring 已裁剪 越界几何进场景
要素模块对退化输入抛异常 一个坏多边形中断整片区域
改材质名只改一处 Cesium 侧调色静默失效
以为改 catalog.CESIUM_EXPORT 会影响导出 它是死代码

相关