#![deny(clippy::all, clippy::pedantic)] use std::collections::BTreeMap; use glfw::{Window, WindowBuilder}; use crate::object::{Id as ObjectId, Object}; use crate::vector::Vec2; mod matrix; mod opengl; mod renderer; mod transform; pub mod color; pub mod object; pub mod vector; pub mod vertex; pub use glfw::WindowSize; #[derive(Debug)] pub struct Engine { /// Objects have to be dropped before window. Otherwise, UB. objects: BTreeMap, window: Window, } impl Engine { /// Creates and initializes a new engine. /// /// # Errors /// Will return `Err` if window creation or window configuration fails. pub fn new(window_size: &WindowSize, window_title: &str) -> Result { let window_builder = WindowBuilder::new(); #[cfg(feature = "debug")] let window_builder = window_builder.hint(glfw::window::Hint::OpenGLDebugContext, 1); let window = window_builder .create(window_size, window_title) .map_err(Error::CreateWindowFailed)?; window .make_context_current() .map_err(Error::ConfigureWindowFailed)?; window.set_framebuffer_size_callback(move |new_window_size| { crate::renderer::set_viewport(&Vec2::ZERO, &new_window_size); }); crate::renderer::initialize(&window).map_err(Error::InitializeRendererFailed)?; Ok(Self { window, objects: BTreeMap::new(), }) } pub fn add_object(&mut self, object: Object) { self.objects.insert(object.id(), object); } pub fn set_objecs(&mut self, objects: impl IntoIterator) { self.objects .extend(objects.into_iter().map(|object| (object.id(), object))); } #[must_use] pub fn get_object_by_id(&self, id: ObjectId) -> Option<&Object> { self.objects.get(&id) } #[must_use] pub fn get_object_by_id_mut(&mut self, id: ObjectId) -> Option<&mut Object> { self.objects.get_mut(&id) } /// Starts the engine. /// /// # Errors /// Will return `Err` if updating the window fails. pub fn start(&mut self, mut func: impl FnMut(&mut Self)) -> Result<(), Error> { while !self.window.should_close() { func(self); crate::renderer::render(self.objects.values()); self.window .swap_buffers() .map_err(Error::UpdateWindowFailed)?; self.window .poll_events() .map_err(Error::UpdateWindowFailed)?; } Ok(()) } } /// Engine Error #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Failed to create window")] CreateWindowFailed(#[source] glfw::Error), #[error("Failed to configure window")] ConfigureWindowFailed(#[source] glfw::Error), #[error("Failed to initialize renderer")] InitializeRendererFailed(#[source] renderer::Error), #[error("Failed to update window")] UpdateWindowFailed(#[source] glfw::Error), }