use egui_winit_vulkano::{Gui, GuiConfig};
use glam::UVec2;
use std::{error::Error, path::Path};
use steel_common::{
app::{App, Command, DrawInfo, InitInfo, UpdateInfo},
asset::AssetInfo,
platform::{BuildTarget, Platform, BUILD_TARGET},
};
use vulkano::{format::Format, image::ImageUsage};
use vulkano_util::{
context::VulkanoContext,
window::{VulkanoWindows, WindowDescriptor},
};
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ControlFlow, EventLoop},
};
pub fn run(mut app: Box<dyn App>) {
#[cfg(not(target_os = "android"))]
env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.parse_default_env()
.init();
#[cfg(target_os = "android")]
android_logger::init_once(
android_logger::Config::default().with_max_level(log::LevelFilter::Trace),
);
let mut event_loop_builder = EventLoop::builder();
#[cfg(target_os = "android")]
{
use winit::platform::android::EventLoopBuilderExtAndroid;
let msg = "Steel must be setup with the #[steel_main] macro on Android";
event_loop_builder.with_android_app(
steel_common::platform::android::ANDROID_APP
.get()
.expect(msg)
.clone(),
);
}
let event_loop = event_loop_builder.build().unwrap();
let platform = Platform::new();
event_loop.set_control_flow(ControlFlow::Poll);
let (context, ray_tracing_supported) = steel_common::create_context();
let windows = VulkanoWindows::default();
let gui = None;
let insert_asset_fn = |asset_info_file: &Path| -> Result<(), Box<dyn Error>> {
let asset_info_string = platform.read_asset_to_string(asset_info_file)?;
let asset_id = serde_json::from_str::<AssetInfo>(&asset_info_string)?.id;
let asest_file_path = AssetInfo::asset_info_path_to_asset_path(asset_info_file);
log::debug!(
"Insert asset \"{}\", id: {asset_id:?}",
asest_file_path.display()
);
app.command(Command::InsertAsset(asset_id, asest_file_path));
Ok(())
};
platform
.list_asset_files()
.unwrap()
.into_iter()
.filter(|f| f.extension().is_some_and(|e| e == "asset"))
.for_each(|asset_info_file| {
if let Err(e) = insert_asset_fn(&asset_info_file) {
log::warn!(
"Failed to insert asset: {}, error: {e}",
asset_info_file.display()
);
}
});
app.init(InitInfo {
platform,
context: &context,
ray_tracing_supported,
scene: None,
});
let mut application = Application {
context,
windows,
gui,
app,
};
log::info!("Start main loop!");
event_loop.run_app(&mut application).unwrap();
}
struct Application {
context: VulkanoContext,
windows: VulkanoWindows,
gui: Option<Gui>,
app: Box<dyn App>,
}
impl ApplicationHandler for Application {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
log::debug!("Event::Resumed");
self.windows.create_window(
&event_loop,
&self.context,
&WindowDescriptor::default(),
|info| {
match BUILD_TARGET {
BuildTarget::Desktop => info.image_format = Format::B8G8R8A8_UNORM,
BuildTarget::Android => info.image_format = Format::R8G8B8A8_UNORM,
}
info.image_usage |= ImageUsage::STORAGE;
},
);
let renderer = self.windows.get_primary_renderer().unwrap();
log::info!("Swapchain image format: {:?}", renderer.swapchain_format());
self.gui = Some(Gui::new(
&event_loop,
renderer.surface(),
renderer.graphics_queue(),
renderer.swapchain_format(),
GuiConfig {
is_overlay: true,
..Default::default()
},
));
}
fn suspended(&mut self, _: &winit::event_loop::ActiveEventLoop) {
log::debug!("Event::Suspended");
self.gui = None;
self.windows
.remove_renderer(self.windows.primary_window_id().unwrap());
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
_: winit::window::WindowId,
event: WindowEvent,
) {
if let Some(gui) = self.gui.as_mut() {
let _pass_events_to_game = !gui.update(&event);
}
match event {
WindowEvent::CloseRequested => {
log::info!("WindowEvent::CloseRequested");
event_loop.exit();
}
WindowEvent::Resized(_) => {
log::debug!("WindowEvent::Resized");
if let Some(renderer) = self.windows.get_primary_renderer_mut() {
renderer.resize();
renderer.window().request_redraw();
}
}
WindowEvent::ScaleFactorChanged { .. } => {
log::debug!("WindowEvent::ScaleFactorChanged");
if let Some(renderer) = self.windows.get_primary_renderer_mut() {
renderer.resize();
renderer.window().request_redraw();
}
}
WindowEvent::RedrawRequested => {
if let Some(renderer) = self.windows.get_primary_renderer_mut() {
let window_size = renderer.window().inner_size();
if window_size.width == 0 || window_size.height == 0 {
return; }
let mut gpu_future = renderer.acquire(None, |_| {}).unwrap();
let gui = self.gui.as_mut().unwrap();
gui.begin_frame();
self.app.update(UpdateInfo {
update: true,
ctx: &gui.egui_ctx,
});
gpu_future = self.app.draw(DrawInfo {
before_future: gpu_future,
context: &self.context,
renderer: &renderer,
image: renderer.swapchain_image_view(),
window_size: UVec2::from_array(renderer.swapchain_image_size()),
editor_info: None,
});
gpu_future = gui.draw_on_image(gpu_future, renderer.swapchain_image_view());
renderer.present(gpu_future, true);
renderer.window().request_redraw();
}
}
_ => (),
}
}
}