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

nordisk/facett

facett-map3d/tests/flythrough_render.rs

//! **Cinematic flythrough render-proof (LAW #4) + the "it actually MOVES" gate.**
//!
//! The whole point of the flight-sim is a camera you WATCH fly the city on its own.
//! This test builds a real (synthetic) OSM city, turns on the cinematic flythrough,
//! and proves two things a still screenshot cannot:
//!   1. **wgpu render-proof (non-blank):** rendered through the GPU depth-tested path
//!      (`gpu::install_renderer` + kittest's wgpu backend) at several clock steps, the
//!      framebuffer is a lit city, not a black pane — at EVERY step of the flight.
//!   2. **the camera actually flies:** the `render_camera` eye moves a real distance
//!      between clock steps AND the town stays framed (the look-at stays over the
//!      city), i.e. it frames the city over steps rather than sitting still.
//!
//! Run: `cargo test -p facett-map3d --features wgpu --test flythrough_render -- --nocapture`

#![cfg(feature = "wgpu")]

use egui_kittest::wgpu::{create_render_state, default_wgpu_setup, WgpuTestRenderer};
use egui_kittest::Harness;
use facett_core::Facet;
use facett_map::Way;
use facett_map3d::Map3D;

/// Fraction of pixels far from the modal (≈background) colour — "is there a lit city
/// on screen, not a blank pane?" as a number.
fn content_ratio(img: &image::RgbaImage) -> f64 {
    let mut hist = std::collections::HashMap::<u32, u32>::new();
    for p in img.pixels() {
        let key = ((p.0[0] as u32 >> 3) << 10) | ((p.0[1] as u32 >> 3) << 5) | (p.0[2] as u32 >> 3);
        *hist.entry(key).or_default() += 1;
    }
    let (modal, _) = hist.iter().max_by_key(|(_, c)| **c).map(|(k, c)| (*k, *c)).unwrap();
    let (mr, mg, mb) = (((modal >> 10) & 0x1f) as i32, ((modal >> 5) & 0x1f) as i32, (modal & 0x1f) as i32);
    let total = (img.width() * img.height()) as usize;
    let mut content = 0usize;
    for p in img.pixels() {
        let (r, g, b) = ((p.0[0] >> 3) as i32, (p.0[1] >> 3) as i32, (p.0[2] >> 3) as i32);
        if (r - mr).abs() + (g - mg).abs() + (b - mb).abs() > 3 {
            content += 1;
        }
    }
    content as f64 / total as f64
}

/// A deterministic procedural city (mirrors the `osm3d_demo` grid): a block of closed,
/// filled building rings so `from_geometry` frames a real densest cluster.
fn grid_city() -> Vec<Way> {
    let lon0 = 9.50;
    let lat0 = 47.10;
    let cell = 0.0016;
    let n = 8;
    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
}

/// Render the flythrough city at a pinned clock through the GPU depth path; return the
/// on-screen content ratio.
fn render_at(ways: &[Way], clock: f64) -> f64 {
    let rs = create_render_state(default_wgpu_setup(), Default::default());
    facett_map3d::gpu::install_renderer(&rs);
    let mut view = Map3D::from_geometry("flythrough-city", ways).with_gpu(true).with_flythrough(true).with_clock(clock);
    let mut h = Harness::builder()
        .with_size(egui::vec2(820.0, 820.0))
        .renderer(WgpuTestRenderer::from_render_state(rs))
        .build_ui(move |ui| {
            facett_core::set_theme(ui.ctx(), facett_core::Theme::sci_fi());
            view.ui(ui);
        });
    h.run_steps(4);
    let img = h.render().expect("wgpu render");
    content_ratio(&img)
}

#[test]
fn flythrough_renders_a_moving_city_on_the_gpu() {
    let ways = grid_city();
    assert!(!ways.is_empty(), "the synthetic city has building footprints");

    // The flythrough captured a real framing (a densest cluster to fly around).
    let probe = Map3D::from_geometry("probe", &ways).with_flythrough(true);
    assert!(probe.flythrough_active(), "flythrough is on");
    let framing = probe.city_framing().expect("from_geometry framed the densest cluster");
    assert!(framing.extent > 0.0 && framing.extent.is_finite(), "real city extent");

    // (2) The camera actually FLIES: the render_camera eye moves between clock steps,
    // while the look-at stays over the town (framed the whole flight).
    let eye = |t: f64| {
        let m = Map3D::from_geometry("c", &ways).with_flythrough(true).with_clock(t);
        m.render_camera().eye()
    };
    let (e0, e1, e2) = (eye(0.0), eye(3.0), eye(7.0));
    let d01 = e1.sub(e0).len();
    let d12 = e2.sub(e1).len();
    eprintln!("[flythrough] eye moved {d01:.4} (0→3s) and {d12:.4} (3→7s)");
    assert!(d01 > 0.02 && d12 > 0.02, "the cinematic camera flies (moved {d01:.4}, {d12:.4})");

    // (1) wgpu render-proof: the GPU depth city is non-blank at EVERY step of the
    // flight (start, mid, later) — you watch a lit city, not a black pane.
    for &t in &[0.0f64, 3.0, 7.0] {
        let ratio = render_at(&ways, t);
        eprintln!("[flythrough] t={t:.0}s content_ratio={ratio:.4}");
        assert!(ratio > 0.05, "the flythrough frame at t={t}s is a lit city (content_ratio {ratio:.4} > 0.05)");
    }

    #[cfg(feature = "testmatrix")]
    facett_core::testmatrix::emit(
        "facett-map3d",
        "flythrough_moving_city",
        d01 > 0.02 && d12 > 0.02,
        &format!("eye_moved_0_3={d01:.4} eye_moved_3_7={d12:.4} extent={:.3}", framing.extent),
    );
}