# Design feature registry ## Goal Introduce a conservative Blender feature registry so `generate_scene.py` no longer hard-codes every OSM feature dispatch branch, while keeping generated `.blend` / `.glb` / scene metadata and stdout contracts unchanged. The user value is maintainability: adding or moving a Blender feature should become a registration-level edit plus a feature module implementation, not a new branch woven through the main scene builder. ## Background - P2 module extraction is now complete for current feature assembly modules: `water.py`, `grass.py`, `scrub.py`, `tree.py`, `fountain.py`, `building.py`, and `roads.py` exist under `blender/osmassets/`. - `.trellis/spec/guides/artifact-parity-guide.md` records the remaining P2 gap as the missing `features/` registry. - `blender/generate_scene.py` still owns the orchestration sequence: material creation, OSM way dispatch, road GeoJSON/fallback dispatch, tree collection/fallback, fountain point dispatch, camera focus points, scene metadata, save/render, and `SCENE_DONE`. - Artifact parity is mandatory because this is intended as a pure refactor. ## Requirements 1. Add a new Blender-side registry module under `blender/osmassets/`, expected path `blender/osmassets/features.py`. 2. Replace the inline OSM way `if` / `elif` feature dispatch in `generate_scene.py` with registry-driven dispatch while preserving exact behavior and order: - water before grass; - grass before scrub; - scrub before tree-row collection; - tree-row collection before building; - first matching handler wins, as the current `elif` chain does. 3. Make the current feature phases explicit in code: - OSM way feature phase; - road GeoJSON / OSM fallback phase; - tree placement phase; - point prop phase for fountains. 4. Keep `generate_scene.py` responsible for high-level scene contracts: - CLI parsing and tree-style validation; - collection creation and collection order; - material creation order; - `counts`, `road_counts`, tree counts, and `focus_points` ownership; - `catalog.check_layers()` and exact `Layer catalog warning:` print text; - scene custom properties; - `SCENE_DONE` marker and JSON shape. 5. Do not change current feature module assembly signatures unless the change is a mechanical adapter around the same behavior. 6. Do not reorder `catalog.MATERIALS`, `catalog.ROAD_LAYERS`, collection creation, object creation phases, or `SCENE_DONE` fields. 7. Do not introduce a second list of osm2streets road layer ids. Road layer iteration must still derive from `catalog.ROAD_LAYERS`. 8. Do not fix unrelated known defects D1-D3 in this task. ## Acceptance Criteria - [ ] `blender/osmassets/features.py` exists and contains the registry / dispatcher contract for Blender feature phases. - [ ] `generate_scene.py` uses the registry for OSM way feature dispatch; the previous inline `if` / `elif` way feature chain is removed or reduced to small handler callbacks. - [ ] Current generated object names, mesh names, material creation order, collection order, scene properties, and `SCENE_DONE` JSON remain unchanged. - [ ] `road_counts`, `counts`, tree counts, and `focus_points` continue to be owned by `generate_scene.py` or by explicit objects passed from it, not hidden in global module state. - [ ] `catalog.check_layers()` warning behavior remains in `generate_scene.py` with the exact `Layer catalog warning:` text. - [ ] `python3 -m py_compile blender/osmassets/features.py` passes. - [ ] `python3 -m py_compile blender/generate_scene.py` passes. - [ ] `python3 -m unittest blender/tests/test_pure.py` passes. - [ ] Blender/Cesium parity before/after is run for `nantaizi-lake-innovation-valley` and `hanyang-block`; expected compare result is `identical` for both. ## Out Of Scope - Changing feature behavior, geometry, z values, material definitions, material order, or catalog declarations. - Moving material creation into feature modules. - Moving scene metadata / `SCENE_DONE` construction out of `generate_scene.py`. - Reworking tree model loading, procedural tree fallback, grass tuft loading, scrub bush loading, or camera focus logic beyond adapter-level plumbing. - Adding new rendered feature types. - Changing parity ignore lists or stdout marker parsing. ## Open Questions None.