# Extract fountain module ## Goal Move the fountain assembly code out of `blender/generate_scene.py` into a dedicated bpy-layer module, keeping the generated `.blend` / `.glb` structure unchanged. This is the first low-risk step toward the documented P2 feature module / registry direction. ## Background - `.trellis/spec/guides/artifact-parity-guide.md` records P2 as partial: `water.py`, `grass.py`, `scrub.py`, and `tree.py` exist, but `building`, `fountain`, and `roads` still live in `generate_scene.py`. - `blender/generate_scene.py:639` defines `add_fountain()` inline. - `blender/generate_scene.py:895` dispatches OSM point features with `amenity=fountain`, calls `add_fountain("Fountain_" + id, ...)`, and increments `counts["fountain_count"]`. - `blender/osmassets/catalog.py` already owns the three fountain materials: `fountain_stone`, `fountain_water`, and `fountain_spray`. - This task is a pure refactor. It must not change fountain geometry, object names, materials, custom properties, counts, stdout markers, or Cesium export behavior. ## Requirements 1. Add a new bpy-layer module at `blender/osmassets/fountain.py`. 2. Move the current fountain assembly behavior from `generate_scene.py` into the new module. 3. Keep `generate_scene.py` responsible for: - creating fountain materials from `catalog.MATERIALS`; - filtering `point_features` for `amenity=fountain`; - projecting coordinates; - incrementing `counts["fountain_count"]`. 4. Preserve the existing object names: - `Fountain__Basin` - `Fountain__Water` - `Fountain__Pedestal` - `Fountain__Water_Crown` - `Fountain__Droplet_` 5. Preserve the existing basin custom property: `osm_feature = "amenity=fountain"`. 6. Do not introduce a full `features/` registry in this task. 7. Do not modify `building`, `roads`, material definitions, `MATERIALS` order, `ROAD_LAYERS` order, stdout markers, or parity ignore lists. 8. Do not fix unrelated documented defects D1, D2, or D3. ## Acceptance Criteria - [x] `blender/osmassets/fountain.py` contains the fountain assembly behavior and imports only bpy-layer-safe dependencies. - [x] `blender/generate_scene.py` imports and calls the new fountain module; no inline `add_fountain()` function remains there. - [x] Scene counts and `SCENE_DONE` JSON keys remain unchanged. - [x] `python3 -m unittest blender/tests/test_pure.py` passes. - [x] `python3 -m py_compile blender/osmassets/fountain.py` passes. - [x] `python3 -m py_compile blender/generate_scene.py` passes. - [x] A before/after parity comparison is run for the Blender/Cesium stages and any non-ignored diff is either absent or explicitly explained as expected. For this refactor, the expected result is no contract diff. ## Out Of Scope - Full feature registry design or implementation. - Building extraction. - Road extraction. - Fountain visual redesign or geometry changes. - Material contract changes. - Documentation-only cleanup of stale comments such as D3. ## Open Questions None.