Sigtuna car-nav tour — the Google-Maps-3D cinematic autopilot
What it is
Map3D::with_tour(true) (→ SigtunaTour) turns the framed 3D city into the demo brief made real: the camera opens HIGH in the air circling the extruded town so you SEE THE HOUSES from a tilted bird view, then descends (bird → street), then drives DOWN THE STREETS at ground level like a car-navigator, following a real OSM road ribbon. One deterministic autopilot, three phases, riding ON TOP of the existing beautiful Map3D GPU render (PBR + IBL + sky + soft shadows + SSAO + bloom + reflective water) — every "expensive look" pass is what you watch the tour through.
It is the car-nav / flight-mode complement to flythrough (which orbits/cruises the skyline but never lands): the tour is the one that lands and drives.
The three phases (deterministic in the clock t, seconds; the tour LOOPS)
Given the framed city (flythrough::CityFraming — centroid, extent, roof, base framing) and an optional projected street route::Route:
- Circle (
0 .. circle_secs): a slow, high, tilted orbit (~30° above horizon) so the houses read as 3D prisms from above — "we want to SEE THE HOUSES." - Descend (
.. + descend_secs): asmoothstepswoop that blends the high air pose into the street-level chase pose (distance + elevation drop, target slides from the town centre onto the route start) — the bird → street reveal. - Drive (
.. + drive_secs): a low, forward-leaning car-nav chase that rides theRoutetangent down the streets (target on the road point, the same close chase tilt/distance the turn-by-turn follow uses), facing FORWARD down the road — the eye sits BEHIND the car (the eye azimuth is the travel bearing turned 180°, since the orbit eye is placedtarget + dist·dir(azimuth)), so you look at the street ahead with the houses flanking both sides, not backward up the road you came from. With no route it degrades to a slow ground-level creep around the town.
Design (deterministic, FC-7)
tour::SigtunaTour::pose(t)— a PURE function of the clock giving the same(target, azimuth, elevation, distance)4-tuple asFlythrough, so the caller writes it onto anOrbitCameraidentically.phase(t)reports which leg the clock lands in.- Reuse, not reinvention (L5): the air phase reuses the framed city + the orbit convention; the drive phase reuses the projected
Route+ the car-nav chase constants — now a single source of truthOrbitCamera::CHASE_TILT_DEG/CHASE_DIST, consumed by BOTH the map's route-follow and the tour drive. - Scale-aware drive (
SigtunaTour::with_scale): the design-unitCHASE_DISTconstant is fine for a normalised city, but on a metric scene (a real OSM clip normalised to a wide design box) it is hundreds of metres out — the drive would hover, houses tiny. When the scene scale is known (Map3D::metres_per_design_unit, passed byset_tour), the drive instead sits a REALSTREET_CHASE_M(30 m) street distance from the road (floored at the camera dolly floor), so houses loom at street level whatever the town's real-world span.0scale keeps the design-unit fallback (tests). render_camera()folds the tour in (likeflythrough/ thespinauto-orbit): when attached it drives a clone of the camera fromclock_s(pinned → deterministic tests) or the livefly_clocktheFrametick advances, thensettle()applies the elevation / never-below-ground / dolly clamps so the tour can never sink through the ground or gimbal-flip. Default OFF — additive, API-safe; the interactive orbit map and every existing render-proof snapshot are unchanged.state_jsonexposestour_active+tour_phase(circle/descend/drive) so a robot-UI / headless matrix can assert the camera walks the three legs as DATA. It also exposeshouses_left/houses_right— the risen building vertices flanking the camera's forward axis on each side (Map3D::flanking_houses), the "am I driving BETWEEN the houses?" oracle: on a real on-street drive pose BOTH are> 0.- Seize the stick (
Map3D::take_manual_control): the tour overrides the manual pose while armed, but any manual gesture now HANDS CONTROL BACK — every manual-inputMsg(orbit / pan / dolly / keyboard / preset / reset) callstake_manual_control()first, which freezes the live camera at the tour's current settled pose (no jump) and drops the tour, so the user flies/drives freely from there. The tour key T (Msg::SetTour(true)) re-arms the autopilot. This is the "the tour plays but I can't grab the flight" fix; proven bymanual_control.rs(a manual orbit disarms the tour + keeps moving the eye; T re-arms).
Why this shape
The camera clamps already guarantee a legal pose, so the tour only has to choose a pose per clock — no new render pass, no new resource, no fork of the pipeline. The half-open phase intervals sample strictly WITHIN the drive leg: frac == 1.0 lands exactly on the loop boundary (t == total_secs), which rem_euclid wraps back to the circle, so the route end is reached just before the wrap. Pinned-clock purity means a snapshot at a fixed clock is exact and a headless test flies a bit-reproducible route.
Tests
tour.rsunit (6): phases sequence & loop; the tour DESCENDS (drive pulls closer + levels vs the air circle); the air phase ORBITS (azimuth advances); the drive RIDES the route (target/bearing matchroute.point_at/bearing_deg_at, deterministic); driving the real camera stays above ground + finite across a full sweep; no-route creep fallback stays low/close/finite.tests/sigtuna_tour_proof.rs(headless, data-level): builds a synthetic town + road, extrudes it (a raised mesh vertex proves houses are 3D), attaches the tour, walks the clock across a full loop off the liveMap3Drender camera, and asserts Circle → Descend → Drive, the bird→street distance drop, the air orbit, and pinned-clock determinism. Emits afunctional_statusmatrix cell (sigtuna_tour_circle_descend_drive) under#[cfg(feature="testmatrix")].
Watch it (the Sigtuna demo)
DISPLAY=:0 cargo run -p facett-map3d --example sigtuna_tour --features wgpu --release
Opens circling the extruded houses, descends, then drives the diagonal street. Untick "auto-tour" to grab the orbit controls yourself; re-tick to resume. The top bar shows the live phase (circle / descend / drive).
Watch it on REAL Sigtuna (facett-demo bin sigtuna_demo)
cargo run -p facett-demo --bin sigtuna_demo --release
Loads the embedded real Sigtuna OSM clip, CROPS it to its dense town core (osm_tabs::crop_to_town_core — the raw ~16 km regional clip normalises real houses to invisible slivers; the core is house-scale so you SEE THE HOUSES), extrudes it into the AAA GPU Map3D + the nav minimap inset, and flies the tour with the scale-aware metric street chase. The drive route is derived from the loaded OSM highway graph (the longest road ribbon in the core). T re-arms the tour, M toggles the minimap, drag/wheel takes manual control. Proven headless on the real clip by facett-demo/tests/sigtuna_tour.rs (houses extrude to real ~7.5 m height, the drive is ~30 m street level, the circle a ~500 m bird height; matrix cell sigtuna_navigator_tour) + the crop_to_town_core unit test.
"SEE THE HOUSES" — the houses-in-frame oracle (buildings_visible)
The brief is visual ("we want to SEE THE HOUSES"), so the render exposes a DATA oracle that a headless / robot test can assert without a GPU or a human eye:
Map3D::buildings_visible() -> usize— the count of risen (wall / roof) mesh vertices currently inside the view frustum. It reuses the liverender_camera()+ the shared perspective projection: a vertex counts when it projects in front of the near plane AND inside the square viewport bound.> 0⇒ extruded houses are on screen at this pose.building_vertices()is the camera-independent total (the risen budget).BUILDING_RISE_EPS(4.0e-3design units) is the wall/roof cutoff — set well clear of the whole ground band (ground-cover caps ≈GROUND_LIFT, lifted road + route ribbons top out ≈1.6·GROUND_LIFT) so a road-only scene reports zero buildings, while a real house (≥ ~7.5e-3even for a low Sigtuna building atmpu≈481) counts.- Both land in
state_json(buildings_visible,building_vertices), so the tour's keyframe test asserts "at the air-circle altitude the houses are visible" as a number. On the real cropped Sigtuna core the air-circle frames 2394 risen verts.
Roof accent (the Google-Maps-3D massing look)
Map3D::with_roof_accent(strength) (opt-in, chainable) lightens the up-facing top-cap triangles of the extruded prisms toward a warm off-white highlight, in proportion to their height, so a town reads as lit rooftops over shaded facades — the Google-Maps-3D massing cue. It only touches cap triangles whose three vertices point +Y and sit above BUILDING_RISE_EPS, so walls (horizontal normals), bottom caps (−Y), and flat ground-cover caps (y≈0) are left untouched. OFF by default → the shared extrusion path and every render-proof golden (incl. golden_248) stay byte-stable (L2); the sigtuna_demo bin opts in at 0.55.
Keyframe / perf coverage (this round)
tests/sigtuna_tour_proof.rsextended: asserts the altitude keyframe (metricaltitude_mHIGH mid air-circle vs LOW mid drive —air ≫ 2·street) ANDbuildings_visible > 0at the circle altitude. RED-when-broken verified by mutation (neutralise the descent → distance stops dropping → the keyframe assertion fails; and a flat road-only scene →buildings_visible == 0, the lib unitbuildings_visible_...).tests/tour_robot.rs(live egui_kittest render) also readsbuildings_visibleoff the rendered Facet at the air-circle frame and asserts the houses are in the shot.- Bench arm
map3d.tour/frame_shade_circle(benches/heavy_render.rs): the per-frame CPU render cost (project + clip + cull + light + sort) at the demo's HIGH opening pose — the heaviest view (whole skyline in frame). Light-verified on Loki (12×12 town, 10948 tris → ~1.86 ms/frame, 537 houses in frame); HEAVY authoritative run defers to the Loki bencher (MAP3D_BENCH_TOWNsets the grid side).
Known gaps / follow-ups
Wire the tour onto the embedded real Sigtuna clip— DONE (sigtuna_demobin, cropped to the town core, scale-aware street chase).Derive the drive route from the loaded OSM highway graph— DONE (the bin picks the longest road ribbon in the cropped core as the drive route).- Optional: expose the crop radius +
STREET_CHASE_Mas live sliders in the demo HUD; and per-vertex terrain-follow for the drive eye height (today it holds the core's ground altitude, fine for a compact town core).