94 lines
4.3 KiB
Markdown
94 lines
4.3 KiB
Markdown
# Extract road module
|
|
|
|
## Goal
|
|
|
|
Move Blender road assembly out of `blender/generate_scene.py` into a dedicated
|
|
bpy-layer module, keeping generated `.blend` / `.glb` / metadata structure
|
|
unchanged. This completes the P2 feature-module extraction except for the
|
|
deferred full `features/` registry.
|
|
|
|
## Background
|
|
|
|
- `.trellis/spec/guides/artifact-parity-guide.md` records P2 as partial:
|
|
`water.py`, `grass.py`, `scrub.py`, `tree.py`, `fountain.py`, and
|
|
`building.py` are already split out, while roads still live in
|
|
`generate_scene.py`.
|
|
- `blender/generate_scene.py:148` defines `add_geojson_layer()`, which reads
|
|
one osm2streets GeoJSON road layer, filters by bounds, projects coordinates,
|
|
clips polygons, batches them as `Road_<layer>`, and returns a feature count.
|
|
- `blender/generate_scene.py:656` creates `road_mats` by zipping
|
|
`catalog.ROAD_LAYERS` with `catalog.road_material_specs()`.
|
|
- `blender/generate_scene.py:741` runs `catalog.check_layers(geojson_dir)` and
|
|
prints `Layer catalog warning:` messages before iterating `ROAD_LAYERS`.
|
|
- `blender/generate_scene.py:750` falls back to simple OSM highway polylines
|
|
when `road_counts["road_surface"]` is zero.
|
|
- `blender/generate_scene.py:866` stores `scene["road_feature_counts"]`; the
|
|
same `road_counts` object is emitted in `SCENE_DONE` as `road_features`.
|
|
|
|
## Requirements
|
|
|
|
1. Add a new bpy-layer module at `blender/osmassets/roads.py`.
|
|
2. Move the current roads assembly behavior into the new module:
|
|
- GeoJSON layer mesh assembly currently in `add_geojson_layer()`;
|
|
- simple OSM highway fallback currently in the `road_surface == 0` block.
|
|
3. Keep `generate_scene.py` responsible for:
|
|
- creating `road_mats` from `catalog.ROAD_LAYERS` and
|
|
`catalog.road_material_specs()`;
|
|
- running `catalog.check_layers()` and printing existing warning text;
|
|
- owning `road_counts`;
|
|
- writing `scene["road_feature_counts"]`;
|
|
- writing the existing `SCENE_DONE` JSON shape.
|
|
4. Preserve existing road object names and mesh names:
|
|
- `Road_<layer_id>`
|
|
- `OSM_Road_<way_id>`
|
|
5. Preserve existing GeoJSON behavior:
|
|
- missing layer file returns count `0`;
|
|
- feature is skipped when `feature_in_bounds()` is false;
|
|
- rings come from `geometry_rings()`;
|
|
- projected rings are clipped with `clip_polygon()`;
|
|
- only rings with at least three points are batched and counted.
|
|
6. Preserve existing OSM fallback behavior:
|
|
- fallback triggers only when `road_counts.get("road_surface", 0) == 0`;
|
|
- only ways with a `highway` tag and at least two coordinates are drawn;
|
|
- widths remain `secondary: 7.0`, `residential: 5.5`, `service: 3.5`,
|
|
default `4.0`;
|
|
- fallback material remains `road_mats["road_surface"]`;
|
|
- fallback z remains `0.03`;
|
|
- fallback does not add to `road_counts`.
|
|
7. Do not introduce a full `features/` registry in this task.
|
|
8. Do not modify `ROAD_LAYERS`, `SCENE_LAYERS`, material definitions,
|
|
`MATERIALS` order, GeoJSON schema, z-index/z-height values, stdout markers,
|
|
parity ignore lists, or known unrelated defects D1-D3.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `blender/osmassets/roads.py` contains road assembly behavior and imports
|
|
only bpy-layer-safe dependencies.
|
|
- [ ] `blender/generate_scene.py` imports and calls the new roads module; no
|
|
inline `add_geojson_layer()` remains there.
|
|
- [ ] `road_counts`, `scene["road_feature_counts"]`, and `SCENE_DONE`
|
|
`road_features` remain unchanged.
|
|
- [ ] Existing `Layer catalog warning:` print behavior remains in
|
|
`generate_scene.py`.
|
|
- [ ] `python3 -m py_compile blender/osmassets/roads.py` passes.
|
|
- [ ] `python3 -m py_compile blender/generate_scene.py` passes.
|
|
- [ ] `python3 -m unittest blender/tests/test_pure.py` passes.
|
|
- [ ] 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 `features/` registry implementation.
|
|
- Any change to osm2streets road splitting, `SCENE_LAYERS`, or QGIS pipeline
|
|
behavior.
|
|
- Any change to `catalog.ROAD_LAYERS`, material names, color values, or order.
|
|
- Any change to fallback highway widths, z values, object names, or counts.
|
|
- Turning `catalog.check_layers()` warnings into errors.
|
|
- Moving `road_mats` creation or `road_feature_counts` scene metadata out of
|
|
`generate_scene.py`.
|
|
|
|
## Open Questions
|
|
|
|
None.
|