use crate::{
fps::FPSCounter,
winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
window::Window,
},
};
use crate::{shaders::ShaderState, wgpu};
use crate::fps::TimingState;
use crate::{graphics::GraphicsState, EngineBuilder};
pub struct Engine {
event_loop: Option<EventLoop<()>>,
pub window: Window,
pub graphics_state: GraphicsState,
pub shader_state: ShaderState,
pub background_color: wgpu::Color,
pub fps: FPSCounter,
}
impl Engine {
pub fn new(eb: EngineBuilder) -> Self {
let event_loop = EventLoop::new();
let window = eb.window_builder.build(&event_loop).unwrap();
let graphics_state = GraphicsState::new(&window);
let mut shader_state = ShaderState::new(&graphics_state);
shader_state.init_shaders();
Self {
event_loop: Some(event_loop),
window,
graphics_state,
shader_state,
background_color: eb.bg_color,
fps: FPSCounter::new(),
}
}
pub fn run<T: 'static>(mut self, screen: impl ToScreen<T>, mut data: T) {
let screen = screen.to_screen();
let evloop = self.event_loop.take().unwrap();
self.fps.set_elements(screen.len());
let mut screens: Vec<Screen<T>> = vec![screen];
evloop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
let last_sc = screens.last_mut();
if let Some(screen) = last_sc {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent {
event: WindowEvent::Resized(_),
..
} => {
let size = self.window.inner_size();
self.graphics_state.set_size(size);
}
Event::MainEventsCleared => self.window.request_redraw(),
Event::RedrawEventsCleared => {
let frame = match self.graphics_state.surface.get_current_texture() {
Ok(frame) => frame,
Err(e) => {
eprintln!("dropped frame: {:?}", e);
return;
}
};
let mut encoder = self.graphics_state.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: None },
);
let view = &frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.background_color),
store: true,
},
}],
depth_stencil_attachment: None,
});
self.fps.start(TimingState::Draw);
for element in screen.iter_mut() {
element.render(&mut self, &mut data, &frame, &mut render_pass);
self.fps.advance();
}
std::mem::drop(render_pass);
self.graphics_state.queue.submit(Some(encoder.finish()));
frame.present();
self.shader_state.cull();
}
_ => (),
}
self.fps.start(TimingState::Update);
for element in screen.iter_mut().rev() {
element.update(&mut self, &mut data, &event);
self.fps.advance();
}
} else {
*control_flow = ControlFlow::Exit;
}
});
}
}
impl AsRef<GraphicsState> for Engine {
fn as_ref(&self) -> &GraphicsState {
&self.graphics_state
}
}
pub trait Element<Data> {
fn update(&mut self, engine: &mut Engine, data: &mut Data, event: &Event<()>);
fn render<'a: 'rp, 'rp>(
&'a mut self,
engine: &mut Engine,
data: &mut Data,
frame: &wgpu::SurfaceTexture,
render_pass: &mut wgpu::RenderPass<'rp>,
);
}
pub type Screen<T> = Vec<Box<dyn Element<T>>>;
pub trait ToScreen<T> {
fn to_screen(self) -> Screen<T>;
}
impl<T> ToScreen<T> for Screen<T> {
fn to_screen(self) -> Screen<T> {
self
}
}
impl<D, T: Into<Box<dyn Element<D>>>> ToScreen<D> for T {
fn to_screen(self) -> Screen<D> {
vec![self.into()]
}
}
#[macro_export]
macro_rules! screen {
($($el:expr),*) => {
vec![$(Box::new($el) as Box<dyn acidalia::Element<_>>, )*]
};
}