# Material export contract **类型**:refactor · **范围**:`blender` · **创建**:2026-08-03 --- ## Goal 把 Cesium 导出调色/覆盖规则从 `blender/export_cesium.py` 里的材质名字符串表迁移到 `blender/osmassets/catalog.py` 的材质声明中,让 Blender 场景材质自己携带 Cesium 导出契约,降低改材质名时静默断开导出效果的风险。 用户价值:后续新增或重命名材质时,只需要维护 `catalog.MATERIALS` 附近的材质定义; Cesium 导出器消费 `.blend` 中保存的契约,而不是靠另一份易漂移的字符串表猜测。 ## Background 当前实际状态由代码和 spec 共同确认: - `blender/osmassets/catalog.py:56` 的 `MATERIALS` 是材质声明源;`materials.py:198` 的 `from_spec()` 把 spec 构造成真实 Blender 材质。 - `blender/export_cesium.py:68,80,86,95` 仍维护四张以 `material.name` 为 key 的导出覆盖表: `EXPORT_TINTS`、`EXPORT_METALLIC_OVERRIDES`、`EXPORT_BASE_COLOR_OVERRIDES`、 `EXPORT_EMISSION_OVERRIDES`。 - `blender/osmassets/catalog.py:142` 已有 `CESIUM_EXPORT`,但 `.trellis/spec/blender/module-structure.md` 记录它是死代码,当前导出器不会读取。 - `"Office White Metal Facade"` 仍出现在 `export_cesium.py:74,82,91,100`,但当前 `catalog.MATERIALS` 已无对应材质;这是 `docs/refactor-plan.md` / spec 记录的 D1。 - `.trellis/spec/guides/artifact-parity-guide.md` 记录 P3 未做:材质契约化仍缺失。 ## Requirements 1. `catalog.MATERIALS` 中需要 Cesium 特殊导出的材质必须就地携带 `cesium` 子契约。 该契约覆盖现有四张表的有效行为,包括 `tint`、`metallic`、`base_color`、`emission`。 2. `materials.from_spec()` 必须在创建材质时把 `spec["cesium"]` 序列化写入材质自定义属性 `material["cesium_export"]`,使 `.blend` 能保存该契约。 3. `export_cesium.py` 必须优先读取 `material["cesium_export"]` 并据此导出;只有旧 `.blend` 或缺失属性的材质才回退到现有四张表。 4. 旧 `.blend` 兼容性必须保留:没有 `cesium_export` 自定义属性时,导出行为不应比当前更少。 5. alpha cut-out foliage 的特殊路径保持现状:`FOLIAGE_ALBEDO_GAIN`、`FOLIAGE_SATURATION`、 `FOLIAGE_EMISSION`、`alpha_dilated_image()` 和 diffuse-as-emissive texture 逻辑不在本任务重构。 6. 本任务是契约迁移,不是视觉调参;目标是新生成产物的结构摘要尽量与迁移前一致。 7. 本任务不得把 `bpy` 引入 `catalog.py`,不得把 `export_cesium.py` 改成直接 import `catalog` 再按材质名反查。 8. 任务完成后需要更新 `.trellis/spec/blender/` 中关于当前材质名字符串对接、`catalog.CESIUM_EXPORT` 死代码、P3 未完成状态的说明。 ## Out of Scope - 不修 `docs/refactor-plan.md` 的 D2:`scene-layers.js` 与 `catalog.py` 颜色不同步已经被 spec 定性为刻意设计。 - 不清理 `generate_scene.py` 的 `tuft_density_wave` 注释 D3。 - 不做 P2 features 注册表重构。 - 不改变道路图层顺序、`MATERIALS` 创建顺序、stage stdout 标记或 Cesium preview 运行时。 - 不新增依赖、lint/type 工具链或 CI。 ## Acceptance Criteria - [ ] 新生成的 `.blend` 材质中,已有 Cesium 覆盖规则的 catalog 材质带有 `cesium_export` 自定义属性。 - [ ] `export_cesium.py` 对带 `cesium_export` 的材质优先使用该属性;对缺失属性的旧材质仍走 旧四张表回退。 - [ ] `"Office White Metal Facade"` 不再出现在新契约源中;若旧表保留它,只能作为旧 `.blend` 回退兼容存在,并在代码注释中说明。 - [ ] `catalog.CESIUM_EXPORT` 不再是死代码:要么被移除并迁入 `MATERIALS[*]["cesium"]`, 要么被真实读取;最终 spec 与代码描述一致。 - [ ] `python3 -m unittest discover blender/tests` 通过。 - [ ] 对默认 parity 样本执行改动前/改动后 capture + compare;如果结构摘要差异存在, 必须逐条解释为预期差异或修复到全绿。 - [ ] `.trellis/spec/blender/module-structure.md` 与 `.trellis/spec/blender/asset-generation.md` 更新为新的材质导出契约,不再把“改 `catalog.CESIUM_EXPORT` 不会生效”描述为当前事实。 ## Key Decisions - 采用材质自定义属性 `cesium_export` 作为跨 `.blend` 的契约载体,而不是让导出器 import `catalog`。原因:旧 `.blend` 没有当前 Python catalog 上下文也应该能导出;契约必须跟着场景文件走。 - 回退旧四张表作为兼容层保留到本任务结束,不在同一任务里删除兼容路径。 - 不触碰 foliage alpha 特殊路径,避免把高风险贴图处理与 contract 迁移混在一起。 ## Open Questions 无阻塞开放问题。实现前仍需要用户确认本规划摘要并允许进入 `in_progress`。