main/
lib.rs

1//! The game client for the [steel game engine](https://github.com/SSSxCCC/steel).
2
3use std::path::PathBuf;
4use egui_winit_vulkano::{Gui, GuiConfig};
5use glam::UVec2;
6use steel_common::{engine::{Command, DrawInfo, FrameInfo, FrameStage, InitInfo}, platform::Platform};
7use vulkano_util::{context::{VulkanoConfig, VulkanoContext}, window::{VulkanoWindows, WindowDescriptor}};
8use winit::{event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder}};
9
10#[cfg(target_os = "android")]
11use winit::platform::android::activity::AndroidApp;
12
13#[cfg(target_os = "android")]
14#[no_mangle]
15fn android_main(app: AndroidApp) {
16    android_logger::init_once(android_logger::Config::default().with_max_level(log::LevelFilter::Trace));
17    use winit::platform::android::EventLoopBuilderExtAndroid;
18    let event_loop = EventLoopBuilder::new().with_android_app(app.clone()).build();
19    let platform = Platform::new(app);
20    _main(event_loop, platform);
21}
22
23#[cfg(not(target_os = "android"))]
24#[allow(dead_code)]
25fn main() {
26    env_logger::builder().filter_level(log::LevelFilter::Debug).parse_default_env().init();
27    let event_loop = EventLoopBuilder::new().build();
28    let platform = Platform::new_client();
29    _main(event_loop, platform);
30}
31
32fn _main(event_loop: EventLoop<()>, platform: Platform) {
33    // graphics
34    let mut config = VulkanoConfig::default();
35    config.device_features.fill_mode_non_solid = true;
36    let context = VulkanoContext::new(config);
37    let mut windows = VulkanoWindows::default();
38
39    // input
40    let mut events = Vec::new();
41
42    // egui
43    let mut gui = None;
44
45    // engine
46    let mut engine = steel::create();
47    engine.init(InitInfo { platform, context: &context, scene: Some(PathBuf::from("scene_path")) }); // scene path will be modified to init scene path temporily while compiling
48
49    log::debug!("Start main loop!");
50    event_loop.run(move |event, event_loop, control_flow| match event {
51        Event::Resumed => {
52            log::debug!("Event::Resumed");
53            windows.create_window(&event_loop, &context,
54                &WindowDescriptor::default(), |_|{});
55            let renderer = windows.get_primary_renderer().unwrap();
56            gui = Some(Gui::new(&event_loop, renderer.surface(),
57                renderer.graphics_queue(),
58                renderer.swapchain_format(),
59                GuiConfig { is_overlay: true, ..Default::default() }));
60        }
61        Event::Suspended => {
62            log::debug!("Event::Suspended");
63            gui = None;
64            windows.remove_renderer(windows.primary_window_id().unwrap());
65        }
66        Event::WindowEvent { event , .. } => {
67            if let Some(gui) = gui.as_mut() {
68                let _pass_events_to_game = !gui.update(&event);
69            }
70            match event {
71                WindowEvent::CloseRequested => {
72                    log::debug!("WindowEvent::CloseRequested");
73                    *control_flow = ControlFlow::Exit;
74                }
75                WindowEvent::Resized(_) => {
76                    log::debug!("WindowEvent::Resized");
77                    if let Some(renderer) = windows.get_primary_renderer_mut() { renderer.resize() }
78                }
79                WindowEvent::ScaleFactorChanged { .. } => {
80                    log::debug!("WindowEvent::ScaleFactorChanged");
81                    if let Some(renderer) = windows.get_primary_renderer_mut() { renderer.resize() }
82                }
83                _ => ()
84            }
85            // Warning: event.to_static() may drop some events, like ScaleFactorChanged
86            // TODO: find a way to deliver all events to WinitInputHelper
87            if let Some(event) = event.to_static() {
88                events.push(event);
89            }
90        }
91        Event::RedrawRequested(_) => {
92            log::trace!("Event::RedrawRequested");
93            engine.command(Command::UpdateInput(&events));
94            events.clear();
95            if let Some(renderer) = windows.get_primary_renderer_mut() {
96                let window_size = renderer.window().inner_size();
97                if window_size.width == 0 || window_size.height == 0 {
98                    return; // Prevent "Failed to recreate swapchain: ImageExtentZeroLengthDimensions" in renderer.acquire().unwrap()
99                }
100                let mut gpu_future = renderer.acquire().unwrap();
101
102                let gui = gui.as_mut().unwrap();
103                gui.begin_frame();
104
105                let mut frame_info = FrameInfo { stage: FrameStage::Maintain, ctx: &gui.egui_ctx };
106                engine.frame(&frame_info);
107                frame_info.stage = FrameStage::Update;
108                engine.frame(&frame_info);
109                frame_info.stage = FrameStage::Finish;
110                engine.frame(&frame_info);
111
112                gpu_future = engine.draw(DrawInfo {
113                    before_future: gpu_future,
114                    context: &context, renderer: &renderer,
115                    image: renderer.swapchain_image_view(),
116                    window_size: UVec2::from_array(renderer.swapchain_image_size()),
117                    editor_info: None,
118                });
119
120                gpu_future = gui.draw_on_image(gpu_future, renderer.swapchain_image_view());
121
122                renderer.present(gpu_future, true);
123            }
124        }
125        Event::MainEventsCleared => {
126            log::trace!("Event::MainEventsCleared");
127            if let Some(renderer) = windows.get_primary_renderer() { renderer.window().request_redraw() }
128        }
129        _ => (),
130    });
131}