1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3use std::error::Error;
4
5#[allow(warnings)]
7pub(crate) mod gl {
8 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
9}
10
11use components::Event;
12use context::fps_manager::FrameInfo;
13use context::scene::Scene;
14use egui_gl_glfw::glfw::Cursor;
15use egui_gl_glfw::glfw::WindowMode;
16pub use maple_derive::Node;
17pub use nalgebra_glm as math;
18
19pub use egui_gl_glfw::egui;
21pub use egui_gl_glfw::glfw;
22
23pub use glfw::Key;
24pub use utils::color;
25pub use utils::config;
26
27use config::EngineConfig;
28
29use egui_gl_glfw::glfw::Context;
30use render_passes::{
31 cube_shadow_pass::CubeShadowPass, main_pass::MainPass, shadow_pass::ShadowPass,
32};
33
34use crate::nodes::UI;
35use renderer::Renderer;
36
37pub mod components;
38pub mod context;
39pub mod nodes;
40pub mod render_passes;
41pub mod renderer;
42pub mod utils;
43
44use context::GameContext;
45
46pub struct Engine {
50 pub context: GameContext,
52 pub config: EngineConfig,
54 renderer: Renderer,
56}
57
58const SAMPLES: u32 = 8;
60
61impl Engine {
62 pub fn init(config: EngineConfig) -> Result<Engine, Box<dyn Error>> {
81 use glfw::fail_on_errors;
82 let mut glfw = glfw::init(fail_on_errors!())?;
83 glfw.window_hint(glfw::WindowHint::ContextVersion(4, 6));
84 glfw.window_hint(glfw::WindowHint::OpenGlProfile(
85 glfw::OpenGlProfileHint::Core,
86 ));
87 glfw.window_hint(glfw::WindowHint::DoubleBuffer(true));
88 glfw.window_hint(glfw::WindowHint::Resizable(false));
89 glfw.window_hint(glfw::WindowHint::Samples(Some(SAMPLES)));
90 let (mut window, events) = match config.window_mode {
93 utils::config::WindowMode::Windowed => glfw
94 .create_window(
95 config.resolution.width,
96 config.resolution.height,
97 &config.window_title,
98 WindowMode::Windowed,
99 )
100 .ok_or("failed to create window")?,
101 utils::config::WindowMode::FullScreen => glfw
102 .with_primary_monitor(|g, monitor| {
103 let mut width = config.resolution.width;
104 let mut height = config.resolution.height;
105
106 if let Some(monitor) = &monitor {
107 if let Some(vid_mode) = monitor.get_video_mode() {
108 width = vid_mode.width;
109 height = vid_mode.height;
110 }
111 }
112
113 g.create_window(
114 width,
115 height,
116 &config.window_title,
117 monitor.map_or(WindowMode::Windowed, |m| WindowMode::FullScreen(m)),
118 )
119 })
120 .ok_or("failed to create window")?,
121 _ => glfw
122 .create_window(
123 config.resolution.width,
124 config.resolution.height,
125 &config.window_title,
126 WindowMode::Windowed,
127 )
128 .ok_or("failed to create window")?,
129 };
130
131 window.set_key_polling(true);
133 window.set_cursor_pos_polling(true);
134 window.set_mouse_button_polling(true);
135 window.set_scroll_polling(true);
136 window.make_current();
137
138 window.set_cursor(Some(Cursor::standard(glfw::StandardCursor::IBeam)));
139
140 if glfw.supports_raw_motion() {
141 window.set_raw_mouse_motion(true);
142 }
143
144 Renderer::context(&mut window);
146
147 glfw.set_swap_interval(glfw::SwapInterval::None);
148
149 let mut renderer = Renderer::init();
150
151 renderer.set_clear_color(config.clear_color);
152
153 Ok(Engine {
154 context: GameContext::new(events, glfw, window),
155 config,
157
158 renderer,
159 })
160 }
161
162 pub fn load_scene(&mut self, scene: Scene) {
167 self.context.scene.load(scene);
168 }
169
170 pub fn begin(&mut self) -> Result<(), Box<dyn Error>> {
192 self.renderer.add_pass(ShadowPass);
193 self.renderer.add_pass(CubeShadowPass);
194 self.renderer.add_pass(MainPass);
195
196 self.context.emit(Event::Ready);
197
198 self.update_ui();
199
200 self.render_loop()
202 }
203
204 fn render_loop(&mut self) -> Result<(), Box<dyn Error>> {
208 while !self.context.window.should_close() {
209 let mut frame_info = FrameInfo::default();
210
211 time!(&mut frame_info.clear_time, { Renderer::clear() });
212
213 time!(&mut frame_info.render_time, {
214 self.renderer.render(&self.context)
215 });
216
217 time!(&mut frame_info.ui_pass_time, { self.render_ui_pass() });
218
219 time!(&mut frame_info.context_update_time, {
221 self.update_context()
222 });
223
224 time!(&mut frame_info.ui_update_time, { self.update_ui() });
225
226 time!(&mut frame_info.event_emit_time, {
227 self.context.emit(Event::Update)
228 });
229
230 time!(&mut frame_info.swap_buffers_time, {
232 self.context.window.swap_buffers()
233 });
234
235 self.context.frame.frame_info = frame_info;
236 }
237 Ok(())
238 }
239
240 pub fn set_window_title(&mut self, title: &str) {
245 self.context.window.set_title(title);
246 }
247
248 fn update_context(&mut self) {
249 let context = &mut self.context;
250
251 context.frame.update();
252
253 context.input.update();
254 }
255
256 fn update_ui(&mut self) {
257 let nodes = self.context.scene.get_iter::<UI>();
258
259 let nodes: Vec<*mut UI> = nodes.map(|node| node as *const UI as *mut UI).collect();
261
262 for ui in nodes {
263 unsafe {
264 (*ui).update(&mut self.context);
265 }
266 }
267 }
268
269 fn render_ui_pass(&mut self) {
270 let nodes = self.context.scene.get_iter::<UI>();
271
272 let nodes: Vec<*mut UI> = nodes.map(|node| node as *const UI as *mut UI).collect();
274
275 for ui in nodes {
276 unsafe {
277 (*ui).render(&mut self.context)
281 }
282 }
283 }
284}