juke/
lib.rs

1//! 🤖 A small engine for prototyping projects
2
3mod gui;
4pub mod resources;
5
6use resources::ResourceManager;
7
8use crate::gui::Framework;
9use log::error;
10use pixels::{Pixels, SurfaceTexture};
11use std::time::{Duration, Instant};
12use winit::dpi::LogicalSize;
13use winit::event::{Event, VirtualKeyCode};
14use winit::event_loop::{ControlFlow, EventLoop};
15use winit::window::{Window, WindowBuilder};
16use winit_input_helper::WinitInputHelper;
17
18pub use egui;
19
20pub struct Engine {
21    window: Window,
22    input: WinitInputHelper,
23    pixels: Pixels,
24    event_loop: EventLoop<()>,
25    framework: Framework,
26
27    resources: ResourceManager
28}
29
30impl Engine {
31    pub fn new(title: &str, w: u32, h: u32, pixel_size: u32) -> Self {
32        env_logger::init();
33        let event_loop = EventLoop::new();
34        let input = WinitInputHelper::new();
35        let window = {
36            let size = LogicalSize::new((w * pixel_size) as f64, (h * pixel_size) as f64);
37            WindowBuilder::new()
38                .with_title(title)
39                .with_inner_size(size)
40                .with_resizable(false)
41                .with_maximized(false)
42                .build(&event_loop)
43                .unwrap()
44        };
45
46        let (pixels, framework) = {
47            let window_size = window.inner_size();
48            let scale_factor = window.scale_factor() as f32;
49            let surface_texture =
50                SurfaceTexture::new(window_size.width, window_size.height, &window);
51            let pixels = Pixels::new(w, h, surface_texture).unwrap();
52            let framework =
53                Framework::new(window_size.width, window_size.height, scale_factor, &pixels);
54
55            (pixels, framework)
56        };
57
58        Self {
59            window,
60            input,
61            pixels,
62            framework,
63            event_loop,
64            resources: ResourceManager::new(),
65        }
66    }
67
68    pub fn run<F: 'static + Fn(FrameContext, &mut ResourceManager)>(mut self, u: F) {
69        let mut frame_count = 0u128;
70        let mut previous_time = Instant::now();
71        const FRAME_TIME: Duration = Duration::from_micros(16666);
72
73        self.event_loop.run(move |event, _, control_flow| {
74            if self.input.update(&event) {
75                if self.input.key_pressed(VirtualKeyCode::Escape) || self.input.quit() {
76                    *control_flow = ControlFlow::Exit;
77                    return;
78                }
79                self.window.request_redraw();
80            }
81
82            match event {
83                Event::WindowEvent { event, .. } => {
84                    // Update egui inputs
85                    self.framework.handle_event(&event);
86                }
87
88                Event::RedrawRequested(_) => {
89                    let delta = {
90                        let real_delta = previous_time.elapsed();
91                        if real_delta < FRAME_TIME {
92                            let sleep_time = FRAME_TIME - real_delta;
93                            std::thread::sleep(sleep_time);
94
95                            FRAME_TIME
96                        } else {
97                            real_delta
98                        }
99                    };
100
101                    frame_count += 1;
102                    previous_time = Instant::now();
103
104                    u(FrameContext {
105                        frame_count,
106                        buffer: self.pixels.get_frame(),
107                        delta,
108                    }, &mut self.resources);
109                    self.framework.prepare(&self.window, &mut self.resources);
110
111                    let render_result =
112                        self.pixels.render_with(|encoder, render_target, context| {
113                            context.scaling_renderer.render(encoder, render_target);
114                            self.framework.render(encoder, render_target, context)?;
115
116                            Ok(())
117                        });
118
119                    if render_result
120                        .map_err(|e| error!("pixels.render() failed: {}", e))
121                        .is_err()
122                    {
123                        *control_flow = ControlFlow::Exit;
124                    }
125                }
126                _ => (),
127            }
128        });
129    }
130
131    pub fn ui<T: 'static + Fn(&egui::Context, &mut ResourceManager)>(mut self, ui: T) -> Self {
132        self.framework.gui = Some(Box::new(ui));
133        self
134    }
135}
136
137pub struct FrameContext<'a> {
138    pub frame_count: u128,
139    pub buffer: &'a mut [u8],
140    pub delta: Duration,
141}