maple/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3use std::error::Error;
4
5// I wish I used glow ngl
6#[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
19//re-exporting the engine module
20pub 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
46/// Represents the main game engine.
47///
48/// The Enigne is responsible for managing the game loop and rendering the scene.
49pub struct Engine {
50    /// The game context such as the frame, input, nodes, and shaders.
51    pub context: GameContext,
52    /// configuration of the engine
53    pub config: EngineConfig,
54    /// renderer of the engine
55    renderer: Renderer,
56}
57
58/// The number of samples for anti-aliasing.
59const SAMPLES: u32 = 8;
60
61impl Engine {
62    /// Initializes the game engine.
63    ///
64    /// # Arguments
65    /// - `config` - initial config of the engine
66    ///
67    /// # Returns
68    /// A new instance of the Engine. or an error if it fails to create a window
69    ///
70    /// # Example
71    /// ```rust
72    /// use maple::{Engine, config::EngineConfig};
73    /// # use std::error::Error;
74    ///
75    /// let mut engine = Engine::init(EngineConfig::default())?;
76    ///
77    /// # Ok::<(), Box<dyn Error>>(())
78    ///
79    /// ```
80    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        //glfw.window_hint(glfw::WindowHint::RefreshRate(Some(60)));
91
92        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        //set up input polling
132        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        //load grahpics api
145        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            //shadow_map: None,
156            config,
157
158            renderer,
159        })
160    }
161
162    /// load a scene into the games Context
163    ///
164    /// # Arguments
165    /// - `scene`: the scene to be added to the context's Scene
166    pub fn load_scene(&mut self, scene: Scene) {
167        self.context.scene.load(scene);
168    }
169
170    /// Starts the game/render loop.
171    ///
172    /// This function is responsible for rendering the scene and updating the game context.
173    ///
174    /// # Returns
175    /// This returns an error if there are any runtime errors (most likely caused by the renderer)
176    ///
177    /// # Example
178    /// ```rust,no_run
179    /// use maple::{Engine, config::EngineConfig};
180    /// # use std::error::Error;
181    ///
182    /// # fn main() -> Result<(), Box<dyn Error>> {
183    /// let mut engine = Engine::init(EngineConfig::default())?;
184    ///
185    /// //set up the scene
186    ///
187    /// engine.begin() // runs until the window closes or returns an error if it fails
188    /// # }
189    ///
190    /// ```
191    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        //render loop
201        self.render_loop()
202    }
203
204    /// The main render loop.
205    /// This function is responsible for rendering the scene and updating the game context.
206    /// It is called by the `begin` function.
207    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            // update context while the gpu is rendering
220            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            // swap buffers
231            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    /// sets the window set_title
241    ///
242    /// # Arguements
243    /// - 'title' - the title
244    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        //map nodes to raw pointer to borrowed twice
260        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        //map nodes to raw pointer to borrowed twice
273        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                // SAFETY: we are using raw pointers here because we guarantee
278                // that the nodes vector will not be modified (no adding/removing nodes)
279                // during this iteration instead that is needs to be handled through a queue system
280                (*ui).render(&mut self.context)
281            }
282        }
283    }
284}