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

nordisk/facett

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)))),
    )
}