facett-map3d/examples/sigtuna_tour.rs
//! **facett — the Sigtuna cinematic tour** (`sigtuna_tour` example).
//!
//! The Google-Maps-3D-quality demo brief, made launchable: a standalone eframe/wgpu
//! window that opens HIGH in the air **circling** an extruded town so you SEE THE
//! HOUSES from a tilted bird view, then **descends** (bird → street) and **drives DOWN
//! THE STREETS** at ground level like a car-navigator following a real road ribbon.
//! One deterministic autopilot ([`Map3D::with_tour`] → [`facett_map3d::SigtunaTour`]),
//! three phases (circle → descend → drive), riding ON TOP of the existing beautiful
//! [`Map3D`] GPU render (PBR + IBL + sky + soft shadows + SSAO + bloom).
//!
//! Untick **auto-tour** to grab the orbit controls yourself; re-tick to resume.
//!
//! Run (on a display):
//! ```bash
//! DISPLAY=:0 cargo run -p facett-map3d --example sigtuna_tour --features wgpu --release
//! ```
// Native-only (opens an eframe window). Empty `main` on wasm so the crate still
// builds for `wasm32-unknown-unknown`.
#[cfg(target_arch = "wasm32")]
fn main() {}
#[cfg(not(target_arch = "wasm32"))]
use app::App;
#[cfg(not(target_arch = "wasm32"))]
mod app {
use eframe::egui;
use facett_core::Facet;
use facett_map::Way;
use facett_map3d::Map3D;
/// A deterministic procedural town (a grid of extruded building rings) + a road
/// that snakes through it — so the demo runs fixture-free while giving the tour
/// both a framed skyline to circle AND a street to drive. Point a real OSM extract
/// in later via `facett-osm` (the demo crate's embedded Sigtuna clip) if wanted.
fn town() -> (Vec<Way>, Vec<(f32, f32)>) {
let lon0 = 17.720; // Sigtuna, Sweden
let lat0 = 59.617;
let cell = 0.0014;
let n = 10;
let mut ways = Vec::new();
let mut id = 1i64;
let hash = |a: i64, b: i64| -> u32 {
let mut h = (a.wrapping_mul(73856093) ^ b.wrapping_mul(19349663)) as u32;
h ^= h >> 13;
h = h.wrapping_mul(0x9E3779B1);
h ^ (h >> 16)
};
for gy in 0..n {
for gx in 0..n {
let h = hash(gx, gy);
// Leave a clear diagonal channel for the street (no houses on the road).
if h % 7 == 0 || (gx - gy).abs() < 1 {
continue;
}
let cx = lon0 + (gx as f64 + 0.5) * cell;
let cy = lat0 + (gy as f64 + 0.5) * cell;
let half = cell * (0.18 + (h % 13) as f64 * 0.012);
let ring = vec![
(cx - half, cy - half),
(cx + half, cy - half),
(cx + half, cy + half),
(cx - half, cy + half),
(cx - half, cy - half),
];
let fill = egui::Color32::from_rgb(90 + (h % 60) as u8, 150 + (h % 70) as u8, 210 + (h % 45) as u8);
let outline = egui::Color32::from_rgb(20, 40, 60);
ways.push(Way::filled(id, "building", ring, fill, outline, 1.0));
id += 1;
}
}
// A road running diagonally across the town — the street the drive phase rides.
let mut road_pts = Vec::new();
for gi in 0..=n {
let g = gi as f64;
road_pts.push((lon0 + (g + 0.5) * cell, lat0 + (g + 0.5) * cell));
}
let road = Way::line(id, "highway", road_pts, egui::Color32::from_rgb(70, 78, 90), 2.5);
// The route follows the road's Mercator-unit points (same as `Map3D::with_route`).
let waypoints = road.points.clone();
ways.push(road);
(ways, waypoints)
}
pub struct App {
map: Map3D,
autotour: bool,
}
impl App {
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// Install the GPU depth-tested renderer into eframe's wgpu render state.
if let Some(rs) = cc.wgpu_render_state.as_ref() {
facett_map3d::gpu::install_renderer(rs);
}
let (ways, waypoints) = town();
let map = Map3D::from_geometry("Sigtuna · cinematic tour", &ways)
.with_gpu(true)
.with_route(&ways, &waypoints)
.with_minimap(true)
.with_tour(true);
Self { map, autotour: true }
}
}
impl eframe::App for App {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
// Keep the tour animating even with no input events.
ui.ctx().request_repaint();
egui::Panel::top("bar").show(ui, |ui| {
ui.horizontal_wrapped(|ui| {
ui.heading("facett · Sigtuna 3D tour");
ui.separator();
if ui.checkbox(&mut self.autotour, "auto-tour (circle → descend → drive)").changed() {
self.map.set_tour(self.autotour);
}
ui.separator();
let phase = self.map.tour_phase().map(|p| p.tag()).unwrap_or("manual");
ui.label(if self.autotour {
format!("phase: {phase} — circling the houses, then driving the streets (untick to take the controls)")
} else {
"drag to orbit · wheel to zoom · re-tick auto-tour to resume".to_string()
});
});
});
egui::CentralPanel::default().show(ui, |ui| {
facett_core::set_theme(ui.ctx(), facett_core::Theme::sci_fi());
Facet::ui(&mut self.map, ui);
});
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn main() -> eframe::Result<()> {
let native_options = eframe::NativeOptions {
viewport: eframe::egui::ViewportBuilder::default()
.with_inner_size([1100.0, 760.0])
.with_title("facett — Sigtuna 3D cinematic tour"),
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"facett-sigtuna-tour",
native_options,
Box::new(|cc| Ok(Box::new(App::new(cc)))),
)
}