1use ponko::*;
13
14struct Spin(f32);
16
17struct Orbit {
19 yaw: f32,
20 pitch: f32,
21 distance: f32,
22}
23
24fn spin(ctx: &mut Ctx) {
25 let dt = ctx.dt();
26 ctx.world.query::<(&mut Transform3D, &Spin)>().for_each(|(t, s)| {
27 t.rotation *= Quat::from_rotation_y(s.0 * dt);
28 });
29}
30
31fn orbit(ctx: &mut Ctx) {
32 if ctx.key_pressed(Key::Space) {
33 if ctx.is_paused() { ctx.resume() } else { ctx.pause() }
34 }
35 if ctx.key_pressed(Key::Escape) {
36 ctx.quit();
37 }
38 let drag = if ctx.mouse_down(MouseButton::Left) {
39 ctx.mouse_delta()
40 } else {
41 Vec2::ZERO
42 };
43 let wheel = ctx.mouse_wheel().y;
44 let o = ctx.world.resource_mut::<Orbit>();
45 o.yaw -= drag.x * 0.005;
46 o.pitch = (o.pitch + drag.y * 0.005).clamp(0.05, 1.5);
47 o.distance = (o.distance * 0.9f32.powf(wheel)).clamp(3.0, 400.0);
48 let (yaw, pitch, distance) = (o.yaw, o.pitch, o.distance);
49 let offset = vec3(yaw.sin() * pitch.cos(), pitch.sin(), yaw.cos() * pitch.cos()) * distance;
50 ctx.camera3d.position = offset;
51 ctx.camera3d.look_at(Vec3::ZERO);
52}
53
54struct Bench {
56 seconds: f64,
57 start: Option<std::time::Instant>,
58 last: Option<std::time::Instant>,
59 frames: u64,
60 frame_ms: f64,
61 render_cpu_ms: f64,
62 busy_ms: f64,
64 gpu_ms: f64,
66}
67
68fn bench(ctx: &mut Ctx) {
69 let now = std::time::Instant::now();
70 let cpu = ctx.render_stats().cpu_ms as f64;
71 let gpu = ctx.render_stats().gpu_ms.unwrap_or(0.0) as f64;
72 let wait = ctx.display_wait_ms() as f64;
73 let b = ctx.world.resource_mut::<Bench>();
74 let start = *b.start.get_or_insert(now);
75 let elapsed = now.duration_since(start).as_secs_f64();
76 let last = b.last.replace(now);
77 let measuring = last.is_some() && elapsed > 3.0;
78 let first = measuring && b.frames == 0;
79 if let (true, Some(last)) = (measuring, last) {
80 b.frames += 1;
81 b.frame_ms += now.duration_since(last).as_secs_f64() * 1000.0;
82 b.render_cpu_ms += cpu;
83 b.gpu_ms += gpu;
84 b.busy_ms += now.duration_since(last).as_secs_f64() * 1000.0 - wait;
85 }
86 let done = elapsed > 3.0 + b.seconds;
87 let (frames, frame_ms, render_cpu_ms, busy_ms, gpu_ms) =
88 (b.frames, b.frame_ms, b.render_cpu_ms, b.busy_ms, b.gpu_ms);
89 if first && let Ok(path) = std::env::var("BENCH_SHOT") {
90 ctx.save_screenshot(path);
91 }
92 if done {
93 let n = frames as f64;
94 println!(
95 "{{\"engine\":\"crabby\",\"frames\":{frames},\"fps\":{:.3},\"frame_ms\":{:.3},\"render_cpu_ms\":{:.3},\"busy_ms\":{:.3},\"gpu_ms\":{:.3}}}",
96 n / (frame_ms / 1000.0),
97 frame_ms / n,
98 render_cpu_ms / n,
99 busy_ms / n,
100 gpu_ms / n
101 );
102 ctx.quit();
103 }
104}
105
106fn main() {
107 let bench_seconds: Option<f64> = std::env::var("BENCH_SECONDS").ok().and_then(|s| s.parse().ok());
108 let cubes: usize = std::env::var("CUBES").ok().and_then(|n| n.parse().ok()).unwrap_or(400);
109 let mut game = Game::new("Crabby Engine: 3D", 1280, 720).system(orbit);
110 game = if bench_seconds.is_some() {
111 let fullscreen = std::env::var_os("BENCH_WINDOWED").is_none();
113 game.vsync(false).fullscreen(fullscreen).system(spin).system(bench)
114 } else {
115 game.sim_system(spin)
116 };
117 game.run(move |ctx| {
118 ctx.clear(Color::hex(0x9CC4E4)); ctx.camera3d.far = 2000.0;
120 ctx.lighting.sun_direction = vec3(-0.8, -0.6, -0.4); if let Some(seconds) = bench_seconds {
122 ctx.world.insert_resource(Bench {
123 seconds,
124 start: None,
125 last: None,
126 frames: 0,
127 frame_ms: 0.0,
128 render_cpu_ms: 0.0,
129 busy_ms: 0.0,
130 gpu_ms: 0.0,
131 });
132 }
133 ctx.world.insert_resource(Orbit {
134 yaw: 0.6,
135 pitch: 0.45,
136 distance: 18.0,
137 });
138
139 let grass = ctx.create_material(MaterialDesc {
140 roughness: 0.95,
141 ..MaterialDesc::color(Color::hex(0x5E8C4A))
142 });
143 let ground = ctx.create_mesh(&MeshData::plane(400.0), grass);
144 ctx.world.spawn((Transform3D::default(), Model::new(ground)));
145
146 let ball = MeshData::sphere(0.45, 32);
148 for row in 0..2 {
149 for col in 0..6 {
150 let material = ctx.create_material(MaterialDesc {
151 roughness: 0.05 + col as f32 * 0.19,
152 metallic: row as f32,
153 ..MaterialDesc::color(if row == 0 {
154 Color::hex(0xD8453B)
155 } else {
156 Color::hex(0xE8C070)
157 })
158 });
159 let mesh = ctx.create_mesh(&ball, material);
160 let at = vec3(col as f32 * 1.1 - 2.75, 0.45, row as f32 * -1.2);
161 ctx.world.spawn((Transform3D::at(at), Model::new(mesh)));
162 }
163 }
164
165 let white = ctx.default_material();
167 let cube = ctx.create_mesh(&MeshData::cube(0.6), white);
168 let side = (cubes as f32).sqrt().ceil() as usize;
169 let mut rng = Rng::new(3);
170 for i in 0..cubes {
171 let (x, z) = ((i % side) as f32, (i / side) as f32);
172 let at = vec3((x - side as f32 / 2.0) * 1.6, 0.3, -6.0 - z * 1.6);
173 let tint = Color::rgb(rng.range(0.3, 1.0), rng.range(0.3, 1.0), rng.range(0.3, 1.0));
174 ctx.world.spawn((
175 Transform3D::at(at).rotated(Quat::from_rotation_y(rng.range(0.0, 6.3))),
176 Model::new(cube).with_color(tint),
177 Spin(rng.range(-2.0, 2.0)),
178 ));
179 }
180 });
181}