facett-map3d/examples/flythrough.rs
//! **facett — OSM 3D flight-sim / cinematic flythrough** (`flythrough` example).
//!
//! A standalone eframe/wgpu window that WATCHES a camera fly the extruded OSM 3D city
//! on its own: the buildings rise from real footprints, the GPU depth buffer orders
//! them, and the [`Map3D`] cinematic flythrough ([`Map3D::with_flythrough`]) orbits +
//! cruises + sweeps the skyline — no keyboard needed. Toggle **auto-fly** off to grab
//! the orbit controls yourself.
//!
//! Run (on a display):
//! ```bash
//! DISPLAY=:0 cargo run -p facett-map3d --example flythrough --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 city (a grid of closed, filled building rings) so the
/// demo runs fixture-free; `from_geometry` frames the densest cluster and the
/// flythrough flies it. Point a real OSM extract in later via `facett-osm` if wanted.
pub fn grid_city() -> Vec<Way> {
let lon0 = 9.50;
let lat0 = 47.10;
let cell = 0.0016;
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);
if h % 7 == 0 {
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;
}
}
ways
}
pub struct App {
map: Map3D,
autofly: 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 = grid_city();
let map = Map3D::from_geometry("OSM 3D flythrough", &ways).with_gpu(true).with_flythrough(true);
Self { map, autofly: true }
}
}
impl eframe::App for App {
// This facett eframe build hands the app a single full-window `Ui` (the same
// `App::ui` signature `osm3d_demo.rs` uses), so panels lay out with the
// `Ui`-taking `show` (egui 0.36's name for `show_inside`).
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
// Keep the flight animating even with no input events.
ui.ctx().request_repaint();
egui::Panel::top("bar").show(ui, |ui| {
ui.horizontal_wrapped(|ui| {
ui.heading("facett · OSM 3D flight-sim");
ui.separator();
if ui.checkbox(&mut self.autofly, "auto-fly (cinematic flythrough)").changed() {
self.map.set_flythrough(self.autofly);
}
ui.separator();
ui.label(if self.autofly {
"watching the camera fly the city — untick auto-fly to take the controls"
} else {
"drag to orbit · wheel to zoom · re-tick auto-fly to resume the flythrough"
});
});
});
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 — OSM 3D flight-sim (cinematic flythrough)"),
renderer: eframe::Renderer::Wgpu,
..Default::default()
};
eframe::run_native(
"facett-flythrough",
native_options,
Box::new(|cc| Ok(Box::new(App::new(cc)))),
)
}