Skip to main content

scene_viewer/
scene_viewer.rs

1//! Looks around a 3D scene file (a `crabby-world` save of `Transform3D` and
2//! `Model` entities, like the Unity importer writes):
3//!
4//!   SCENE=town/Town.json cargo run --release --features 3d --example scene_viewer
5//!
6//! W, A, S and D fly, Q and E go down and up, the arrow keys turn, Shift
7//! flies faster. It starts above the middle of the scene.
8//! `CAMERA=x,y,z,yaw,pitch` (degrees) starts there instead.
9//! `SHOT=path.png` saves frame 300 and quits. `HEADLESS=1` only loads the
10//! scene, without a window, and says what's in it and how long it took.
11
12use std::time::Instant;
13
14use ponko::*;
15
16/// The middle and size of where the scene's models are.
17fn extent(world: &mut World) -> (Vec3, f32) {
18    let (mut min, mut max) = (Vec3::splat(f32::MAX), Vec3::splat(f32::MIN));
19    world.query::<(&Transform3D, &Model)>().for_each(|(t, _)| {
20        min = min.min(t.position);
21        max = max.max(t.position);
22    });
23    if min.x > max.x {
24        return (Vec3::ZERO, 10.0);
25    }
26    ((min + max) * 0.5, (max - min).max_element().max(10.0))
27}
28
29fn camera_from_env() -> Option<(Vec3, f32, f32)> {
30    let v: Vec<f32> = std::env::var("CAMERA")
31        .ok()?
32        .split(',')
33        .map(|n| n.trim().parse().ok())
34        .collect::<Option<_>>()?;
35    let [x, y, z, yaw, pitch] = v[..] else { return None };
36    Some((vec3(x, y, z), yaw.to_radians(), pitch.to_radians()))
37}
38
39fn main() {
40    let scene = std::env::var("SCENE").unwrap_or_else(|_| {
41        eprintln!("say which scene: SCENE=town/Town.json (a file under assets/)");
42        std::process::exit(2);
43    });
44    if std::env::var("HEADLESS").is_ok() {
45        Game::new("scene viewer", 1, 1).run_headless(0, |ctx| {
46            let start = Instant::now();
47            ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
48            let models = ctx.world.query::<&Model>().iter().count();
49            let (middle, size) = extent(&mut ctx.world);
50            println!(
51                "{scene}: {models} models in {:.2} s, around {middle} across {size:.0} m",
52                start.elapsed().as_secs_f32()
53            );
54        });
55        return;
56    }
57    let shot = std::env::var("SHOT").ok();
58    let (mut yaw, mut pitch) = (0.0f32, -0.3f32);
59    Game::new("Crabby Engine: scene viewer", 1280, 720)
60        .system(move |ctx: &mut Ctx| {
61            if ctx.key_pressed(Key::Escape) {
62                ctx.quit();
63            }
64            let dt = ctx.dt();
65            if ctx.frame() == 1 {
66                let (y, x, _) = ctx.camera3d.rotation.to_euler(glam::EulerRot::YXZ);
67                (yaw, pitch) = (y, x);
68            }
69            let turn = 1.2 * dt;
70            if ctx.key_down(Key::Left) {
71                yaw += turn;
72            }
73            if ctx.key_down(Key::Right) {
74                yaw -= turn;
75            }
76            if ctx.key_down(Key::Up) {
77                pitch = (pitch + turn).min(1.5);
78            }
79            if ctx.key_down(Key::Down) {
80                pitch = (pitch - turn).max(-1.5);
81            }
82            let rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
83            let speed = if ctx.key_down(Key::LeftShift) { 120.0 } else { 20.0 } * dt;
84            let mut go = Vec3::ZERO;
85            for (key, way) in [
86                (Key::W, Vec3::NEG_Z),
87                (Key::S, Vec3::Z),
88                (Key::A, Vec3::NEG_X),
89                (Key::D, Vec3::X),
90            ] {
91                if ctx.key_down(key) {
92                    go += rotation * way;
93                }
94            }
95            if ctx.key_down(Key::E) {
96                go += Vec3::Y;
97            }
98            if ctx.key_down(Key::Q) {
99                go -= Vec3::Y;
100            }
101            ctx.camera3d.position += go * speed;
102            ctx.camera3d.rotation = rotation;
103            if ctx.frame() == 300
104                && let Some(path) = &shot
105            {
106                ctx.save_screenshot(path);
107            }
108            if ctx.frame() == 302 && shot.is_some() {
109                ctx.quit();
110            }
111        })
112        .run(move |ctx| {
113            ctx.load_scene(&scene).unwrap_or_else(|e| panic!("{e}"));
114            ctx.lighting.sun_direction = Sky::sun_direction_at(15.0);
115            ctx.lighting.shadow_distance = 400.0;
116            ctx.camera3d.far = 3000.0;
117            ctx.post.bloom = 0.05;
118            let (middle, size) = extent(&mut ctx.world);
119            let (at, yaw, pitch) =
120                camera_from_env().unwrap_or((middle + vec3(0.0, size * 0.25, size * 0.4), 0.0, -0.5));
121            ctx.camera3d.position = at;
122            ctx.camera3d.rotation = Quat::from_rotation_y(yaw) * Quat::from_rotation_x(pitch);
123        });
124}