Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

.nornir/osm-3d-gaps.md

OSM-3D — exhaustive feature + gap ledger

nornir:class=reasoning

The single hero-feature ledger for facett's 3D OSM map (facett-map3d, facett-map3d-flight, facett-map, facett-osm). Mined from the git history + the .nornir map/OSM docs, this is the exhaustive "what ships / what is planned / what is missing" list — the backlog every OSM-3D round is dispatched against. Newest structural facts first; keep this file the source of truth for the gap set.

Legend: DONE shipped + proven (test/testmatrix cell) · PARTIAL exists but incomplete · GAP not built · DEFERRED intentionally parked (off-limits shader edit, heavy dep, needs korp contract).


A. Rendering / geometry (the extruded city)

Feature State Where / note
Building footprint → 3D prism extrusion DONE extrude.rs extrude_ways; height-by-OSM-tag proof building_extrusion_proof.rs
Height scales by real metric OSM tag DONE building_height + building_extrusion_proof (monotone A/B)
The real OSM height TAG ZOO parses (feet, comma decimals, semicolon lists, ranges, junk) DONE (2026-08-31) facett-map/src/height.rs; mesh gate no_building_spikes.rs; METRES_PER_LEVEL 3.0 → measured 3.2; agreement corpus vs osm_katana::shared::height. .nornir/building-height-design.md
Line ways → ground ribbons DONE roads/paths as ribbons, road_ribbon
Road hierarchy (width/casing/fill/glow by class) DONE road_hierarchy_proof.rs
Rivers / water polygons → reflective water DONE Surface::Water → WaterMesh, Gerstner + Fresnel + foam; flightsim_water.rs
Rail / tram lines as distinct ribbons GAP no rail-specific styling; folds into generic line ways today
Bridges / tunnels (layer/layer=* z-offset) GAP layer tag ignored — no over/under crossing separation
Landuse / parks / greens as ground layers DONE depth-ordered ground layers (GroundLayer, layer_lift)
Terrain plane under the city DONE terrain_plane
Real DEM / procedural relief heightfield DONE terrain.rs Dem/ProceduralTerrain; $FACETT_HGT SRTM .hgt
Roof shapes (gabled/hipped/pyramidal) DONE extrude_prism_styled gabled/hipped from roof:shape (default gabled small houses); watertight — overhanging gabled roofs fall back to the hipped fan so no see-through gap under the eave at street level (irregular_roof_stays_within_the_wall_footprint_no_overhang)
Building colour by material/building:colour DONE resolve_prism_style reads building:colour/roof:colour/roof:material, tasteful class defaults otherwise; pitched_roofs/coloured_buildings oracles
Both demo surfaces use the extruded/roofed/coloured path (no flat twin) DONE sigtuna_demo + facett-demo osm3d_view both from_geometry_relief_roofed().with_roof_accent() (L5, was flat from_geometry_relief in the tab)
Instanced GPU prisms + draw_indirect PARTIAL GPU depth path exists; full instanced-indirect fast-lane planned

B. Materials / lighting / post (the "expensive look")

Feature State Where / note
Filament PBR BRDF + physical exposure + ACES DONE map3d.wgsl; commit 483e296
IBL split-sum (SH-L2 diffuse + prefiltered spec) DONE commit 8c55c56
Sun + sky analytic atmosphere / skybox DONE sky.rs SunSky, sky.wgsl; sky_proof.rs
Cascaded shadow maps + PCF DONE shadow.wgsl; flightsim_shadows.rs
PCSS soft shadows DONE with_soft_shadows; shadow_softness.rs
SSAO / GTAO DONE ssao.wgsl; ssao_proof.rs
Bloom (HDR highlight glow) DONE bloom.wgsl; bloom_proof.rs
Planar reflection pass (+ wasm sky-gradient fallback) DONE with_planar_reflection
Aerial / height fog DONE map3d.wgsl aerial fog
Vignette / film grain / colour grade DONE grade pass (graphics roadmap P0)
Time-of-day sun-angle sweep (dawn→noon→dusk) DONE facett-map3d-flight/src/daycycle.rs DayCycle: real solar position (rises east → season-dependent noon → sets west), golden-hour reddening derived from the reused atmos transmittance LUT, twilight/night dimming; demo facett-demo/src/demo_photoreal.rs; bench heavy_daycycle; daycycle-{design,guide,for-idiots}.md
Weather (rain/snow/cloud/overcast) PARTIAL overcast/cloud variant DONE (daycycle::Weather cloudiness — greys+desaturates sky, dims sun, lifts ambient, monotone); precipitation particles + cloud-deck geometry still GAP
SSR (screen-space reflections) DEFERRED needs gpu.rs depth-sample seam (P2)
FFT ocean spectrum DEFERRED would replace water.wgsl (off-limits) (P3)
Parallax-occlusion mapping DEFERRED mesh-shader edit (off-limits) (P3)

C. Camera / navigation

Feature State Where / note
Orbit / pan / dolly-to-cursor DONE camera.rs OrbitCamera; damped + inertial
View presets (top/iso/oblique/front) + reset DONE ViewPreset; keyboard [1..4] R
Idle auto-rotate (spins when untouched) DONE MapFacet idle auto-rotate; idle-auto-rotate.md
Ground-floor clamp (never dive below terrain) DONE clamp_elev; robot_ui.rs
Garmin-nav bearing + tilt vocabulary DONE bearing_deg/tilt_deg; camera_nav_proof.rs
Cinematic flythrough autopilot (watch mode) DONE flythrough.rs; d6530ee
Sigtuna car-nav tour (air-circle → descend → drive the streets) DONE tour.rs SigtunaTour; scale-aware metric street chase (with_scale); Map3D::with_tour; real-data demo bin sigtuna_demo over the cropped embedded Sigtuna core; sigtuna_tour_proof.rs + tour_robot.rs + demo sigtuna_tour.rs; sigtuna-car-nav-tour.md
Minimap inset (top-down overview + heading cone) DONE (this round) minimap.rs MinimapView; minimap_proof.rs + minimap_robot.rs
Turn-by-turn route following (chase a route) DONE (this round) route.rs Route + apply_route_camera; SetRouteFollow/RouteProgress; camera target+bearing ride the polyline; route_proof.rs + route_robot.rs
6DOF flight camera (planetary, floating origin) DONE (this round) facett-map3d-flight/src/facet.rs FlightFacet — the engine finally has a host-facing pane. It owns no renderer: it drives facett_map3d::Map3D as the shared render core through the existing Garmin set_bearing_deg/set_tilt_deg vocabulary. tests/facet_robot.rs + probe_and_emit matrix cell. Also fixed the up-axis the whole flight layer had wrong — see flight-facet-and-world-frame-design.md §3: camera_fly::forward put the vertical in the Mercator NORTHING slot, so climbing moved the aircraft north and turning changed its altitude. Every rig test was basis-relative, so all of them passed on it.
Seize manual control from the autopilot (grab the stick) DONE Map3D::take_manual_control; any manual gesture disarms the tour (no jump), T (Msg::SetTour) re-arms; manual_control.rs robot proof
On-street drive faces FORWARD, between the houses DONE tour.rs drive eye azimuth = travel bearing + 180° (eye behind, looks down the road); flanking_houses() houses_left/houses_right oracle; osm3d_street_tour.rs (street_m=30, both_sides=290). FLIGHT viewer.
First-person / walk / street-level mode (pedestrian eye height) DONE (this round) FlightFacet Locomotion::Walk: eye pinned at WALK_EYE_HEIGHT_M = 1.70 above host-supplied ground, horizontal-only travel (looking up and walking forward does NOT take off), WALK_SPEED_MPS = 1.40 cap, 70° neck clamp — each a separate RED-when-broken assertion. FLIGHT viewer, as the partition requires. Remaining: no collision (a walker passes through walls — wants the picking/footprint-identity refactor first).
Smooth "fly-to" a named place / bbox PARTIAL framing + presets exist; no animated goto-target easing API

D. Overlays / HUD / interaction

Feature State Where / note
Street-name labels (car-nav, LOD-gated, billboarded) DONE street_label_anchors; 5aa8482
MSDF crisp glowing labels at any zoom DONE msdf.rs/msdf.wgsl (graphics roadmap P1)
HUD (angle / distance / visible-tri / hints) DONE view() bottom-left HUD
Minimap focus marker + heading cone DONE (this round) paint_minimap; state_json.minimap*
POI billboards (shops/amenities as icons) GAP only street-name labels; no amenity/POI point billboards. NB the 2D NAVIGATOR's pin overlay now clusters (facett-geomap/src/osm2d/cluster.rs); when POI billboards land in 3D they should consume that same screen-space binning rather than twin it.
Picking / click-to-inspect entity GAP mesh is an identity-less triangle soup; no ray-pick → feature id/tags
Route overlay polyline (from a collector) DONE (this round) extrude::project_route+route_overlay_into bake a bright ribbon+waypoint markers onto the streets; drawn in the minimap HUD (paint_minimap); state_json.route_*; route-overlay-nav.md
North / compass rose indicator PARTIAL minimap heading cone shows heading; no dedicated north rose
Distance / scale bar GAP metres_per_design_unit exists; not surfaced as an on-screen scale bar
Selection highlight / hover outline GAP depends on picking (above)

E. Data / streaming / LOD / source

Feature State Where / note
Altitude → LOD tier ladder (Ground/Low/Mid/High) DONE lod.rs; hysteresis; < 300 m → reflective water
Per-tier feature/way budget decimation DONE LodTier::feature_budget
Douglas–Peucker line simplification per tier DONE graphics roadmap
Tile streaming (plan resident/in-flight/evict) DONE facett-map3d-flight plan_tiles/TileSource
On-disk GeoParquet per-tile source DONE DirTileSource (facett_osm::read_ways)
Floating-origin f64→f32 rebase (planetary) DONE origin.rs FloatingOrigin
Region picker (Sweden poly + osm-katana clip) DONE region_picker; osm-region-picker.md
korp connector as the real layer source PARTIAL korp.rs load_layers(ways) seam; awaits korp's contract (intentionally thin)
Live overpass / tile-server fetch GAP reads local .osm.bz2/parquet only; no network fetch

F. Cross-cutting / proof surface

Item State Note
CPU painter path (headless, no-GPU) DONE every feature must survive here (self-test)
CPU painter software z-buffer + ACES tonemap DONE paint_cpu rasterizes through raster_zbuffer (a true 1/z per-pixel depth test carrying each vertex's view-space vz) instead of a painter's-order centroid sort — the depth-tearing fix where a small near face overlapped a large far one (buildings/terrain/ground-cover now resolve by TRUE depth on llvmpipe/wasm). Shaded colour runs through aces_tonemap/tonemap_shaded (Narkowicz ACES) to match the wgpu curve. Centroid depth_monotone stays the device-free conformance oracle.
Swappable RenderBackend seam DONE backend.rs; CPU / wgpu+vello / null
Robot-UI scenarios PARTIAL orbit-confinement + minimap + route covered; POI/pick pending their features
testmatrix cells per feature PARTIAL grows with each feature; minimap + route cells added
.nornir design + guide + SVG per feature PARTIAL this ledger + minimap-nav.md + route-overlay-nav.md current
Ground-plane mesh faces render (roads/route ribbons) DONE (2026-07-15) the CPU painter's screen-space back-face cull (shade_mesh_faces STEP 4) dropped flat +Y ground-plane ribbons because their screen-winding sign flips with the viewing hemisphere. Fix: keep any strongly up-facing face (nv.y > GROUND_FACE_UP_NY, i.e. roads · route ribbon · terrain cover · roof caps) regardless of screen winding — the map camera is always above the ground, so an up-facing surface is always front. Roads + the 3-D route ribbon now pixel-draw from above. Proven headless by ground_roads_proof.rs (a road adds visible faces from oblique and overhead) + route_robot.rs (the ribbon now changes the main 3-D view, not only the minimap). Goldens (golden_248/render_proof) unaffected beyond a deterministic 0.008 % sub-budget delta — those scenes carry no roads.

TWO distinct viewers (Rickard's partition — do NOT merge their feature sets)

Round landed 2026-07-22 (this round)

Round landed 2026-07-22 (late) — the three things Rickard saw on the running demo

Highest-value OPEN gaps (dispatch order)

  1. §F ground-plane ribbon render — INVISIBLE ROADS — DONE 2026-07-15. The CPU painter back-face-culled +Y ground-plane road/route ribbons (their screen-winding sign flips with the viewing hemisphere). Fixed in shade_mesh_faces STEP 4: keep any up-facing face (nv.y > GROUND_FACE_UP_NY) regardless of screen winding. Roads + the 3-D route ribbon now draw from above (headless oracle ground_roads_proof.rs; route_robot asserts the ribbon in the main view). Goldens unchanged (their scenes carry no roads).

  2. Picking / click-to-inspect (D) — thread a per-footprint feature id through extrude so a screen ray resolves to an OSM entity + its tags. Unlocks selection highlight, hover, and the inspect panel. Largest unlock; needs a mesh identity refactor (id buffer parallel to triangles).

  3. Route overlay polyline (D/E) — DONE this round. route.rs + extrude::{project_route, route_overlay_into} (reuses ribbon_rounded) + a minimap route line. Remaining: wire it into korp::load_layers as a real Route layer + fix ground-plane rendering so the 3D ribbon shows in the painter (see §F GAP).

  4. Turn-by-turn route following (C) — DONE this round. apply_route_camera

    • SetRouteFollow/RouteProgress; camera chases the route, bearing on the tangent. Remaining: ETA / next-turn HUD text.
  5. POI billboards (D) — amenity/shop nodes as LOD-gated icon billboards (reuse the street-label billboard machinery).

  6. Time-of-day lever + weather (B) — animate SunSky through a day cycle; add a cloud-deck / overcast sky variant + a cheap precipitation particle pass.

  7. Rail/tram + bridge/tunnel layering (A) — class-specific ribbons + layer tag z-offset so crossings read correctly.

(Rounds landed so far: #C-minimap, then #2 route overlay + #3 turn-by-turn follow. Next: #1 picking/click-to-inspect, and the §F ground-plane-render fix that lets the 3D route/road ribbons show in the CPU painter.)